summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFalko <git@habales.de>2019-01-05 13:05:17 +0100
committerGitHub <noreply@github.com>2019-01-05 13:05:17 +0100
commit73ce903a49d7afe8ef41985bc98d94359823eb9e (patch)
treea4c4d6e9d54581e451c0acb9d90de6877a3ad546
parent90cc07c9bb7335e5b8a2e4035f11f111d0af76cc (diff)
parent8252f6a41d76e5ed9899d5a1d6ac926d77a4bc9b (diff)
downloadAntennaPod-73ce903a49d7afe8ef41985bc98d94359823eb9e.zip
Merge pull request #1 from AntennaPod/develop
upstream
-rw-r--r--.circleci/config.yml36
-rw-r--r--.classpath9
-rw-r--r--.project33
-rw-r--r--.travis.yml17
-rw-r--r--.tx/config2
-rw-r--r--CHANGELOG.md32
-rw-r--r--CONTRIBUTORS125
-rw-r--r--README.md2
-rw-r--r--app/build.gradle99
-rw-r--r--app/proguard.cfg13
-rw-r--r--app/sampledata/episodes.json34
-rw-r--r--app/sampledata/inplaylist2
-rw-r--r--app/sampledata/secondaryaction3
-rw-r--r--app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java18
-rw-r--r--app/src/androidTest/java/de/test/antennapod/NthMatcher.java38
-rw-r--r--app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java38
-rw-r--r--app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java46
-rw-r--r--app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java32
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java16
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java9
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java3
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java204
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java216
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java285
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java35
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java11
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/URIUtilTest.java1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java1
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java152
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/FeedGenerator.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java3
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java8
-rw-r--r--app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java2
-rw-r--r--app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java2
-rw-r--r--app/src/main/AndroidManifest.xml43
-rw-r--r--app/src/main/assets/LICENSE_SIL.txt91
-rwxr-xr-xapp/src/main/assets/logo.pngbin58799 -> 60183 bytes
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java154
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java53
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java299
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java364
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java190
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java130
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java249
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java91
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java158
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java17
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java95
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java97
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java50
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java38
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java182
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java84
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java43
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java113
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/CoverTarget.java58
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java27
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java43
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java40
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java51
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java23
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java45
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java110
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java14
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java114
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java63
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java53
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java16
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java124
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java109
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java122
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java35
-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.java97
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java2
-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.java75
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java48
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java107
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java757
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java37
-rw-r--r--app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java245
-rw-r--r--app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java5
-rw-r--r--app/src/main/play/ar/listing/fulldescription2
-rw-r--r--app/src/main/play/bg/listing/fulldescription43
-rw-r--r--app/src/main/play/ca/listing/fulldescription68
-rw-r--r--app/src/main/play/ca/listing/video0
-rw-r--r--app/src/main/play/contactPhone0
-rw-r--r--app/src/main/play/cs-CZ/listing/video0
-rw-r--r--app/src/main/play/da-DK/listing/video0
-rw-r--r--app/src/main/play/de-DE/listing/video0
-rw-r--r--app/src/main/play/de/listing/fulldescription4
-rw-r--r--app/src/main/play/el/listing/fulldescription26
-rw-r--r--app/src/main/play/en-US/listing/featureGraphic/feature-graphic.pngbin0 -> 60183 bytes
-rw-r--r--app/src/main/play/en-US/listing/video0
-rw-r--r--app/src/main/play/es-ES/listing/video0
-rw-r--r--app/src/main/play/es/listing/fulldescription2
-rw-r--r--app/src/main/play/fa/listing/fulldescription4
-rw-r--r--app/src/main/play/fr-FR/listing/video0
-rw-r--r--app/src/main/play/he_IL/listing/fulldescription62
-rw-r--r--app/src/main/play/hi-IN/listing/video0
-rw-r--r--app/src/main/play/it-IT/listing/video0
-rw-r--r--app/src/main/play/it/listing/fulldescription38
-rw-r--r--app/src/main/play/it_IT/listing/fulldescription24
-rw-r--r--app/src/main/play/iw-IL/listing/video0
-rw-r--r--app/src/main/play/ja-JP/listing/video0
-rw-r--r--app/src/main/play/ko-KR/listing/video0
-rw-r--r--app/src/main/play/ms_MY/listing/fulldescription43
-rw-r--r--app/src/main/play/nb_NO/listing/fulldescription8
-rw-r--r--app/src/main/play/nl-NL/listing/video0
-rw-r--r--app/src/main/play/pl-PL/listing/video0
-rw-r--r--app/src/main/play/pt-BR/listing/video0
-rw-r--r--app/src/main/play/pt-PT/listing/video0
-rw-r--r--app/src/main/play/ro/listing/video0
-rw-r--r--app/src/main/play/ru-RU/listing/video0
-rw-r--r--app/src/main/play/sl_SI/listing/fulldescription43
-rw-r--r--app/src/main/play/sv-SE/listing/video0
-rw-r--r--app/src/main/play/tr-TR/listing/video0
-rw-r--r--app/src/main/play/uk/listing/video0
-rw-r--r--app/src/main/play/vi/listing/fulldescription8
-rw-r--r--app/src/main/play/zh-CN/listing/video0
-rw-r--r--app/src/main/res/layout-v14/authentication_dialog.xml87
-rw-r--r--app/src/main/res/layout-v14/directory_chooser.xml114
-rw-r--r--app/src/main/res/layout-v14/download_authentication_activity.xml98
-rw-r--r--app/src/main/res/layout-v14/opml_selection.xml61
-rw-r--r--app/src/main/res/layout-v14/time_dialog.xml67
-rw-r--r--app/src/main/res/layout/about.xml4
-rw-r--r--app/src/main/res/layout/all_episodes_fragment.xml8
-rw-r--r--app/src/main/res/layout/audio_controls.xml7
-rw-r--r--app/src/main/res/layout/authentication_dialog.xml61
-rw-r--r--app/src/main/res/layout/cover_fragment.xml3
-rw-r--r--app/src/main/res/layout/directory_chooser.xml67
-rw-r--r--app/src/main/res/layout/download_authentication_activity.xml55
-rw-r--r--app/src/main/res/layout/downloaded_episodeslist_item.xml89
-rw-r--r--app/src/main/res/layout/downloadlist_item.xml7
-rw-r--r--app/src/main/res/layout/downloadlog_item.xml17
-rw-r--r--app/src/main/res/layout/ellipsize_start_listitem.xml21
-rw-r--r--app/src/main/res/layout/external_player_fragment.xml152
-rw-r--r--app/src/main/res/layout/feedinfo.xml250
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml10
-rw-r--r--app/src/main/res/layout/feeditemlist_header.xml35
-rw-r--r--app/src/main/res/layout/feeditemlist_item.xml30
-rw-r--r--app/src/main/res/layout/feedsettings.xml218
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_listitem.xml7
-rw-r--r--app/src/main/res/layout/gpodnet_tag_listitem.xml3
-rw-r--r--app/src/main/res/layout/gpodnetauth_credentials.xml6
-rw-r--r--app/src/main/res/layout/gpodnetauth_device.xml17
-rw-r--r--app/src/main/res/layout/import_export_activity.xml34
-rw-r--r--app/src/main/res/layout/itemdescription_listitem.xml4
-rw-r--r--app/src/main/res/layout/itunes_podcast_listitem.xml3
-rw-r--r--app/src/main/res/layout/main.xml2
-rw-r--r--app/src/main/res/layout/mediaplayerinfo_activity.xml27
-rw-r--r--app/src/main/res/layout/nav_feedlistitem.xml12
-rw-r--r--app/src/main/res/layout/nav_list.xml9
-rw-r--r--app/src/main/res/layout/nav_listitem.xml8
-rw-r--r--app/src/main/res/layout/new_episodes_listitem.xml48
-rw-r--r--app/src/main/res/layout/numberpicker.xml16
-rw-r--r--app/src/main/res/layout/onlinefeedview_header.xml10
-rw-r--r--app/src/main/res/layout/opml_import.xml2
-rw-r--r--app/src/main/res/layout/opml_selection.xml54
-rw-r--r--app/src/main/res/layout/preference_switch_layout.xml9
-rw-r--r--app/src/main/res/layout/queue_listitem.xml22
-rw-r--r--app/src/main/res/layout/searchlist_item.xml14
-rw-r--r--app/src/main/res/layout/secondary_action.xml3
-rw-r--r--app/src/main/res/layout/simplechapter_item.xml5
-rw-r--r--app/src/main/res/layout/splash.xml14
-rw-r--r--app/src/main/res/layout/statistics_listitem.xml10
-rw-r--r--app/src/main/res/layout/subscription_item.xml2
-rw-r--r--app/src/main/res/layout/time_dialog.xml9
-rw-r--r--app/src/main/res/layout/videoplayer_activity.xml14
-rw-r--r--app/src/main/res/menu/allepisodes_context.xml6
-rw-r--r--app/src/main/res/menu/downloads_completed.xml3
-rw-r--r--app/src/main/res/menu/feedlist.xml3
-rw-r--r--app/src/main/res/menu/mediaplayer.xml8
-rw-r--r--app/src/main/res/menu/queue.xml19
-rw-r--r--app/src/main/res/xml/preferences.xml361
-rw-r--r--app/src/main/res/xml/preferences_autodownload.xml41
-rw-r--r--app/src/main/res/xml/preferences_flattr.xml21
-rw-r--r--app/src/main/res/xml/preferences_gpodder.xml35
-rw-r--r--app/src/main/res/xml/preferences_integrations.xml16
-rw-r--r--app/src/main/res/xml/preferences_network.xml42
-rw-r--r--app/src/main/res/xml/preferences_playback.xml130
-rw-r--r--app/src/main/res/xml/preferences_storage.xml43
-rw-r--r--app/src/main/res/xml/preferences_user_interface.xml70
-rw-r--r--app/src/main/templates/about.html100
-rw-r--r--app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java30
-rw-r--r--artwork/feature-graphic.svg164
-rw-r--r--artwork/ic_launcher.svg75
-rw-r--r--build.gradle49
-rw-r--r--ci/wait_for_emulator.sh17
-rw-r--r--circle.yml22
-rwxr-xr-xcontributers.template.py11
-rw-r--r--core/build.gradle68
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedImageMother.java9
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/tests/AntennaPodTestRunner.java15
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/service/download/DownloadServiceTest.java35
-rw-r--r--core/src/androidTest/java/de/danoeh/antennapod/core/util/DateUtilsTest.java (renamed from core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java)41
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java2
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java4
-rw-r--r--core/src/main/AndroidManifest.xml15
-rw-r--r--core/src/main/java/android/support/v4/app/SafeJobIntentService.java118
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java43
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/DBTaskLoader.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrStatusFetcher.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrTokenFetcher.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java37
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedEvent.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java92
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java64
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java39
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java35
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java27
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java17
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceBadStatusCodeException.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceException.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java195
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java (renamed from app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java)43
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateJobService.java35
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java128
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java180
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java167
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/Downloader.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderCallback.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java250
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java175
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java399
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java169
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java190
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java293
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java108
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java40
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemStatistics.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java628
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java23
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java28
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java18
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/AtomText.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java309
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Consumer.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Converter.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/EpisodeFilter.java50
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java31
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FileNameGenerator.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Function.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/IntList.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java113
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Permutor.java17
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java109
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/StorageUtils.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java185
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/exception/MediaFileNotFoundException.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrServiceCreator.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrStatus.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrThing.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java50
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/flattr/SimpleFlattrThing.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java65
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/gui/PictureInPictureUtil.java27
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java23
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ID3Reader.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/MediaPlayerError.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java22
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java145
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java78
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java29
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java38
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java14
-rw-r--r--core/src/main/res/drawable-hdpi-v11/ic_stat_antenna_default.pngbin541 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-hdpi-v11/ic_stat_authentication.pngbin371 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.pngbin0 -> 187 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_bug_grey600_24dp.pngbin0 -> 813 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_bug_white_24dp.pngbin0 -> 653 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cellphone_text_grey600_24dp.pngbin0 -> 649 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_cellphone_text_white_24dp.pngbin0 -> 592 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.pngbin0 -> 828 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_white_24dp.pngbin0 -> 635 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.pngbin0 -> 492 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.pngbin0 -> 459 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.pngbin0 -> 601 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_launcher.pngbin3887 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_poll_box_grey600_24dp.pngbin0 -> 608 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_poll_box_white_24dp.pngbin0 -> 558 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_sd_grey600_24dp.pngbin0 -> 533 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_sd_white_24dp.pngbin0 -> 472 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_stat_antenna_default.pngbin473 -> 541 bytes
-rw-r--r--[-rwxr-xr-x]core/src/main/res/drawable-hdpi/ic_stat_authentication.pngbin446 -> 371 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_swap_vertical_grey600_24dp.pngbin0 -> 581 bytes
-rw-r--r--core/src/main/res/drawable-hdpi/ic_swap_vertical_white_24dp.pngbin0 -> 510 bytes
-rw-r--r--core/src/main/res/drawable-ldpi-v11/ic_stat_antenna_default.pngbin259 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-ldpi/ic_launcher.pngbin1645 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-ldpi/ic_stat_antenna_default.pngbin202 -> 259 bytes
-rw-r--r--core/src/main/res/drawable-mdpi-v11/ic_stat_antenna_default.pngbin349 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-mdpi-v11/ic_stat_authentication.pngbin244 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.pngbin0 -> 139 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_bug_grey600_24dp.pngbin0 -> 663 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_bug_white_24dp.pngbin0 -> 529 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cellphone_text_grey600_24dp.pngbin0 -> 471 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_cellphone_text_white_24dp.pngbin0 -> 440 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.pngbin0 -> 619 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_white_24dp.pngbin0 -> 534 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.pngbin0 -> 423 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.pngbin0 -> 406 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.pngbin0 -> 488 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_launcher.pngbin2359 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_poll_box_grey600_24dp.pngbin0 -> 463 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_poll_box_white_24dp.pngbin0 -> 429 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_sd_grey600_24dp.pngbin0 -> 456 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_sd_white_24dp.pngbin0 -> 417 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_stat_antenna_default.pngbin297 -> 349 bytes
-rw-r--r--[-rwxr-xr-x]core/src/main/res/drawable-mdpi/ic_stat_authentication.pngbin317 -> 244 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_swap_vertical_grey600_24dp.pngbin0 -> 484 bytes
-rw-r--r--core/src/main/res/drawable-mdpi/ic_swap_vertical_white_24dp.pngbin0 -> 421 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi-v11/ic_stat_antenna_default.pngbin783 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-xhdpi-v11/ic_stat_authentication.pngbin418 -> 0 bytes
-rwxr-xr-xcore/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.pngbin0 -> 212 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_bug_grey600_24dp.pngbin0 -> 960 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_bug_white_24dp.pngbin0 -> 710 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cellphone_text_grey600_24dp.pngbin0 -> 614 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_cellphone_text_white_24dp.pngbin0 -> 564 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.pngbin0 -> 862 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_white_24dp.pngbin0 -> 738 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.pngbin0 -> 504 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.pngbin0 -> 462 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.pngbin0 -> 602 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_launcher.pngbin5519 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_poll_box_grey600_24dp.pngbin0 -> 592 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_poll_box_white_24dp.pngbin0 -> 565 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_sd_grey600_24dp.pngbin0 -> 580 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_sd_white_24dp.pngbin0 -> 535 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.pngbin693 -> 783 bytes
-rw-r--r--[-rwxr-xr-x]core/src/main/res/drawable-xhdpi/ic_stat_authentication.pngbin584 -> 418 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_swap_vertical_grey600_24dp.pngbin0 -> 569 bytes
-rw-r--r--core/src/main/res/drawable-xhdpi/ic_swap_vertical_white_24dp.pngbin0 -> 496 bytes
-rwxr-xr-xcore/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.pngbin0 -> 276 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_bug_grey600_24dp.pngbin0 -> 1270 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_bug_white_24dp.pngbin0 -> 926 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cellphone_text_grey600_24dp.pngbin0 -> 801 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_cellphone_text_white_24dp.pngbin0 -> 739 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.pngbin0 -> 1155 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.pngbin0 -> 965 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.pngbin0 -> 631 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.pngbin0 -> 594 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.pngbin0 -> 766 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_launcher.pngbin9483 -> 0 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_poll_box_grey600_24dp.pngbin0 -> 787 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_poll_box_white_24dp.pngbin0 -> 704 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_sd_grey600_24dp.pngbin0 -> 791 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_sd_white_24dp.pngbin0 -> 684 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_swap_vertical_grey600_24dp.pngbin0 -> 710 bytes
-rw-r--r--core/src/main/res/drawable-xxhdpi/ic_swap_vertical_white_24dp.pngbin0 -> 582 bytes
-rwxr-xr-xcore/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.pngbin0 -> 310 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_bug_grey600_24dp.pngbin0 -> 1577 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_bug_white_24dp.pngbin0 -> 1180 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_grey600_24dp.pngbin0 -> 971 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_white_24dp.pngbin0 -> 902 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.pngbin0 -> 1366 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.pngbin0 -> 1200 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.pngbin0 -> 738 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.pngbin0 -> 666 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.pngbin0 -> 900 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_poll_box_grey600_24dp.pngbin0 -> 918 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_poll_box_white_24dp.pngbin0 -> 848 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_sd_grey600_24dp.pngbin0 -> 925 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_sd_white_24dp.pngbin0 -> 842 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_grey600_24dp.pngbin0 -> 818 bytes
-rw-r--r--core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_white_24dp.pngbin0 -> 664 bytes
-rw-r--r--core/src/main/res/drawable/bg_splash.xml4
-rw-r--r--core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml15
-rw-r--r--core/src/main/res/drawable/progress_bar_horizontal_trueblack.xml15
-rw-r--r--core/src/main/res/layout/player_widget.xml (renamed from app/src/main/res/layout/player_widget.xml)2
-rw-r--r--core/src/main/res/mipmap-anydpi-v26/ic_launcher.xml5
-rw-r--r--core/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml5
-rw-r--r--core/src/main/res/mipmap-hdpi/ic_launcher.pngbin0 -> 4628 bytes
-rw-r--r--core/src/main/res/mipmap-hdpi/ic_launcher_foreground.pngbin0 -> 4876 bytes
-rw-r--r--core/src/main/res/mipmap-hdpi/ic_launcher_round.pngbin0 -> 4628 bytes
-rw-r--r--core/src/main/res/mipmap-mdpi/ic_launcher.pngbin0 -> 2752 bytes
-rw-r--r--core/src/main/res/mipmap-mdpi/ic_launcher_foreground.pngbin0 -> 2884 bytes
-rw-r--r--core/src/main/res/mipmap-mdpi/ic_launcher_round.pngbin0 -> 2752 bytes
-rw-r--r--core/src/main/res/mipmap-xhdpi/ic_launcher.pngbin0 -> 6709 bytes
-rw-r--r--core/src/main/res/mipmap-xhdpi/ic_launcher_foreground.pngbin0 -> 6980 bytes
-rw-r--r--core/src/main/res/mipmap-xhdpi/ic_launcher_round.pngbin0 -> 6709 bytes
-rw-r--r--core/src/main/res/mipmap-xxhdpi/ic_launcher.pngbin0 -> 10809 bytes
-rw-r--r--core/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.pngbin0 -> 12641 bytes
-rw-r--r--core/src/main/res/mipmap-xxhdpi/ic_launcher_round.pngbin0 -> 10809 bytes
-rw-r--r--core/src/main/res/mipmap-xxxhdpi/ic_launcher.pngbin0 -> 15604 bytes
-rw-r--r--core/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.pngbin0 -> 19600 bytes
-rw-r--r--core/src/main/res/mipmap-xxxhdpi/ic_launcher_round.pngbin0 -> 15604 bytes
-rw-r--r--core/src/main/res/values-ar/strings.xml73
-rw-r--r--core/src/main/res/values-az/strings.xml3
-rw-r--r--core/src/main/res/values-b+ast/strings.xml5
-rw-r--r--core/src/main/res/values-bg/strings.xml256
-rw-r--r--core/src/main/res/values-ca-rES/strings.xml3
-rw-r--r--core/src/main/res/values-ca/strings.xml27
-rw-r--r--core/src/main/res/values-cs-rCZ/strings.xml13
-rw-r--r--core/src/main/res/values-da/strings.xml17
-rw-r--r--core/src/main/res/values-de/strings.xml88
-rw-r--r--core/src/main/res/values-el/strings.xml50
-rw-r--r--core/src/main/res/values-es-rES/strings.xml4
-rw-r--r--core/src/main/res/values-es/strings.xml74
-rw-r--r--core/src/main/res/values-et/strings.xml21
-rw-r--r--core/src/main/res/values-fa/strings.xml167
-rw-r--r--core/src/main/res/values-fi/strings.xml2
-rw-r--r--core/src/main/res/values-fr/strings.xml142
-rw-r--r--core/src/main/res/values-gl-rES/strings.xml68
-rw-r--r--core/src/main/res/values-hi-rIN/strings.xml5
-rw-r--r--core/src/main/res/values-hu/strings.xml207
-rw-r--r--core/src/main/res/values-id/strings.xml3
-rw-r--r--core/src/main/res/values-is-rIS/strings.xml2
-rw-r--r--core/src/main/res/values-it-rIT/strings.xml314
-rw-r--r--core/src/main/res/values-it/strings.xml280
-rw-r--r--core/src/main/res/values-iw-rIL/strings.xml727
-rw-r--r--core/src/main/res/values-ja/strings.xml64
-rw-r--r--core/src/main/res/values-kn-rIN/strings.xml2
-rw-r--r--core/src/main/res/values-ko-rKR/strings.xml2
-rw-r--r--core/src/main/res/values-ko/strings.xml28
-rw-r--r--core/src/main/res/values-lt/strings.xml32
-rw-r--r--core/src/main/res/values-mk/strings.xml54
-rw-r--r--core/src/main/res/values-nb/strings.xml7
-rw-r--r--core/src/main/res/values-nl/strings.xml88
-rw-r--r--core/src/main/res/values-no-rNB/strings.xml122
-rw-r--r--core/src/main/res/values-no/strings.xml2
-rw-r--r--core/src/main/res/values-pl-rPL/strings.xml30
-rw-r--r--core/src/main/res/values-pl/strings.xml3
-rw-r--r--core/src/main/res/values-pt-rBR/strings.xml10
-rw-r--r--core/src/main/res/values-pt/strings.xml66
-rw-r--r--core/src/main/res/values-ro-rRO/strings.xml25
-rw-r--r--core/src/main/res/values-ru/strings.xml71
-rw-r--r--core/src/main/res/values-sv-rSE/strings.xml64
-rw-r--r--core/src/main/res/values-sw-rKE/strings.xml2
-rw-r--r--core/src/main/res/values-te/strings.xml50
-rw-r--r--core/src/main/res/values-tr/strings.xml7
-rw-r--r--core/src/main/res/values-uk-rUA/strings.xml33
-rw-r--r--core/src/main/res/values-v11/colors.xml5
-rw-r--r--core/src/main/res/values-v14/dimens.xml5
-rw-r--r--core/src/main/res/values-v14/styles.xml9
-rw-r--r--core/src/main/res/values-v21/styles.xml19
-rw-r--r--core/src/main/res/values-vi-rVN/strings.xml2
-rw-r--r--core/src/main/res/values-vi/strings.xml4
-rw-r--r--core/src/main/res/values-zh-rCN/strings.xml14
-rw-r--r--core/src/main/res/values-zh-rHK/strings.xml2
-rw-r--r--core/src/main/res/values-zh-rTW/strings.xml283
-rw-r--r--core/src/main/res/values/arrays.xml81
-rw-r--r--core/src/main/res/values/attrs.xml15
-rw-r--r--core/src/main/res/values/colors.xml11
-rw-r--r--core/src/main/res/values/dimens.xml7
-rw-r--r--core/src/main/res/values/ic_launcher_background.xml4
-rw-r--r--core/src/main/res/values/strings.xml115
-rw-r--r--core/src/main/res/values/styles.xml545
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java1
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java6
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java9
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/RemoteMedia.java13
-rw-r--r--core/src/test/java/android/text/TextUtils.java32
-rw-r--r--core/src/test/java/android/util/Log.java245
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java (renamed from core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedItemMother.java)4
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java (renamed from core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedItemTest.java)45
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java (renamed from core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedMother.java)8
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java (renamed from core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedTest.java)42
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/LongLongMapTest.java (renamed from core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/LongLongMapTest.java)12
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin54708 -> 56177 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--ic_launcher-web.pngbin40025 -> 53849 bytes
-rw-r--r--project.properties13
603 files changed, 13324 insertions, 8248 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
new file mode 100644
index 000000000..bec559ed2
--- /dev/null
+++ b/.circleci/config.yml
@@ -0,0 +1,36 @@
+version: 2
+
+jobs:
+ build:
+ docker:
+ - image: circleci/android:api-26-alpha
+
+ working_directory: ~/AntennaPod
+
+ environment:
+ GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx1536m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError"'
+ _JAVA_OPTIONS: "-Xms256m -Xmx1280m"
+
+ steps:
+ - checkout
+
+ - restore_cache:
+ keys:
+ - v1-android-{{ checksum "build.gradle" }}
+ # fallback to using the latest cache if no exact match is found
+ - v1-android-
+
+ - run:
+ command: ./gradlew assembleDebug :core:testPlayDebugUnitTest -PdisablePreDex
+ no_output_timeout: 1800
+
+ - store_artifacts:
+ path: app/build/outputs/apk
+ destination: apks
+
+ - save_cache:
+ paths:
+ - ~/.android
+ - ~/.gradle
+ - ~/android
+ key: v1-android-{{ checksum "build.gradle" }}
diff --git a/.classpath b/.classpath
deleted file mode 100644
index 3a0c88fe4..000000000
--- a/.classpath
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.LIBRARIES"/>
- <classpathentry kind="src" path="src"/>
- <classpathentry kind="src" path="gen"/>
- <classpathentry kind="con" path="com.android.ide.eclipse.adt.DEPENDENCIES"/>
- <classpathentry kind="output" path="bin/classes"/>
-</classpath>
diff --git a/.project b/.project
deleted file mode 100644
index b3b010772..000000000
--- a/.project
+++ /dev/null
@@ -1,33 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
- <name>AntennaPod</name>
- <comment></comment>
- <projects>
- </projects>
- <buildSpec>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>org.eclipse.jdt.core.javabuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- <buildCommand>
- <name>com.android.ide.eclipse.adt.ApkBuilder</name>
- <arguments>
- </arguments>
- </buildCommand>
- </buildSpec>
- <natures>
- <nature>com.android.ide.eclipse.adt.AndroidNature</nature>
- <nature>org.eclipse.jdt.core.javanature</nature>
- </natures>
-</projectDescription>
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 6677e0cd1..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,17 +0,0 @@
-language: android
-jdk: oraclejdk7
-
-env:
- matrix:
- - ANDROID_SDKS=android-19,sysimg-19 ANDROID_TARGET=android-19 ANDROID_ABI=armeabi-v7a
-before_install:
- - echo no | android create avd --force -n test -t $ANDROID_TARGET --abi $ANDROID_ABI
- - emulator -avd test -no-skin -no-audio -no-window &
- - cp src/de/danoeh/antennapod/util/flattr/FlattrConfig.java.example src/de/danoeh/antennapod/util/flattr/FlattrConfig.java
-
-before_script:
- - chmod -R 777 ./ci/wait_for_emulator.sh
- - ./ci/wait_for_emulator.sh
-
-script:
- - gradle connectedAndroidTest
diff --git a/.tx/config b/.tx/config
index 5baf71152..5b9066e64 100644
--- a/.tx/config
+++ b/.tx/config
@@ -7,6 +7,7 @@ source_lang = en
trans.ast_ES = core/src/main/res/values-b+ast/strings.xml
trans.ar = core/src/main/res/values-ar/strings.xml
trans.az = core/src/main/res/values-az/strings.xml
+trans.bg = core/src/main/res/values-bg/strings.xml
trans.ca = core/src/main/res/values-ca/strings.xml
trans.ca_ES = core/src/main/res/values-ca-rES/strings.xml
trans.cs_CZ = core/src/main/res/values-cs-rCZ/strings.xml
@@ -32,6 +33,7 @@ trans.kn_IN = core/src/main/res/values-kn-rIN/strings.xml
trans.ko = core/src/main/res/values-ko/strings.xml
trans.ko_KR = core/src/main/res/values-ko-rKR/strings.xml
trans.lt = core/src/main/res/values-lt/strings.xml
+trans.mk = core/src/main/res/values-mk/strings.xml
trans.nb = core/src/main/res/values-nb/strings.xml
trans.no = core/src/main/res/values-no/strings.xml
trans.nl = core/src/main/res/values-nl/strings.xml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ef72e5a45..2899d1fd2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,38 @@
Change Log
==========
+
+Version 1.7.1
+-------------
+
+* Fix for database corruption
+
+Version 1.7.0
+-------------
+
+* NEW ExoPlayer (experimental)
+* Fix for Bluetooth Forward (Oreo)
+* Preference redesign + search
+* Notification improvements
+* Different screens for feed info and settings
+* Sort Queue with Random or Smart Shuffle
+* True Black Theme for AMOLED
+* Improvements to feed parsing
+* Fix for app being killed by Android Oreo
+
+Version 1.6.5
+-------------
+
+* Fix database corruption
+* Improvements to Feed parsing
+
+Version 1.6.4
+-------------
+
+* Fixes issues on Android Oreo
+* Avoids duplicate chapters
+* Experimental: Database import & export
+
Version 1.6.3
-------------
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
index 837c412c5..891a9784c 100644
--- a/CONTRIBUTORS
+++ b/CONTRIBUTORS
@@ -1,107 +1,168 @@
DEVELOPERS
==========
+Alan Orth
Alexander Terczka
+alifeflow
+amhokies
+Anders Bo Rasmussen
Andrew Gaul
Andrey Krutov
Anthony Lieuallen
axq
+brad
ByteHamster
+Cameron Banga
Christian Ludwig
Christopher Szucko
+Cj Malone
Colin Willson
+Cédric Cabessa
+Danial Klimkin
Daniel Oeh
David Carver
David Reiss
Dean Brettle
dethstar
+Dirk Mueller
Domingos Lopes
drabux
+egsavage
+EirikV
Eoin Mcloughlin
+eraymond
Ercan Erden
+Falko Lehmann
+Hannes Achleitner
+hannesa2
Hanno Zulla
heckler01
Holger Jeromin
+Humberto Fraga
+InsidE
James Falcon
+Jan Niehusmann
+Jens Klingenberg
Jens Müller
Johan Liesén
+Kaligule
Katrin Leinweber
-keunes
+Kevin Dalley
Koen Glotzbach
+kroegerama
+Kurian Vithayathil
LatinSuD
Lee Yeong Khang
lightonflux
ligi
+Luis Cruz
+Marc Lasson
Martin Fietz
Martin Olsson
mat tso
+mateoeh
+Matthew Gaffen
Matthias Schütz
+Maurice Gilden
+Meir Schwarz
Michael Kaiser
Michael Scarito
Mike Chelen
+minusf
+MolarAmbiguity
Mounir Lamouri
+mr-intj
Nis Wechselberg
+Oliver Crow
+orelogo
Paul Ortyl
+Raghul
+Raghul Jagannathan
recalculated
Ross Harrison
+Sam Lee
Sam Whited
+saqura
+Selivanov Pavel
+Serge
Seth Golub
+sevenmaster
+Shantana Hardy
Simon Danner
Simon Rutishauser
Simon Schubert
+Soso Tughushi
+Spencer Visick
Stefan Mitrik
Terence Eden
Tim Butram
Tobias Preuss
Tom Hennen
+Tom Tom
tommy watson
+tuxayo
twiceyuan
Udi Finkelstein
+VarunBarad
volhol
Volker Hollich
WangYun
William Seemann
+ydinath
TRANSLATORS
===========
-Arabic: abuzar3.khalid, iDemo
-Azerbaijani: phoenixar
-Catalan: dvd1985, javiercoll, xc70
-Catalan (Spain): javiercoll
-Chinese (China): bebeauties38, dudeG, Felix2yu, gaohongyuan, Guaidaodl, linxiangyu, molisiye, tupunco, wongsyrone, yangyang, YogaGuru
+Arabic: abdelrahman.fahem93, abdunnasir, abuzar3.khalid, desha, iDemo, mohamedagamy, msahouli, nabilMaghura
+Asturian (Spain): enolp
+Azerbaijani: danieloeh, kotfenix
+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, jhxie, kavdx, kyleehee, linxiangyu, molisiye, owen8877, RainSlide, stellaxuyi, tupunco, wi24rd, wongsyrone, xukeek, yangyang, yiqiok, YogaGuru
+Chinese (Taiwan): gugod, nigelinux, pggdt, ymhuang0808
Czech (Czech Republic): elich, Hanzmeister, mcepl, petnek, svetlemodry
-Danish: CasperHN
-Dutch: e2jk, glotzbach, rwv
+Danish: CasperHN, jhertel
+Dutch: e2jk, glotzbach, rwv, Vistaus
English: mfietz, sterylmreep
+Estonian: Eraser
Finnish: danieloeh
-French: cactux, ChaoticMind, clombion, e2jk, lacouture, Matth78, mfietz, repat, sterylmreep, vcariven
-German: 112358, altegedanken, bitsunited, ChaoticMind, Chaquotay, dab0015, DJaeger, HolgerJeromin, kalei, lohmann, mfietz, nilso, picsel2, repat, SAPlayer, schafia, ypid
-Greek: AlexanderKanetakis
-Hebrew (Israel): amir.dafnyman
-Hindi (India): purple.coder, siddhusengar
-Hungarian: glatz.balazs, naren93
-Indonesian: luke137, silvanael16
-Italian: aalex70, apanontin, Guybrush88, theloca95
-Italian (Italy): aalex70, apanontin, Guybrush88, m.chinni, theloca95
+French: cactux, ChaoticMind, clombion, e2jk, lacouture, Matth78, mfietz, Poussinou, PRIMOKORN, repat, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
+Galician: antiparvos, pikamoku, Raichely
+German: 112358, altegedanken, barilla, bitsunited, Buggi, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, 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): nmabhinandan, purple.coder, siddhusengar
+Hungarian: glatz.balazs, lna91, naren93, tszauer, ttyborg42
+Icelandic: marthjod
+Indonesian: jff, 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, nixxo, sevenmaster, theloca95
Japanese: Naofumi, RACER1, sh3llc4t, TranslatorG
Kannada (India): thejeshgn
-Korean: changwoo, halcyonest, seungrye, skcha
+Korean: changwoo, seungrye, skcha
Korean (South Korea): changwoo, seungrye
+Lithuanian: naglis
+Macedonian: krisfremen
Norwegian: hakonanes, timbast
-Norwegian Bokmål: hakonanes
-Norwegian Bokmål (Norway): hakonanes, kongk, swordfighter, timbast
-Polish: Iwangelion, maniexx, thedead4fun
-Polish (Poland): Iwangelion, lomapur, maniexx, Mephistofeles, shark103, tyle
-Portuguese: emansije, smarquespt
-Portuguese (Brazil): alexupits, edman, Firmino, lipefire, lucasmotacr, mbaltar, rogervezaro, SamWilliam, silvanael16
-Romanian (Romania): corneliu.e, fuzzmz
-Russian (Russia): astra1, Duke_Raven, mercutiy, null, overmind88, phoenixar, s.chebotar, skvheadless, whereisthetea, zhenya97
-Spanish: coperfix, dvd1985, Fitoschido, frandavid100, javiercoll, LatinSuD, tres.14159
-Spanish (Spain): dvd1985, e2jk, frandavid100
-Swedish (Sweden): albin.brantin, Bio, bpnilsson, ChaoticMind, Lumen, nilso, SharpMelon, TwoD
-Turkish: basarancaner, brsata, overbite
-Ukrainian (Ukraine): older, zhenya97
-Vietnamese: ppanhh, vietnamesel10n
+Norwegian Bokmål: corkie, hakonanes
+Norwegian Bokmål (Norway): corkie, hakonanes, kongk, timbast
+Persian: ahangarha, F7D
+Polish: Iwangelion, maniexx, mfloryan, thedead4fun
+Polish (Poland): d6210809, Iwangelion, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
+Portuguese: domingos86, emansije, smarquespt
+Portuguese (Brazil): alexupits, alysonborges, 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, GaynullinDima, MegMasters98, mercutiy, null, overmind88, s.chebotar, shams4real, skvheadless, un_logic, whereisthetea, zhenya97
+Slovenian (Slovenia): panter23
+Spanish: AleksSyntek, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
+Spanish (Spain): dvd1985, e2jk, frandavid100, hard_ware, palopezv, Raichely, TacoTheDank
+Swahili (Kenya): BonfaceKilz
+Swedish (Sweden): albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TwoD
+Telugu: veeven
+Turkish: basarancaner, brsata, Erdy, golcuk, overbite
+Ukrainian (Ukraine): older, sergiyr, zhenya97
+Vietnamese: abnvolk, nguyenvui, ppanhh, vietnamesel10n
Vietnamese (Vietnam): bizover
diff --git a/README.md b/README.md
index 23d7e06d5..b84022e1f 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ This is the official repository of AntennaPod, the easy-to-use, flexible and ope
[<img src="https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png"
alt="Get it on Google Play"
height="90">](https://play.google.com/store/apps/details?id=de.danoeh.antennapod)
-[<img src="https://f-droid.org/badge/get-it-on.png"
+[<img src="https://fdroid.gitlab.io/artwork/badge/get-it-on.png"
alt="Get it on F-Droid"
height="90">](https://f-droid.org/app/de.danoeh.antennapod)
diff --git a/app/build.gradle b/app/build.gradle
index ad388bd02..a79ad180e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,7 +1,6 @@
import org.apache.tools.ant.filters.ReplaceTokens
apply plugin: "com.android.application"
-apply plugin: "me.tatarka.retrolambda"
apply plugin: 'com.github.triplet.play'
apply plugin: 'com.getkeepsafe.dexcount'
@@ -15,7 +14,7 @@ buildscript {
jcenter()
}
dependencies {
- classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.1'
+ classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.8.2'
}
}
@@ -44,7 +43,7 @@ android {
versionCode getMyVersionCode()
versionName "${getMyVersionName()}"
testApplicationId "de.test.antennapod"
- testInstrumentationRunner "de.test.antennapod.AntennaPodTestRunner"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
generatedDensities = []
}
@@ -84,9 +83,17 @@ android {
applicationIdSuffix ".debug"
resValue "string", "provider_authority", "de.danoeh.antennapod.debug.provider"
buildConfigField STRING, FLATTR_APP_KEY, mFlattrAppKey
- buildConfigField STRING, FLATTR_APP_SECRET, mFlattrAppSecret
+ buildConfigField STRING, FLATTR_APP_SECRET, mFlattrAppSecret
+ dexcount {
+ if (project.hasProperty("enableDexcountInDebug")) {
+ runOnEachPackage enableDexcountInDebug.toBoolean()
+ } else { // default to not running dexcount
+ runOnEachPackage false
+ }
+ }
}
release {
+ resValue "string", "provider_authority", "de.danoeh.antennapod.provider"
minifyEnabled true
proguardFile "proguard.cfg"
signingConfig signingConfigs.releaseConfig
@@ -113,10 +120,17 @@ android {
additionalParameters "--no-version-vectors"
}
+ testOptions {
+ animationsDisabled = true
+ }
+
+ flavorDimensions "market"
productFlavors {
free {
+ dimension "market"
}
play {
+ dimension "market"
}
}
@@ -125,59 +139,60 @@ android {
}
}
-configurations {
- freeDebugCompile
- freeReleaseCompile
- playDebugCompile
- playReleaseCompile
-}
-
dependencies {
- freeDebugCompile project(path: ":core", configuration: "freeDebug")
- freeReleaseCompile project(path: ":core", configuration: "freeRelease")
+ freeImplementation project(":core")
// free build hack: skip some dependencies
if (!doFreeBuild()) {
- playDebugCompile project(path: ":core", configuration: "playDebug")
- playReleaseCompile project(path: ":core", configuration: "playRelease")
+ playImplementation project(":core")
} else {
System.out.println("app: free build hack, skipping some dependencies")
}
- compile "com.android.support:support-v4:$supportVersion"
- compile "com.android.support:appcompat-v7:$supportVersion"
- compile "com.android.support:design:$supportVersion"
- compile "com.android.support:gridlayout-v7:$supportVersion"
- compile "com.android.support:percent:$supportVersion"
- compile "com.android.support:recyclerview-v7:$supportVersion"
- compile "org.apache.commons:commons-lang3:$commonslangVersion"
- compile("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
+ implementation "com.android.support:support-v4:$supportVersion"
+ implementation "com.android.support:appcompat-v7:$supportVersion"
+ implementation "com.android.support:design:$supportVersion"
+ implementation "com.android.support:preference-v14:$supportVersion"
+ implementation "com.android.support:gridlayout-v7:$supportVersion"
+ implementation "com.android.support:percent:$supportVersion"
+ implementation "com.android.support:recyclerview-v7:$supportVersion"
+ compileOnly 'com.google.android.wearable:wearable:2.2.0'
+ implementation "org.apache.commons:commons-lang3:$commonslangVersion"
+ implementation("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
exclude group: "org.json", module: "json"
}
- compile "commons-io:commons-io:$commonsioVersion"
- compile "org.jsoup:jsoup:$jsoupVersion"
- compile "com.github.bumptech.glide:glide:$glideVersion"
- compile "com.squareup.okhttp3:okhttp:$okhttpVersion"
- compile "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
- compile "com.squareup.okio:okio:$okioVersion"
- compile "de.greenrobot:eventbus:$eventbusVersion"
- compile "io.reactivex:rxandroid:$rxAndroidVersion"
- compile "io.reactivex:rxjava:$rxJavaVersion"
- // And ProGuard rules for RxJava!
- compile "com.artemzin.rxjava:proguard-rules:$rxJavaRulesVersion"
- compile "com.joanzapata.iconify:android-iconify-fontawesome:$iconifyVersion"
- compile "com.joanzapata.iconify:android-iconify-material:$iconifyVersion"
- compile("com.afollestad.material-dialogs:commons:$materialDialogsVersion") {
+ implementation "commons-io:commons-io:$commonsioVersion"
+ implementation "org.jsoup:jsoup:$jsoupVersion"
+ implementation "com.github.bumptech.glide:glide:$glideVersion"
+ annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion"
+ implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
+ implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
+ implementation "com.squareup.okio:okio:$okioVersion"
+ implementation "de.greenrobot:eventbus:$eventbusVersion"
+ implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
+ implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
+
+ 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
}
- compile "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
- compile("com.githang:viewpagerindicator:2.5@aar") {
+ implementation "com.yqritc:recyclerview-flexibledivider:$recyclerviewFlexibledividerVersion"
+ implementation("com.githang:viewpagerindicator:2.5.1@aar") {
exclude module: "support-v4"
}
- compile "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
+ implementation "com.github.shts:TriangleLabelView:$triangleLabelViewVersion"
+
+ implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
- compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
+ implementation 'com.github.mfietz:fyydlin:v0.4.1'
+ implementation 'com.github.ByteHamster:SearchPreference:v1.0.8'
- compile 'com.github.mfietz:fyydlin:v0.3'
+ androidTestImplementation "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.2'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.2'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test:rules:1.0.2'
}
play {
diff --git a/app/proguard.cfg b/app/proguard.cfg
index 01b1708f7..6bb98dc9e 100644
--- a/app/proguard.cfg
+++ b/app/proguard.cfg
@@ -71,8 +71,6 @@
-dontwarn android.support.v7.**
-dontwarn com.google.android.wearable.**
--keepattributes *Annotation*
-
-keep class org.shredzone.flattr4j.** { *; }
-dontwarn org.shredzone.flattr4j.**
@@ -94,6 +92,7 @@
-keepclassmembers class ** {
public void onEvent*(**);
}
+-keep class de.danoeh.antennapod.core.event.*
# android-iconify
-keep class com.joanzapata.** { *; }
@@ -125,3 +124,13 @@
-keep class com.squareup.moshi.** { *; }
-keep interface com.squareup.moshi.** { *; }
-keep public class retrofit2.adapter.rxjava.RxJavaCallAdapterFactory { *; }
+
+# awaitility
+-dontwarn java.beans.BeanInfo
+-dontwarn java.beans.Introspector
+-dontwarn java.beans.IntrospectionException
+-dontwarn java.beans.PropertyDescriptor
+-dontwarn java.lang.management.ManagementFactory
+-dontwarn java.lang.management.ThreadInfo
+-dontwarn java.lang.management.ThreadMXBean
+
diff --git a/app/sampledata/episodes.json b/app/sampledata/episodes.json
new file mode 100644
index 000000000..209cbf180
--- /dev/null
+++ b/app/sampledata/episodes.json
@@ -0,0 +1,34 @@
+{
+ "data": [
+ {
+ "title": "FLOSS Weekly 482: PyPI",
+ "status_label": "NEW",
+ "duration": "00:52:40",
+ "published_at": "2. May"
+ },
+ {
+ "title": "FLOSS Weekly 479: Pidgin",
+ "status_label": " ",
+ "duration": "01:08:08",
+ "published_at": "11. Apr"
+ },
+ {
+ "title": "Linux Outlaws 370 - Stay Free, Stay Open Source",
+ "status_label": "NEW",
+ "duration": "02:52:51",
+ "published_at": "29. Dec 2014"
+ },
+ {
+ "title": "Linux Outlaws 368 - The Dark Ages of Free Software",
+ "status_label": " ",
+ "duration": "02:26:54",
+ "published_at": "14. Dec 2014"
+ },
+ {
+ "title": "Linux Outlaws 365 - Last Stand",
+ "status_label": " ",
+ "duration": "00:39:59",
+ "published_at": "3. Nov 2014"
+ }
+ ]
+}
diff --git a/app/sampledata/inplaylist b/app/sampledata/inplaylist
new file mode 100644
index 000000000..e78406d75
--- /dev/null
+++ b/app/sampledata/inplaylist
@@ -0,0 +1,2 @@
+@null
+@drawable/ic_list_grey600_24dp \ No newline at end of file
diff --git a/app/sampledata/secondaryaction b/app/sampledata/secondaryaction
new file mode 100644
index 000000000..26083bc9c
--- /dev/null
+++ b/app/sampledata/secondaryaction
@@ -0,0 +1,3 @@
+@drawable/ic_play_arrow_grey600_36dp
+@drawable/ic_file_download_grey600_24dp
+@drawable/ic_cancel_grey600_24dp \ No newline at end of file
diff --git a/app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java b/app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java
deleted file mode 100644
index c321e6494..000000000
--- a/app/src/androidTest/java/de/test/antennapod/AntennaPodTestRunner.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package de.test.antennapod;
-
-import android.test.InstrumentationTestRunner;
-import android.test.suitebuilder.TestSuiteBuilder;
-
-import junit.framework.TestSuite;
-
-public class AntennaPodTestRunner extends InstrumentationTestRunner {
-
- @Override
- public TestSuite getAllTests() {
- return new TestSuiteBuilder(AntennaPodTestRunner.class)
- .includeAllPackagesUnderHere()
- .excludePackages("de.test.antennapod.gpodnet")
- .build();
- }
-
-}
diff --git a/app/src/androidTest/java/de/test/antennapod/NthMatcher.java b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
new file mode 100644
index 000000000..f9ecacda5
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
@@ -0,0 +1,38 @@
+package de.test.antennapod;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class NthMatcher {
+ public static <T> Matcher<T> first(final Matcher<T> matcher) {
+ 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) {
+ return new BaseMatcher<T>() {
+ AtomicInteger count = new AtomicInteger(0);
+
+ @Override
+ public boolean matches(final Object item) {
+ if (matcher.matches(item)) {
+ if (count.incrementAndGet() == index) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void describeTo(final Description description) {
+ description.appendText("should return first matching item");
+ }
+ };
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java b/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
new file mode 100644
index 000000000..ced0d7a28
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
@@ -0,0 +1,38 @@
+package de.test.antennapod.feed;
+
+import android.test.AndroidTestCase;
+
+import de.danoeh.antennapod.core.feed.FeedItem;
+
+public class FeedItemTest extends AndroidTestCase {
+ private static final String TEXT_LONG = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+ private static final String TEXT_SHORT = "Lorem ipsum";
+
+ /**
+ * If one of `description` or `content:encoded` is null, use the other one.
+ */
+ public void testShownotesNullValues() throws Exception {
+ testShownotes(null, TEXT_LONG);
+ testShownotes(TEXT_LONG, null);
+ }
+
+ /**
+ * If `description` is reasonably longer than `content:encoded`, use `description`.
+ */
+ public void testShownotesLength() throws Exception {
+ testShownotes(TEXT_SHORT, TEXT_LONG);
+ testShownotes(TEXT_LONG, TEXT_SHORT);
+ }
+
+ /**
+ * Checks if the shownotes equal TEXT_LONG, using the given `description` and `content:encoded`
+ * @param description Description of the feed item
+ * @param contentEncoded `content:encoded` of the feed item
+ */
+ private void testShownotes(String description, String contentEncoded) throws Exception {
+ FeedItem item = new FeedItem();
+ item.setDescription(description);
+ item.setContentEncoded(contentEncoded);
+ assertEquals(TEXT_LONG, item.loadShownotes().call());
+ }
+}
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 14a3b27b0..91e31e73c 100644
--- a/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
@@ -1,113 +1,127 @@
package de.test.antennapod.gpodnet;
-import android.test.AndroidTestCase;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import android.support.test.runner.AndroidJUnit4;
import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetDevice;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetTag;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
+import static java.util.Collections.singletonList;
/**
* Test class for GpodnetService
*/
-public class GPodnetServiceTest extends AndroidTestCase {
+@Ignore
+@RunWith(AndroidJUnit4.class)
+public class GPodnetServiceTest {
private GpodnetService service;
private static final String USER = "";
private static final String PW = "";
- @Override
- protected void setUp() throws Exception {
- super.setUp();
+ @Before
+ protected void setUp() {
service = new GpodnetService();
}
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
private void authenticate() throws GpodnetServiceException {
service.authenticate(USER, PW);
}
+ @Test
public void testUploadSubscription() throws GpodnetServiceException {
authenticate();
- ArrayList<String> l = new ArrayList<String>();
+ ArrayList<String> l = new ArrayList<>();
l.add("http://bitsundso.de/feed");
service.uploadSubscriptions(USER, "radio", l);
}
+ @Test
public void testUploadSubscription2() throws GpodnetServiceException {
authenticate();
- ArrayList<String> l = new ArrayList<String>();
+ ArrayList<String> l = new ArrayList<>();
l.add("http://bitsundso.de/feed");
l.add("http://gamesundso.de/feed");
service.uploadSubscriptions(USER, "radio", l);
}
+ @Test
public void testUploadChanges() throws GpodnetServiceException {
authenticate();
String[] URLS = {"http://bitsundso.de/feed", "http://gamesundso.de/feed", "http://cre.fm/feed/mp3/", "http://freakshow.fm/feed/m4a/"};
List<String> subscriptions = Arrays.asList(URLS[0], URLS[1]);
- List<String> removed = Arrays.asList(URLS[0]);
+ List<String> removed = singletonList(URLS[0]);
List<String> added = Arrays.asList(URLS[2], URLS[3]);
service.uploadSubscriptions(USER, "radio", subscriptions);
service.uploadChanges(USER, "radio", added, removed);
}
+ @Test
public void testGetSubscriptionChanges() throws GpodnetServiceException {
authenticate();
service.getSubscriptionChanges(USER, "radio", 1362322610L);
}
+ @Test
public void testGetSubscriptionsOfUser()
throws GpodnetServiceException {
authenticate();
service.getSubscriptionsOfUser(USER);
}
+ @Test
public void testGetSubscriptionsOfDevice()
throws GpodnetServiceException {
authenticate();
service.getSubscriptionsOfDevice(USER, "radio");
}
+ @Test
public void testConfigureDevices() throws GpodnetServiceException {
authenticate();
service.configureDevice(USER, "foo", "This is an updated caption",
GpodnetDevice.DeviceType.LAPTOP);
}
+ @Test
public void testGetDevices() throws GpodnetServiceException {
authenticate();
service.getDevices(USER);
}
+ @Test
public void testGetSuggestions() throws GpodnetServiceException {
authenticate();
service.getSuggestions(10);
}
+ @Test
public void testTags() throws GpodnetServiceException {
service.getTopTags(20);
}
+ @Test
public void testPodcastForTags() throws GpodnetServiceException {
List<GpodnetTag> tags = service.getTopTags(20);
service.getPodcastsForTag(tags.get(1),
10);
}
+ @Test
public void testSearch() throws GpodnetServiceException {
service.searchPodcasts("linux", 64);
}
+ @Test
public void testToplist() throws GpodnetServiceException {
service.getPodcastToplist(10);
}
diff --git a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
index ee454ce8a..6908e2ec7 100644
--- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
@@ -17,7 +17,6 @@ import javax.xml.parsers.ParserConfigurationException;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.FeedHandler;
@@ -32,8 +31,8 @@ import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
public class FeedHandlerTest extends InstrumentationTestCase {
private static final String FEEDS_DIR = "testfeeds";
- File file = null;
- OutputStream outputStream = null;
+ private File file = null;
+ private OutputStream outputStream = null;
protected void setUp() throws Exception {
super.setUp();
@@ -82,15 +81,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
assertEquals(feed.getLink(), parsedFeed.getLink());
assertEquals(feed.getDescription(), parsedFeed.getDescription());
assertEquals(feed.getPaymentLink(), parsedFeed.getPaymentLink());
-
- if (feed.getImage() != null) {
- FeedImage image = feed.getImage();
- FeedImage parsedImage = parsedFeed.getImage();
- assertNotNull(parsedImage);
-
- assertEquals(image.getTitle(), parsedImage.getTitle());
- assertEquals(image.getDownload_url(), parsedImage.getDownload_url());
- }
+ assertEquals(feed.getImageUrl(), parsedFeed.getImageUrl());
if (feed.getItems() != null) {
assertNotNull(parsedFeed.getItems());
@@ -119,14 +110,7 @@ public class FeedHandlerTest extends InstrumentationTestCase {
assertEquals(media.getMime_type(), parsedMedia.getMime_type());
}
- if (item.hasItemImage()) {
- assertTrue(parsedItem.hasItemImage());
- FeedImage image = item.getImage();
- FeedImage parsedImage = parsedItem.getImage();
-
- assertEquals(image.getTitle(), parsedImage.getTitle());
- assertEquals(image.getDownload_url(), parsedImage.getDownload_url());
- }
+ assertEquals(item.getImageUrl(), parsedFeed.getImageUrl());
if (item.getChapters() != null) {
assertNotNull(parsedItem.getChapters());
@@ -158,14 +142,10 @@ public class FeedHandlerTest extends InstrumentationTestCase {
}
private Feed createTestFeed(int numItems, boolean withImage, boolean withFeedMedia, boolean withChapters) {
- FeedImage image = null;
- if (withImage) {
- image = new FeedImage(0, "image", null, "http://example.com/picture", false);
- }
Feed feed = new Feed(0, null, "title", "http://example.com", "This is the description",
- "http://example.com/payment", "Daniel", "en", null, "http://example.com/feed", image, file.getAbsolutePath(),
+ "http://example.com/payment", "Daniel", "en", null, "http://example.com/feed", "http://example.com/picture", file.getAbsolutePath(),
"http://example.com/feed", true);
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "item-" + i, "http://example.com/item-" + i,
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 9ac92c2f9..2f53ea8a6 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
@@ -23,8 +23,8 @@ import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.LocalPSMP;
+import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable;
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 6ab6e5c61..8be57a074 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
@@ -9,10 +9,10 @@ import java.util.List;
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.event.QueueEvent;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable;
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 5e5eb1e8b..5cd4e9906 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
@@ -27,12 +27,12 @@ import static de.test.antennapod.storage.DBTestUtils.saveFeedlist;
public class DBCleanupTests extends InstrumentationTestCase {
private static final String TAG = "DBTasksTest";
- protected static final int EPISODE_CACHE_SIZE = 5;
+ static final int EPISODE_CACHE_SIZE = 5;
private final int cleanupAlgorithm;
- protected Context context;
+ Context context;
- protected File destFolder;
+ private File destFolder;
public DBCleanupTests() {
this.cleanupAlgorithm = UserPreferences.EPISODE_CLEANUP_DEFAULT;
@@ -104,9 +104,9 @@ public class DBCleanupTests extends InstrumentationTestCase {
}
}
- protected void populateItems(final int numItems, Feed feed, List<FeedItem> items,
- List<File> files, int itemState, boolean addToQueue,
- boolean addToFavorites) throws IOException {
+ void populateItems(final int numItems, Feed feed, List<FeedItem> items,
+ List<File> files, int itemState, boolean addToQueue,
+ boolean addToFavorites) throws IOException {
for (int i = 0; i < numItems; i++) {
Date itemDate = new Date(numItems - i);
Date playbackCompletionDate = null;
@@ -145,9 +145,9 @@ public class DBCleanupTests extends InstrumentationTestCase {
final int NUM_ITEMS = EPISODE_CACHE_SIZE * 2;
Feed feed = new Feed("url", null, "title");
- List<FeedItem> items = new ArrayList<FeedItem>();
+ List<FeedItem> items = new ArrayList<>();
feed.setItems(items);
- List<File> files = new ArrayList<File>();
+ List<File> files = new ArrayList<>();
populateItems(NUM_ITEMS, feed, items, files, FeedItem.UNPLAYED, false, false);
DBTasks.performAutoCleanup(context);
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
index 3bd508eaf..d602d150b 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java
@@ -5,7 +5,6 @@ import android.test.FlakyTest;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Date;
import java.util.List;
import de.danoeh.antennapod.core.feed.Feed;
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 785d32e93..9cd7689ba 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -5,7 +5,6 @@ import android.test.FlakyTest;
import android.test.InstrumentationTestCase;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
@@ -18,6 +17,8 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import static java.util.Collections.singletonList;
+
/**
* Test class for DBTasks
*/
@@ -100,7 +101,7 @@ public class DBTasksTest extends InstrumentationTestCase {
assertTrue(feed.getId() != 0);
final long feedID = feed.getId();
feed.setId(0);
- List<Long> itemIDs = new ArrayList<Long>();
+ List<Long> itemIDs = new ArrayList<>();
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
itemIDs.add(item.getId());
@@ -125,7 +126,7 @@ public class DBTasksTest extends InstrumentationTestCase {
public void testUpdateFeedMediaUrlResetState() {
final Feed feed = new Feed("url", null, "title");
FeedItem item = new FeedItem(0, "item", "id", "link", new Date(), FeedItem.PLAYED, feed);
- feed.setItems(Arrays.asList(item));
+ feed.setItems(singletonList(item));
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -138,7 +139,7 @@ public class DBTasksTest extends InstrumentationTestCase {
FeedMedia media = new FeedMedia(item, "url", 1024, "mime/type");
item.setMedia(media);
- feed.setItems(Arrays.asList(item));
+ feed.setItems(singletonList(item));
final Feed newFeed = DBTasks.updateFeed(context, feed)[0];
assertTrue(feed != newFeed);
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 d02efa521..a577e5e36 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -19,8 +19,9 @@ import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
/**
* Utility methods for DB* tests.
*/
-public class DBTestUtils {
+class DBTestUtils {
+ private DBTestUtils(){}
/**
* Use this method when tests don't involve chapters.
*/
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 40083e507..427cc8ddd 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -15,12 +15,9 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.SimpleChapter;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
@@ -124,90 +121,14 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNull(media.getFile_url());
}
- public void testDeleteFeed() throws IOException, ExecutionException, InterruptedException, TimeoutException {
+ public void testDeleteFeed() throws ExecutionException, InterruptedException, IOException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
- // create Feed image
- File imgFile = new File(destFolder, "image");
- assertTrue(imgFile.createNewFile());
- FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
- image.setOwner(feed);
- feed.setImage(image);
-
- List<File> itemFiles = new ArrayList<File>();
- // 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, true);
- feed.getItems().add(item);
-
- File enc = new File(destFolder, "file " + i);
- assertTrue(enc.createNewFile());
- itemFiles.add(enc);
-
- FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", true, null, 0, 0);
- item.setMedia(media);
-
- item.setChapters(new ArrayList<Chapter>());
- item.getChapters().add(new SimpleChapter(0, "item " + i, item, "example.com"));
- }
-
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- assertTrue(feed.getImage().getId() != 0);
- for (FeedItem item : feed.getItems()) {
- assertTrue(item.getId() != 0);
- assertTrue(item.getMedia().getId() != 0);
- assertTrue(item.getChapters().get(0).getId() != 0);
- }
-
- DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
-
- // check if files still exist
- assertFalse(imgFile.exists());
- for (File f : itemFiles) {
- assertFalse(f.exists());
- }
-
- adapter = PodDBAdapter.getInstance();
- adapter.open();
- Cursor c = adapter.getFeedCursor(feed.getId());
- assertEquals(0, c.getCount());
- c.close();
- c = adapter.getImageCursor(String.valueOf(image.getId()));
- assertEquals(0, c.getCount());
- c.close();
- for (FeedItem item : feed.getItems()) {
- c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
- assertEquals(0, c.getCount());
- c.close();
- c = adapter.getSingleFeedMediaCursor(item.getMedia().getId());
- assertEquals(0, c.getCount());
- c.close();
- c = adapter.getSimpleChaptersOfFeedItemCursor(item);
- assertEquals(0, c.getCount());
- c.close();
- }
- adapter.close();
- }
-
- public void testDeleteFeedNoImage() throws ExecutionException, InterruptedException, IOException, TimeoutException {
- File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
- assertNotNull(destFolder);
-
- Feed feed = new Feed("url", null, "title");
- feed.setItems(new ArrayList<>());
-
- feed.setImage(null);
-
- List<File> itemFiles = new ArrayList<File>();
+ 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);
@@ -261,13 +182,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title");
feed.setItems(null);
-
- // create Feed image
- File imgFile = new File(destFolder, "image");
- assertTrue(imgFile.createNewFile());
- FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
- image.setOwner(feed);
- feed.setImage(image);
+ feed.setImageUrl("url");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -275,21 +190,14 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
assertTrue(feed.getId() != 0);
- assertTrue(feed.getImage().getId() != 0);
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
- // check if files still exist
- assertFalse(imgFile.exists());
-
adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(String.valueOf(image.getId()));
- assertTrue(c.getCount() == 0);
- c.close();
adapter.close();
}
@@ -300,12 +208,7 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
- // create Feed image
- File imgFile = new File(destFolder, "image");
- assertTrue(imgFile.createNewFile());
- FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
- image.setOwner(feed);
- feed.setImage(image);
+ feed.setImageUrl("url");
// create items
for (int i = 0; i < 10; i++) {
@@ -320,24 +223,18 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
assertTrue(feed.getId() != 0);
- assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
}
DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
- // check if files still exist
- assertFalse(imgFile.exists());
adapter = PodDBAdapter.getInstance();
adapter.open();
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(String.valueOf(image.getId()));
- assertTrue(c.getCount() == 0);
- c.close();
for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0);
@@ -346,65 +243,6 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
}
- public void testDeleteFeedWithItemImages() throws InterruptedException, ExecutionException, TimeoutException, IOException {
- File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
- assertNotNull(destFolder);
-
- Feed feed = new Feed("url", null, "title");
- feed.setItems(new ArrayList<>());
-
- // create Feed image
- File imgFile = new File(destFolder, "image");
- assertTrue(imgFile.createNewFile());
- FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
- image.setOwner(feed);
- feed.setImage(image);
-
- // create items with images
- 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 itemImageFile = new File(destFolder, "item-image-" + i);
- FeedImage itemImage = new FeedImage(0, "item-image" + i, itemImageFile.getAbsolutePath(), "url", true);
- item.setImage(itemImage);
- }
-
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.setCompleteFeed(feed);
- adapter.close();
-
- assertTrue(feed.getId() != 0);
- assertTrue(feed.getImage().getId() != 0);
- for (FeedItem item : feed.getItems()) {
- assertTrue(item.getId() != 0);
- assertTrue(item.getImage().getId() != 0);
- }
-
- DBWriter.deleteFeed(getInstrumentation().getTargetContext(), feed.getId()).get(TIMEOUT, TimeUnit.SECONDS);
-
- // check if files still exist
- assertFalse(imgFile.exists());
-
- adapter = PodDBAdapter.getInstance();
- adapter.open();
- Cursor c = adapter.getFeedCursor(feed.getId());
- assertTrue(c.getCount() == 0);
- c.close();
- c = adapter.getImageCursor(String.valueOf(image.getId()));
- assertTrue(c.getCount() == 0);
- c.close();
- for (FeedItem item : feed.getItems()) {
- c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
- assertTrue(c.getCount() == 0);
- c.close();
- c = adapter.getImageCursor(String.valueOf(item.getImage().getId()));
- assertEquals(0, c.getCount());
- c.close();
- }
- adapter.close();
- }
-
public void testDeleteFeedWithQueueItems() throws ExecutionException, InterruptedException, TimeoutException {
File destFolder = getInstrumentation().getTargetContext().getExternalFilesDir(TEST_FOLDER);
assertNotNull(destFolder);
@@ -412,13 +250,9 @@ public class DBWriterTest extends InstrumentationTestCase {
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
- // create Feed image
- File imgFile = new File(destFolder, "image");
- FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
- image.setOwner(feed);
- feed.setImage(image);
+ feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<File>();
+ 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);
@@ -437,14 +271,13 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
assertTrue(feed.getId() != 0);
- assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0);
}
- List<FeedItem> queue = new ArrayList<FeedItem>();
+ List<FeedItem> queue = new ArrayList<>();
queue.addAll(feed.getItems());
adapter.open();
adapter.setQueue(queue);
@@ -460,9 +293,6 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(String.valueOf(image.getId()));
- assertTrue(c.getCount() == 0);
- c.close();
for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0);
@@ -482,15 +312,11 @@ public class DBWriterTest extends InstrumentationTestCase {
assertNotNull(destFolder);
Feed feed = new Feed("url", null, "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
- // create Feed image
- File imgFile = new File(destFolder, "image");
- FeedImage image = new FeedImage(0, "image", imgFile.getAbsolutePath(), "url", true);
- image.setOwner(feed);
- feed.setImage(image);
+ feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<File>();
+ 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);
@@ -509,7 +335,6 @@ public class DBWriterTest extends InstrumentationTestCase {
adapter.close();
assertTrue(feed.getId() != 0);
- assertTrue(feed.getImage().getId() != 0);
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
assertTrue(item.getMedia().getId() != 0);
@@ -522,9 +347,6 @@ public class DBWriterTest extends InstrumentationTestCase {
Cursor c = adapter.getFeedCursor(feed.getId());
assertTrue(c.getCount() == 0);
c.close();
- c = adapter.getImageCursor(String.valueOf(image.getId()));
- assertTrue(c.getCount() == 0);
- c.close();
for (FeedItem item : feed.getItems()) {
c = adapter.getFeedItemCursor(String.valueOf(item.getId()));
assertTrue(c.getCount() == 0);
@@ -539,7 +361,7 @@ public class DBWriterTest extends InstrumentationTestCase {
private FeedMedia playbackHistorySetup(Date playbackCompletionDate) {
final Context context = getInstrumentation().getTargetContext();
Feed feed = new Feed("url", null, "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
FeedItem item = new FeedItem(0, "title", "id", "link", new Date(), FeedItem.PLAYED, feed);
FeedMedia media = new FeedMedia(0, item, 10, 0, 1, "mime", null, "url", false, playbackCompletionDate, 0, 0);
feed.getItems().add(item);
@@ -598,7 +420,7 @@ public class DBWriterTest extends InstrumentationTestCase {
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
}
- List<Future<?>> futures = new ArrayList<Future<?>>();
+ List<Future<?>> futures = new ArrayList<>();
for (FeedItem item : feed.getItems()) {
futures.add(DBWriter.addQueueItem(context, item));
}
@@ -791,7 +613,7 @@ public class DBWriterTest extends InstrumentationTestCase {
public void testMarkFeedRead() throws InterruptedException, ExecutionException, TimeoutException {
final int NUM_ITEMS = 10;
Feed feed = new Feed("url", null, "title");
- feed.setItems(new ArrayList<FeedItem>());
+ feed.setItems(new ArrayList<>());
for (int i = 0; i < NUM_ITEMS; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.UNPLAYED, feed);
feed.getItems().add(item);
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 bd9057b47..9a60b04b8 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -2,19 +2,18 @@ package de.test.antennapod.ui;
import android.content.Context;
import android.content.SharedPreferences;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.FlakyTest;
+import android.support.test.espresso.contrib.DrawerActions;
+import android.support.test.espresso.intent.Intents;
+import android.support.test.filters.FlakyTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
import android.widget.ListView;
-
import com.robotium.solo.Solo;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
+import com.robotium.solo.Timeout;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
@@ -22,26 +21,46 @@ import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
-import de.danoeh.antennapod.preferences.PreferenceController;
+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 java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.action.ViewActions.longClick;
+import static android.support.test.espresso.intent.Intents.intended;
+import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.NthMatcher.first;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertEquals;
/**
* User interface tests for MainActivity
*/
-public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActivity> {
+@RunWith(AndroidJUnit4.class)
+public class MainActivityTest {
private Solo solo;
private UITestUtils uiTestUtils;
-
private SharedPreferences prefs;
- public MainActivityTest() {
- super(MainActivity.class);
- }
+ @Rule
+ public ActivityTestRule<MainActivity> mActivityRule = new ActivityTestRule<>(MainActivity.class);
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- Context context = getInstrumentation().getTargetContext();
+ @Before
+ public void setUp() throws IOException {
+ Intents.init();
+ Context context = mActivityRule.getActivity();
uiTestUtils = new UITestUtils(context);
uiTestUtils.setup();
@@ -54,30 +73,26 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
// override first launch preference
// do this BEFORE calling getActivity()!
- prefs = getInstrumentation().getTargetContext().getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
+ prefs = context.getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean(MainActivity.PREF_IS_FIRST_LAUNCH, false).commit();
- solo = new Solo(getInstrumentation(), getActivity());
+ solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
}
- @Override
- protected void tearDown() throws Exception {
+ @After
+ public void tearDown() throws Exception {
uiTestUtils.tearDown();
solo.finishOpenedActivities();
-
+ Intents.release();
PodDBAdapter.deleteDatabase();
-
- // reset preferences
prefs.edit().clear().commit();
-
- super.tearDown();
}
private void openNavDrawer() {
- solo.clickOnImageButton(0);
- getInstrumentation().waitForIdleSync();
+ onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
+ @Test
public void testAddFeed() throws Exception {
uiTestUtils.addHostedFeedData();
final Feed feed = uiTestUtils.hostedFeeds.get(0);
@@ -89,14 +104,16 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
solo.waitForView(R.id.butSubscribe);
assertEquals(solo.getString(R.string.subscribe_label), solo.getButton(0).getText().toString());
solo.clickOnButton(0);
- solo.waitForText(solo.getString(R.string.subscribed_label));
+ assertTrue(solo.waitForText(solo.getString(R.string.open_podcast), 0, Timeout.getLargeTimeout(), false));
}
- @FlakyTest(tolerance = 3)
+
+ @Test
+ @FlakyTest
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
- UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
// queue
openNavDrawer();
@@ -150,57 +167,60 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
return ((MainActivity) solo.getCurrentActivity()).getSupportActionBar().getTitle().toString();
}
- @SuppressWarnings("unchecked")
- @FlakyTest(tolerance = 3)
+
+ @Test
+ @FlakyTest
public void testGoToPreferences() {
openNavDrawer();
- solo.clickOnText(solo.getString(R.string.settings_label));
- solo.waitForActivity(PreferenceController.getPreferenceActivity());
+ onView(withText(R.string.settings_label)).perform(click());
+ intended(hasComponent(PreferenceActivity.class.getName()));
}
+ @Test
public void testDrawerPreferencesHideSomeElements() {
- UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer();
- solo.clickLongOnText(solo.getString(R.string.queue_label));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.episodes_label));
- solo.clickOnText(solo.getString(R.string.playback_history_label));
- solo.clickOnText(solo.getString(R.string.confirm_label));
- solo.waitForDialogToClose();
+ onView(first(withText(R.string.queue_label))).perform(longClick());
+ onView(withText(R.string.episodes_label)).perform(click());
+ onView(withText(R.string.playback_history_label)).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
+
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
assertTrue(hidden.contains(EpisodesFragment.TAG));
assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
+ @Test
public void testDrawerPreferencesUnhideSomeElements() {
List<String> hidden = Arrays.asList(PlaybackHistoryFragment.TAG, DownloadsFragment.TAG);
UserPreferences.setHiddenDrawerItems(hidden);
openNavDrawer();
- solo.clickLongOnText(solo.getString(R.string.queue_label));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.downloads_label));
- solo.clickOnText(solo.getString(R.string.queue_label));
- solo.clickOnText(solo.getString(R.string.confirm_label));
- solo.waitForDialogToClose();
+ onView(first(withText(R.string.queue_label))).perform(longClick());
+
+ onView(withText(R.string.downloads_label)).perform(click());
+ onView(withText(R.string.queue_label)).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
+
hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(2, hidden.size());
assertTrue(hidden.contains(QueueFragment.TAG));
assertTrue(hidden.contains(PlaybackHistoryFragment.TAG));
}
+
+ @Test
public void testDrawerPreferencesHideAllElements() {
- UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
- String[] titles = getInstrumentation().getTargetContext().getResources().getStringArray(R.array.nav_drawer_titles);
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
+ String[] titles = mActivityRule.getActivity().getResources().getStringArray(R.array.nav_drawer_titles);
openNavDrawer();
- solo.clickLongOnText(solo.getString(R.string.queue_label));
- solo.waitForDialogToOpen();
+ onView(first(withText(R.string.queue_label))).perform(longClick());
for (String title : titles) {
- solo.clickOnText(title);
+ onView(first(withText(title))).perform(click());
}
- solo.clickOnText(solo.getString(R.string.confirm_label));
- solo.waitForDialogToClose();
+ onView(withText(R.string.confirm_label)).perform(click());
+
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(titles.length, hidden.size());
for (String tag : MainActivity.NAV_DRAWER_TAGS) {
@@ -208,21 +228,85 @@ public class MainActivityTest extends ActivityInstrumentationTestCase2<MainActiv
}
}
+ @Test
public void testDrawerPreferencesHideCurrentElement() {
- UserPreferences.setHiddenDrawerItems(new ArrayList<String>());
-
+ UserPreferences.setHiddenDrawerItems(new ArrayList<>());
openNavDrawer();
- String downloads = solo.getString(R.string.downloads_label);
- solo.clickOnText(downloads);
- solo.waitForView(android.R.id.list);
+ onView(withText(R.string.downloads_label)).perform(click());
openNavDrawer();
- solo.clickLongOnText(downloads);
- solo.waitForDialogToOpen();
- solo.clickOnText(downloads);
- solo.clickOnText(solo.getString(R.string.confirm_label));
- solo.waitForDialogToClose();
+
+ onView(first(withText(R.string.queue_label))).perform(longClick());
+ onView(first(withText(R.string.downloads_label))).perform(click());
+ onView(withText(R.string.confirm_label)).perform(click());
+
List<String> hidden = UserPreferences.getHiddenDrawerItems();
assertEquals(1, hidden.size());
assertTrue(hidden.contains(DownloadsFragment.TAG));
}
+
+ @Test
+ public void testBackButtonBehaviorGoToPage() {
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.settings_label));
+ solo.clickOnText(solo.getString(R.string.user_interface_label));
+ solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
+ solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.subscriptions_label));
+ solo.clickOnText(solo.getString(R.string.confirm_label));
+ solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack();
+ assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle());
+ }
+
+ @Test
+ public void testBackButtonBehaviorOpenDrawer() {
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.settings_label));
+ solo.clickOnText(solo.getString(R.string.user_interface_label));
+ solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
+ solo.clickOnText(solo.getString(R.string.back_button_open_drawer));
+ solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack();
+ assertTrue(((MainActivity)solo.getCurrentActivity()).isDrawerOpen());
+ }
+
+ @Test
+ public void testBackButtonBehaviorDoubleTap() {
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.settings_label));
+ solo.clickOnText(solo.getString(R.string.user_interface_label));
+ solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
+ solo.clickOnText(solo.getString(R.string.back_button_double_tap));
+ solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack();
+ solo.goBack();
+ assertTrue(solo.getCurrentActivity().isFinishing());
+ }
+
+ @Test
+ public void testBackButtonBehaviorPrompt() {
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.settings_label));
+ solo.clickOnText(solo.getString(R.string.user_interface_label));
+ solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
+ solo.clickOnText(solo.getString(R.string.back_button_show_prompt));
+ solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack();
+ solo.clickOnText(solo.getString(R.string.yes));
+ solo.waitForDialogToClose();
+ assertTrue(solo.getCurrentActivity().isFinishing());
+ }
+
+ @Test
+ public void testBackButtonBehaviorDefault() {
+ openNavDrawer();
+ solo.clickOnText(solo.getString(R.string.settings_label));
+ solo.clickOnText(solo.getString(R.string.user_interface_label));
+ solo.clickOnText(solo.getString(R.string.pref_back_button_behavior_title));
+ solo.clickOnText(solo.getString(R.string.back_button_default));
+ solo.goBackToActivity(MainActivity.class.getSimpleName());
+ solo.goBack();
+ assertTrue(solo.getCurrentActivity().isFinishing());
+ }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
index bfbeedd83..55ed998bb 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
@@ -33,8 +33,8 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter;
public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActivity> {
private static final String TAG = PlaybackTest.class.getSimpleName();
- public static final int EPISODES_DRAWER_LIST_INDEX = 1;
- public static final int QUEUE_DRAWER_LIST_INDEX = 0;
+ private static final int EPISODES_DRAWER_LIST_INDEX = 1;
+ private static final int QUEUE_DRAWER_LIST_INDEX = 0;
private Solo solo;
private UITestUtils uiTestUtils;
@@ -59,7 +59,7 @@ public class PlaybackSonicTest extends ActivityInstrumentationTestCase2<MainActi
.clear()
.putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
.putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
- .putBoolean(UserPreferences.PREF_SONIC, true)
+ .putString(UserPreferences.PREF_MEDIA_PLAYER, "sonic")
.commit();
solo = new Solo(getInstrumentation(), getActivity());
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
index 661c2200b..74d59abd7 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
@@ -30,8 +30,8 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter;
public class PlaybackTest extends ActivityInstrumentationTestCase2<MainActivity> {
private static final String TAG = PlaybackTest.class.getSimpleName();
- public static final int EPISODES_DRAWER_LIST_INDEX = 1;
- public static final int QUEUE_DRAWER_LIST_INDEX = 0;
+ private static final int EPISODES_DRAWER_LIST_INDEX = 1;
+ private static final int QUEUE_DRAWER_LIST_INDEX = 0;
private Solo solo;
private UITestUtils uiTestUtils;
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 91928f01e..f217ecffa 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -1,16 +1,14 @@
package de.test.antennapod.ui;
-import android.content.Context;
+import android.content.SharedPreferences;
import android.content.res.Resources;
-import android.test.ActivityInstrumentationTestCase2;
-import android.util.Log;
-
+import android.preference.PreferenceManager;
+import android.support.test.espresso.contrib.RecyclerViewActions;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
import com.robotium.solo.Solo;
import com.robotium.solo.Timeout;
-
-import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
-
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -18,36 +16,58 @@ import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
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;
-public class PreferencesTest extends ActivityInstrumentationTestCase2<PreferenceActivity> {
+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;
- private static final String TAG = "PreferencesTest";
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.action.ViewActions.click;
+import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+
+@RunWith(AndroidJUnit4.class)
+public class PreferencesTest {
private Solo solo;
- private Context context;
private Resources res;
+ private SharedPreferences prefs;
- public PreferencesTest() {
- super(PreferenceActivity.class);
- }
+ @Rule
+ public ActivityTestRule<PreferenceActivity> mActivityRule = new ActivityTestRule<>(PreferenceActivity.class);
- @Override
- public void setUp() throws Exception {
- super.setUp();
- solo = new Solo(getInstrumentation(), getActivity());
+ @Before
+ public void setUp() {
+ solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
Timeout.setSmallTimeout(500);
Timeout.setLargeTimeout(1000);
- context = getInstrumentation().getTargetContext();
- res = getActivity().getResources();
- UserPreferences.init(context);
+ res = mActivityRule.getActivity().getResources();
+ UserPreferences.init(mActivityRule.getActivity());
+
+ prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
+ prefs.edit().clear();
+ prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
}
- @Override
- public void tearDown() throws Exception {
+ @After
+ public void tearDown() {
solo.finishOpenedActivities();
- super.tearDown();
+ prefs.edit().clear();
}
+ @Test
public void testSwitchTheme() {
final int theme = UserPreferences.getTheme();
int otherTheme;
@@ -56,12 +76,13 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} else {
otherTheme = R.string.pref_theme_title_light;
}
- solo.clickOnText(solo.getString(R.string.pref_set_theme_title));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(otherTheme));
+ clickPreference(withText(R.string.user_interface_label));
+ clickPreference(withText(R.string.pref_set_theme_title));
+ onView(withText(otherTheme)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
+ @Test
public void testSwitchThemeBack() {
final int theme = UserPreferences.getTheme();
int otherTheme;
@@ -70,37 +91,34 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
} else {
otherTheme = R.string.pref_theme_title_light;
}
- solo.clickOnText(solo.getString(R.string.pref_set_theme_title));
- solo.waitForDialogToOpen(1000);
- solo.clickOnText(solo.getString(otherTheme));
+ clickPreference(withText(R.string.user_interface_label));
+ clickPreference(withText(R.string.pref_set_theme_title));
+ onView(withText(otherTheme)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
}
- public void testExpandNotification() {
- final int priority = UserPreferences.getNotifyPriority();
- solo.clickOnText(solo.getString(R.string.pref_expandNotify_title));
- assertTrue(solo.waitForCondition(() -> priority != UserPreferences.getNotifyPriority(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_expandNotify_title));
- assertTrue(solo.waitForCondition(() -> priority == UserPreferences.getNotifyPriority(), Timeout.getLargeTimeout()));
- }
-
+ @Test
public void testEnablePersistentPlaybackControls() {
final boolean persistNotify = UserPreferences.isPersistNotify();
- solo.clickOnText(solo.getString(R.string.pref_persistNotify_title));
+ clickPreference(withText(R.string.user_interface_label));
+ clickPreference(withText(R.string.pref_persistNotify_title));
assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_persistNotify_title));
+ clickPreference(withText(R.string.pref_persistNotify_title));
assertTrue(solo.waitForCondition(() -> persistNotify == UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
}
+ @Test
public void testSetLockscreenButtons() {
+ solo.clickOnText(solo.getString(R.string.user_interface_label));
+ solo.scrollDown();
String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
solo.clickOnText(solo.getString(R.string.pref_compact_notification_buttons_title));
solo.waitForDialogToOpen(1000);
// First uncheck every checkbox
- for (int i=0; i<buttons.length; i++) {
- assertTrue(solo.searchText(buttons[i]));
- if (solo.isTextChecked(buttons[i])) {
- solo.clickOnText(buttons[i]);
+ for (String button : buttons) {
+ assertTrue(solo.searchText(button));
+ if (solo.isTextChecked(button)) {
+ solo.clickOnText(button);
}
}
// Now try to check all checkboxes
@@ -111,20 +129,26 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(!solo.isTextChecked(buttons[2]));
solo.clickOnText(solo.getString(R.string.confirm_label));
solo.waitForDialogToClose(1000);
- assertTrue(solo.waitForCondition(() -> UserPreferences.showRewindOnCompactNotification(), Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.showFastForwardOnCompactNotification(), Timeout.getLargeTimeout()));
+ assertTrue(solo.waitForCondition(UserPreferences::showRewindOnCompactNotification, Timeout.getLargeTimeout()));
+ assertTrue(solo.waitForCondition(UserPreferences::showFastForwardOnCompactNotification, Timeout.getLargeTimeout()));
assertTrue(solo.waitForCondition(() -> !UserPreferences.showSkipOnCompactNotification(), Timeout.getLargeTimeout()));
}
+ @Test
public void testEnqueueAtFront() {
+ solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
+ solo.scrollDown();
+ solo.scrollDown();
solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_queueAddToFront_title));
assertTrue(solo.waitForCondition(() -> enqueueAtFront == UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
}
+ @Test
public void testHeadPhonesDisconnect() {
+ solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
@@ -132,10 +156,12 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
}
+ @Test
public void testHeadPhonesReconnect() {
+ solo.clickOnText(solo.getString(R.string.playback_pref));
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
- assertTrue(solo.waitForCondition(() -> UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
}
final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect();
solo.clickOnText(solo.getString(R.string.pref_unpauseOnHeadsetReconnect_title));
@@ -144,10 +170,12 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
}
+ @Test
public void testBluetoothReconnect() {
+ solo.clickOnText(solo.getString(R.string.playback_pref));
if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
solo.clickOnText(solo.getString(R.string.pref_pauseOnHeadsetDisconnect_title));
- assertTrue(solo.waitForCondition(() -> UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
}
final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect();
solo.clickOnText(solo.getString(R.string.pref_unpauseOnBluetoothReconnect_title));
@@ -156,15 +184,21 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
}
+ @Test
public void testContinuousPlayback() {
+ solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean continuousPlayback = UserPreferences.isFollowQueue();
+ solo.scrollDown();
+ solo.scrollDown();
solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
solo.clickOnText(solo.getString(R.string.pref_followQueue_title));
assertTrue(solo.waitForCondition(() -> continuousPlayback == UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
}
+ @Test
public void testAutoDelete() {
+ solo.clickOnText(solo.getString(R.string.storage_pref));
final boolean autoDelete = UserPreferences.isAutoDelete();
solo.clickOnText(solo.getString(R.string.pref_auto_delete_title));
assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
@@ -172,15 +206,17 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> autoDelete == UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
}
+ @Test
public void testPlaybackSpeeds() {
- solo.clickOnText(solo.getString(R.string.pref_playback_speed_title));
- solo.waitForDialogToOpen(1000);
+ clickPreference(withText(R.string.playback_pref));
+ clickPreference(withText(R.string.pref_playback_speed_title));
assertTrue(solo.searchText(res.getStringArray(R.array.playback_speed_values)[0]));
- solo.clickOnText(solo.getString(R.string.cancel_label));
- solo.waitForDialogToClose(1000);
+ onView(withText(R.string.cancel_label)).perform(click());
}
+ @Test
public void testPauseForInterruptions() {
+ solo.clickOnText(solo.getString(R.string.playback_pref));
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
solo.clickOnText(solo.getString(R.string.pref_pausePlaybackForFocusLoss_title));
assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
@@ -188,35 +224,40 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
}
+ @Test
public void testDisableUpdateInterval() {
+ solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_sum));
solo.waitForDialogToOpen();
solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Disable));
assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
}
+ @Test
public void testSetUpdateInterval() {
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_title));
- solo.waitForDialogToOpen();
- solo.clickOnText(solo.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
- solo.waitForDialogToOpen();
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(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);
- solo.clickOnText(search);
- solo.waitForDialogToClose();
+ onView(withText(search)).perform(click());
assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() ==
TimeUnit.HOURS.toMillis(12), Timeout.getLargeTimeout()));
}
+ @Test
public void testMobileUpdates() {
+ clickPreference(withText(R.string.network_pref));
final boolean mobileUpdates = UserPreferences.isAllowMobileUpdate();
- solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title));
+ clickPreference(withText(R.string.pref_mobileUpdate_title));
assertTrue(solo.waitForCondition(() -> mobileUpdates != UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
- solo.clickOnText(solo.getString(R.string.pref_mobileUpdate_title));
+ clickPreference(withText(R.string.pref_mobileUpdate_title));
assertTrue(solo.waitForCondition(() -> mobileUpdates == UserPreferences.isAllowMobileUpdate(), Timeout.getLargeTimeout()));
}
+ @Test
public void testSetSequentialDownload() {
- solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "1");
@@ -224,8 +265,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 1, Timeout.getLargeTimeout()));
}
+ @Test
public void testSetParallelDownloads() {
- solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "10");
@@ -233,71 +276,74 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 10, Timeout.getLargeTimeout()));
}
+ @Test
public void testSetParallelDownloadsInvalidInput() {
- solo.clickOnText(solo.getString(R.string.pref_parallel_downloads_title));
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(R.string.pref_parallel_downloads_title));
solo.waitForDialogToOpen();
solo.clearEditText(0);
solo.enterText(0, "0");
- assertEquals("1", solo.getEditText(0).getText().toString());
+ assertEquals("", solo.getEditText(0).getText().toString());
solo.clearEditText(0);
solo.enterText(0, "100");
- assertEquals("50", solo.getEditText(0).getText().toString());
+ assertEquals("", solo.getEditText(0).getText().toString());
}
+ @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]);
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
- solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(R.string.pref_automatic_download_title));
+ clickPreference(withText(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen();
solo.clickOnText(entry);
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == value, Timeout.getLargeTimeout()));
}
+ @Test
public void testSetEpisodeCacheMin() {
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]);
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
- if(!UserPreferences.isEnableAutodownload()) {
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- }
- solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
+
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(R.string.pref_automatic_download_title));
+ clickPreference(withText(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen(1000);
solo.scrollUp();
solo.clickOnText(minEntry);
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == minValue, Timeout.getLargeTimeout()));
}
+ @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]);
+ solo.clickOnText(solo.getString(R.string.network_pref));
solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
- if(!UserPreferences.isEnableAutodownload()) {
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- }
solo.clickOnText(solo.getString(R.string.pref_episode_cache_title));
solo.waitForDialogToOpen();
solo.clickOnText(maxEntry);
assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == maxValue, Timeout.getLargeTimeout()));
}
+ @Test
public void testAutomaticDownload() {
final boolean automaticDownload = UserPreferences.isEnableAutodownload();
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
- solo.waitForText(solo.getString(R.string.pref_automatic_download_title));
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(R.string.pref_automatic_download_title));
+ clickPreference(withText(R.string.pref_automatic_download_title));
+
assertTrue(solo.waitForCondition(() -> automaticDownload != UserPreferences.isEnableAutodownload(), Timeout.getLargeTimeout()));
if(UserPreferences.isEnableAutodownload() == false) {
- solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
+ clickPreference(withText(R.string.pref_automatic_download_title));
}
assertTrue(solo.waitForCondition(() -> UserPreferences.isEnableAutodownload() == true, Timeout.getLargeTimeout()));
final boolean enableAutodownloadOnBattery = UserPreferences.isEnableAutodownloadOnBattery();
@@ -312,7 +358,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
assertTrue(solo.waitForCondition(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
}
+ @Test
public void testEpisodeCleanupQueueOnly() {
+ solo.clickOnText(solo.getString(R.string.network_pref));
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal));
solo.clickOnText(solo.getString(R.string.episode_cleanup_queue_removal));
@@ -323,7 +372,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.getLargeTimeout()));
}
+ @Test
public void testEpisodeCleanupNeverAlg() {
+ solo.clickOnText(solo.getString(R.string.network_pref));
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
solo.waitForText(solo.getString(R.string.episode_cleanup_never));
solo.clickOnText(solo.getString(R.string.episode_cleanup_never));
@@ -334,7 +386,10 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.getLargeTimeout()));
}
+ @Test
public void testEpisodeCleanupClassic() {
+ solo.clickOnText(solo.getString(R.string.network_pref));
+ solo.clickOnText(solo.getString(R.string.pref_automatic_download_title));
solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
solo.clickOnText(solo.getString(R.string.episode_cleanup_after_listening));
@@ -349,10 +404,14 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.getLargeTimeout()));
}
+ @Test
public void testEpisodeCleanupNumDays() {
- solo.clickOnText(solo.getString(R.string.pref_episode_cleanup_title));
- solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
- solo.clickOnText("5");
+ clickPreference(withText(R.string.network_pref));
+ clickPreference(withText(R.string.pref_automatic_download_title));
+ clickPreference(withText(R.string.pref_episode_cleanup_title));
+ solo.waitForDialogToOpen();
+ String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 5, 5);
+ onView(withText(search)).perform(click());
assertTrue(solo.waitForCondition(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
@@ -364,12 +423,13 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
Timeout.getLargeTimeout()));
}
-
+ @Test
public void testRewindChange() {
int seconds = UserPreferences.getRewindSecs();
int deltas[] = res.getIntArray(R.array.seek_delta_values);
- solo.clickOnText(solo.getString(R.string.pref_rewind));
+ clickPreference(withText(R.string.playback_pref));
+ clickPreference(withText(R.string.pref_rewind));
solo.waitForDialogToOpen();
int currentIndex = Arrays.binarySearch(deltas, seconds);
@@ -377,21 +437,22 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
-
- solo.clickOnText(String.valueOf(deltas[newIndex]) + " seconds");
- solo.clickOnButton("Confirm");
+ onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
+ onView(withText("Confirm")).perform(click());
solo.waitForDialogToClose();
assertTrue(solo.waitForCondition(() -> UserPreferences.getRewindSecs() == deltas[newIndex],
Timeout.getLargeTimeout()));
}
+ @Test
public void testFastForwardChange() {
+ clickPreference(withText(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);
- solo.clickOnText(solo.getString(R.string.pref_fast_forward));
+ clickPreference(withText(R.string.pref_fast_forward));
solo.waitForDialogToOpen();
int currentIndex = Arrays.binarySearch(deltas, seconds);
@@ -400,12 +461,52 @@ public class PreferencesTest extends ActivityInstrumentationTestCase2<Preference
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
- solo.clickOnText(String.valueOf(deltas[newIndex]) + " seconds");
- solo.clickOnButton("Confirm");
+ onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
+ onView(withText("Confirm")).perform(click());
solo.waitForDialogToClose();
assertTrue(solo.waitForCondition(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex],
Timeout.getLargeTimeout()));
}
}
+
+ @Test
+ public void testBackButtonBehaviorGoToPageSelector() {
+ clickPreference(withText(R.string.user_interface_label));
+ clickPreference(withText(R.string.pref_back_button_behavior_title));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.queue_label));
+ solo.clickOnText(solo.getString(R.string.confirm_label));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
+ Timeout.getLargeTimeout()));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG),
+ Timeout.getLargeTimeout()));
+ clickPreference(withText(R.string.pref_back_button_behavior_title));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.episodes_label));
+ solo.clickOnText(solo.getString(R.string.confirm_label));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
+ Timeout.getLargeTimeout()));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG),
+ Timeout.getLargeTimeout()));
+ clickPreference(withText(R.string.pref_back_button_behavior_title));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.back_button_go_to_page));
+ solo.waitForDialogToOpen();
+ solo.clickOnText(solo.getString(R.string.subscriptions_label));
+ solo.clickOnText(solo.getString(R.string.confirm_label));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
+ Timeout.getLargeTimeout()));
+ assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG),
+ Timeout.getLargeTimeout()));
+ }
+
+ private void clickPreference(Matcher<View> matcher) {
+ onView(withId(R.id.list))
+ .perform(RecyclerViewActions.actionOnItem(hasDescendant(matcher), click()));
+ }
}
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 432d4a4e6..ff5374268 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -1,9 +1,7 @@
package de.test.antennapod.ui;
-import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
-import android.os.Build;
import android.util.Log;
import junit.framework.Assert;
@@ -20,14 +18,12 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import de.danoeh.antennapod.R;
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.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.fragment.ExternalPlayerFragment;
@@ -39,27 +35,25 @@ import de.test.antennapod.util.syndication.feedgenerator.RSS2Generator;
* Utility methods for UI tests.
* Starts a web server that hosts feeds, episodes and images.
*/
-@TargetApi(Build.VERSION_CODES.HONEYCOMB)
-public class UITestUtils {
+class UITestUtils {
private static final String TAG = UITestUtils.class.getSimpleName();
private static final String DATA_FOLDER = "test/UITestUtils";
- public static final int NUM_FEEDS = 5;
- public static final int NUM_ITEMS_PER_FEED = 10;
+ private static final int NUM_FEEDS = 5;
+ private static final int NUM_ITEMS_PER_FEED = 10;
- public static final int HOME_VIEW = (Build.VERSION.SDK_INT >= 11) ? android.R.id.home : R.id.home;
- public static final String TEST_FILE_NAME = "3sec.mp3";
+ private static final String TEST_FILE_NAME = "3sec.mp3";
- private Context context;
- private HTTPBin server = new HTTPBin();
+ private final Context context;
+ private final HTTPBin server = new HTTPBin();
private File destDir;
private File hostedFeedDir;
private File hostedMediaDir;
- public List<Feed> hostedFeeds = new ArrayList<Feed>();
+ public final List<Feed> hostedFeeds = new ArrayList<>();
public UITestUtils(Context context) {
this.context = context;
@@ -141,15 +135,12 @@ public class UITestUtils {
public void addHostedFeedData() throws IOException {
if (feedDataHosted) throw new IllegalStateException("addHostedFeedData was called twice on the same instance");
for (int i = 0; i < NUM_FEEDS; i++) {
- File bitmapFile = newBitmapFile("image" + i);
- FeedImage image = new FeedImage(0, "image " + i, null, hostFile(bitmapFile), false);
Feed feed = new Feed(0, null, "Title " + i, "http://example.com/" + i, "Description of feed " + i,
- "http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, image, null,
+ "http://example.com/pay/feed" + i, "author " + i, "en", Feed.TYPE_RSS2, "feed" + i, null, null,
"http://example.com/feed/src/" + i, false);
- image.setOwner(feed);
// create items
- List<FeedItem> items = new ArrayList<FeedItem>();
+ List<FeedItem> items = new ArrayList<>();
for (int j = 0; j < NUM_ITEMS_PER_FEED; j++) {
FeedItem item = new FeedItem(j, "Feed " + (i+1) + ": Item " + (j+1), "item" + j,
"http://example.com/feed" + i + "/item/" + j, new Date(), FeedItem.UNPLAYED, feed);
@@ -192,12 +183,6 @@ public class UITestUtils {
List<FeedItem> queue = new ArrayList<>();
for (Feed feed : hostedFeeds) {
feed.setDownloaded(true);
- if (feed.getImage() != null) {
- FeedImage image = feed.getImage();
- int fileId = Integer.parseInt(StringUtils.substringAfter(image.getDownload_url(), "files/"));
- image.setFile_url(server.accessFile(fileId).getAbsolutePath());
- image.setDownloaded(true);
- }
if (downloadEpisodes) {
for (FeedItem item : feed.getItems()) {
if (item.hasMedia()) {
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
index 53fd7d7fd..45ba472ff 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
@@ -38,9 +38,6 @@ public class UITestUtilsTest extends InstrumentationTestCase {
for (Feed feed : feeds) {
testUrlReachable(feed.getDownload_url());
- if (feed.getImage() != null) {
- testUrlReachable(feed.getImage().getDownload_url());
- }
for (FeedItem item : feed.getItems()) {
if (item.hasMedia()) {
testUrlReachable(item.getMedia().getDownload_url());
@@ -66,9 +63,6 @@ public class UITestUtilsTest extends InstrumentationTestCase {
for (Feed feed : uiTestUtils.hostedFeeds) {
assertTrue(feed.getId() != 0);
- if (feed.getImage() != null) {
- assertTrue(feed.getImage().getId() != 0);
- }
for (FeedItem item : feed.getItems()) {
assertTrue(item.getId() != 0);
if (item.hasMedia()) {
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 13e8b9582..a36b3b65a 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
@@ -1,10 +1,12 @@
package de.test.antennapod.util;
+import android.test.AndroidTestCase;
+import android.text.TextUtils;
+
import java.io.File;
import java.io.IOException;
import de.danoeh.antennapod.core.util.FileNameGenerator;
-import android.test.AndroidTestCase;
public class FilenameGeneratorTest extends AndroidTestCase {
@@ -41,7 +43,12 @@ public class FilenameGeneratorTest extends AndroidTestCase {
public void testFeedTitleContainsDash() {
String result = FileNameGenerator.generateFileName("Left - Right");
- assertEquals("Left Right", result);
+ assertEquals("Left - Right", result);
+ }
+
+ public void testInvalidInput() {
+ String result = FileNameGenerator.generateFileName("???");
+ assertTrue(!TextUtils.isEmpty(result));
}
/**
diff --git a/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java b/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java
index fcfb16eb4..d564d0492 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/RewindAfterPauseUtilTest.java
@@ -1,8 +1,8 @@
package de.test.antennapod.util;
-import junit.framework.*;
+import junit.framework.TestCase;
-import de.danoeh.antennapod.core.util.*;
+import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
/**
* Tests for {@link RewindAfterPauseUtils}.
diff --git a/app/src/androidTest/java/de/test/antennapod/util/URIUtilTest.java b/app/src/androidTest/java/de/test/antennapod/util/URIUtilTest.java
index 7bdcfb898..2cca6b4dc 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/URIUtilTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/URIUtilTest.java
@@ -1,6 +1,7 @@
package de.test.antennapod.util;
import android.test.AndroidTestCase;
+
import de.danoeh.antennapod.core.util.URIUtil;
/**
diff --git a/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java b/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java
index aa197b6e1..1b444bfa9 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java
@@ -1,6 +1,7 @@
package de.test.antennapod.util;
import android.test.AndroidTestCase;
+
import de.danoeh.antennapod.core.util.URLChecker;
/**
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 2f2c3fe5b..cde93fd7e 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
@@ -45,11 +45,11 @@ public class HTTPBin extends NanoHTTPD {
private static final String MIME_HTML = "text/html";
private static final String MIME_PLAIN = "text/plain";
- private List<File> servedFiles;
+ private final List<File> servedFiles;
public HTTPBin() {
super(PORT);
- this.servedFiles = new ArrayList<File>();
+ this.servedFiles = new ArrayList<>();
}
/**
diff --git a/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java b/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
index 28ff6694e..61ff65809 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/service/download/NanoHTTPD.java
@@ -88,15 +88,15 @@ public abstract class NanoHTTPD {
* This is required as the Keep-Alive HTTP connections would otherwise
* block the socket reading thread forever (or as long the browser is open).
*/
- public static final int SOCKET_READ_TIMEOUT = 5000;
+ private static final int SOCKET_READ_TIMEOUT = 5000;
/**
* Common mime type for dynamic content: plain text
*/
- public static final String MIME_PLAINTEXT = "text/plain";
+ private static final String MIME_PLAINTEXT = "text/plain";
/**
* Common mime type for dynamic content: html
*/
- public static final String MIME_HTML = "text/html";
+ private static final String MIME_HTML = "text/html";
/**
* Pseudo-Parameter to use to store the actual query string in the parameters map for later re-processing.
*/
@@ -104,7 +104,7 @@ public abstract class NanoHTTPD {
private final String hostname;
private final int myPort;
private ServerSocket myServerSocket;
- private Set<Socket> openConnections = new HashSet<Socket>();
+ private final Set<Socket> openConnections = new HashSet<>();
private Thread myThread;
/**
* Pluggable strategy for asynchronously executing requests.
@@ -118,14 +118,14 @@ public abstract class NanoHTTPD {
/**
* Constructs an HTTP server on given port.
*/
- public NanoHTTPD(int port) {
+ NanoHTTPD(int port) {
this(null, port);
}
/**
* Constructs an HTTP server on given hostname and port.
*/
- public NanoHTTPD(String hostname, int port) {
+ private NanoHTTPD(String hostname, int port) {
this.hostname = hostname;
this.myPort = port;
setTempFileManagerFactory(new DefaultTempFileManagerFactory());
@@ -168,44 +168,38 @@ public abstract class NanoHTTPD {
myServerSocket = new ServerSocket();
myServerSocket.bind((hostname != null) ? new InetSocketAddress(hostname, myPort) : new InetSocketAddress(myPort));
- myThread = new Thread(new Runnable() {
- @Override
- public void run() {
- do {
- try {
- final Socket finalAccept = myServerSocket.accept();
- registerConnection(finalAccept);
- finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
- final InputStream inputStream = finalAccept.getInputStream();
- asyncRunner.exec(new Runnable() {
- @Override
- public void run() {
- OutputStream outputStream = null;
- try {
- outputStream = finalAccept.getOutputStream();
- TempFileManager tempFileManager = tempFileManagerFactory.create();
- HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
- while (!finalAccept.isClosed()) {
- session.execute();
- }
- } catch (Exception e) {
- // When the socket is closed by the client, we throw our own SocketException
- // to break the "keep alive" loop above.
- if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {
- e.printStackTrace();
- }
- } finally {
- safeClose(outputStream);
- safeClose(inputStream);
- safeClose(finalAccept);
- unRegisterConnection(finalAccept);
- }
+ myThread = new Thread(() -> {
+ do {
+ try {
+ final Socket finalAccept = myServerSocket.accept();
+ registerConnection(finalAccept);
+ finalAccept.setSoTimeout(SOCKET_READ_TIMEOUT);
+ final InputStream inputStream = finalAccept.getInputStream();
+ asyncRunner.exec(() -> {
+ OutputStream outputStream = null;
+ try {
+ outputStream = finalAccept.getOutputStream();
+ TempFileManager tempFileManager = tempFileManagerFactory.create();
+ HTTPSession session = new HTTPSession(tempFileManager, inputStream, outputStream, finalAccept.getInetAddress());
+ while (!finalAccept.isClosed()) {
+ session.execute();
}
- });
- } catch (IOException e) {
- }
- } while (!myServerSocket.isClosed());
- }
+ } catch (Exception e) {
+ // When the socket is closed by the client, we throw our own SocketException
+ // to break the "keep alive" loop above.
+ if (!(e instanceof SocketException && "NanoHttpd Shutdown".equals(e.getMessage()))) {
+ e.printStackTrace();
+ }
+ } finally {
+ safeClose(outputStream);
+ safeClose(inputStream);
+ safeClose(finalAccept);
+ unRegisterConnection(finalAccept);
+ }
+ });
+ } catch (IOException e) {
+ }
+ } while (!myServerSocket.isClosed());
});
myThread.setDaemon(true);
myThread.setName("NanoHttpd Main Listener");
@@ -232,7 +226,7 @@ public abstract class NanoHTTPD {
*
* @param socket the {@link Socket} for the connection.
*/
- public synchronized void registerConnection(Socket socket) {
+ private synchronized void registerConnection(Socket socket) {
openConnections.add(socket);
}
@@ -242,14 +236,14 @@ public abstract class NanoHTTPD {
* @param socket
* the {@link Socket} for the connection.
*/
- public synchronized void unRegisterConnection(Socket socket) {
+ private synchronized void unRegisterConnection(Socket socket) {
openConnections.remove(socket);
}
/**
* Forcibly closes all connections that are open.
*/
- public synchronized void closeAllConnections() {
+ private synchronized void closeAllConnections() {
for (Socket socket : openConnections) {
safeClose(socket);
}
@@ -259,7 +253,7 @@ public abstract class NanoHTTPD {
return myServerSocket == null ? -1 : myServerSocket.getLocalPort();
}
- public final boolean wasStarted() {
+ private boolean wasStarted() {
return myServerSocket != null && myThread != null;
}
@@ -294,7 +288,7 @@ public abstract class NanoHTTPD {
* @param session The HTTP session
* @return HTTP response, see class Response for details
*/
- public Response serve(IHTTPSession session) {
+ Response serve(IHTTPSession session) {
Map<String, String> files = new ArrayMap<>();
Method method = session.getMethod();
if (Method.PUT.equals(method) || Method.POST.equals(method)) {
@@ -318,7 +312,7 @@ public abstract class NanoHTTPD {
* @param str the percent encoded <code>String</code>
* @return expanded form of the input, for example "foo%20bar" becomes "foo bar"
*/
- protected String decodePercent(String str) {
+ private String decodePercent(String str) {
String decoded = null;
try {
decoded = URLDecoder.decode(str, "UTF8");
@@ -347,8 +341,8 @@ public abstract class NanoHTTPD {
* @param queryString a query string pulled from the URL.
* @return a map of <code>String</code> (parameter name) to <code>List&lt;String&gt;</code> (a list of the values supplied).
*/
- protected Map<String, List<String>> decodeParameters(String queryString) {
- Map<String, List<String>> parms = new ArrayMap<String, List<String>>();
+ private Map<String, List<String>> decodeParameters(String queryString) {
+ Map<String, List<String>> parms = new ArrayMap<>();
if (queryString != null) {
StringTokenizer st = new StringTokenizer(queryString, "&");
while (st.hasMoreTokens()) {
@@ -356,7 +350,7 @@ public abstract class NanoHTTPD {
int sep = e.indexOf('=');
String propertyName = (sep >= 0) ? decodePercent(e.substring(0, sep)).trim() : decodePercent(e).trim();
if (!parms.containsKey(propertyName)) {
- parms.put(propertyName, new ArrayList<String>());
+ parms.put(propertyName, new ArrayList<>());
}
String propertyValue = (sep >= 0) ? decodePercent(e.substring(sep + 1)) : null;
if (propertyValue != null) {
@@ -378,7 +372,7 @@ public abstract class NanoHTTPD {
*
* @param asyncRunner new strategy for handling threads.
*/
- public void setAsyncRunner(AsyncRunner asyncRunner) {
+ private void setAsyncRunner(AsyncRunner asyncRunner) {
this.asyncRunner = asyncRunner;
}
@@ -393,7 +387,7 @@ public abstract class NanoHTTPD {
*
* @param tempFileManagerFactory new strategy for handling temp files.
*/
- public void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
+ private void setTempFileManagerFactory(TempFileManagerFactory tempFileManagerFactory) {
this.tempFileManagerFactory = tempFileManagerFactory;
}
@@ -448,9 +442,9 @@ public abstract class NanoHTTPD {
* themselves up when no longer needed.</p>
*/
public interface TempFile {
- OutputStream open() throws Exception;
+ OutputStream open();
- void delete() throws Exception;
+ void delete();
String getName();
}
@@ -490,7 +484,7 @@ public abstract class NanoHTTPD {
public DefaultTempFileManager() {
tmpdir = System.getProperty("java.io.tmpdir");
- tempFiles = new ArrayList<TempFile>();
+ tempFiles = new ArrayList<>();
}
@Override
@@ -528,12 +522,12 @@ public abstract class NanoHTTPD {
}
@Override
- public OutputStream open() throws Exception {
+ public OutputStream open() {
return fstream;
}
@Override
- public void delete() throws Exception {
+ public void delete() {
safeClose(fstream);
file.delete();
}
@@ -563,7 +557,7 @@ public abstract class NanoHTTPD {
/**
* Headers for the HTTP response. Use addHeader() to add lines.
*/
- private Map<String, String> header = new ArrayMap<String, String>();
+ private final Map<String, String> header = new ArrayMap<>();
/**
* The request method that spawned this response.
*/
@@ -616,7 +610,7 @@ public abstract class NanoHTTPD {
/**
* Sends given response to the socket.
*/
- protected void send(OutputStream outputStream) {
+ void send(OutputStream outputStream) {
String mime = mimeType;
SimpleDateFormat gmtFrmt = new SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US);
gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
@@ -661,13 +655,13 @@ public abstract class NanoHTTPD {
}
}
- protected void sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, int size) {
+ void sendContentLengthHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header, int size) {
if (!headerAlreadySent(header, "content-length")) {
pw.print("Content-Length: "+ size +"\r\n");
}
}
- protected void sendConnectionHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header) {
+ void sendConnectionHeaderIfNotAlreadyPresent(PrintWriter pw, Map<String, String> header) {
if (!headerAlreadySent(header, "connection")) {
pw.print("Connection: keep-alive\r\n");
}
@@ -694,7 +688,7 @@ public abstract class NanoHTTPD {
outputStream.write(buff, 0, read);
outputStream.write(CRLF);
}
- outputStream.write(String.format("0\r\n\r\n").getBytes());
+ outputStream.write("0\r\n\r\n".getBytes());
}
private void sendAsFixedLength(OutputStream outputStream, int pending) throws IOException {
@@ -844,7 +838,7 @@ public abstract class NanoHTTPD {
public static final int BUFSIZE = 8192;
private final TempFileManager tempFileManager;
private final OutputStream outputStream;
- private PushbackInputStream inputStream;
+ private final PushbackInputStream inputStream;
private int splitbyte;
private int rlen;
private String uri;
@@ -865,7 +859,7 @@ public abstract class NanoHTTPD {
this.inputStream = new PushbackInputStream(inputStream, BUFSIZE);
this.outputStream = outputStream;
String remoteIp = inetAddress.isLoopbackAddress() || inetAddress.isAnyLocalAddress() ? "127.0.0.1" : inetAddress.getHostAddress().toString();
- headers = new ArrayMap<String, String>();
+ headers = new ArrayMap<>();
headers.put("remote-addr", remoteIp);
headers.put("http-client-ip", remoteIp);
@@ -909,16 +903,16 @@ public abstract class NanoHTTPD {
inputStream.unread(buf, splitbyte, rlen - splitbyte);
}
- parms = new ArrayMap<String, String>();
+ parms = new ArrayMap<>();
if(null == headers) {
- headers = new ArrayMap<String, String>();
+ headers = new ArrayMap<>();
}
// Create a BufferedReader for parsing the header.
BufferedReader hin = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(buf, 0, rlen)));
// Decode the header into parms and header java properties
- Map<String, String> pre = new ArrayMap<String, String>();
+ Map<String, String> pre = new ArrayMap<>();
decodeHeader(hin, pre, parms, headers);
method = Method.lookup(pre.get("method"));
@@ -1116,7 +1110,7 @@ public abstract class NanoHTTPD {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but next chunk does not start with boundary. Usage: GET /example/file.html");
}
boundarycount++;
- Map<String, String> item = new ArrayMap<String, String>();
+ Map<String, String> item = new ArrayMap<>();
mpline = in.readLine();
while (mpline != null && mpline.trim().length() > 0) {
int p = mpline.indexOf(':');
@@ -1131,7 +1125,7 @@ public abstract class NanoHTTPD {
throw new ResponseException(Response.Status.BAD_REQUEST, "BAD REQUEST: Content type is multipart/form-data but no content-disposition info found. Usage: GET /example/file.html");
}
StringTokenizer st = new StringTokenizer(contentDisposition, ";");
- Map<String, String> disposition = new ArrayMap<String, String>();
+ Map<String, String> disposition = new ArrayMap<>();
while (st.hasMoreTokens()) {
String token = st.nextToken().trim();
int p = token.indexOf('=');
@@ -1144,17 +1138,19 @@ public abstract class NanoHTTPD {
String value = "";
if (item.get("content-type") == null) {
+ StringBuilder tmp = new StringBuilder();
while (mpline != null && !mpline.contains(boundary)) {
mpline = in.readLine();
if (mpline != null) {
int d = mpline.indexOf(boundary);
if (d == -1) {
- value += mpline;
+ tmp.append(mpline);
} else {
- value += mpline.substring(0, d - 2);
+ tmp.append(mpline.substring(0, d - 2));
}
}
}
+ value = tmp.toString();
} else {
if (boundarycount > bpositions.length) {
throw new ResponseException(Response.Status.INTERNAL_ERROR, "Error processing request");
@@ -1196,7 +1192,7 @@ public abstract class NanoHTTPD {
private int[] getBoundaryPositions(ByteBuffer b, byte[] boundary) {
int matchcount = 0;
int matchbyte = -1;
- List<Integer> matchbytes = new ArrayList<Integer>();
+ List<Integer> matchbytes = new ArrayList<>();
for (int i = 0; i < b.limit(); i++) {
if (b.get(i) == boundary[matchcount]) {
if (matchcount == 0)
@@ -1326,7 +1322,9 @@ public abstract class NanoHTTPD {
}
public static class Cookie {
- private String n, v, e;
+ private final String n;
+ private final String v;
+ private final String e;
public Cookie(String name, String value, String expires) {
n = name;
@@ -1366,8 +1364,8 @@ public abstract class NanoHTTPD {
* @author LordFokas
*/
public class CookieHandler implements Iterable<String> {
- private ArrayMap<String, String> cookies = new ArrayMap<String, String>();
- private ArrayList<Cookie> queue = new ArrayList<Cookie>();
+ private final ArrayMap<String, String> cookies = new ArrayMap<>();
+ private final ArrayList<Cookie> queue = new ArrayList<>();
public CookieHandler(Map<String, String> httpHeaders) {
String raw = httpHeaders.get("cookie");
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java
index 4e5d0297f..809b9769a 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java
@@ -1,7 +1,7 @@
package de.test.antennapod.util.syndication;
import android.test.InstrumentationTestCase;
-import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
+
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
@@ -9,6 +9,8 @@ import java.io.File;
import java.io.FileOutputStream;
import java.util.Map;
+import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
+
/**
* Test class for FeedDiscoverer
*/
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java
index bd3df0f9d..afe15f1b2 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/AtomGenerator.java
@@ -19,7 +19,7 @@ public class AtomGenerator implements FeedGenerator{
private static final String NS_ATOM = "http://www.w3.org/2005/Atom";
- public static final long FEATURE_USE_RFC3339LOCAL = 1;
+ private static final long FEATURE_USE_RFC3339LOCAL = 1;
@Override
public void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException {
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/FeedGenerator.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/FeedGenerator.java
index fe5afd847..b63159384 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/FeedGenerator.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/FeedGenerator.java
@@ -1,10 +1,10 @@
package de.test.antennapod.util.syndication.feedgenerator;
-import de.danoeh.antennapod.core.feed.Feed;
-
import java.io.IOException;
import java.io.OutputStream;
+import de.danoeh.antennapod.core.feed.Feed;
+
/**
* Generates a machine-readable, platform-independent representation of a Feed object.
*/
@@ -24,5 +24,5 @@ public interface FeedGenerator {
* @param encoding The encoding to use. Must not be null.
* @param flags Optional argument for enabling implementation-dependent features.
*/
- public void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException;
+ void writeFeed(Feed feed, OutputStream outputStream, String encoding, long flags) throws IOException;
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java
index e7cbb1b42..89542d222 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/GeneratorUtil.java
@@ -7,7 +7,8 @@ import java.io.IOException;
/**
* Utility methods for FeedGenerator
*/
-public class GeneratorUtil {
+class GeneratorUtil {
+ private GeneratorUtil(){}
public static void addPaymentLink(XmlSerializer xml, String paymentLink, boolean withNamespace) throws IOException {
String ns = (withNamespace) ? "http://www.w3.org/2005/Atom" : null;
diff --git a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java
index 27e89620d..f2d53799d 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/syndication/feedgenerator/RSS2Generator.java
@@ -1,14 +1,16 @@
package de.test.antennapod.util.syndication.feedgenerator;
import android.util.Xml;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.util.DateUtils;
+
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.OutputStream;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.util.DateUtils;
+
/**
* Creates RSS 2.0 feeds. See FeedGenerator for more information.
*/
diff --git a/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java b/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java
index 5e714f02c..fb23dfa1a 100644
--- a/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java
+++ b/app/src/free/java/de/danoeh/antennapod/config/CastCallbackImpl.java
@@ -2,6 +2,6 @@ package de.danoeh.antennapod.config;
import de.danoeh.antennapod.core.CastCallbacks;
-public class CastCallbackImpl implements CastCallbacks {
+class CastCallbackImpl implements CastCallbacks {
}
diff --git a/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
index de604c7c4..4e2c40885 100644
--- a/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
+++ b/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
@@ -5,7 +5,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
* Implements functions from PreferenceController that are flavor dependent.
*/
-public class PreferenceControllerFlavorHelper {
+class PreferenceControllerFlavorHelper {
static void setupFlavoredUI(PreferenceController.PreferenceUI ui) {
ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setEnabled(false);
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d8c1ab137..cff85e905 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -2,8 +2,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="de.danoeh.antennapod"
android:installLocation="auto"
- android:versionCode="1060401"
- android:versionName="1.6.4.1">
+ android:versionCode="1070195"
+ android:versionName="1.7.1">
<!--
Version code schema:
"1.2.3-SNAPSHOT" -> 1020300
@@ -33,11 +33,12 @@
<application
android:name="de.danoeh.antennapod.PodcastApp"
- android:icon="@drawable/ic_launcher"
+ android:icon="@mipmap/ic_launcher"
+ android:roundIcon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:backupAgent=".core.backup.OpmlBackupAgent"
android:restoreAnyVersion="true"
- android:logo="@drawable/ic_launcher">
+ android:logo="@mipmap/ic_launcher">
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
android:resource="@drawable/ic_notification" />
<meta-data
@@ -97,7 +98,7 @@
android:launchMode="singleInstance"/>
<activity
- android:name=".activity.PreferenceActivityGingerbread"
+ android:name=".activity.PreferenceActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/settings_label">
<meta-data
@@ -106,24 +107,24 @@
</activity>
<activity
- android:name=".activity.PreferenceActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:label="@string/settings_label">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.MainActivity"/>
+ android:name=".activity.FeedInfoActivity"
+ android:label="@string/feed_info_label">
</activity>
- <activity android:name=".activity.FeedInfoActivity">
+ <activity
+ android:name=".activity.FeedSettingsActivity"
+ android:windowSoftInputMode="stateHidden"
+ android:label="@string/feed_settings_label">
</activity>
<service
- android:name=".service.PlayerWidgetService"
+ android:name=".core.service.PlayerWidgetJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true"
android:exported="false">
</service>
- <receiver android:name=".receiver.PlayerWidget">
+ <receiver android:name=".core.receiver.PlayerWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
@@ -177,6 +178,13 @@
android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
</activity>
<activity
+ android:name=".activity.ImportExportActivity"
+ android:label="@string/import_export">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
+ </activity>
+ <activity
android:name=".activity.OpmlImportFromPathActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:label="@string/opml_import_label">
@@ -224,7 +232,8 @@
<activity
android:name=".activity.VideoplayerActivity"
- android:configChanges="keyboardHidden|orientation"
+ android:configChanges="keyboardHidden|orientation|screenSize|screenLayout|smallestScreenSize"
+ android:supportsPictureInPicture="true"
android:screenOrientation="sensorLandscape">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -383,10 +392,6 @@
</provider>
<meta-data
- android:name="de.danoeh.antennapod.core.glide.ApGlideModule"
- android:value="GlideModule" />
-
- <meta-data
android:name="com.google.android.gms.car.application"
android:resource="@xml/automotive_app_desc"/>
</application>
diff --git a/app/src/main/assets/LICENSE_SIL.txt b/app/src/main/assets/LICENSE_SIL.txt
new file mode 100644
index 000000000..f5ed6fa72
--- /dev/null
+++ b/app/src/main/assets/LICENSE_SIL.txt
@@ -0,0 +1,91 @@
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded,
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE. \ No newline at end of file
diff --git a/app/src/main/assets/logo.png b/app/src/main/assets/logo.png
index d0e988a6d..3b5261b28 100755
--- a/app/src/main/assets/logo.png
+++ b/app/src/main/assets/logo.png
Binary files differ
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index f6a8db5fb..fde9af16f 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -20,7 +20,7 @@ public class PodcastApp extends Application {
try {
Class.forName("de.danoeh.antennapod.config.ClientConfigurator");
} catch (Exception e) {
- throw new RuntimeException("ClientConfigurator not found");
+ throw new RuntimeException("ClientConfigurator not found", e);
}
}
@@ -41,10 +41,8 @@ public class PodcastApp extends Application {
.detectLeakedSqlLiteObjects()
.penaltyLog()
.penaltyDropBox();
- if (Build.VERSION.SDK_INT >= 11) {
- builder.detectActivityLeaks();
- builder.detectLeakedClosableObjects();
- }
+ builder.detectActivityLeaks();
+ builder.detectLeakedClosableObjects();
if(Build.VERSION.SDK_INT >= 16) {
builder.detectLeakedRegistrationObjects();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
index 1b42b274c..ecfdf24b0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
@@ -21,11 +21,10 @@ import java.nio.charset.Charset;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import rx.Observable;
-import rx.Subscriber;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Single;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays the 'about' screen
@@ -34,11 +33,9 @@ public class AboutActivity extends AppCompatActivity {
private static final String TAG = AboutActivity.class.getSimpleName();
- private WebView webview;
- private LinearLayout webviewContainer;
- private int depth = 0;
-
- private Subscription subscription;
+ private WebView webView;
+ private LinearLayout webViewContainer;
+ private Disposable disposable;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -46,28 +43,25 @@ public class AboutActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
getSupportActionBar().setDisplayShowHomeEnabled(true);
setContentView(R.layout.about);
- webviewContainer = (LinearLayout) findViewById(R.id.webvContainer);
- webview = (WebView) findViewById(R.id.webvAbout);
- webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
+ webViewContainer = findViewById(R.id.webViewContainer);
+ webView = findViewById(R.id.webViewAbout);
+ webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- if (Build.VERSION.SDK_INT >= 11
- && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
- webview.setBackgroundColor(Color.TRANSPARENT);
+ webView.setBackgroundColor(Color.TRANSPARENT);
}
- webview.setWebViewClient(new WebViewClient() {
+ webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if(url.startsWith("http")) {
- depth++;
- return false;
- } else {
+ if (!url.startsWith("http")) {
url = url.replace("file:///android_asset/", "");
loadAsset(url);
return true;
}
+ return false;
}
});
@@ -75,69 +69,65 @@ public class AboutActivity extends AppCompatActivity {
}
private void loadAsset(String filename) {
- subscription = Observable.create(new Observable.OnSubscribe<String>() {
- @Override
- public void call(Subscriber<? super String> subscriber) {
- InputStream input = null;
- try {
- TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes(
- new int[] { android.R.attr.textColorPrimary });
- int colorResource = res.getColor(0, 0);
- String colorString = String.format("#%06X", 0xFFFFFF & colorResource);
- res.recycle();
- input = getAssets().open(filename);
- String webViewData = IOUtils.toString(input, Charset.defaultCharset());
- if(!webViewData.startsWith("<!DOCTYPE html>")) {
- //webViewData = webViewData.replace("\n\n", "</p><p>");
- 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: %s;" +
- " font-family: roboto-Light;" +
- " font-size: 8pt;" +
- " }" +
- " </style>" +
- "</head><body><p>" + webViewData + "</p></body></html>";
- webViewData = webViewData.replace("\n", "<br/>");
- depth++;
- } else {
- depth = 0;
- }
- webViewData = String.format(webViewData, colorString);
- subscriber.onNext(webViewData);
- } catch (IOException e) {
- subscriber.onError(e);
- } finally {
- IOUtils.closeQuietly(input);
- }
- subscriber.onCompleted();
- }
- })
- .subscribeOn(Schedulers.newThread())
+ 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, "text/html", "utf-8", "about:blank"),
+ 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() {
- Log.d(TAG, "depth: " + depth);
- if(depth == 1) {
- loadAsset("about.html");
- } else if(depth > 1) {
- webview.goBack();
+ if (webView.canGoBack()) {
+ webView.goBack();
} else {
super.onBackPressed();
}
@@ -156,12 +146,12 @@ public class AboutActivity extends AppCompatActivity {
@Override
protected void onDestroy() {
super.onDestroy();
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
- if (webviewContainer != null && webview != null) {
- webviewContainer.removeAllViews();
- webview.destroy();
+ 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 ca214de9e..67dda01cf 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -6,41 +6,31 @@ import android.text.TextUtils;
import android.util.Log;
import android.view.View;
+import java.text.DecimalFormat;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
/**
* Activity for playing audio files.
*/
public class AudioplayerActivity extends MediaplayerInfoActivity {
- public static final String TAG = "AudioPlayerActivity";
+ private static final String TAG = "AudioPlayerActivity";
- private AtomicBoolean isSetup = new AtomicBoolean(false);
+ private final AtomicBoolean isSetup = new AtomicBoolean(false);
@Override
protected void onResume() {
super.onResume();
if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
- Intent intent = getIntent();
- Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
- ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
- MediaType.AUDIO);
- Intent launchIntent = new Intent(this, PlaybackService.class);
- launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
- launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED,
- true);
- launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false);
- launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
- true);
- startService(launchIntent);
+ playExternalMedia(getIntent(), MediaType.AUDIO);
} else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this);
- if (!intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) {
+ if (intent.getComponent() != null &&
+ !intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) {
saveCurrentFragment();
finish();
startActivity(intent);
@@ -95,7 +85,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
UserPreferences.setPlaybackSpeed(String.valueOf(speed));
}
}
- String speedStr = String.format("%.2fx", speed);
+ String speedStr = new DecimalFormat("0.00x").format(speed);
butPlaybackSpeed.setText(speedStr);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
index a7e9b1e70..871e9c279 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
@@ -13,9 +13,9 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
* Activity for controlling the remote playback on a Cast device.
*/
public class CastplayerActivity extends MediaplayerInfoActivity {
- public static final String TAG = "CastPlayerActivity";
+ private static final String TAG = "CastPlayerActivity";
- private AtomicBoolean isSetup = new AtomicBoolean(false);
+ private final AtomicBoolean isSetup = new AtomicBoolean(false);
@Override
protected void onCreate(Bundle savedInstanceState) {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
index 390d4cef8..33def125e 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
@@ -64,11 +64,11 @@ public class DirectoryChooserActivity extends AppCompatActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.directory_chooser);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
- butNavUp = (ImageButton) findViewById(R.id.butNavUp);
- txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder);
- listDirectories = (ListView) findViewById(R.id.directory_list);
+ butConfirm = findViewById(R.id.butConfirm);
+ butCancel = findViewById(R.id.butCancel);
+ butNavUp = findViewById(R.id.butNavUp);
+ txtvSelectedFolder = findViewById(R.id.txtvSelectedFolder);
+ listDirectories = findViewById(R.id.directory_list);
butConfirm.setOnClickListener(new OnClickListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
index 41b2debdc..5e04d743d 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
@@ -3,15 +3,14 @@ package de.danoeh.antennapod.activity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
-import android.util.Log;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import org.apache.commons.lang3.Validate;
-import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
@@ -23,8 +22,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
* Other arguments are optional.
* The activity's result will be the same DownloadRequest with the entered username and password.
*/
-public class DownloadAuthenticationActivity extends ActionBarActivity {
- private static final String TAG = "DownloadAuthenticationActivity";
+public class DownloadAuthenticationActivity extends AppCompatActivity {
/**
* The download request object that contains information about the resource that requires a username and a password
@@ -36,47 +34,39 @@ public class DownloadAuthenticationActivity extends ActionBarActivity {
*/
public static final String ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL = "send_to_downloadrequester";
- public static final String RESULT_REQUEST = "request";
+ private static final String RESULT_REQUEST = "request";
private EditText etxtUsername;
private EditText etxtPassword;
- private Button butConfirm;
- private Button butCancel;
- private TextView txtvDescription;
-
- private DownloadRequest request;
- private boolean sendToDownloadRequester;
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
- getSupportActionBar().hide();
- setContentView(R.layout.download_authentication_activity);
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.hide();
+ }
- etxtUsername = (EditText) findViewById(R.id.etxtUsername);
- etxtPassword = (EditText) findViewById(R.id.etxtPassword);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
- txtvDescription = (TextView) findViewById(R.id.txtvDescription);
+ setContentView(R.layout.download_authentication_activity);
+ TextView txtvDescription = findViewById(R.id.txtvDescription);
+ etxtUsername = findViewById(R.id.etxtUsername);
+ etxtPassword = findViewById(R.id.etxtPassword);
+ Button butConfirm = findViewById(R.id.butConfirm);
+ Button butCancel = findViewById(R.id.butCancel);
Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing");
+ DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
+ boolean sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false);
- request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST);
- sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false);
+ String newDescription = txtvDescription.getText() + ":\n\n" + request.getTitle();
+ txtvDescription.setText(newDescription);
if (savedInstanceState != null) {
etxtUsername.setText(savedInstanceState.getString("username"));
etxtPassword.setText(savedInstanceState.getString("password"));
}
- txtvDescription.setText(txtvDescription.getText() + ":\n\n" + request.getTitle());
-
- butCancel.setOnClickListener(v -> {
- setResult(Activity.RESULT_CANCELED);
- finish();
- });
-
butConfirm.setOnClickListener(v -> {
String username = etxtUsername.getText().toString();
String password = etxtPassword.getText().toString();
@@ -87,11 +77,16 @@ public class DownloadAuthenticationActivity extends ActionBarActivity {
setResult(Activity.RESULT_OK, result);
if (sendToDownloadRequester) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Sending request to DownloadRequester");
DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request);
}
finish();
});
+
+ butCancel.setOnClickListener(v -> {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ });
+
}
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
index 2a58d5104..bfa694e5c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
@@ -2,30 +2,23 @@ package de.danoeh.antennapod.activity;
import android.content.ClipData;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
+import android.graphics.LightingColorFilter;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
-import android.text.Editable;
import android.text.TextUtils;
-import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemSelectedListener;
-import android.widget.CheckBox;
-import android.widget.EditText;
import android.widget.ImageView;
-import android.widget.RadioButton;
-import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import org.apache.commons.lang3.StringUtils;
@@ -33,24 +26,22 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedFilter;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
-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.LangUtils;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Maybe;
+import io.reactivex.MaybeOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays information about a feed.
@@ -59,7 +50,6 @@ public class FeedInfoActivity extends AppCompatActivity {
public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
private static final String TAG = "FeedInfoActivity";
- private boolean autoDeleteChanged = false;
private Feed feed;
private ImageView imgvCover;
@@ -70,17 +60,8 @@ public class FeedInfoActivity extends AppCompatActivity {
private TextView lblAuthor;
private TextView txtvAuthor;
private TextView txtvUrl;
- private EditText etxtUsername;
- private EditText etxtPassword;
- private EditText etxtFilterText;
- private RadioButton rdoFilterInclude;
- private RadioButton rdoFilterExclude;
- private CheckBox cbxAutoDownload;
- private CheckBox cbxKeepUpdated;
- private Spinner spnAutoDelete;
- private boolean filterInclude = true;
- private Subscription subscription;
+ private Disposable disposable;
private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
@@ -88,56 +69,16 @@ public class FeedInfoActivity extends AppCompatActivity {
public void onClick(View v) {
if(feed != null && feed.getDownload_url() != null) {
String url = feed.getDownload_url();
- if (android.os.Build.VERSION.SDK_INT >= 11) {
- ClipData clipData = ClipData.newPlainText(url, url);
- android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setPrimaryClip(clipData);
- } else {
- android.text.ClipboardManager cm = (android.text.ClipboardManager) FeedInfoActivity.this
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setText(url);
- }
+ ClipData clipData = ClipData.newPlainText(url, url);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
Toast t = Toast.makeText(FeedInfoActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT);
t.show();
}
}
};
- private boolean authInfoChanged = false;
-
- private TextWatcher authTextWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- authInfoChanged = true;
- }
- };
-
- private boolean filterTextChanged = false;
-
- private TextWatcher filterTextWatcher = new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
-
- @Override
- public void afterTextChanged(Editable s) {
- filterTextChanged = true;
- }
- };
-
@Override
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
@@ -146,54 +87,58 @@ public class FeedInfoActivity extends AppCompatActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
- imgvCover = (ImageView) findViewById(R.id.imgvCover);
- txtvTitle = (TextView) findViewById(R.id.txtvTitle);
- txtvDescription = (TextView) findViewById(R.id.txtvDescription);
- lblLanguage = (TextView) findViewById(R.id.lblLanguage);
- txtvLanguage = (TextView) findViewById(R.id.txtvLanguage);
- lblAuthor = (TextView) findViewById(R.id.lblAuthor);
- txtvAuthor = (TextView) findViewById(R.id.txtvAuthor);
- txtvUrl = (TextView) findViewById(R.id.txtvUrl);
- cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload);
- cbxKeepUpdated = (CheckBox) findViewById(R.id.cbxKeepUpdated);
- spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete);
- etxtUsername = (EditText) findViewById(R.id.etxtUsername);
- etxtPassword = (EditText) findViewById(R.id.etxtPassword);
- etxtFilterText = (EditText) findViewById(R.id.etxtEpisodeFilterText);
- rdoFilterInclude = (RadioButton) findViewById(R.id.radio_filter_include);
- rdoFilterInclude.setOnClickListener(v -> {
- filterInclude = true;
- filterTextChanged = true;
- });
- rdoFilterExclude = (RadioButton) findViewById(R.id.radio_filter_exclude);
- rdoFilterExclude.setOnClickListener(v -> {
- filterInclude = false;
- filterTextChanged = true;
- });
+ imgvCover = findViewById(R.id.imgvCover);
+ txtvTitle = findViewById(R.id.txtvTitle);
+ TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor);
+ ImageView imgvBackground = findViewById(R.id.imgvBackground);
+ findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
+ findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
+ // https://github.com/bumptech/glide/issues/529
+ imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
+
+
+ txtvDescription = findViewById(R.id.txtvDescription);
+ lblLanguage = findViewById(R.id.lblLanguage);
+ txtvLanguage = findViewById(R.id.txtvLanguage);
+ lblAuthor = findViewById(R.id.lblAuthor);
+ txtvAuthor = findViewById(R.id.txtvDetailsAuthor);
+ txtvUrl = findViewById(R.id.txtvUrl);
txtvUrl.setOnClickListener(copyUrlToClipboard);
- subscription = Observable.fromCallable(()-> DBReader.getFeed(feedId))
- .subscribeOn(Schedulers.newThread())
+ disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
+ Feed feed = DBReader.getFeed(feedId);
+ if (feed != null) {
+ emitter.onSuccess(feed);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- if (result == null) {
- Log.e(TAG, "Activity was started with invalid arguments");
- finish();
- }
feed = result;
Log.d(TAG, "Language is " + feed.getLanguage());
Log.d(TAG, "Author is " + feed.getAuthor());
Log.d(TAG, "URL is " + feed.getDownload_url());
- FeedPreferences prefs = feed.getPreferences();
Glide.with(FeedInfoActivity.this)
.load(feed.getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(imgvCover);
+ Glide.with(FeedInfoActivity.this)
+ .load(feed.getImageLocation())
+ .apply(new RequestOptions()
+ .placeholder(R.color.image_readability_tint)
+ .error(R.color.image_readability_tint)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transform(new FastBlurTransformation())
+ .dontAnimate())
+ .into(imgvBackground);
txtvTitle.setText(feed.getTitle());
@@ -211,6 +156,7 @@ public class FeedInfoActivity extends AppCompatActivity {
if (!TextUtils.isEmpty(feed.getAuthor())) {
txtvAuthor.setText(feed.getAuthor());
+ txtvAuthorHeader.setText(feed.getAuthor());
} else {
lblAuthor.setVisibility(View.GONE);
txtvAuthor.setVisibility(View.GONE);
@@ -224,118 +170,21 @@ public class FeedInfoActivity extends AppCompatActivity {
txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
Iconify.addIcons(txtvUrl);
- cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
- cbxAutoDownload.setChecked(prefs.getAutoDownload());
- cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> {
- feed.getPreferences().setAutoDownload(checked);
- feed.savePreferences();
- updateAutoDownloadSettings();
- ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this,
- feed, checked);
- dialog.createNewDialog().show();
- });
- cbxKeepUpdated.setChecked(prefs.getKeepUpdated());
- cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> {
- feed.getPreferences().setKeepUpdated(checked);
- feed.savePreferences();
- });
- spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
- @Override
- public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
- FeedPreferences.AutoDeleteAction auto_delete_action;
- switch (parent.getSelectedItemPosition()) {
- case 0:
- auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL;
- break;
- case 1:
- auto_delete_action = FeedPreferences.AutoDeleteAction.YES;
- break;
- case 2:
- auto_delete_action = FeedPreferences.AutoDeleteAction.NO;
- break;
- default: // TODO - add exceptions here
- return;
- }
- feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
- autoDeleteChanged = true;
- }
-
- @Override
- public void onNothingSelected(AdapterView<?> parent) {
- // Another interface callback
- }
- });
- spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal());
-
- etxtUsername.setText(prefs.getUsername());
- etxtPassword.setText(prefs.getPassword());
-
- etxtUsername.addTextChangedListener(authTextWatcher);
- etxtPassword.addTextChangedListener(authTextWatcher);
-
- FeedFilter filter = prefs.getFilter();
- if (filter.includeOnly()) {
- etxtFilterText.setText(filter.getIncludeFilter());
- rdoFilterInclude.setChecked(true);
- rdoFilterExclude.setChecked(false);
- filterInclude = true;
- } else if (filter.excludeOnly()) {
- etxtFilterText.setText(filter.getExcludeFilter());
- rdoFilterInclude.setChecked(false);
- rdoFilterExclude.setChecked(true);
- filterInclude = false;
- } else {
- Log.d(TAG, "No filter set");
- rdoFilterInclude.setChecked(false);
- rdoFilterExclude.setChecked(false);
- etxtFilterText.setText("");
- }
- etxtFilterText.addTextChangedListener(filterTextWatcher);
-
supportInvalidateOptionsMenu();
- updateAutoDownloadSettings();
}, error -> {
Log.d(TAG, Log.getStackTraceString(error));
finish();
+ }, () -> {
+ Log.e(TAG, "Activity was started with invalid arguments");
+ finish();
});
}
@Override
- protected void onPause() {
- super.onPause();
- if (feed != null) {
- FeedPreferences prefs = feed.getPreferences();
- if (authInfoChanged) {
- Log.d(TAG, "Auth info changed, saving credentials");
- prefs.setUsername(etxtUsername.getText().toString());
- prefs.setPassword(etxtPassword.getText().toString());
- }
- if (filterTextChanged) {
- Log.d(TAG, "Filter info changed, saving...");
- String filterText = etxtFilterText.getText().toString();
- String includeString = "";
- String excludeString = "";
- if (filterInclude) {
- includeString = filterText;
- } else {
- excludeString = filterText;
- }
- prefs.setFilter(new FeedFilter(includeString, excludeString));
- }
- if (authInfoChanged || autoDeleteChanged || filterTextChanged) {
- DBWriter.setFeedPreferences(prefs);
- }
- authInfoChanged = false;
- autoDeleteChanged = false;
- filterTextChanged = false;
- }
- }
-
- @Override
public void onDestroy() {
super.onDestroy();
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
}
@@ -375,34 +224,4 @@ public class FeedInfoActivity extends AppCompatActivity {
return super.onOptionsItemSelected(item);
}
}
-
- private void updateAutoDownloadSettings() {
- if (feed != null && feed.getPreferences() != null) {
- boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
- rdoFilterInclude.setEnabled(enabled);
- rdoFilterExclude.setEnabled(enabled);
- etxtFilterText.setEnabled(enabled);
- }
- }
-
- private class ApplyToEpisodesDialog extends ConfirmationDialog {
-
- private final Feed feed;
- private final boolean autoDownload;
-
- ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
- super(context, R.string.auto_download_apply_to_items_title,
- R.string.auto_download_apply_to_items_message);
- this.feed = feed;
- this.autoDownload = autoDownload;
- setPositiveText(R.string.yes);
- setNegativeText(R.string.no);
- }
-
- @Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
- DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
- }
- }
-
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java
new file mode 100644
index 000000000..4698ed90e
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java
@@ -0,0 +1,364 @@
+package de.danoeh.antennapod.activity;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.LightingColorFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import android.support.v7.app.AppCompatActivity;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.AdapterView;
+import android.widget.AdapterView.OnItemSelectedListener;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.RadioButton;
+import android.widget.Spinner;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedFilter;
+import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.DBReader;
+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.menuhandler.FeedMenuHandler;
+import io.reactivex.Maybe;
+import io.reactivex.MaybeOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Displays information about a feed.
+ */
+public class FeedSettingsActivity extends AppCompatActivity {
+
+ public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
+ private static final String TAG = "FeedSettingsActivity";
+ private boolean autoDeleteChanged = false;
+ private Feed feed;
+
+ private ImageView imgvCover;
+ private TextView txtvTitle;
+ private EditText etxtUsername;
+ private EditText etxtPassword;
+ private EditText etxtFilterText;
+ private RadioButton rdoFilterInclude;
+ private RadioButton rdoFilterExclude;
+ private CheckBox cbxAutoDownload;
+ private CheckBox cbxKeepUpdated;
+ private Spinner spnAutoDelete;
+ private boolean filterInclude = true;
+
+ private Disposable disposable;
+
+ private boolean authInfoChanged = false;
+
+ private final TextWatcher authTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ authInfoChanged = true;
+ }
+ };
+
+ private boolean filterTextChanged = false;
+
+ private final TextWatcher filterTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ filterTextChanged = true;
+ }
+ };
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.feedsettings);
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
+
+ imgvCover = findViewById(R.id.imgvCover);
+ txtvTitle = findViewById(R.id.txtvTitle);
+ TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor);
+ ImageView imgvBackground = findViewById(R.id.imgvBackground);
+ findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
+ findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
+ // https://github.com/bumptech/glide/issues/529
+ imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
+
+ cbxAutoDownload = findViewById(R.id.cbxAutoDownload);
+ cbxKeepUpdated = findViewById(R.id.cbxKeepUpdated);
+ spnAutoDelete = findViewById(R.id.spnAutoDelete);
+ etxtUsername = findViewById(R.id.etxtUsername);
+ etxtPassword = findViewById(R.id.etxtPassword);
+ etxtFilterText = findViewById(R.id.etxtEpisodeFilterText);
+ rdoFilterInclude = findViewById(R.id.radio_filter_include);
+ rdoFilterInclude.setOnClickListener(v -> {
+ filterInclude = true;
+ filterTextChanged = true;
+ });
+ rdoFilterExclude = findViewById(R.id.radio_filter_exclude);
+ rdoFilterExclude.setOnClickListener(v -> {
+ filterInclude = false;
+ filterTextChanged = true;
+ });
+
+ disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> {
+ Feed feed = DBReader.getFeed(feedId);
+ if (feed != null) {
+ emitter.onSuccess(feed);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ feed = result;
+ FeedPreferences prefs = feed.getPreferences();
+ Glide.with(FeedSettingsActivity.this)
+ .load(feed.getImageLocation())
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
+ .into(imgvCover);
+ Glide.with(FeedSettingsActivity.this)
+ .load(feed.getImageLocation())
+ .apply(new RequestOptions()
+ .placeholder(R.color.image_readability_tint)
+ .error(R.color.image_readability_tint)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transform(new FastBlurTransformation())
+ .dontAnimate())
+ .into(imgvBackground);
+
+ txtvTitle.setText(feed.getTitle());
+
+ if (!TextUtils.isEmpty(feed.getAuthor())) {
+ txtvAuthorHeader.setText(feed.getAuthor());
+ }
+
+ cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload());
+ cbxAutoDownload.setChecked(prefs.getAutoDownload());
+ cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> {
+ feed.getPreferences().setAutoDownload(checked);
+ feed.savePreferences();
+ updateAutoDownloadSettings();
+ ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedSettingsActivity.this,
+ feed, checked);
+ dialog.createNewDialog().show();
+ });
+ cbxKeepUpdated.setChecked(prefs.getKeepUpdated());
+ cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> {
+ feed.getPreferences().setKeepUpdated(checked);
+ feed.savePreferences();
+ });
+ spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() {
+ @Override
+ public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
+ FeedPreferences.AutoDeleteAction auto_delete_action;
+ switch (parent.getSelectedItemPosition()) {
+ case 0:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL;
+ break;
+ case 1:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.YES;
+ break;
+ case 2:
+ auto_delete_action = FeedPreferences.AutoDeleteAction.NO;
+ break;
+ default: // TODO - add exceptions here
+ return;
+ }
+ feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p
+ autoDeleteChanged = true;
+ }
+
+ @Override
+ public void onNothingSelected(AdapterView<?> parent) {
+ // Another interface callback
+ }
+ });
+ spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal());
+
+ etxtUsername.setText(prefs.getUsername());
+ etxtPassword.setText(prefs.getPassword());
+
+ etxtUsername.addTextChangedListener(authTextWatcher);
+ etxtPassword.addTextChangedListener(authTextWatcher);
+
+ FeedFilter filter = prefs.getFilter();
+ if (filter.includeOnly()) {
+ etxtFilterText.setText(filter.getIncludeFilter());
+ rdoFilterInclude.setChecked(true);
+ rdoFilterExclude.setChecked(false);
+ filterInclude = true;
+ } else if (filter.excludeOnly()) {
+ etxtFilterText.setText(filter.getExcludeFilter());
+ rdoFilterInclude.setChecked(false);
+ rdoFilterExclude.setChecked(true);
+ filterInclude = false;
+ } else {
+ Log.d(TAG, "No filter set");
+ rdoFilterInclude.setChecked(false);
+ rdoFilterExclude.setChecked(false);
+ etxtFilterText.setText("");
+ }
+ etxtFilterText.addTextChangedListener(filterTextWatcher);
+
+ supportInvalidateOptionsMenu();
+ updateAutoDownloadSettings();
+ }, error -> {
+ Log.d(TAG, Log.getStackTraceString(error));
+ finish();
+ }, () -> {
+ Log.e(TAG, "Activity was started with invalid arguments");
+ finish();
+ });
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ if (feed != null) {
+ FeedPreferences prefs = feed.getPreferences();
+ if (authInfoChanged) {
+ Log.d(TAG, "Auth info changed, saving credentials");
+ prefs.setUsername(etxtUsername.getText().toString());
+ prefs.setPassword(etxtPassword.getText().toString());
+ }
+ if (filterTextChanged) {
+ Log.d(TAG, "Filter info changed, saving...");
+ String filterText = etxtFilterText.getText().toString();
+ String includeString = "";
+ String excludeString = "";
+ if (filterInclude) {
+ includeString = filterText;
+ } else {
+ excludeString = filterText;
+ }
+ prefs.setFilter(new FeedFilter(includeString, excludeString));
+ }
+ if (authInfoChanged || autoDeleteChanged || filterTextChanged) {
+ DBWriter.setFeedPreferences(prefs);
+ }
+ authInfoChanged = false;
+ autoDeleteChanged = false;
+ filterTextChanged = false;
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.feedinfo, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ menu.findItem(R.id.support_item).setVisible(
+ feed != null && feed.getPaymentLink() != null);
+ menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null);
+ menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null &&
+ IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ finish();
+ return true;
+ default:
+ try {
+ return FeedMenuHandler.onOptionsItemClicked(this, item, feed);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(this,
+ e.getMessage());
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void updateAutoDownloadSettings() {
+ if (feed != null && feed.getPreferences() != null) {
+ boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload();
+ rdoFilterInclude.setEnabled(enabled);
+ rdoFilterExclude.setEnabled(enabled);
+ etxtFilterText.setEnabled(enabled);
+ }
+ }
+
+ private static class ApplyToEpisodesDialog extends ConfirmationDialog {
+
+ private final Feed feed;
+ private final boolean autoDownload;
+
+ ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) {
+ super(context, R.string.auto_download_apply_to_items_title,
+ R.string.auto_download_apply_to_items_message);
+ this.feed = feed;
+ this.autoDownload = autoDownload;
+ setPositiveText(R.string.yes);
+ setNegativeText(R.string.no);
+ }
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
+ }
+ }
+
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java
index be1c9f9e6..2b4384a02 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java
@@ -4,7 +4,7 @@ package de.danoeh.antennapod.activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
-import android.support.v7.app.ActionBarActivity;
+import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -18,11 +18,10 @@ import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-import de.danoeh.antennapod.preferences.PreferenceController;
/** Guides the user through the authentication process */
-public class FlattrAuthActivity extends ActionBarActivity {
+public class FlattrAuthActivity extends AppCompatActivity {
private static final String TAG = "FlattrAuthActivity";
private TextView txtvExplanation;
@@ -42,9 +41,9 @@ public class FlattrAuthActivity extends ActionBarActivity {
if (BuildConfig.DEBUG) Log.d(TAG, "Activity created");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.flattr_auth);
- txtvExplanation = (TextView) findViewById(R.id.txtvExplanation);
- butAuthenticate = (Button) findViewById(R.id.but_authenticate);
- butReturn = (Button) findViewById(R.id.but_return_home);
+ txtvExplanation = findViewById(R.id.txtvExplanation);
+ butAuthenticate = findViewById(R.id.but_authenticate);
+ butReturn = findViewById(R.id.but_return_home);
butReturn.setOnClickListener(v -> {
Intent intent = new Intent(FlattrAuthActivity.this, MainActivity.class);
@@ -104,7 +103,7 @@ public class FlattrAuthActivity extends ActionBarActivity {
switch (item.getItemId()) {
case android.R.id.home:
if (authSuccessful) {
- Intent intent = new Intent(this, PreferenceController.getPreferenceActivity());
+ Intent intent = new Intent(this, PreferenceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
new file mode 100644
index 000000000..e6c9c37cc
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
@@ -0,0 +1,190 @@
+package de.danoeh.antennapod.activity;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.ParcelFileDescriptor;
+import android.support.design.widget.Snackbar;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.view.MenuItem;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.channels.FileChannel;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+
+/**
+ * Displays the 'import/export' screen
+ */
+public class ImportExportActivity extends AppCompatActivity {
+ private static final int REQUEST_CODE_RESTORE = 43;
+ private static final int REQUEST_CODE_BACKUP_DOCUMENT = 44;
+ private static final String EXPORT_FILENAME = "AntennaPodBackup.db";
+ private static final String TAG = ImportExportActivity.class.getSimpleName();
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ setContentView(R.layout.import_export_activity);
+
+ findViewById(R.id.button_export).setOnClickListener(view -> backup());
+ findViewById(R.id.button_import).setOnClickListener(view -> restore());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+ return true;
+ } else {
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void backup() {
+ if (Build.VERSION.SDK_INT >= 19) {
+ Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType("application/x-sqlite3")
+ .putExtra(Intent.EXTRA_TITLE, EXPORT_FILENAME);
+
+ startActivityForResult(intent, REQUEST_CODE_BACKUP_DOCUMENT);
+ } else {
+ try {
+ File sd = Environment.getExternalStorageDirectory();
+ File backupDB = new File(sd, EXPORT_FILENAME);
+ writeBackupTo(new FileOutputStream(backupDB));
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
+ }
+ }
+ }
+
+ private void restore() {
+ if (Build.VERSION.SDK_INT >= 19) {
+ Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
+ intent.setType("*/*");
+ startActivityForResult(intent, REQUEST_CODE_RESTORE);
+ } else {
+ Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
+ intent.setType("*/*");
+ startActivityForResult(Intent.createChooser(intent,
+ getString(R.string.import_select_file)), REQUEST_CODE_RESTORE);
+ }
+ }
+
+ @Override
+ public void onActivityResult(int requestCode, int resultCode, Intent resultData) {
+ if (resultCode != RESULT_OK || resultData == null) {
+ return;
+ }
+ Uri uri = resultData.getData();
+
+ if (requestCode == REQUEST_CODE_RESTORE) {
+ restoreFrom(uri);
+ } else if (requestCode == REQUEST_CODE_BACKUP_DOCUMENT) {
+ backupToDocument(uri);
+ }
+ }
+
+ private void restoreFrom(Uri inputUri) {
+ File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
+ InputStream inputStream = null;
+ try {
+ inputStream = getContentResolver().openInputStream(inputUri);
+ FileUtils.copyInputStreamToFile(inputStream, currentDB);
+ displayImportSuccessDialog();
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
+ } finally {
+ IOUtils.closeQuietly(inputStream);
+ }
+ }
+
+ private void displayImportSuccessDialog() {
+ AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this);
+ d.setMessage(R.string.import_ok);
+ d.setCancelable(false);
+ d.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
+ Intent intent = new Intent(getApplicationContext(), SplashActivity.class);
+ ComponentName cn = intent.getComponent();
+ Intent mainIntent = Intent.makeRestartActivityTask(cn);
+ startActivity(mainIntent);
+ });
+ d.show();
+ }
+
+ private void backupToDocument(Uri uri) {
+ ParcelFileDescriptor pfd = null;
+ FileOutputStream fileOutputStream = null;
+ try {
+ pfd = getContentResolver().openFileDescriptor(uri, "w");
+ fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
+ writeBackupTo(fileOutputStream);
+
+ Snackbar.make(findViewById(R.id.import_export_layout),
+ R.string.export_ok, Snackbar.LENGTH_SHORT).show();
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
+ } finally {
+ IOUtils.closeQuietly(fileOutputStream);
+
+ if (pfd != null) {
+ try {
+ pfd.close();
+ } catch (IOException e) {
+ Log.d(TAG, "Unable to close ParcelFileDescriptor");
+ }
+ }
+ }
+ }
+
+ private void writeBackupTo(FileOutputStream outFileStream) {
+ FileChannel src = null;
+ FileChannel dst = null;
+ try {
+ File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME);
+
+ if (currentDB.exists()) {
+ src = new FileInputStream(currentDB).getChannel();
+ dst = outFileStream.getChannel();
+ dst.transferFrom(src, 0, src.size());
+
+ Snackbar.make(findViewById(R.id.import_export_layout),
+ R.string.export_ok, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(findViewById(R.id.import_export_layout),
+ "Can not access current database", Snackbar.LENGTH_SHORT).show();
+ }
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
+ } finally {
+ IOUtils.closeQuietly(src);
+ IOUtils.closeQuietly(dst);
+ }
+ }
+}
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 f0fcdca90..9f4fbe271 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -27,6 +27,7 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
+import android.widget.Toast;
import com.bumptech.glide.Glide;
@@ -42,17 +43,20 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
+import de.danoeh.antennapod.core.event.ServiceEvent;
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;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
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.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.Flavors;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
@@ -64,12 +68,11 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
-import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* The activity that is shown when the user launches the app.
@@ -83,7 +86,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
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";
+ private static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag";
public static final String EXTRA_NAV_TYPE = "nav_type";
public static final String EXTRA_NAV_INDEX = "nav_index";
@@ -91,8 +94,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
public static final String EXTRA_FEED_ID = "fragment_feed_id";
- public static final String SAVE_BACKSTACK_COUNT = "backstackCount";
- public static final String SAVE_TITLE = "title";
+ private static final String SAVE_BACKSTACK_COUNT = "backstackCount";
+ private static final String SAVE_TITLE = "title";
public static final String[] NAV_DRAWER_TAGS = {
QueueFragment.TAG,
@@ -119,7 +122,9 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private ProgressDialog pd;
- private Subscription subscription;
+ private Disposable disposable;
+
+ private long lastBackButtonPressTime = 0;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -128,7 +133,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
StorageUtils.checkStorageAvailability(this);
setContentView(R.layout.main);
- toolbar = (Toolbar) findViewById(R.id.toolbar);
+ toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -140,8 +145,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
currentTitle = getTitle();
- drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- navList = (ListView) findViewById(R.id.nav_list);
+ drawerLayout = findViewById(R.id.drawer_layout);
+ navList = findViewById(R.id.nav_list);
navDrawer = findViewById(R.id.nav_layout);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
@@ -173,7 +178,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
findViewById(R.id.nav_settings).setOnClickListener(v -> {
drawerLayout.closeDrawer(navDrawer);
- startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity()));
+ startActivity(new Intent(MainActivity.this, PreferenceActivity.class));
});
FragmentTransaction transaction = fm.beginTransaction();
@@ -201,6 +206,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
transaction.commit();
checkFirstLaunch();
+ NotificationUtils.createChannels(this);
+ UserPreferences.restartUpdateAlarm(false);
}
private void saveLastNavFragment(String tag) {
@@ -236,7 +243,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
}
- public void showDrawerPreferencesDialog() {
+ private void showDrawerPreferencesDialog() {
final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
String[] navLabels = new String[NAV_DRAWER_TAGS.length];
final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length];
@@ -270,7 +277,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
return (navDrawerData != null) ? navDrawerData.feeds : null;
}
- public void loadFragment(int index, Bundle args) {
+ private void loadFragment(int index, Bundle args) {
Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")");
if (index < navAdapter.getSubscriptionOffset()) {
String tag = navAdapter.getTags().get(index);
@@ -399,7 +406,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
}
- private AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() {
+ private final AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
int viewType = parent.getAdapter().getItemViewType(position);
@@ -410,7 +417,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
};
- private AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() {
+ private final AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if(position < navAdapter.getTags().size()) {
@@ -467,7 +474,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
protected void onResume() {
super.onResume();
StorageUtils.checkStorageAvailability(this);
- DBTasks.checkShouldRefreshFeeds(getApplicationContext());
+ AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
Intent intent = getIntent();
if (intent.hasExtra(EXTRA_FEED_ID) ||
@@ -484,8 +491,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
if(pd != null) {
pd.dismiss();
@@ -573,10 +580,29 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset());
switch(item.getItemId()) {
case R.id.mark_all_seen_item:
- DBWriter.markFeedSeen(feed.getId());
+ ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(this,
+ R.string.mark_all_seen_label,
+ R.string.mark_all_seen_confirmation_msg) {
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ dialog.dismiss();
+ DBWriter.markFeedSeen(feed.getId());
+ }
+ };
+ markAllSeenConfirmationDialog.createNewDialog().show();
return true;
case R.id.mark_all_read_item:
- DBWriter.markFeedRead(feed.getId());
+ ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(this,
+ R.string.mark_all_read_label,
+ R.string.mark_all_read_confirmation_msg) {
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ dialog.dismiss();
+ DBWriter.markFeedRead(feed.getId());
+ }
+ };
+ markAllReadConfirmationDialog.createNewDialog().show();
return true;
case R.id.rename_item:
new RenameFeedDialog(this, feed).show();
@@ -605,8 +631,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
remover.skipOnCompletion = true;
int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
- sendBroadcast(new Intent(
- PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ IntentUtils.sendLocalBroadcast(MainActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
}
}
remover.executeAsync();
@@ -621,17 +646,47 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
public void onBackPressed() {
- if(isDrawerOpen()) {
+ if (isDrawerOpen()) {
drawerLayout.closeDrawer(navDrawer);
- } else {
+ } else if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
super.onBackPressed();
+ } else {
+ switch (UserPreferences.getBackButtonBehavior()) {
+ case OPEN_DRAWER:
+ drawerLayout.openDrawer(navDrawer);
+ break;
+ case SHOW_PROMPT:
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.close_prompt)
+ .setPositiveButton(R.string.yes, (dialogInterface, i) -> MainActivity.super.onBackPressed())
+ .setNegativeButton(R.string.no, null)
+ .setCancelable(false)
+ .show();
+ break;
+ case DOUBLE_TAP:
+ if (lastBackButtonPressTime < System.currentTimeMillis() - 2000) {
+ Toast.makeText(this, R.string.double_tap_toast, Toast.LENGTH_SHORT).show();
+ lastBackButtonPressTime = System.currentTimeMillis();
+ } else {
+ super.onBackPressed();
+ }
+ break;
+ case GO_TO_PAGE:
+ if (getLastNavFragment().equals(UserPreferences.getBackButtonGoToPage())) {
+ super.onBackPressed();
+ } else {
+ loadFragment(UserPreferences.getBackButtonGoToPage(), null);
+ }
+ break;
+ default: super.onBackPressed();
+ }
}
}
private DBReader.NavDrawerData navDrawerData;
private int selectedNavListIndex = 0;
- private NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
+ private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@Override
public int getCount() {
if (navDrawerData != null) {
@@ -695,8 +750,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
};
private void loadData() {
- subscription = Observable.fromCallable(DBReader::getNavDrawerData)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(DBReader::getNavDrawerData)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
boolean handleIntent = (navDrawerData == null);
@@ -721,6 +776,15 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
loadData();
}
+ public void onEventMainThread(ServiceEvent event) {
+ Log.d(TAG, "onEvent(" + event + ")");
+ switch(event.action) {
+ case SERVICE_STARTED:
+ externalPlayerFragment.connectToPlaybackService();
+ break;
+ }
+ }
+
public void onEventMainThread(ProgressEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
switch(event.action) {
@@ -744,14 +808,12 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
View parentLayout = findViewById(R.id.drawer_layout);
Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT);
if(event.action != null) {
- snackbar.setAction(getString(R.string.undo), v -> {
- event.action.run();
- });
+ snackbar.setAction(getString(R.string.undo), v -> event.action.run());
}
snackbar.show();
}
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
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 21a0fa66f..1cddaa655 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -1,9 +1,11 @@
package de.danoeh.antennapod.activity;
+import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Intent;
import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
@@ -11,6 +13,9 @@ import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.app.ActivityOptionsCompat;
+import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
@@ -33,28 +38,36 @@ import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import java.util.Locale;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.ServiceEvent;
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.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
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.util.Consumer;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.Flavors;
+import de.danoeh.antennapod.core.util.Function;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.Supplier;
+import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
+import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.dialog.SleepTimerDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
-import rx.Observable;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.functions.Action1;
-import rx.functions.Func1;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
@@ -65,23 +78,26 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static final String TAG = "MediaplayerActivity";
private static final String PREFS = "MediaPlayerActivityPreferences";
private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
+ private static final int REQUEST_CODE_STORAGE = 42;
- protected PlaybackController controller;
+ PlaybackController controller;
- protected TextView txtvPosition;
- protected TextView txtvLength;
- protected SeekBar sbPosition;
- protected ImageButton butRev;
- protected TextView txtvRev;
- protected ImageButton butPlay;
- protected ImageButton butFF;
- protected TextView txtvFF;
- protected ImageButton butSkip;
+ private TextView txtvPosition;
+ private TextView txtvLength;
+ SeekBar sbPosition;
+ private ImageButton butRev;
+ private TextView txtvRev;
+ private ImageButton butPlay;
+ private ImageButton butFF;
+ private TextView txtvFF;
+ private ImageButton butSkip;
- protected boolean showTimeLeft = false;
+ private boolean showTimeLeft = false;
private boolean isFavorite = false;
+ private Disposable disposable;
+
private PlaybackController newPlaybackController() {
return new PlaybackController(this, false) {
@@ -183,31 +199,31 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
};
}
- protected static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
+ private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
return activity.txtvFF;
}
- protected static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
+ private static TextView getTxtvRevFromActivity(MediaplayerActivity activity) {
return activity.txtvRev;
}
- protected void onSetSpeedAbilityChanged() {
+ private void onSetSpeedAbilityChanged() {
Log.d(TAG, "onSetSpeedAbilityChanged()");
updatePlaybackSpeedButton();
}
- protected void onPlaybackSpeedChange() {
+ private void onPlaybackSpeedChange() {
updatePlaybackSpeedButtonText();
}
- protected void onServiceQueried() {
+ private void onServiceQueried() {
supportInvalidateOptionsMenu();
}
- protected void chooseTheme() {
+ void chooseTheme() {
setTheme(UserPreferences.getTheme());
}
- protected void setScreenOn(boolean enable) {
+ void setScreenOn(boolean enable) {
}
@Override
@@ -218,15 +234,16 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
Log.d(TAG, "onCreate()");
StorageUtils.checkStorageAvailability(this);
- orientation = getResources().getConfiguration().orientation;
getWindow().setFormat(PixelFormat.TRANSPARENT);
}
@Override
protected void onPause() {
- if(controller != null) {
- controller.reinitServiceIfPaused();
- controller.pause();
+ if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
+ if (controller != null) {
+ controller.reinitServiceIfPaused();
+ controller.pause();
+ }
}
super.onPause();
}
@@ -248,17 +265,12 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
*/
protected abstract void onBufferEnd();
- protected void onBufferUpdate(float progress) {
+ private void onBufferUpdate(float progress) {
if (sbPosition != null) {
- sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax());
+ sbPosition.setSecondaryProgress((int) (progress * sbPosition.getMax()));
}
}
- /**
- * Current screen orientation.
- */
- protected int orientation;
-
@Override
protected void onStart() {
super.onStart();
@@ -266,6 +278,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release();
}
controller = newPlaybackController();
+ setupGUI();
+ loadMediaInfo();
+ onPositionObserverUpdate();
}
@Override
@@ -275,6 +290,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.release();
controller = null; // prevent leak
}
+ if (disposable != null) {
+ disposable.dispose();
+ }
super.onStop();
}
@@ -316,11 +334,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
((FeedMedia) media).getItem().getFlattrStatus().flattrable()
);
- boolean hasWebsiteLink = media != null && media.getWebsiteLink() != null;
+ boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null );
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
boolean isItemAndHasLink = isFeedMedia &&
- ((FeedMedia) media).getItem() != null && ((FeedMedia) media).getItem().getLink() != null;
+ ShareUtils.hasLinkToShare(((FeedMedia) media).getItem());
menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink);
menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink);
@@ -368,7 +386,17 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
| Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
+
+ View cover = findViewById(R.id.imgvCover);
+ if (cover != null && Build.VERSION.SDK_INT >= 16) {
+ ActivityOptionsCompat options = ActivityOptionsCompat.
+ makeSceneTransitionAnimation(MediaplayerActivity.this,
+ cover, "coverTransition");
+ startActivity(intent, options.toBundle());
+ } else {
+ startActivity(intent);
+ }
+ finish();
return true;
} else {
if (media != null) {
@@ -546,7 +574,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
});
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(media.getWebsiteLink());
+ Uri uri = Uri.parse(getWebsiteLinkWithFallback(media));
startActivity(new Intent(Intent.ACTION_VIEW, uri));
break;
case R.id.support_item:
@@ -589,16 +617,36 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
}
+ private static String getWebsiteLinkWithFallback(Playable media) {
+ if (media == null) {
+ return null;
+ } else if (media.getWebsiteLink() != null) {
+ return media.getWebsiteLink();
+ } else if (media instanceof FeedMedia) {
+ return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem());
+ }
+ return null;
+ }
+
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume()");
StorageUtils.checkStorageAvailability(this);
- if(controller != null) {
+ if (controller != null) {
controller.init();
}
}
+ public void onEventMainThread(ServiceEvent event) {
+ Log.d(TAG, "onEvent(" + event + ")");
+ if (event.action == ServiceEvent.Action.SERVICE_STARTED) {
+ if (controller != null) {
+ controller.init();
+ }
+ }
+ }
+
/**
* Called by 'handleStatus()' when the PlaybackService is waiting for
* a video surface.
@@ -609,7 +657,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
protected abstract void clearStatusMsg();
- protected void onPositionObserverUpdate() {
+ void onPositionObserverUpdate() {
if (controller == null || txtvPosition == null || txtvLength == null) {
return;
}
@@ -645,12 +693,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* to the PlaybackService to ensure that the activity has the right
* FeedMedia object.
*/
- protected boolean loadMediaInfo() {
+ boolean loadMediaInfo() {
Log.d(TAG, "loadMediaInfo()");
if(controller == null || controller.getMedia() == null) {
return false;
}
- Playable media = controller.getMedia();
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
onPositionObserverUpdate();
@@ -659,18 +706,18 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return true;
}
- protected void updatePlaybackSpeedButton() {
+ void updatePlaybackSpeedButton() {
// Only meaningful on AudioplayerActivity, where it is overridden.
}
- protected void updatePlaybackSpeedButtonText() {
+ void updatePlaybackSpeedButtonText() {
// Only meaningful on AudioplayerActivity, where it is overridden.
}
/**
* Abstract directions to skip forward or back (rewind) and encapsulates behavior to get or set preference (including update of UI on the skip buttons).
*/
- static public enum SkipDirection {
+ public enum SkipDirection {
SKIP_FORWARD(
UserPreferences::getFastForwardSecs,
MediaplayerActivity::getTxtvFFFromActivity,
@@ -682,8 +729,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
R.string.pref_rewind);
private final Supplier<Integer> getPrefSecsFn;
- private final Func1<MediaplayerActivity, TextView> getTextViewFn;
- private final Action1<Integer> setPrefSecsFn;
+ private final Function<MediaplayerActivity, TextView> getTextViewFn;
+ private final Consumer<Integer> setPrefSecsFn;
private final int titleResourceID;
/**
@@ -695,7 +742,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* @param setPrefSecsFn Handle to function that sets the preference (setting) for the skip delta value (and optionally updates the button label with the current values)
* @param titleResourceID ID of the resource string with the title for a view
*/
- SkipDirection(Supplier<Integer> getPrefSecsFn, Func1<MediaplayerActivity, TextView> getTextViewFn, Action1<Integer> setPrefSecsFn, int titleResourceID) {
+ SkipDirection(Supplier<Integer> getPrefSecsFn, Function<MediaplayerActivity, TextView> getTextViewFn, Consumer<Integer> setPrefSecsFn, int titleResourceID) {
this.getPrefSecsFn = getPrefSecsFn;
this.getTextViewFn = getTextViewFn;
this.setPrefSecsFn = setPrefSecsFn;
@@ -714,10 +761,10 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
* @param activity MediaplyerActivity that contains textview to update the display of the skip delta setting (or null if nothing to update)
*/
public void setPrefSkipSeconds(int seconds, @Nullable Activity activity) {
- setPrefSecsFn.call(seconds);
+ setPrefSecsFn.accept(seconds);
if (activity != null && activity instanceof MediaplayerActivity) {
- TextView tv = getTextViewFn.call((MediaplayerActivity)activity);
+ TextView tv = getTextViewFn.apply((MediaplayerActivity)activity);
if (tv != null) tv.setText(String.valueOf(seconds));
}
}
@@ -753,15 +800,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
builder.create().show();
}
- protected void setupGUI() {
+ void setupGUI() {
setContentView(getContentViewResourceId());
- sbPosition = (SeekBar) findViewById(R.id.sbPosition);
- txtvPosition = (TextView) findViewById(R.id.txtvPosition);
+ sbPosition = findViewById(R.id.sbPosition);
+ txtvPosition = findViewById(R.id.txtvPosition);
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
Log.d("timeleft", showTimeLeft ? "true" : "false");
- txtvLength = (TextView) findViewById(R.id.txtvLength);
+ txtvLength = findViewById(R.id.txtvLength);
if (txtvLength != null) {
txtvLength.setOnClickListener(v -> {
showTimeLeft = !showTimeLeft;
@@ -785,18 +832,18 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
});
}
- butRev = (ImageButton) findViewById(R.id.butRev);
- txtvRev = (TextView) findViewById(R.id.txtvRev);
+ butRev = findViewById(R.id.butRev);
+ txtvRev = findViewById(R.id.txtvRev);
if (txtvRev != null) {
txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs()));
}
- butPlay = (ImageButton) findViewById(R.id.butPlay);
- butFF = (ImageButton) findViewById(R.id.butFF);
- txtvFF = (TextView) findViewById(R.id.txtvFF);
+ butPlay = findViewById(R.id.butPlay);
+ butFF = findViewById(R.id.butFF);
+ txtvFF = findViewById(R.id.txtvFF);
if (txtvFF != null) {
txtvFF.setText(String.valueOf(UserPreferences.getFastForwardSecs()));
}
- butSkip = (ImageButton) findViewById(R.id.butSkip);
+ butSkip = findViewById(R.id.butSkip);
// SEEKBAR SETUP
@@ -823,11 +870,12 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
if (butSkip != null) {
- butSkip.setOnClickListener(v -> sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)));
+ butSkip.setOnClickListener(v ->
+ IntentUtils.sendLocalBroadcast(MediaplayerActivity.this, PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
}
}
- protected void onRewind() {
+ void onRewind() {
if (controller == null) {
return;
}
@@ -835,14 +883,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
}
- protected void onPlayPause() {
+ void onPlayPause() {
if(controller == null) {
return;
}
+ controller.init();
controller.playPause();
}
- protected void onFastForward() {
+ void onFastForward() {
if (controller == null) {
return;
}
@@ -852,7 +901,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
protected abstract int getContentViewResourceId();
- void handleError(int errorCode) {
+ private void handleError(int errorCode) {
final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
errorDialog.setTitle(R.string.error_label);
errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode));
@@ -865,7 +914,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
errorDialog.create().show();
}
- float prog;
+ private float prog;
@Override
public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) {
@@ -896,22 +945,62 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private void checkFavorite() {
Playable playable = controller.getMedia();
- if (playable != null && playable instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia) playable).getItem();
- if (feedItem != null) {
- Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- item -> {
- boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
- if (isFavorite != isFav) {
- isFavorite = isFav;
- invalidateOptionsMenu();
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ if (!(playable instanceof FeedMedia)) {
+ return;
+ }
+ FeedItem feedItem = ((FeedMedia) playable).getItem();
+ if (feedItem == null) {
+ return;
+ }
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ item -> {
+ boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
+ if (isFavorite != isFav) {
+ isFavorite = isFav;
+ invalidateOptionsMenu();
+ }
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
+ void playExternalMedia(Intent intent, MediaType type) {
+ if (intent == null || intent.getData() == null) {
+ return;
+ }
+ if (Build.VERSION.SDK_INT >= 23
+ && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
+ != PackageManager.PERMISSION_GRANTED) {
+
+ if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
+ Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
+ } else {
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
+ REQUEST_CODE_STORAGE);
}
+ return;
}
+
+ Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
+ ExternalMedia media = new ExternalMedia(intent.getData().getPath(), type);
+
+ new PlaybackServiceStarter(this, media)
+ .startWhenPrepared(true)
+ .shouldStream(false)
+ .prepareImmediately(true)
+ .start();
}
+ @Override
+ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+ if (requestCode == REQUEST_CODE_STORAGE) {
+ if (grantResults.length <= 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+ Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
+ }
+ }
+ }
}
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 b3cda69d3..a2389dabd 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -6,6 +6,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
+import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.Snackbar;
@@ -45,8 +46,9 @@ 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.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
@@ -60,28 +62,28 @@ import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
-import de.danoeh.antennapod.preferences.PreferenceController;
import de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Activity for playing files that do not require a video surface.
*/
public abstract class MediaplayerInfoActivity extends MediaplayerActivity implements NavDrawerActivity {
+ private static final String TAG = "MediaplayerInfoActivity";
+
private static final int POS_COVER = 0;
private static final int POS_DESCR = 1;
private static final int POS_CHAPTERS = 2;
private static final int NUM_CONTENT_FRAGMENTS = 3;
- final String TAG = "MediaplayerInfoActivity";
private static final String PREFS = "AudioPlayerActivityPreferences";
private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition";
- public static final String[] NAV_DRAWER_TAGS = {
+ private static final String[] NAV_DRAWER_TAGS = {
QueueFragment.TAG,
EpisodesFragment.TAG,
SubscriptionFragment.TAG,
@@ -91,8 +93,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
NavListAdapter.SUBSCRIPTION_LIST_TAG
};
- protected Button butPlaybackSpeed;
- protected ImageButton butCastDisconnect;
+ Button butPlaybackSpeed;
+ ImageButton butCastDisconnect;
private DrawerLayout drawerLayout;
private NavListAdapter navAdapter;
private ListView navList;
@@ -104,7 +106,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
private ViewPager pager;
private MediaplayerInfoPagerAdapter pagerAdapter;
- private Subscription subscription;
+ private Disposable disposable;
@Override
protected void onPause() {
@@ -113,14 +115,20 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ supportPostponeEnterTransition();
+ }
+
+ @Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop()");
if(pagerAdapter != null) {
pagerAdapter.setController(null);
}
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
EventDistributor.getInstance().unregister(contentUpdate);
saveCurrentFragment();
@@ -145,7 +153,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
setTheme(UserPreferences.getNoTitleTheme());
}
- protected void saveCurrentFragment() {
+ void saveCurrentFragment() {
if(pager == null) {
return;
}
@@ -153,7 +161,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
prefs.edit()
.putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, pager.getCurrentItem())
- .commit();
+ .apply();
}
@Override
@@ -179,7 +187,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
pagerAdapter.onMediaChanged(media);
pagerAdapter.setController(controller);
}
- DBTasks.checkShouldRefreshFeeds(getApplicationContext());
+ AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext());
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
@@ -219,23 +227,23 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
protected void setupGUI() {
super.setupGUI();
- Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setTitle("");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
findViewById(R.id.shadow).setVisibility(View.GONE);
- AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBar);
+ AppBarLayout appBarLayout = findViewById(R.id.appBar);
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics());
appBarLayout.setElevation(px);
}
- drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
- navList = (ListView) findViewById(R.id.nav_list);
+ drawerLayout = findViewById(R.id.drawer_layout);
+ navList = findViewById(R.id.nav_list);
navDrawer = findViewById(R.id.nav_layout);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
drawerToggle.setDrawerIndicatorEnabled(false);
- drawerLayout.setDrawerListener(drawerToggle);
+ drawerLayout.addDrawerListener(drawerToggle);
navAdapter = new NavListAdapter(itemAccess, this);
navList.setAdapter(navAdapter);
@@ -263,20 +271,22 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
findViewById(R.id.nav_settings).setOnClickListener(v -> {
drawerLayout.closeDrawer(navDrawer);
- startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceController.getPreferenceActivity()));
+ startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceActivity.class));
});
- butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed);
- butCastDisconnect = (ImageButton) findViewById(R.id.butCastDisconnect);
+ butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed);
+ butCastDisconnect = findViewById(R.id.butCastDisconnect);
- pager = (ViewPager) findViewById(R.id.pager);
+ pager = findViewById(R.id.pager);
pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager(), media);
pagerAdapter.setController(controller);
pager.setAdapter(pagerAdapter);
- CirclePageIndicator pageIndicator = (CirclePageIndicator) findViewById(R.id.page_indicator);
+ CirclePageIndicator pageIndicator = findViewById(R.id.page_indicator);
pageIndicator.setViewPager(pager);
loadLastFragment();
pager.onSaveInstanceState();
+
+ navList.post(this::supportStartPostponedEnterTransition);
}
@Override
@@ -297,7 +307,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
return true;
}
- public void notifyMediaPositionChanged() {
+ private void notifyMediaPositionChanged() {
if(pagerAdapter == null) {
return;
}
@@ -347,7 +357,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
public boolean onOptionsItemSelected(MenuItem item) {
- return drawerToggle != null && drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item);
+ return (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item);
}
@Override
@@ -387,12 +397,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
new RenameFeedDialog(this, feed).show();
return true;
case R.id.remove_item:
- final FeedRemover remover = new FeedRemover(this, feed) {
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- }
- };
+ final FeedRemover remover = new FeedRemover(this, feed);
ConfirmationDialog conDialog = new ConfirmationDialog(this,
R.string.remove_feed_label,
getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) {
@@ -404,12 +409,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
Playable playable = controller.getMedia();
if (playable != null && playable instanceof FeedMedia) {
FeedMedia media = (FeedMedia) playable;
- if (media.getItem().getFeed().getId() == feed.getId()) {
+ if (media.getItem() != null && media.getItem().getFeed() != null &&
+ media.getItem().getFeed().getId() == feed.getId()) {
Log.d(TAG, "Currently playing episode is about to be deleted, skipping");
remover.skipOnCompletion = true;
if(controller.getStatus() == PlayerStatus.PLAYING) {
- sendBroadcast(new Intent(
- PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ IntentUtils.sendLocalBroadcast(MediaplayerInfoActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
}
}
}
@@ -438,7 +443,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
}
}
- public void showDrawerPreferencesDialog() {
+ private void showDrawerPreferencesDialog() {
final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems();
String[] navLabels = new String[NAV_DRAWER_TAGS.length];
final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length];
@@ -467,8 +472,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
private DBReader.NavDrawerData navDrawerData;
private void loadData() {
- subscription = Observable.fromCallable(DBReader::getNavDrawerData)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(DBReader::getNavDrawerData)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
navDrawerData = result;
@@ -483,14 +488,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
View parentLayout = findViewById(R.id.drawer_layout);
Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT);
if (event.action != null) {
- snackbar.setAction(getString(R.string.undo), v -> {
- event.action.run();
- });
+ snackbar.setAction(getString(R.string.undo), v -> event.action.run());
}
snackbar.show();
}
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
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 f6bf11e66..73da9a834 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -5,8 +5,9 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
-import android.os.Looper;
+import android.support.annotation.UiThread;
import android.support.v4.app.NavUtils;
+import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
@@ -27,6 +28,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@@ -55,7 +57,6 @@ import de.danoeh.antennapod.core.storage.DBReader;
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.DownloadError;
import de.danoeh.antennapod.core.util.FileNameGenerator;
@@ -65,11 +66,10 @@ import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscriber;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Downloads a feed from a feed URL and parses it. Subclasses can display the
@@ -84,7 +84,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
public static final String ARG_FEEDURL = "arg.feedurl";
// Optional argument: specify a title for the actionbar.
public static final String ARG_TITLE = "title";
- public static final int RESULT_ERROR = 2;
+ 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;
@@ -98,23 +98,21 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private Button subscribeButton;
- private Subscription download;
- private Subscription parser;
- private Subscription updater;
- private EventDistributor.EventListener listener = new EventDistributor.EventListener() {
+ 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.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
feeds -> {
OnlineFeedViewActivity.this.feeds = feeds;
setSubscribeButtonState(feed);
- }, error -> {
- Log.e(TAG, Log.getStackTraceString(error));
- }
+ }, error -> Log.e(TAG, Log.getStackTraceString(error))
);
} else if ((arg & EVENTS) != 0) {
setSubscribeButtonState(feed);
@@ -131,10 +129,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getTheme());
super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
- if (getIntent() != null && getIntent().hasExtra(ARG_TITLE)) {
- getSupportActionBar().setTitle(getIntent().getStringExtra(ARG_TITLE));
+ if (actionBar != null && getIntent() != null && getIntent().hasExtra(ARG_TITLE)) {
+ actionBar.setTitle(getIntent().getStringExtra(ARG_TITLE));
}
StorageUtils.checkStorageAvailability(this);
@@ -144,9 +145,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
feedUrl = getIntent().getStringExtra(ARG_FEEDURL);
} else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
|| TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
- feedUrl = (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND))
+ feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
- getSupportActionBar().setTitle(R.string.add_feed_label);
+ if (actionBar != null) {
+ actionBar.setTitle(R.string.add_feed_label);
+ }
} else {
throw new IllegalArgumentException("Activity must be started with feedurl argument!");
}
@@ -210,13 +213,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
public void onDestroy() {
super.onDestroy();
if(updater != null) {
- updater.unsubscribe();
+ updater.dispose();
}
if(download != null) {
- download.unsubscribe();
+ download.dispose();
}
if(parser != null) {
- parser.unsubscribe();
+ parser.dispose();
}
}
@@ -265,18 +268,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password,
true, null);
- download = Observable.create(new Observable.OnSubscribe<DownloadStatus>() {
- @Override
- public void call(Subscriber<? super DownloadStatus> subscriber) {
- feeds = DBReader.getFeedList();
- downloader = new HttpDownloader(request);
- downloader.call();
- Log.d(TAG, "Download was completed");
- subscriber.onNext(downloader.getResult());
- subscriber.onCompleted();
- }
+ download = Observable.fromCallable(() -> {
+ feeds = DBReader.getFeedList();
+ downloader = new HttpDownloader(request);
+ downloader.call();
+ return downloader.getResult();
})
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::checkDownloadResult,
error -> Log.e(TAG, Log.getStackTraceString(error)));
@@ -286,6 +284,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
if (status == null) {
Log.wtf(TAG, "DownloadStatus returned by Downloader was null");
finish();
+ return;
}
if (status.isCancelled()) {
return;
@@ -300,7 +299,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
} else {
String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this);
- if (errorMsg != null && status.getReasonDetailed() != null) {
+ if (status.getReasonDetailed() != null) {
errorMsg += " (" + status.getReasonDetailed() + ")";
}
showErrorDialog(errorMsg);
@@ -308,44 +307,43 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
private void parseFeed() {
- if (feed == null || feed.getFile_url() == null && feed.isDownloaded()) {
+ if (feed == null || (feed.getFile_url() == null && feed.isDownloaded())) {
throw new IllegalStateException("feed must be non-null and downloaded when parseFeed is called");
}
Log.d(TAG, "Parsing feed");
- parser = Observable.create(new Observable.OnSubscribe<FeedHandlerResult>() {
- @Override
- public void call(Subscriber<? super FeedHandlerResult> subscriber) {
- FeedHandler handler = new FeedHandler();
- try {
- FeedHandlerResult result = handler.parseFeed(feed);
- subscriber.onNext(result);
- } catch (UnsupportedFeedtypeException e) {
- Log.d(TAG, "Unsupported feed type detected");
- if (TextUtils.equals("html", e.getRootElement().toLowerCase())) {
- showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
- } else {
- subscriber.onError(e);
- }
- } catch (Exception e) {
- Log.e(TAG, Log.getStackTraceString(e));
- subscriber.onError(e);
- } finally {
- boolean rc = new File(feed.getFile_url()).delete();
- Log.d(TAG, "Deleted feed source file. Result: " + rc);
- subscriber.onCompleted();
+ parser = Observable.fromCallable(() -> {
+ FeedHandler handler = new FeedHandler();
+ try {
+ return handler.parseFeed(feed);
+ } catch (UnsupportedFeedtypeException e) {
+ Log.d(TAG, "Unsupported feed type detected");
+ if ("html".equalsIgnoreCase(e.getRootElement())) {
+ showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url());
+ return null;
+ } else {
+ throw e;
}
+ } catch (Exception e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ throw e;
+ } finally {
+ boolean rc = new File(feed.getFile_url()).delete();
+ Log.d(TAG, "Deleted feed source file. Result: " + rc);
}
})
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- beforeShowFeedInformation(result.feed);
- showFeedInformation(result.feed, result.alternateFeedUrls);
+ if(result != null) {
+ beforeShowFeedInformation(result.feed);
+ showFeedInformation(result.feed, result.alternateFeedUrls);
+ }
}, error -> {
String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString(
OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")";
showErrorDialog(errorMsg);
+ Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error));
});
}
@@ -382,30 +380,30 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
this.feed = feed;
this.selectedDownloadUrl = feed.getDownload_url();
EventDistributor.getInstance().register(listener);
- ListView listView = (ListView) findViewById(R.id.listview);
- LayoutInflater inflater = (LayoutInflater)
- getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ ListView listView = findViewById(R.id.listview);
+ 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 = (ImageView) header.findViewById(R.id.imgvCover);
- TextView title = (TextView) header.findViewById(R.id.txtvTitle);
- TextView author = (TextView) header.findViewById(R.id.txtvAuthor);
- TextView description = (TextView) header.findViewById(R.id.txtvDescription);
- Spinner spAlternateUrls = (Spinner) header.findViewById(R.id.spinnerAlternateUrls);
+ ImageView cover = header.findViewById(R.id.imgvCover);
+ TextView title = header.findViewById(R.id.txtvTitle);
+ TextView author = header.findViewById(R.id.txtvAuthor);
+ TextView description = header.findViewById(R.id.txtvDescription);
+ Spinner spAlternateUrls = header.findViewById(R.id.spinnerAlternateUrls);
- subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
+ subscribeButton = header.findViewById(R.id.butSubscribe);
- if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) {
+ if (StringUtils.isNotBlank(feed.getImageUrl())) {
Glide.with(this)
- .load(feed.getImage().getDownload_url())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .load(feed.getImageUrl())
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(cover);
}
@@ -414,7 +412,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
description.setText(feed.getDescription());
subscribeButton.setOnClickListener(v -> {
- if(feed != null && feedInFeedlist(feed)) {
+ if(feedInFeedlist(feed)) {
Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class);
// feed.getId() is always 0, we have to retrieve the id from the feed list from
// the database
@@ -508,8 +506,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
return 0;
}
+ @UiThread
private void showErrorDialog(String errorMsg) {
- assert(Looper.myLooper() == Looper.getMainLooper()); // run on UI thread
if (!isFinishing() && !isPaused) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.error_label);
@@ -586,7 +584,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
private class FeedViewAuthenticationDialog extends AuthenticationDialog {
- private String feedUrl;
+ private final String feedUrl;
FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) {
super(context, titleRes, true, false, null, null);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
index 355e0f372..72759c59c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
@@ -39,9 +39,9 @@ public class OpmlFeedChooserActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
setContentView(R.layout.opml_selection);
- butConfirm = (Button) findViewById(R.id.butConfirm);
- butCancel = (Button) findViewById(R.id.butCancel);
- feedlist = (ListView) findViewById(R.id.feedlist);
+ butConfirm = findViewById(R.id.butConfirm);
+ butCancel = findViewById(R.id.butCancel);
+ feedlist = findViewById(R.id.feedlist);
feedlist.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
listAdapter = new ArrayAdapter<>(this,
@@ -97,7 +97,7 @@ public class OpmlFeedChooserActivity extends AppCompatActivity {
}
private List<String> getTitleList() {
- List<String> result = new ArrayList<String>();
+ List<String> result = new ArrayList<>();
if (OpmlImportHolder.getReadElements() != null) {
for (OpmlElement element : OpmlImportHolder.getReadElements()) {
result.add(element.getText());
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 07b0b3cdb..c04ae051e 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
@@ -68,7 +68,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
}
}
- protected void importUri(@Nullable Uri uri) {
+ void importUri(@Nullable Uri uri) {
if(uri == null) {
new MaterialDialog.Builder(this)
.content(R.string.opml_import_error_no_file)
@@ -114,7 +114,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
}
/** Starts the import process. */
- protected void startImport() {
+ private void startImport() {
try {
Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8);
importWorker = new OpmlImportWorker(this, mReader) {
@@ -144,7 +144,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
}
}
- protected boolean finishWhenCanceled() {
+ boolean finishWhenCanceled() {
return false;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
index eb6b473d2..a63d3b735 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java
@@ -36,26 +36,25 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
setContentView(R.layout.opml_import);
- final TextView txtvHeaderExplanation1 = (TextView) findViewById(R.id.txtvHeadingExplanation1);
- final TextView txtvExplanation1 = (TextView) findViewById(R.id.txtvExplanation1);
- final TextView txtvHeaderExplanation2 = (TextView) findViewById(R.id.txtvHeadingExplanation2);
- final TextView txtvExplanation2 = (TextView) findViewById(R.id.txtvExplanation2);
- final TextView txtvHeaderExplanation3 = (TextView) findViewById(R.id.txtvHeadingExplanation3);
+ final TextView txtvHeaderExplanation1 = findViewById(R.id.txtvHeadingExplanation1);
+ final TextView txtvExplanation1 = findViewById(R.id.txtvExplanation1);
+ final TextView txtvHeaderExplanation2 = findViewById(R.id.txtvHeadingExplanation2);
+ final TextView txtvExplanation2 = findViewById(R.id.txtvExplanation2);
+ final TextView txtvHeaderExplanation3 = findViewById(R.id.txtvHeadingExplanation3);
- Button butChooseFilesystem = (Button) findViewById(R.id.butChooseFileFromFilesystem);
+ Button butChooseFilesystem = findViewById(R.id.butChooseFileFromFilesystem);
butChooseFilesystem.setOnClickListener(v -> chooseFileFromFilesystem());
- Button butChooseExternal = (Button) findViewById(R.id.butChooseFileFromExternal);
+ Button butChooseExternal = findViewById(R.id.butChooseFileFromExternal);
butChooseExternal.setOnClickListener(v -> chooseFileFromExternal());
int nextOption = 1;
String optionLabel = getString(R.string.opml_import_option);
intentPickAction = new Intent(Intent.ACTION_PICK);
- intentPickAction.setData(Uri.parse("file://"));
if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
intentPickAction.setData(null);
- if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
+ if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) {
txtvHeaderExplanation1.setVisibility(View.GONE);
txtvExplanation1.setVisibility(View.GONE);
findViewById(R.id.divider1).setVisibility(View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java
index b01cf43e4..dc5570dc0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java
@@ -14,6 +14,8 @@ import de.danoeh.antennapod.core.export.opml.OpmlElement;
*/
public class OpmlImportHolder {
+ private OpmlImportHolder(){}
+
private static ArrayList<OpmlElement> readElements;
public static ArrayList<OpmlElement> getReadElements() {
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
index dd932814f..452e91bd3 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -1,19 +1,20 @@
package de.danoeh.antennapod.activity;
-import android.annotation.TargetApi;
-import android.app.Activity;
import android.content.Intent;
-import android.os.Build;
import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceFragment;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.support.v7.preference.PreferenceScreen;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.widget.FrameLayout;
+import com.bytehamster.lib.preferencesearch.SearchPreferenceResult;
+import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener;
+
import java.lang.ref.WeakReference;
import de.danoeh.antennapod.R;
@@ -24,25 +25,40 @@ import de.danoeh.antennapod.preferences.PreferenceController;
* PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see
* PreferenceController.
*/
-public class PreferenceActivity extends AppCompatActivity {
+public class PreferenceActivity extends AppCompatActivity implements SearchPreferenceResultListener {
+ public static final String PARAM_RESOURCE = "resource";
private static WeakReference<PreferenceActivity> instance;
private PreferenceController preferenceController;
- private MainFragment prefFragment;
private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() {
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
+ private PreferenceFragmentCompat fragment;
+
+ @Override
+ public void setFragment(PreferenceFragmentCompat fragment) {
+ this.fragment = fragment;
+ }
+
+ @Override
+ public PreferenceFragmentCompat getFragment() {
+ return fragment;
+ }
+
@Override
public Preference findPreference(CharSequence key) {
- return prefFragment.findPreference(key);
+ return fragment.findPreference(key);
+ }
+
+ @Override
+ public PreferenceScreen getPreferenceScreen() {
+ return fragment.getPreferenceScreen();
}
@Override
- public Activity getActivity() {
+ public AppCompatActivity getActivity() {
return PreferenceActivity.this;
}
};
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
protected void onCreate(Bundle savedInstanceState) {
// This must be the FIRST thing we do, otherwise other code may not have the
@@ -68,8 +84,21 @@ public class PreferenceActivity extends AppCompatActivity {
// since the MainFragment depends on the preferenceController already being created
preferenceController = new PreferenceController(preferenceUI);
- prefFragment = new MainFragment();
- getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
+ showPreferenceScreen(R.xml.preferences, false);
+ }
+
+ private void showPreferenceScreen(int screen, boolean addHistory) {
+ PreferenceFragmentCompat prefFragment = new MainFragment();
+ preferenceUI.setFragment(prefFragment);
+ Bundle args = new Bundle();
+ args.putInt(PARAM_RESOURCE, screen);
+ prefFragment.setArguments(args);
+ if (addHistory) {
+ getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment)
+ .addToBackStack(getString(PreferenceController.getTitleOfPage(screen))).commit();
+ } else {
+ getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit();
+ }
}
@Override
@@ -88,24 +117,40 @@ public class PreferenceActivity extends AppCompatActivity {
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
- finish();
+ if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
+ finish();
+ } else {
+ getSupportFragmentManager().popBackStack();
+ }
return true;
default:
return false;
}
}
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- public static class MainFragment extends PreferenceFragment {
+ @Override
+ public void onSearchResultClicked(SearchPreferenceResult result) {
+ showPreferenceScreen(result.getResourceFile(), true);
+ result.highlight(preferenceUI.getFragment());
+ }
+
+ public static class MainFragment extends PreferenceFragmentCompat {
+ private int screen;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
- addPreferencesFromResource(R.xml.preferences);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ screen = getArguments().getInt(PARAM_RESOURCE);
+ addPreferencesFromResource(screen);
PreferenceActivity activity = instance.get();
- if(activity != null && activity.preferenceController != null) {
- activity.preferenceController.onCreate();
+ if (activity != null && activity.preferenceController != null) {
+ activity.preferenceUI.setFragment(this);
+ activity.preferenceController.onCreate(screen);
}
}
@@ -114,15 +159,17 @@ public class PreferenceActivity extends AppCompatActivity {
super.onResume();
PreferenceActivity activity = instance.get();
if(activity != null && activity.preferenceController != null) {
- activity.preferenceController.onResume();
+ activity.setTitle(PreferenceController.getTitleOfPage(screen));
+ activity.preferenceUI.setFragment(this);
+ activity.preferenceController.onResume(screen);
}
}
@Override
public void onPause() {
PreferenceActivity activity = instance.get();
- if(activity != null && activity.preferenceController != null) {
- activity.preferenceController.onPause();
+ if (screen == R.xml.preferences_gpodder) {
+ activity.preferenceController.unregisterGpodnet();
}
super.onPause();
}
@@ -130,8 +177,8 @@ public class PreferenceActivity extends AppCompatActivity {
@Override
public void onStop() {
PreferenceActivity activity = instance.get();
- if(activity != null && activity.preferenceController != null) {
- activity.preferenceController.onStop();
+ if (screen == R.xml.preferences_storage) {
+ activity.preferenceController.unsubscribeExportSubscription();
}
super.onStop();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java
deleted file mode 100644
index 390bec15c..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java
+++ /dev/null
@@ -1,97 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.annotation.SuppressLint;
-import android.app.Activity;
-import android.content.Intent;
-import android.content.res.Resources.Theme;
-import android.os.Bundle;
-import android.preference.Preference;
-import android.preference.PreferenceScreen;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.preferences.PreferenceController;
-
-/**
- * PreferenceActivity for API 10. In order to change the behavior of the preference UI, see
- * PreferenceController.
- */
-public class PreferenceActivityGingerbread extends android.preference.PreferenceActivity {
- private static final String TAG = "PreferenceActivity";
- private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() {
-
- @SuppressWarnings("deprecation")
- @Override
- public Preference findPreference(CharSequence key) {
- return PreferenceActivityGingerbread.this.findPreference(key);
- }
-
- @Override
- public Activity getActivity() {
- return PreferenceActivityGingerbread.this;
- }
- };
- private PreferenceController preferenceController;
-
- @SuppressLint("NewApi")
- @SuppressWarnings("deprecation")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
-
- addPreferencesFromResource(R.xml.preferences);
- preferenceController = new PreferenceController(preferenceUI);
- preferenceController.onCreate();
- }
-
-
- @Override
- protected void onResume() {
- super.onResume();
- preferenceController.onResume();
- }
-
- @Override
- protected void onPause() {
- preferenceController.onPause();
- super.onPause();
- }
-
- @Override
- protected void onStop() {
- preferenceController.onStop();
- super.onStop();
- }
-
- @Override
- protected void onApplyThemeResource(Theme theme, int resid, boolean first) {
- theme.applyStyle(UserPreferences.getTheme(), true);
- }
-
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- preferenceController.onActivityResult(requestCode, resultCode, data);
- }
-
- @SuppressWarnings("deprecation")
- @Override
- public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen,
- Preference preference) {
- super.onPreferenceTreeClick(preferenceScreen, preference);
- if (preference != null)
- if (preference instanceof PreferenceScreen)
- if (((PreferenceScreen) preference).getDialog() != null)
- ((PreferenceScreen) preference)
- .getDialog()
- .getWindow()
- .getDecorView()
- .setBackgroundDrawable(
- this.getWindow().getDecorView()
- .getBackground().getConstantState()
- .newDrawable()
- );
- return false;
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java
index b92ac8577..52102eee1 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java
@@ -1,23 +1,51 @@
package de.danoeh.antennapod.activity;
import android.content.Intent;
+import android.graphics.PorterDuff;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.Bundle;
import android.support.annotation.Nullable;
+import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.AppCompatActivity;
+import android.widget.ProgressBar;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import io.reactivex.Completable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
/**
- * Creator: vbarad
- * Date: 2016-12-03
- * Project: AntennaPod
+ * Shows the AntennaPod logo while waiting for the main activity to start
*/
-
public class SplashActivity extends AppCompatActivity {
- @Override
- protected void onCreate(@Nullable Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.splash);
+
+ ProgressBar progressBar = findViewById(R.id.progressBar);
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
+ Drawable wrapDrawable = DrawableCompat.wrap(progressBar.getIndeterminateDrawable());
+ DrawableCompat.setTint(wrapDrawable, 0xffffffff);
+ progressBar.setIndeterminateDrawable(DrawableCompat.unwrap(wrapDrawable));
+ } else {
+ progressBar.getIndeterminateDrawable().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN);
+ }
- Intent intent = new Intent(this, MainActivity.class);
- startActivity(intent);
- finish();
- }
+ Completable.create(subscriber -> {
+ // Trigger schema updates
+ PodDBAdapter.getInstance().open();
+ PodDBAdapter.getInstance().close();
+ subscriber.onComplete();
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(() -> {
+ Intent intent = new Intent(SplashActivity.this, MainActivity.class);
+ startActivity(intent);
+ finish();
+ });
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
index b2ff43c43..37199ccf7 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
@@ -20,10 +20,10 @@ import de.danoeh.antennapod.adapter.StatisticsListAdapter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays the 'statistics' screen
@@ -35,7 +35,7 @@ public class StatisticsActivity extends AppCompatActivity
private static final String PREF_NAME = "StatisticsActivityPrefs";
private static final String PREF_COUNT_ALL = "countAll";
- private Subscription subscription;
+ private Disposable disposable;
private TextView totalTimeTextView;
private ListView feedStatisticsList;
private ProgressBar progressBar;
@@ -53,9 +53,9 @@ public class StatisticsActivity extends AppCompatActivity
prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
countAll = prefs.getBoolean(PREF_COUNT_ALL, false);
- totalTimeTextView = (TextView) findViewById(R.id.total_time);
- feedStatisticsList = (ListView) findViewById(R.id.statistics_list);
- progressBar = (ProgressBar) findViewById(R.id.progressBar);
+ totalTimeTextView = findViewById(R.id.total_time);
+ feedStatisticsList = findViewById(R.id.statistics_list);
+ progressBar = findViewById(R.id.progressBar);
listAdapter = new StatisticsListAdapter(this);
listAdapter.setCountAll(countAll);
feedStatisticsList.setAdapter(listAdapter);
@@ -119,21 +119,19 @@ public class StatisticsActivity extends AppCompatActivity
}
private void loadStatistics() {
- if (subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
- subscription = Observable.fromCallable(() -> DBReader.getStatistics(countAll))
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(() -> DBReader.getStatistics(countAll))
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- if (result != null) {
- totalTimeTextView.setText(Converter
- .shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime));
- listAdapter.update(result.feedTime);
- progressBar.setVisibility(View.GONE);
- totalTimeTextView.setVisibility(View.VISIBLE);
- feedStatisticsList.setVisibility(View.VISIBLE);
- }
+ totalTimeTextView.setText(Converter
+ .shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime));
+ listAdapter.update(result.feedTime);
+ progressBar.setVisibility(View.GONE);
+ totalTimeTextView.setVisibility(View.VISIBLE);
+ feedStatisticsList.setVisibility(View.VISIBLE);
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
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 d104a9e93..20e34cc52 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -42,7 +42,7 @@ public class StorageErrorActivity extends AppCompatActivity {
setContentView(R.layout.storage_error);
- Button btnChooseDataFolder = (Button) findViewById(R.id.btnChooseDataFolder);
+ 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) {
@@ -164,7 +164,7 @@ public class StorageErrorActivity extends AppCompatActivity {
startActivity(new Intent(this, MainActivity.class));
}
- private BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
+ private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
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 8531a7356..78cc15b2c 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -7,14 +7,19 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.view.WindowCompat;
+import android.support.v7.app.ActionBar;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
+import android.view.Menu;
+import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.SeekBar;
@@ -24,9 +29,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.MediaType;
+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.util.playback.ExternalMedia;
+import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.view.AspectRatioVideoView;
@@ -45,12 +51,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
private VideoControlsHider videoControlsHider = new VideoControlsHider(this);
- private AtomicBoolean isSetup = new AtomicBoolean(false);
+ private final AtomicBoolean isSetup = new AtomicBoolean(false);
private LinearLayout controls;
private LinearLayout videoOverlay;
private AspectRatioVideoView videoview;
private ProgressBar progressIndicator;
+ private FrameLayout videoframe;
@Override
protected void chooseTheme() {
@@ -70,20 +77,8 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onResume() {
super.onResume();
- if (getIntent().getAction() != null
- && getIntent().getAction().equals(Intent.ACTION_VIEW)) {
- Intent intent = getIntent();
- Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath());
- ExternalMedia media = new ExternalMedia(intent.getData().getPath(),
- MediaType.VIDEO);
- Intent launchIntent = new Intent(this, PlaybackService.class);
- launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
- launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED,
- true);
- launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false);
- launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
- true);
- startService(launchIntent);
+ if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
+ playExternalMedia(getIntent(), MediaType.VIDEO);
} else if (PlaybackService.isCasting()) {
Intent intent = PlaybackService.getPlayerActivityIntent(this);
if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) {
@@ -95,10 +90,27 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
@Override
+ protected void onStop() {
+ super.onStop();
+ if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
+ videoControlsHider.stop();
+ }
+ }
+
+ @Override
+ public void onUserLeaveHint () {
+ if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior()
+ == UserPreferences.VideoBackgroundBehavior.PICTURE_IN_PICTURE) {
+ compatEnterPictureInPicture();
+ }
+ }
+
+ @Override
protected void onPause() {
- videoControlsHider.stop();
- if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
- controller.pause();
+ if (!PictureInPictureUtil.isInPictureInPictureMode(this)) {
+ if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) {
+ controller.pause();
+ }
}
super.onPause();
}
@@ -126,42 +138,38 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void setupGUI() {
- if(isSetup.getAndSet(true)) {
+ if (isSetup.getAndSet(true)) {
return;
}
super.setupGUI();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- controls = (LinearLayout) findViewById(R.id.controls);
- videoOverlay = (LinearLayout) findViewById(R.id.overlay);
- videoview = (AspectRatioVideoView) findViewById(R.id.videoview);
- progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator);
+ controls = findViewById(R.id.controls);
+ videoOverlay = findViewById(R.id.overlay);
+ videoview = findViewById(R.id.videoview);
+ videoframe = findViewById(R.id.videoframe);
+ progressIndicator = findViewById(R.id.progressIndicator);
videoview.getHolder().addCallback(surfaceHolderCallback);
- videoview.setOnTouchListener(onVideoviewTouched);
+ 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);
}
- if (Build.VERSION.SDK_INT >= 14) {
- videoOverlay.setFitsSystemWindows(true);
- }
+ videoOverlay.setFitsSystemWindows(true);
setupVideoControlsToggler();
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
+
+ videoframe.getViewTreeObserver().addOnGlobalLayoutListener(() ->
+ videoview.setAvailableSize(videoframe.getWidth(), videoframe.getHeight()));
}
@Override
protected void onAwaitingVideoSurface() {
+ setupVideoAspectRatio();
if (videoSurfaceCreated && controller != null) {
Log.d(TAG, "Videosurface already created, setting videosurface now");
-
- Pair<Integer, Integer> videoSize = controller.getVideoSize();
- if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
- Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
- videoview.setVideoSize(videoSize.first, videoSize.second);
- } else {
- Log.e(TAG, "Could not determine video size");
- }
controller.setVideoSurface(videoview.getHolder());
}
}
@@ -180,8 +188,11 @@ public class VideoplayerActivity extends MediaplayerActivity {
progressIndicator.setVisibility(View.INVISIBLE);
}
- View.OnTouchListener onVideoviewTouched = (v, event) -> {
+ private final View.OnTouchListener onVideoviewTouched = (v, event) -> {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
+ if (PictureInPictureUtil.isInPictureInPictureMode(this)) {
+ return true;
+ }
videoControlsHider.stop();
toggleVideoControlsVisibility();
if (videoControlsShowing) {
@@ -194,11 +205,23 @@ public class VideoplayerActivity extends MediaplayerActivity {
};
@SuppressLint("NewApi")
- void setupVideoControlsToggler() {
+ private void setupVideoControlsToggler() {
videoControlsHider.stop();
videoControlsHider.start();
}
+ private void setupVideoAspectRatio() {
+ if (videoSurfaceCreated && controller != null) {
+ Pair<Integer, Integer> videoSize = controller.getVideoSize();
+ if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) {
+ Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second);
+ videoview.setVideoSize(videoSize.first, videoSize.second);
+ } else {
+ Log.e(TAG, "Could not determine video size");
+ }
+ }
+ }
+
private void toggleVideoControlsVisibility() {
if (videoControlsShowing) {
getSupportActionBar().hide();
@@ -247,14 +270,16 @@ public class VideoplayerActivity extends MediaplayerActivity {
Log.e(TAG, "Couldn't attach surface to mediaplayer - reference to service was null");
}
}
-
+ setupVideoAspectRatio();
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "Videosurface was destroyed");
videoSurfaceCreated = false;
- if (controller != null && !destroyingDueToReload) {
+ if (controller != null && !destroyingDueToReload
+ && UserPreferences.getVideoBackgroundBehavior()
+ != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) {
controller.notifyVideoSurfaceAbandoned();
}
}
@@ -263,6 +288,13 @@ public class VideoplayerActivity extends MediaplayerActivity {
@Override
protected void onReloadNotification(int notificationCode) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && PictureInPictureUtil.isInPictureInPictureMode(this)) {
+ if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO
+ || notificationCode == PlaybackService.EXTRA_CODE_CAST) {
+ finish();
+ }
+ return;
+ }
if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) {
Log.d(TAG, "ReloadNotification received, switching to Audioplayer now");
destroyingDueToReload = true;
@@ -307,28 +339,31 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoOverlay.startAnimation(animation);
controls.startAnimation(animation);
}
- if (Build.VERSION.SDK_INT >= 14) {
- videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
- }
+ videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
}
@SuppressLint("NewApi")
- private void hideVideoControls() {
- final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
- if (animation != null) {
- videoOverlay.startAnimation(animation);
- controls.startAnimation(animation);
- }
- if (Build.VERSION.SDK_INT >= 14) {
- 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);
- videoOverlay.setFitsSystemWindows(true);
+ private void hideVideoControls(boolean showAnimation) {
+ if (showAnimation) {
+ final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out);
+ if (animation != null) {
+ videoOverlay.startAnimation(animation);
+ 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);
+ videoOverlay.setFitsSystemWindows(true);
+
videoOverlay.setVisibility(View.GONE);
controls.setVisibility(View.GONE);
}
+ private void hideVideoControls() {
+ hideVideoControls(true);
+ }
+
@Override
protected int getContentViewResourceId() {
return R.layout.videoplayer_activity;
@@ -344,24 +379,53 @@ public class VideoplayerActivity extends MediaplayerActivity {
}
}
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ if (PictureInPictureUtil.supportsPictureInPicture(this)) {
+ menu.findItem(R.id.player_go_to_picture_in_picture).setVisible(true);
+ }
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.player_go_to_picture_in_picture) {
+ compatEnterPictureInPicture();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void compatEnterPictureInPicture() {
+ if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ getSupportActionBar().hide();
+ hideVideoControls(false);
+ enterPictureInPictureMode();
+ }
+ }
+
private static class VideoControlsHider extends Handler {
private static final int DELAY = 2500;
private WeakReference<VideoplayerActivity> activity;
- public VideoControlsHider(VideoplayerActivity activity) {
+ VideoControlsHider(VideoplayerActivity activity) {
this.activity = new WeakReference<>(activity);
}
private final Runnable hideVideoControls = () -> {
- VideoplayerActivity vpa = activity.get();
- if(vpa == null) {
+ VideoplayerActivity vpa = activity != null ? activity.get() : null;
+ if (vpa == null) {
return;
}
if (vpa.videoControlsShowing) {
Log.d(TAG, "Hiding video controls");
- vpa.getSupportActionBar().hide();
+ ActionBar actionBar = vpa.getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.hide();
+ }
vpa.hideVideoControls();
vpa.videoControlsShowing = false;
}
@@ -371,7 +435,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
this.postDelayed(hideVideoControls, DELAY);
}
- public void stop() {
+ void stop() {
this.removeCallbacks(hideVideoControls);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
index 8ede947c5..8fcdb4371 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
@@ -45,8 +45,6 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService;
public class GpodnetAuthenticationActivity extends AppCompatActivity {
private static final String TAG = "GpodnetAuthActivity";
- private static final String CURRENT_STEP = "current_step";
-
private ViewFlipper viewFlipper;
private static final int STEP_DEFAULT = -1;
@@ -61,7 +59,7 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
private volatile String password;
private volatile GpodnetDevice selectedDevice;
- View[] views;
+ private View[] views;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -72,7 +70,7 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
setContentView(R.layout.gpodnetauth_activity);
service = new GpodnetService();
- viewFlipper = (ViewFlipper) findViewById(R.id.viewflipper);
+ viewFlipper = findViewById(R.id.viewflipper);
LayoutInflater inflater = (LayoutInflater)
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
views = new View[]{
@@ -109,11 +107,11 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
}
private void setupLoginView(View view) {
- final EditText username = (EditText) view.findViewById(R.id.etxtUsername);
- final EditText password = (EditText) view.findViewById(R.id.etxtPassword);
- final Button login = (Button) view.findViewById(R.id.butLogin);
- final TextView txtvError = (TextView) view.findViewById(R.id.txtvError);
- final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progBarLogin);
+ final EditText username = view.findViewById(R.id.etxtUsername);
+ final EditText password = view.findViewById(R.id.etxtPassword);
+ final Button login = view.findViewById(R.id.butLogin);
+ final TextView txtvError = view.findViewById(R.id.txtvError);
+ final ProgressBar progressBar = view.findViewById(R.id.progBarLogin);
password.setOnEditorActionListener((v, actionID, event) ->
actionID == EditorInfo.IME_ACTION_GO && login.performClick());
@@ -171,23 +169,19 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
return null;
}
};
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service);
- } else {
- authTask.execute();
- }
+ authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service);
}
});
}
private void setupDeviceView(View view) {
- final EditText deviceID = (EditText) view.findViewById(R.id.etxtDeviceID);
- final EditText caption = (EditText) view.findViewById(R.id.etxtCaption);
- final Button createNewDevice = (Button) view.findViewById(R.id.butCreateNewDevice);
- final Button chooseDevice = (Button) view.findViewById(R.id.butChooseExistingDevice);
- final TextView txtvError = (TextView) view.findViewById(R.id.txtvError);
- final ProgressBar progBarCreateDevice = (ProgressBar) view.findViewById(R.id.progbarCreateDevice);
- final Spinner spinnerDevices = (Spinner) view.findViewById(R.id.spinnerChooseDevice);
+ final EditText deviceID = view.findViewById(R.id.etxtDeviceID);
+ final EditText caption = view.findViewById(R.id.etxtCaption);
+ final Button createNewDevice = view.findViewById(R.id.butCreateNewDevice);
+ final Button chooseDevice = view.findViewById(R.id.butChooseExistingDevice);
+ final TextView txtvError = view.findViewById(R.id.txtvError);
+ final ProgressBar progBarCreateDevice = view.findViewById(R.id.progbarCreateDevice);
+ final Spinner spinnerDevices = view.findViewById(R.id.spinnerChooseDevice);
// load device list
@@ -352,8 +346,8 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity {
}
private void setupFinishView(View view) {
- final Button sync = (Button) view.findViewById(R.id.butSyncNow);
- final Button back = (Button) view.findViewById(R.id.butGoMainscreen);
+ final Button sync = view.findViewById(R.id.butSyncNow);
+ final Button back = view.findViewById(R.id.butGoMainscreen);
sync.setOnClickListener(v -> {
GpodnetSyncService.sendSyncIntent(GpodnetAuthenticationActivity.this);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java
index c18564351..e6b42efcb 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.adapter;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.util.LongList;
-public interface ActionButtonCallback {
+interface ActionButtonCallback {
/** Is called when the action button of a list item has been pressed. */
void onActionButtonPressed(FeedItem item, LongList queueIds);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
index f0210f983..a915692d1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java
@@ -16,7 +16,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
* Utility methods for the action button that is displayed on the right hand side
* of a listitem.
*/
-public class ActionButtonUtils {
+class ActionButtonUtils {
private final int[] labels;
private final TypedArray drawables;
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 3e8bbc488..0b2b81edb 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.os.Build;
+import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
@@ -19,9 +20,7 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.bumptech.glide.Glide;
import com.joanzapata.iconify.Iconify;
-import com.nineoldandroids.view.ViewHelper;
import java.lang.ref.WeakReference;
@@ -29,13 +28,12 @@ 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.glide.ApGlideSettings;
-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.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;
@@ -52,7 +50,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
private final ActionButtonUtils actionButtonUtils;
private final boolean showOnlyNewEpisodes;
- private int position = -1;
+ private FeedItem selectedItem;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -68,11 +66,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
this.actionButtonCallback = actionButtonCallback;
this.showOnlyNewEpisodes = showOnlyNewEpisodes;
- if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
- } else {
- playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
- }
+ playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
}
@@ -81,27 +75,26 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.new_episodes_listitem, parent, false);
Holder holder = new Holder(view);
- holder.container = (FrameLayout) view.findViewById(R.id.container);
- holder.content = (LinearLayout) view.findViewById(R.id.content);
- holder.placeholder = (TextView) view.findViewById(R.id.txtvPlaceholder);
- holder.title = (TextView) view.findViewById(R.id.txtvTitle);
+ holder.container = view.findViewById(R.id.container);
+ holder.content = view.findViewById(R.id.content);
+ holder.placeholder = view.findViewById(R.id.txtvPlaceholder);
+ holder.title = view.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
- holder.pubDate = (TextView) view
+ holder.pubDate = view
.findViewById(R.id.txtvPublished);
holder.statusUnread = view.findViewById(R.id.statusUnread);
- holder.butSecondary = (ImageButton) view
+ holder.butSecondary = view
.findViewById(R.id.butSecondaryAction);
- holder.queueStatus = (ImageView) view
+ holder.queueStatus = view
.findViewById(R.id.imgvInPlaylist);
- holder.progress = (ProgressBar) view
+ holder.progress = view
.findViewById(R.id.pbar_progress);
- holder.cover = (ImageView) view.findViewById(R.id.imgvCover);
- holder.txtvDuration = (TextView) view.findViewById(R.id.txtvDuration);
+ holder.cover = view.findViewById(R.id.imgvCover);
+ holder.txtvDuration = view.findViewById(R.id.txtvDuration);
holder.item = null;
holder.mainActivityRef = mainActivityRef;
- holder.position = -1;
// so we can grab this later
view.setTag(holder);
@@ -113,11 +106,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
final FeedItem item = itemAccess.getItem(position);
if (item == null) return;
holder.itemView.setOnLongClickListener(v -> {
- this.position = position;
+ this.selectedItem = item;
return false;
});
holder.item = item;
- holder.position = position;
holder.placeholder.setVisibility(View.VISIBLE);
holder.placeholder.setText(item.getFeed().getTitle());
holder.title.setText(item.getTitle());
@@ -129,9 +121,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.statusUnread.setVisibility(View.VISIBLE);
}
if(item.isPlayed()) {
- ViewHelper.setAlpha(holder.content, 0.5f);
+ holder.content.setAlpha(0.5f);
} else {
- ViewHelper.setAlpha(holder.content, 1.0f);
+ holder.content.setAlpha(1.0f);
}
FeedMedia media = item.getMedia();
@@ -174,7 +166,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.progress.setVisibility(View.VISIBLE);
}
} else {
- holder.progress.setVisibility(View.GONE);
+ holder.progress.setVisibility(View.INVISIBLE);
}
if(media.isCurrentlyPlaying()) {
@@ -183,7 +175,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.container.setBackgroundColor(normalBackGroundColor);
}
} else {
- holder.progress.setVisibility(View.GONE);
+ holder.progress.setVisibility(View.INVISIBLE);
holder.txtvDuration.setVisibility(View.GONE);
}
@@ -199,12 +191,17 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
- Glide.with(mainActivityRef.get())
- .load(item.getImageLocation())
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(new CoverTarget(item.getFeed().getImageLocation(), holder.placeholder, holder.cover, mainActivityRef.get()));
+ new CoverLoader(mainActivityRef.get())
+ .withUri(item.getImageLocation())
+ .withFallbackUri(item.getFeed().getImageLocation())
+ .withPlaceholderView(holder.placeholder)
+ .withCoverView(holder.cover)
+ .load();
+ }
+
+ @Nullable
+ public FeedItem getSelectedItem() {
+ return selectedItem;
}
@Override
@@ -218,17 +215,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
return itemAccess.getCount();
}
- public FeedItem getItem(int position) {
- return itemAccess.getItem(position);
- }
-
- public int getPosition() {
- int pos = position;
- position = -1; // reset
- return pos;
- }
-
- private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
+ private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
@@ -253,7 +240,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
ImageButton butSecondary;
FeedItem item;
WeakReference<MainActivity> mainActivityRef;
- int position;
public Holder(View itemView) {
super(itemView);
@@ -266,18 +252,18 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) {
long[] ids = itemAccess.getItemsIds().toArray();
- mainActivity.loadChildFragment(ItemFragment.newInstance(ids, position));
+ mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition()));
}
}
@Override
public void onItemSelected() {
- ViewHelper.setAlpha(itemView, 0.5f);
+ itemView.setAlpha(0.5f);
}
@Override
public void onItemClear() {
- ViewHelper.setAlpha(itemView, 1.0f);
+ itemView.setAlpha(1.0f);
}
public FeedItem getFeedItem() { return item; }
@@ -303,6 +289,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
}
};
FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
+
+ contextMenuInterface.setItemVisibility(R.id.mark_as_seen_item, item.isNew());
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
index d7bebb672..c3fac7e18 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.text.Layout;
import android.text.Selection;
@@ -8,7 +9,6 @@ import android.text.Spannable;
import android.text.Spanned;
import android.text.style.ClickableSpan;
import android.text.util.Linkify;
-import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -19,9 +19,9 @@ import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
@@ -42,8 +42,9 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
this.media = media;
}
+ @NonNull
@Override
- public View getView(final int position, View convertView, ViewGroup parent) {
+ public View getView(final int position, View convertView, @NonNull ViewGroup parent) {
Holder holder;
Chapter sc = getItem(position);
@@ -56,12 +57,12 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
convertView = inflater.inflate(R.layout.simplechapter_item, parent, false);
holder.view = convertView;
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
defaultTextColor = holder.title.getTextColors().getDefaultColor();
- holder.start = (TextView) convertView.findViewById(R.id.txtvStart);
- holder.link = (TextView) convertView.findViewById(R.id.txtvLink);
- holder.duration = (TextView) convertView.findViewById(R.id.txtvDuration);
- holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter);
+ holder.start = convertView.findViewById(R.id.txtvStart);
+ holder.link = convertView.findViewById(R.id.txtvLink);
+ holder.duration = convertView.findViewById(R.id.txtvDuration);
+ holder.butPlayChapter = convertView.findViewById(R.id.butPlayChapter);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@@ -120,7 +121,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(widget);
- } else if (action == MotionEvent.ACTION_DOWN){
+ } else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer,
buffer.getSpanStart(link[0]),
buffer.getSpanEnd(link[0]));
@@ -139,23 +140,15 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
callback.onPlayChapterButtonClicked(position);
}
});
+
Chapter current = ChapterUtils.getCurrentChapter(media);
- if (current != null) {
- if (current == sc) {
- int playingBackGroundColor;
- if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_dark);
- } else {
- playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_light);
- }
- holder.view.setBackgroundColor(playingBackGroundColor);
- } else {
- holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
- holder.title.setTextColor(defaultTextColor);
- holder.start.setTextColor(defaultTextColor);
- }
+ if (current == sc) {
+ int playingBackGroundColor = ThemeUtils.getColorFromAttr(getContext(), R.attr.currently_playing_background);
+ holder.view.setBackgroundColor(playingBackGroundColor);
} else {
- Log.w(TAG, "Could not find out what the current chapter is.");
+ holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent));
+ holder.title.setTextColor(defaultTextColor);
+ holder.start.setTextColor(defaultTextColor);
}
return convertView;
@@ -172,7 +165,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> {
@Override
public int getCount() {
- if(media == null || media.getChapters() == null) {
+ if (media == null || media.getChapters() == null) {
return 0;
}
// ignore invalid chapters
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
new file mode 100644
index 000000000..54ecdae77
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
@@ -0,0 +1,113 @@
+package de.danoeh.antennapod.adapter;
+
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.RequestBuilder;
+import com.bumptech.glide.request.RequestOptions;
+import com.bumptech.glide.request.target.CustomViewTarget;
+
+import java.lang.ref.WeakReference;
+
+import com.bumptech.glide.request.transition.Transition;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+
+public class CoverLoader {
+ private String uri;
+ private String fallbackUri;
+ private TextView txtvPlaceholder;
+ private ImageView imgvCover;
+ private MainActivity activity;
+ private int errorResource = -1;
+
+ public CoverLoader(MainActivity activity) {
+ this.activity = activity;
+ }
+
+ public CoverLoader withUri(String uri) {
+ this.uri = uri;
+ return this;
+ }
+
+ public CoverLoader withFallbackUri(String uri) {
+ fallbackUri = uri;
+ return this;
+ }
+
+ public CoverLoader withCoverView(ImageView coverView) {
+ imgvCover = coverView;
+ return this;
+ }
+
+ public CoverLoader withError(int errorResource) {
+ this.errorResource = errorResource;
+ return this;
+ }
+
+ public CoverLoader withPlaceholderView(TextView placeholderView) {
+ txtvPlaceholder = placeholderView;
+ return this;
+ }
+
+ public void load() {
+ RequestOptions options = new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate();
+
+ if (errorResource != -1) {
+ options = options.error(errorResource);
+ }
+
+ RequestBuilder builder = Glide.with(activity)
+ .load(uri)
+ .apply(options);
+
+ if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
+ builder = builder.error(Glide.with(activity)
+ .load(fallbackUri)
+ .apply(options));
+ }
+
+ builder.into(new CoverTarget(txtvPlaceholder, imgvCover));
+ }
+
+ class CoverTarget extends CustomViewTarget<ImageView, Drawable> {
+ private final WeakReference<TextView> placeholder;
+ private final WeakReference<ImageView> cover;
+
+ public CoverTarget(TextView txtvPlaceholder, ImageView imgvCover) {
+ super(imgvCover);
+ placeholder = new WeakReference<>(txtvPlaceholder);
+ cover = new WeakReference<>(imgvCover);
+ }
+
+ @Override
+ public void onLoadFailed(Drawable errorDrawable) {
+
+ }
+
+ @Override
+ public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
+ TextView txtvPlaceholder = placeholder.get();
+ if (txtvPlaceholder != null) {
+ txtvPlaceholder.setVisibility(View.INVISIBLE);
+ }
+ ImageView ivCover = cover.get();
+ ivCover.setImageDrawable(resource);
+ }
+
+ @Override
+ protected void onResourceCleared(@Nullable Drawable placeholder) {
+ ImageView ivCover = cover.get();
+ ivCover.setImageDrawable(placeholder);
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverTarget.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverTarget.java
deleted file mode 100644
index 538af8c79..000000000
--- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverTarget.java
+++ /dev/null
@@ -1,58 +0,0 @@
-package de.danoeh.antennapod.adapter;
-
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.view.View;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.load.resource.drawable.GlideDrawable;
-import com.bumptech.glide.request.animation.GlideAnimation;
-import com.bumptech.glide.request.target.GlideDrawableImageViewTarget;
-
-import java.lang.ref.WeakReference;
-
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
-
-class CoverTarget extends GlideDrawableImageViewTarget {
-
- private final WeakReference<String> fallback;
- private final WeakReference<TextView> placeholder;
- private final WeakReference<ImageView> cover;
- private final WeakReference<MainActivity> mainActivity;
-
- public CoverTarget(String fallbackUri, TextView txtvPlaceholder, ImageView imgvCover, MainActivity activity) {
- super(imgvCover);
- fallback = new WeakReference<>(fallbackUri);
- placeholder = new WeakReference<>(txtvPlaceholder);
- cover = new WeakReference<>(imgvCover);
- mainActivity = new WeakReference<>(activity);
- }
-
- @Override
- public void onLoadFailed(Exception e, Drawable errorDrawable) {
- String fallbackUri = fallback.get();
- TextView txtvPlaceholder = placeholder.get();
- ImageView imgvCover = cover.get();
- if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) {
- MainActivity activity = mainActivity.get();
- Glide.with(activity)
- .load(fallbackUri)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(new CoverTarget(null, txtvPlaceholder, imgvCover, activity));
- }
- }
-
- @Override
- public void onResourceReady(GlideDrawable drawable, GlideAnimation<? super GlideDrawable> anim) {
- super.onResourceReady(drawable, anim);
- TextView txtvPlaceholder = placeholder.get();
- if (txtvPlaceholder != null) {
- txtvPlaceholder.setVisibility(View.INVISIBLE);
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
index 4a53be9dc..1286d9dc7 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.support.annotation.NonNull;
import android.content.Intent;
import android.widget.Toast;
@@ -19,8 +20,10 @@ 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.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
/**
* Default implementation of an ActionButtonCallback
@@ -80,13 +83,19 @@ public class DefaultActionButtonCallback implements ActionButtonCallback {
Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show();
}
} else { // media is downloaded
- if (item.hasMedia() && item.getMedia().isCurrentlyPlaying()) {
- context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
- }
- else if (item.hasMedia() && item.getMedia().isCurrentlyPaused()) {
- context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE));
- }
- else {
+ if (media.isCurrentlyPlaying()) {
+ new PlaybackServiceStarter(context, media)
+ .startWhenPrepared(true)
+ .shouldStream(false)
+ .start();
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ } else if (media.isCurrentlyPaused()) {
+ new PlaybackServiceStarter(context, media)
+ .startWhenPrepared(true)
+ .shouldStream(false)
+ .start();
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE);
+ } else {
DBTasks.playMedia(context, media, false, true, false);
}
}
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 e271b5eed..789c01a26 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -19,7 +19,6 @@ import com.joanzapata.iconify.widget.IconTextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -29,11 +28,11 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException;
/** Displays a list of DownloadStatus entries. */
public class DownloadLogAdapter extends BaseAdapter {
- private final String TAG = "DownloadLogAdapter";
+ private static final String TAG = "DownloadLogAdapter";
- private Context context;
+ private final Context context;
- private ItemAccess itemAccess;
+ private final ItemAccess itemAccess;
public DownloadLogAdapter(Context context, ItemAccess itemAccess) {
super();
@@ -50,15 +49,15 @@ public class DownloadLogAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlog_item, parent, false);
- holder.icon = (IconTextView) convertView.findViewById(R.id.txtvIcon);
- holder.retry = (IconButton) convertView.findViewById(R.id.btnRetry);
- holder.date = (TextView) convertView.findViewById(R.id.txtvDate);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.icon = convertView.findViewById(R.id.txtvIcon);
+ holder.retry = convertView.findViewById(R.id.btnRetry);
+ holder.date = convertView.findViewById(R.id.txtvDate);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
- holder.type = (TextView) convertView.findViewById(R.id.txtvType);
- holder.reason = (TextView) convertView.findViewById(R.id.txtvReason);
+ holder.type = convertView.findViewById(R.id.txtvType);
+ holder.reason = convertView.findViewById(R.id.txtvReason);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@@ -67,8 +66,6 @@ public class DownloadLogAdapter extends BaseAdapter {
holder.type.setText(R.string.download_type_feed);
} else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
holder.type.setText(R.string.download_type_media);
- } else if (status.getFeedfileType() == FeedImage.FEEDFILETYPE_FEEDIMAGE) {
- holder.type.setText(R.string.download_type_image);
}
if (status.getTitle() != null) {
holder.title.setText(status.getTitle());
@@ -94,8 +91,7 @@ public class DownloadLogAdapter extends BaseAdapter {
}
holder.reason.setText(reasonText);
holder.reason.setVisibility(View.VISIBLE);
- if(status.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE &&
- !newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
+ if(!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) {
holder.retry.setVisibility(View.VISIBLE);
holder.retry.setOnClickListener(clickListener);
ButtonHolder btnHolder;
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 6907e467b..cd636af43 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -12,11 +12,12 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.nineoldandroids.view.ViewHelper;
+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;
@@ -61,16 +62,16 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloaded_episodeslist_item,
parent, false);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.imageView = convertView.findViewById(R.id.imgvImage);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
- holder.txtvSize = (TextView) convertView.findViewById(R.id.txtvSize);
- holder.queueStatus = (ImageView) convertView.findViewById(R.id.imgvInPlaylist);
- holder.pubDate = (TextView) convertView
+ holder.txtvSize = convertView.findViewById(R.id.txtvSize);
+ holder.queueStatus = convertView.findViewById(R.id.imgvInPlaylist);
+ holder.pubDate = convertView
.findViewById(R.id.txtvPublished);
- holder.butSecondary = (ImageButton) convertView
+ holder.butSecondary = convertView
.findViewById(R.id.butSecondaryAction);
convertView.setTag(holder);
} else {
@@ -79,17 +80,18 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
Glide.with(context)
.load(item.getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(holder.imageView);
if(item.isPlayed()) {
- ViewHelper.setAlpha(convertView, 0.5f);
+ convertView.setAlpha(0.5f);
} else {
- ViewHelper.setAlpha(convertView, 1.0f);
+ convertView.setAlpha(1.0f);
}
holder.title.setText(item.getTitle());
@@ -99,10 +101,12 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
holder.pubDate.setText(pubDateStr);
FeedItem.State state = item.getState();
- if (state == FeedItem.State.PLAYING) {
+ if (state == FeedItem.State.PLAYING && PlaybackService.isRunning) {
holder.butSecondary.setEnabled(false);
+ holder.butSecondary.setAlpha(0.5f);
} else {
holder.butSecondary.setEnabled(true);
+ holder.butSecondary.setAlpha(1.0f);
}
holder.butSecondary.setFocusable(false);
holder.butSecondary.setTag(item);
@@ -111,7 +115,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
return convertView;
}
- private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
+ private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
index e1efdaa7b..b85d1d35d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java
@@ -19,11 +19,11 @@ import de.danoeh.antennapod.core.util.ThemeUtils;
public class DownloadlistAdapter extends BaseAdapter {
- public static final int SELECTION_NONE = -1;
+ private static final int SELECTION_NONE = -1;
private int selectedItemIndex;
- private ItemAccess itemAccess;
- private Context context;
+ private final ItemAccess itemAccess;
+ private final Context context;
public DownloadlistAdapter(Context context,
ItemAccess itemAccess) {
@@ -59,14 +59,14 @@ public class DownloadlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.downloadlist_item, parent, false);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.downloaded = (TextView) convertView
+ holder.title = convertView.findViewById(R.id.txtvTitle);
+ holder.downloaded = convertView
.findViewById(R.id.txtvDownloaded);
- holder.percent = (TextView) convertView
+ holder.percent = convertView
.findViewById(R.id.txtvPercent);
- holder.progbar = (ProgressBar) convertView
+ holder.progbar = convertView
.findViewById(R.id.progProgress);
- holder.butSecondary = (ImageButton) convertView
+ holder.butSecondary = convertView
.findViewById(R.id.butSecondaryAction);
convertView.setTag(holder);
@@ -105,7 +105,7 @@ public class DownloadlistAdapter extends BaseAdapter {
return convertView;
}
- private View.OnClickListener butSecondaryListener = new View.OnClickListener() {
+ private final View.OnClickListener butSecondaryListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Downloader downloader = (Downloader) v.getTag();
@@ -121,15 +121,6 @@ public class DownloadlistAdapter extends BaseAdapter {
ImageButton butSecondary;
}
- public int getSelectedItemIndex() {
- return selectedItemIndex;
- }
-
- public void setSelectedItemIndex(int selectedItemIndex) {
- this.selectedItemIndex = selectedItemIndex;
- notifyDataSetChanged();
- }
-
public interface ItemAccess {
int getCount();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index 35c42725c..738a0a636 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -17,13 +17,10 @@ import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.nineoldandroids.view.ViewHelper;
-
import de.danoeh.antennapod.R;
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.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.LongList;
@@ -34,16 +31,16 @@ import de.danoeh.antennapod.core.util.ThemeUtils;
*/
public class FeedItemlistAdapter extends BaseAdapter {
- private ActionButtonCallback callback;
+ private final ActionButtonCallback callback;
private final ItemAccess itemAccess;
private final Context context;
- private boolean showFeedtitle;
- private int selectedItemIndex;
+ private final boolean showFeedtitle;
+ private final int selectedItemIndex;
/** true if played items should be made partially transparent */
- private boolean makePlayedItemsTransparent;
+ private final boolean makePlayedItemsTransparent;
private final ActionButtonUtils actionButtonUtils;
- public static final int SELECTION_NONE = -1;
+ private static final int SELECTION_NONE = -1;
private final int playingBackGroundColor;
private final int normalBackGroundColor;
@@ -62,11 +59,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
this.actionButtonUtils = new ActionButtonUtils(context);
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
- if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_dark);
- } else {
- playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_light);
- }
+ playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(context, android.R.color.transparent);
}
@@ -97,24 +90,24 @@ public class FeedItemlistAdapter extends BaseAdapter {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false);
- holder.container = (LinearLayout) convertView
+ holder.container = convertView
.findViewById(R.id.container);
- holder.title = (TextView) convertView.findViewById(R.id.txtvItemname);
+ holder.title = convertView.findViewById(R.id.txtvItemname);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
- holder.lenSize = (TextView) convertView
+ holder.lenSize = convertView
.findViewById(R.id.txtvLenSize);
- holder.butAction = (ImageButton) convertView
+ holder.butAction = convertView
.findViewById(R.id.butSecondaryAction);
- holder.published = (TextView) convertView
+ holder.published = convertView
.findViewById(R.id.txtvPublished);
- holder.inPlaylist = (ImageView) convertView
+ holder.inPlaylist = convertView
.findViewById(R.id.imgvInPlaylist);
- holder.type = (ImageView) convertView.findViewById(R.id.imgvType);
+ holder.type = convertView.findViewById(R.id.imgvType);
holder.statusUnread = convertView
.findViewById(R.id.statusUnread);
- holder.episodeProgress = (ProgressBar) convertView
+ holder.episodeProgress = convertView
.findViewById(R.id.pbar_episode_progress);
convertView.setTag(holder);
@@ -145,9 +138,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.statusUnread.setVisibility(View.INVISIBLE);
}
if(item.isPlayed() && makePlayedItemsTransparent) {
- ViewHelper.setAlpha(convertView, 0.5f);
+ convertView.setAlpha(0.5f);
} else {
- ViewHelper.setAlpha(convertView, 1.0f);
+ convertView.setAlpha(1.0f);
}
String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate());
@@ -157,7 +150,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
FeedMedia media = item.getMedia();
if (media == null) {
- holder.episodeProgress.setVisibility(View.GONE);
+ holder.episodeProgress.setVisibility(View.INVISIBLE);
holder.inPlaylist.setVisibility(View.INVISIBLE);
holder.type.setVisibility(View.INVISIBLE);
holder.lenSize.setVisibility(View.INVISIBLE);
@@ -176,7 +169,7 @@ public class FeedItemlistAdapter extends BaseAdapter {
holder.episodeProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item));
} else {
if(media.getPosition() == 0) {
- holder.episodeProgress.setVisibility(View.GONE);
+ holder.episodeProgress.setVisibility(View.INVISIBLE);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
index 5b205e91f..c10bb7638 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -34,9 +34,9 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
LayoutInflater inflater = (LayoutInflater) getContext()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate);
- holder.description = (TextView) convertView.findViewById(R.id.txtvDescription);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
+ holder.pubDate = convertView.findViewById(R.id.txtvPubDate);
+ holder.description = convertView.findViewById(R.id.txtvDescription);
convertView.setTag(holder);
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
index 465497b55..fbf6b804a 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -17,6 +17,7 @@ import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconTextView;
@@ -48,10 +49,10 @@ import de.danoeh.antennapod.fragment.SubscriptionFragment;
public class NavListAdapter extends BaseAdapter
implements SharedPreferences.OnSharedPreferenceChangeListener {
- public static final int VIEW_TYPE_COUNT = 3;
+ private static final int VIEW_TYPE_COUNT = 3;
public static final int VIEW_TYPE_NAV = 0;
public static final int VIEW_TYPE_SECTION_DIVIDER = 1;
- public static final int VIEW_TYPE_SUBSCRIPTION = 2;
+ private static final int VIEW_TYPE_SUBSCRIPTION = 2;
/**
* a tag used as a placeholder to indicate if the subscription list should be displayed or not
@@ -62,8 +63,8 @@ public class NavListAdapter extends BaseAdapter
private static List<String> tags;
private static String[] titles;
- private ItemAccess itemAccess;
- private WeakReference<Activity> activity;
+ private final ItemAccess itemAccess;
+ private final WeakReference<Activity> activity;
private boolean showSubscriptionList = true;
public NavListAdapter(ItemAccess itemAccess, Activity context) {
@@ -86,9 +87,7 @@ public class NavListAdapter extends BaseAdapter
private void loadItems() {
List<String> newTags = new ArrayList<>(Arrays.asList(MainActivity.NAV_DRAWER_TAGS));
List<String> hiddenFragments = UserPreferences.getHiddenDrawerItems();
- for(String hidden : hiddenFragments) {
- newTags.remove(hidden);
- }
+ newTags.removeAll(hiddenFragments);
if (newTags.contains(SUBSCRIPTION_LIST_TAG)) {
// we never want SUBSCRIPTION_LIST_TAG to be in 'tags'
@@ -214,7 +213,7 @@ public class NavListAdapter extends BaseAdapter
v = getFeedView(position, convertView, parent);
}
if (v != null && viewType != VIEW_TYPE_SECTION_DIVIDER) {
- TextView txtvTitle = (TextView) v.findViewById(R.id.txtvTitle);
+ TextView txtvTitle = v.findViewById(R.id.txtvTitle);
if (position == itemAccess.getSelectedItemIndex()) {
txtvTitle.setTypeface(null, Typeface.BOLD);
} else {
@@ -237,9 +236,9 @@ public class NavListAdapter extends BaseAdapter
convertView = inflater.inflate(R.layout.nav_listitem, parent, false);
- holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.count = (TextView) convertView.findViewById(R.id.txtvCount);
+ holder.image = convertView.findViewById(R.id.imgvCover);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
+ holder.count = convertView.findViewById(R.id.txtvCount);
convertView.setTag(holder);
} else {
holder = (NavHolder) convertView.getTag();
@@ -327,10 +326,10 @@ public class NavListAdapter extends BaseAdapter
convertView = inflater.inflate(R.layout.nav_feedlistitem, parent, false);
- holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.failure = (IconTextView) convertView.findViewById(R.id.itxtvFailure);
- holder.count = (TextView) convertView.findViewById(R.id.txtvCount);
+ holder.image = convertView.findViewById(R.id.imgvCover);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
+ holder.failure = convertView.findViewById(R.id.itxtvFailure);
+ holder.count = convertView.findViewById(R.id.txtvCount);
convertView.setTag(holder);
} else {
holder = (FeedHolder) convertView.getTag();
@@ -338,11 +337,12 @@ public class NavListAdapter extends BaseAdapter
Glide.with(context)
.load(feed.getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(holder.image);
holder.title.setText(feed.getTitle());
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 c6ddc6c86..df8cafb9d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -23,8 +23,8 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
-import com.nineoldandroids.view.ViewHelper;
import org.apache.commons.lang3.ArrayUtils;
@@ -41,6 +41,7 @@ import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
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;
@@ -51,7 +52,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
private static final String TAG = QueueRecyclerAdapter.class.getSimpleName();
- private WeakReference<MainActivity> mainActivity;
+ private final WeakReference<MainActivity> mainActivity;
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
@@ -76,11 +77,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
this.itemTouchHelper = itemTouchHelper;
locked = UserPreferences.isQueueLocked();
- if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark);
- } else {
- playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light);
- }
+ playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background);
normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent);
}
@@ -138,19 +135,19 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
public ViewHolder(View v) {
super(v);
- container = (FrameLayout) v.findViewById(R.id.container);
- dragHandle = (ImageView) v.findViewById(R.id.drag_handle);
- placeholder = (TextView) v.findViewById(R.id.txtvPlaceholder);
- cover = (ImageView) v.findViewById(R.id.imgvCover);
- title = (TextView) v.findViewById(R.id.txtvTitle);
+ container = v.findViewById(R.id.container);
+ dragHandle = v.findViewById(R.id.drag_handle);
+ placeholder = v.findViewById(R.id.txtvPlaceholder);
+ cover = v.findViewById(R.id.imgvCover);
+ title = v.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
- pubDate = (TextView) v.findViewById(R.id.txtvPubDate);
- progressLeft = (TextView) v.findViewById(R.id.txtvProgressLeft);
- progressRight = (TextView) v.findViewById(R.id.txtvProgressRight);
- butSecondary = (ImageButton) v.findViewById(R.id.butSecondaryAction);
- progressBar = (ProgressBar) v.findViewById(R.id.progressBar);
+ pubDate = v.findViewById(R.id.txtvPubDate);
+ progressLeft = v.findViewById(R.id.txtvProgressLeft);
+ progressRight = v.findViewById(R.id.txtvProgressRight);
+ butSecondary = v.findViewById(R.id.butSecondaryAction);
+ progressBar = v.findViewById(R.id.progressBar);
v.setTag(this);
v.setOnClickListener(this);
v.setOnCreateContextMenuListener(this);
@@ -198,12 +195,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
@Override
public void onItemSelected() {
- ViewHelper.setAlpha(itemView, 0.5f);
+ itemView.setAlpha(0.5f);
}
@Override
public void onItemClear() {
- ViewHelper.setAlpha(itemView, 1.0f);
+ itemView.setAlpha(1.0f);
}
public void bind(FeedItem item) {
@@ -280,7 +277,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
progressLeft.setText("");
}
progressRight.setText(Converter.getDurationStringLong(media.getDuration()));
- progressBar.setVisibility(View.GONE);
+ progressBar.setVisibility(View.INVISIBLE);
}
if(media.isCurrentlyPlaying()) {
@@ -295,17 +292,17 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
butSecondary.setTag(item);
butSecondary.setOnClickListener(secondaryActionListener);
- Glide.with(mainActivity.get())
- .load(item.getImageLocation())
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(new CoverTarget(item.getFeed().getImageLocation(), placeholder, cover, mainActivity.get()));
+ new CoverLoader(mainActivity.get())
+ .withUri(item.getImageLocation())
+ .withFallbackUri(item.getFeed().getImageLocation())
+ .withPlaceholderView(placeholder)
+ .withCoverView(cover)
+ .load();
}
}
- private View.OnClickListener secondaryActionListener = new View.OnClickListener() {
+ private final View.OnClickListener secondaryActionListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
FeedItem item = (FeedItem) v.getTag();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
index 8e1aa24e0..45cb4af87 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java
@@ -11,8 +11,8 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
-import com.nineoldandroids.view.ViewHelper;
+import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
@@ -62,13 +62,13 @@ public class SearchlistAdapter extends BaseAdapter {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.searchlist_item, parent, false);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
- holder.cover = (ImageView) convertView
+ holder.cover = convertView
.findViewById(R.id.imgvFeedimage);
- holder.subtitle = (TextView) convertView
+ holder.subtitle = convertView
.findViewById(R.id.txtvSubtitle);
convertView.setTag(holder);
@@ -82,11 +82,12 @@ public class SearchlistAdapter extends BaseAdapter {
Glide.with(context)
.load(feed.getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(holder.cover);
} else if (component.getClass() == FeedItem.class) {
@@ -97,15 +98,16 @@ public class SearchlistAdapter extends BaseAdapter {
holder.subtitle.setText(result.getSubtitle());
}
- ViewHelper.setAlpha(convertView, item.isPlayed() ? 0.5f : 1.0f);
+ convertView.setAlpha(item.isPlayed() ? 0.5f : 1.0f);
Glide.with(context)
.load(item.getFeed().getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(holder.cover);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
index c060083a6..31e82dbe0 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
@@ -13,6 +13,7 @@ import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;
+import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@@ -23,8 +24,8 @@ import de.danoeh.antennapod.core.util.Converter;
* Adapter for the statistics list
*/
public class StatisticsListAdapter extends BaseAdapter {
- private Context context;
- List<DBReader.StatisticsItem> feedTime = new ArrayList<>();
+ private final Context context;
+ private List<DBReader.StatisticsItem> feedTime = new ArrayList<>();
private boolean countAll = true;
public StatisticsListAdapter(Context context) {
@@ -62,9 +63,9 @@ public class StatisticsListAdapter extends BaseAdapter {
convertView = inflater.inflate(R.layout.statistics_listitem, parent, false);
- holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.time = (TextView) convertView.findViewById(R.id.txtvTime);
+ holder.image = convertView.findViewById(R.id.imgvCover);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
+ holder.time = convertView.findViewById(R.id.txtvTime);
convertView.setTag(holder);
} else {
holder = (StatisticsHolder) convertView.getTag();
@@ -72,11 +73,12 @@ public class StatisticsListAdapter extends BaseAdapter {
Glide.with(context)
.load(feed.getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(holder.image);
holder.title.setText(feed.getTitle());
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 6d19bfa6c..763dcb57d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -14,6 +14,7 @@ import com.bumptech.glide.Glide;
import java.lang.ref.WeakReference;
+import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.feed.Feed;
@@ -50,7 +51,6 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
}
private int getAdjustedPosition(int origPosition) {
- assert(origPosition != getAddTilePosition());
return origPosition < getAddTilePosition() ? origPosition : origPosition - 1;
}
@@ -90,9 +90,9 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
LayoutInflater layoutInflater =
(LayoutInflater) mainActivityRef.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.subscription_item, parent, false);
- holder.feedTitle = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.imageView = (ImageView) convertView.findViewById(R.id.imgvCover);
- holder.count = (TriangleLabelView) convertView.findViewById(R.id.triangleCountView);
+ holder.feedTitle = convertView.findViewById(R.id.txtvTitle);
+ holder.imageView = convertView.findViewById(R.id.imgvCover);
+ holder.count = convertView.findViewById(R.id.triangleCountView);
convertView.setTag(holder);
@@ -109,7 +109,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
holder.count.setVisibility(View.INVISIBLE);
// when this holder is reused, we could else end up with a cover image
- Glide.clear(holder.imageView);
+ Glide.with(mainActivityRef.get()).clear(holder.imageView);
return convertView;
}
@@ -126,13 +126,13 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
} else {
holder.count.setVisibility(View.GONE);
}
- Glide.with(mainActivityRef.get())
- .load(feed.getImageLocation())
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
- .into(new CoverTarget(null, holder.feedTitle, holder.imageView, mainActivityRef.get()));
+
+ new CoverLoader(mainActivityRef.get())
+ .withUri(feed.getImageLocation())
+ .withPlaceholderView(holder.feedTitle)
+ .withCoverView(holder.imageView)
+ .withError(R.color.light_gray)
+ .load();
return convertView;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
index aea3d583f..06c80e173 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java
@@ -10,6 +10,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import org.apache.commons.lang3.StringUtils;
import java.util.List;
@@ -40,10 +41,10 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.gpodnet_podcast_listitem, parent, false);
- holder.image = (ImageView) convertView.findViewById(R.id.imgvCover);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.subscribers = (TextView) convertView.findViewById(R.id.txtvSubscribers);
- holder.url = (TextView) convertView.findViewById(R.id.txtvUrl);
+ holder.image = convertView.findViewById(R.id.imgvCover);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
+ holder.subscribers = convertView.findViewById(R.id.txtvSubscribers);
+ holder.url = convertView.findViewById(R.id.txtvUrl);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
@@ -52,11 +53,12 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
if (StringUtils.isNotBlank(podcast.getLogoUrl())) {
Glide.with(convertView.getContext())
.load(podcast.getLogoUrl())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(holder.image);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java
index b4eadefb5..52fca792e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java
@@ -34,8 +34,8 @@ public class TagListAdapter extends ArrayAdapter<GpodnetTag> {
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.gpodnet_tag_listitem, parent, false);
- holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
- holder.usage = (TextView) convertView.findViewById(R.id.txtvUsage);
+ holder.title = convertView.findViewById(R.id.txtvTitle);
+ holder.usage = convertView.findViewById(R.id.txtvUsage);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
index f1f8be559..2cf17c85f 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
@@ -12,6 +12,8 @@ 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.core.glide.ApGlideSettings;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -80,10 +82,11 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
//Update the empty imageView with the image from the feed
Glide.with(context)
.load(podcast.imageUrl)
- .placeholder(R.color.light_gray)
- .diskCacheStrategy(DiskCacheStrategy.NONE)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+ .dontAnimate())
.into(viewHolder.coverView);
//Feed the grid view
@@ -124,7 +127,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
* @param json object holding the podcast information
* @throws JSONException
*/
- public static Podcast fromSearch(JSONObject json) throws JSONException {
+ public static Podcast fromSearch(JSONObject json) {
String title = json.optString("collectionName", "");
String imageUrl = json.optString("artworkUrl100", null);
String feedUrl = json.optString("feedUrl", null);
@@ -132,7 +135,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
}
public static Podcast fromSearch(SearchHit searchHit) {
- return new Podcast(searchHit.getTitle(), searchHit.getImageUrl(), searchHit.getXmlUrl());
+ return new Podcast(searchHit.getTitle(), searchHit.getThumbImageURL(), searchHit.getXmlUrl());
}
/**
@@ -162,7 +165,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
/**
* View holder object for the GridView
*/
- class PodcastViewHolder {
+ static class PodcastViewHolder {
/**
* ImageView holding the Podcast image
@@ -182,9 +185,9 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> {
* @param view GridView cell
*/
PodcastViewHolder(View view){
- coverView = (ImageView) view.findViewById(R.id.imgvCover);
- titleView = (TextView) view.findViewById(R.id.txtvTitle);
- urlView = (TextView) view.findViewById(R.id.txtvUrl);
+ coverView = view.findViewById(R.id.imgvCover);
+ titleView = view.findViewById(R.id.txtvTitle);
+ urlView = view.findViewById(R.id.txtvUrl);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
index 192df8ca5..7c3c570a0 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
@@ -12,7 +12,7 @@ import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.LangUtils;
-import rx.Observable;
+import io.reactivex.Observable;
/**
* Writes an OPML file into the export directory in the background.
@@ -23,15 +23,15 @@ public class ExportWorker {
private static final String TAG = "ExportWorker";
private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds";
- private ExportWriter exportWriter;
- private File output;
+ private final ExportWriter exportWriter;
+ private final File output;
public ExportWorker(ExportWriter exportWriter) {
this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR),
DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()));
}
- public ExportWorker(ExportWriter exportWriter, @NonNull File output) {
+ private ExportWorker(ExportWriter exportWriter, @NonNull File output) {
this.exportWriter = exportWriter;
this.output = output;
}
@@ -57,7 +57,7 @@ public class ExportWorker {
subscriber.onError(e);
}
}
- subscriber.onCompleted();
+ subscriber.onComplete();
}
});
}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
index 4449d82c2..ea5128102 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.asynctask;
-import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
@@ -16,9 +15,9 @@ import de.danoeh.antennapod.core.storage.DownloadRequester;
/** Queues items for download in the background. */
public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> {
- private Context context;
+ private final Context context;
private ProgressDialog progDialog;
- private int[] selection;
+ private final int[] selection;
public OpmlFeedQueuer(Context context, int[] selection) {
super();
@@ -56,13 +55,8 @@ public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> {
return null;
}
- @SuppressLint("NewApi")
public void executeAsync() {
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(THREAD_POOL_EXECUTOR);
- } else {
- execute();
- }
+ executeOnExecutor(THREAD_POOL_EXECUTOR);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
index 62ea85811..13b95907f 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.asynctask;
-import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
@@ -21,12 +20,12 @@ public class OpmlImportWorker extends
AsyncTask<Void, Void, ArrayList<OpmlElement>> {
private static final String TAG = "OpmlImportWorker";
- private Context context;
+ private final Context context;
private Exception exception;
private ProgressDialog progDialog;
- private Reader mReader;
+ private final Reader mReader;
public OpmlImportWorker(Context context, Reader reader) {
super();
@@ -93,13 +92,8 @@ public class OpmlImportWorker extends
return exception != null;
}
- @SuppressLint("NewApi")
public void executeAsync() {
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(THREAD_POOL_EXECUTOR);
- } else {
- execute();
- }
+ executeOnExecutor(THREAD_POOL_EXECUTOR);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
index f26f2ea76..4138738f6 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java
@@ -6,7 +6,9 @@ import de.danoeh.antennapod.core.ClientConfig;
/**
* Configures the ClientConfig class of the core package.
*/
-public class ClientConfigurator {
+class ClientConfigurator {
+
+ private ClientConfigurator(){}
static {
ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME;
diff --git a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
index 1ab60ef61..eb70d8e0b 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.config;
import android.content.Context;
import android.content.Intent;
-
+import android.os.Build;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.CastplayerActivity;
@@ -18,7 +18,11 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
return new Intent(context, CastplayerActivity.class);
}
if (mediaType == MediaType.VIDEO) {
- return new Intent(context, VideoplayerActivity.class);
+ Intent i = new Intent(context, VideoplayerActivity.class);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT);
+ }
+ return i;
} else {
return new Intent(context, AudioplayerActivity.class);
}
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 6f9e221ec..2c41b8cb8 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java
@@ -35,11 +35,11 @@ public abstract class AuthenticationDialog extends Dialog {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.authentication_dialog);
- final EditText etxtUsername = (EditText) findViewById(R.id.etxtUsername);
- final EditText etxtPassword = (EditText) findViewById(R.id.etxtPassword);
- final CheckBox saveUsernamePassword = (CheckBox) findViewById(R.id.chkSaveUsernamePassword);
- final Button butConfirm = (Button) findViewById(R.id.butConfirm);
- final Button butCancel = (Button) findViewById(R.id.butCancel);
+ 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);
if (titleRes != 0) {
setTitle(titleRes);
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
index 93425949c..c28342374 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java
@@ -29,9 +29,9 @@ public class AutoFlattrPreferenceDialog {
AlertDialog.Builder builder = new AlertDialog.Builder(activity);
@SuppressLint("InflateParams") View view = activity.getLayoutInflater().inflate(R.layout.autoflattr_preference_dialog, null);
- final CheckBox chkAutoFlattr = (CheckBox) view.findViewById(R.id.chkAutoFlattr);
- final SeekBar skbPercent = (SeekBar) view.findViewById(R.id.skbPercent);
- final TextView txtvStatus = (TextView) view.findViewById(R.id.txtvStatus);
+ final CheckBox chkAutoFlattr = view.findViewById(R.id.chkAutoFlattr);
+ final SeekBar skbPercent = view.findViewById(R.id.skbPercent);
+ final TextView txtvStatus = view.findViewById(R.id.txtvStatus);
chkAutoFlattr.setChecked(UserPreferences.isAutoFlattr());
skbPercent.setEnabled(chkAutoFlattr.isChecked());
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 ac073141d..07a64cde8 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.dialog;
+import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
@@ -32,14 +33,14 @@ import de.danoeh.antennapod.core.util.LongList;
public class EpisodesApplyActionFragment extends Fragment {
- public String TAG = "EpisodeActionFragment";
+ public static final String TAG = "EpisodeActionFragment";
public static final int ACTION_QUEUE = 1;
- public static final int ACTION_MARK_PLAYED = 2;
- public static final int ACTION_MARK_UNPLAYED = 4;
- public static final int ACTION_DOWNLOAD = 8;
+ private static final int ACTION_MARK_PLAYED = 2;
+ private static final int ACTION_MARK_UNPLAYED = 4;
+ private static final int ACTION_DOWNLOAD = 8;
public static final int ACTION_REMOVE = 16;
- public static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED
+ private static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED
| ACTION_DOWNLOAD | ACTION_REMOVE;
private ListView mListView;
@@ -84,7 +85,7 @@ public class EpisodesApplyActionFragment extends Fragment {
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.episodes_apply_action_fragment, container, false);
- mListView = (ListView) view.findViewById(android.R.id.list);
+ mListView = view.findViewById(android.R.id.list);
mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
mListView.setOnItemClickListener((ListView, view1, position, rowId) -> {
long id = episodes.get(position).getId();
@@ -95,6 +96,28 @@ public class EpisodesApplyActionFragment extends Fragment {
}
refreshCheckboxes();
});
+ mListView.setOnItemLongClickListener((adapterView, view12, position, id) -> {
+ new AlertDialog.Builder(getActivity())
+ .setItems(R.array.batch_long_press_options, (dialogInterface, item) -> {
+ int direction;
+ if (item == 0) {
+ direction = -1;
+ } else {
+ direction = 1;
+ }
+
+ int currentPosition = position + direction;
+ while (currentPosition >= 0 && currentPosition < episodes.size()) {
+ long id1 = episodes.get(currentPosition).getId();
+ if (!checkedIds.contains(id1)) {
+ checkedIds.add(id1);
+ }
+ currentPosition += direction;
+ }
+ refreshCheckboxes();
+ }).show();
+ return true;
+ });
for(FeedItem episode : episodes) {
titles.add(episode.getTitle());
@@ -106,7 +129,7 @@ public class EpisodesApplyActionFragment extends Fragment {
checkAll();
int lastVisibleDiv = 0;
- btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue);
+ btnAddToQueue = view.findViewById(R.id.btnAddToQueue);
if((actions & ACTION_QUEUE) != 0) {
btnAddToQueue.setOnClickListener(v -> queueChecked());
lastVisibleDiv = R.id.divider1;
@@ -114,7 +137,7 @@ public class EpisodesApplyActionFragment extends Fragment {
btnAddToQueue.setVisibility(View.GONE);
view.findViewById(R.id.divider1).setVisibility(View.GONE);
}
- btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed);
+ btnMarkAsPlayed = view.findViewById(R.id.btnMarkAsPlayed);
if((actions & ACTION_MARK_PLAYED) != 0) {
btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed());
lastVisibleDiv = R.id.divider2;
@@ -122,7 +145,7 @@ public class EpisodesApplyActionFragment extends Fragment {
btnMarkAsPlayed.setVisibility(View.GONE);
view.findViewById(R.id.divider2).setVisibility(View.GONE);
}
- btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed);
+ btnMarkAsUnplayed = view.findViewById(R.id.btnMarkAsUnplayed);
if((actions & ACTION_MARK_UNPLAYED) != 0) {
btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed());
lastVisibleDiv = R.id.divider3;
@@ -130,7 +153,7 @@ public class EpisodesApplyActionFragment extends Fragment {
btnMarkAsUnplayed.setVisibility(View.GONE);
view.findViewById(R.id.divider3).setVisibility(View.GONE);
}
- btnDownload = (Button) view.findViewById(R.id.btnDownload);
+ btnDownload = view.findViewById(R.id.btnDownload);
if((actions & ACTION_DOWNLOAD) != 0) {
btnDownload.setOnClickListener(v -> downloadChecked());
lastVisibleDiv = R.id.divider4;
@@ -138,7 +161,7 @@ public class EpisodesApplyActionFragment extends Fragment {
btnDownload.setVisibility(View.GONE);
view.findViewById(R.id.divider4).setVisibility(View.GONE);
}
- btnDelete = (Button) view.findViewById(R.id.btnDelete);
+ btnDelete = view.findViewById(R.id.btnDelete);
if((actions & ACTION_REMOVE) != 0) {
btnDelete.setOnClickListener(v -> deleteChecked());
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
index db6b48735..933ced0f9 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
@@ -8,6 +8,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
@@ -16,6 +17,9 @@ import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
* Creates a dialog that lets the user change the hostname for the gpodder.net service.
*/
public class GpodnetSetHostnameDialog {
+
+ private GpodnetSetHostnameDialog(){}
+
private static final String TAG = "GpodnetSetHostnameDialog";
public static AlertDialog createDialog(final Context context) {
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 0bd75b5b0..8f2629b43 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
@@ -29,21 +29,21 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
+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 okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
-import rx.Observable;
-import rx.Subscriber;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
public class ProxyDialog {
private static final String TAG = "ProxyDialog";
- private Context context;
+ private final Context context;
private MaterialDialog dialog;
@@ -55,7 +55,7 @@ public class ProxyDialog {
private boolean testSuccessful = false;
private TextView txtvMessage;
- private Subscription subscription;
+ private Disposable disposable;
public ProxyDialog(Context context) {
this.context = context;
@@ -102,7 +102,7 @@ public class ProxyDialog {
.autoDismiss(false)
.build();
View view = dialog.getCustomView();
- spType = (Spinner) view.findViewById(R.id.spType);
+ spType = view.findViewById(R.id.spType);
String[] types = { Proxy.Type.DIRECT.name(), Proxy.Type.HTTP.name() };
ArrayAdapter<String> adapter = new ArrayAdapter<>(context,
android.R.layout.simple_spinner_item, types);
@@ -110,22 +110,22 @@ public class ProxyDialog {
spType.setAdapter(adapter);
ProxyConfig proxyConfig = UserPreferences.getProxyConfig();
spType.setSelection(adapter.getPosition(proxyConfig.type.name()));
- etHost = (EditText) view.findViewById(R.id.etHost);
+ etHost = view.findViewById(R.id.etHost);
if(!TextUtils.isEmpty(proxyConfig.host)) {
etHost.setText(proxyConfig.host);
}
etHost.addTextChangedListener(requireTestOnChange);
- etPort = (EditText) view.findViewById(R.id.etPort);
+ etPort = view.findViewById(R.id.etPort);
if(proxyConfig.port > 0) {
etPort.setText(String.valueOf(proxyConfig.port));
}
etPort.addTextChangedListener(requireTestOnChange);
- etUsername = (EditText) view.findViewById(R.id.etUsername);
+ etUsername = view.findViewById(R.id.etUsername);
if(!TextUtils.isEmpty(proxyConfig.username)) {
etUsername.setText(proxyConfig.username);
}
etUsername.addTextChangedListener(requireTestOnChange);
- etPassword = (EditText) view.findViewById(R.id.etPassword);
+ etPassword = view.findViewById(R.id.etPassword);
if(!TextUtils.isEmpty(proxyConfig.password)) {
etPassword.setText(proxyConfig.username);
}
@@ -146,7 +146,7 @@ public class ProxyDialog {
enableSettings(false);
}
});
- txtvMessage = (TextView) view.findViewById(R.id.txtvMessage);
+ txtvMessage = view.findViewById(R.id.txtvMessage);
checkValidity();
return dialog;
}
@@ -229,8 +229,8 @@ public class ProxyDialog {
}
private void test() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
if(!checkValidity()) {
setTestRequired(true);
@@ -243,48 +243,44 @@ public class ProxyDialog {
txtvMessage.setTextColor(textColorPrimary);
txtvMessage.setText("{fa-circle-o-notch spin} " + checking);
txtvMessage.setVisibility(View.VISIBLE);
- subscription = Observable.create(new Observable.OnSubscribe<Response>() {
- @Override
- public void call(Subscriber<? super Response> subscriber) {
- String type = (String) spType.getSelectedItem();
- String host = etHost.getText().toString();
- String port = etPort.getText().toString();
- String username = etUsername.getText().toString();
- String password = etPassword.getText().toString();
- int portValue = 8080;
- if(!TextUtils.isEmpty(port)) {
- portValue = Integer.valueOf(port);
- }
- SocketAddress address = InetSocketAddress.createUnresolved(host, portValue);
- Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase());
- Proxy proxy = new Proxy(proxyType, address);
- OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder()
- .connectTimeout(10, TimeUnit.SECONDS)
- .proxy(proxy);
- builder.interceptors().clear();
- OkHttpClient client = builder.build();
- if(!TextUtils.isEmpty(username)) {
- String credentials = Credentials.basic(username, password);
- client.interceptors().add(chain -> {
- Request request = chain.request().newBuilder()
- .header("Proxy-Authorization", credentials).build();
- return chain.proceed(request);
- });
- }
- Request request = new Request.Builder()
- .url("http://www.google.com")
- .head()
- .build();
- try {
- Response response = client.newCall(request).execute();
- subscriber.onNext(response);
- } catch(IOException e) {
- subscriber.onError(e);
- }
- subscriber.onCompleted();
- }
- })
- .subscribeOn(Schedulers.newThread())
+ disposable = Single.create((SingleOnSubscribe<Response>) emitter -> {
+ String type = (String) spType.getSelectedItem();
+ String host = etHost.getText().toString();
+ String port = etPort.getText().toString();
+ String username = etUsername.getText().toString();
+ String password = etPassword.getText().toString();
+ int portValue = 8080;
+ if(!TextUtils.isEmpty(port)) {
+ portValue = Integer.valueOf(port);
+ }
+ SocketAddress address = InetSocketAddress.createUnresolved(host, portValue);
+ Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase());
+ Proxy proxy = new Proxy(proxyType, address);
+ OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder()
+ .connectTimeout(10, TimeUnit.SECONDS)
+ .proxy(proxy);
+ builder.interceptors().clear();
+ OkHttpClient client = builder.build();
+ if(!TextUtils.isEmpty(username)) {
+ String credentials = Credentials.basic(username, password);
+ client.interceptors().add(chain -> {
+ Request request = chain.request().newBuilder()
+ .header("Proxy-Authorization", credentials).build();
+ return chain.proceed(request);
+ });
+ }
+ Request request = new Request.Builder()
+ .url("http://www.google.com")
+ .head()
+ .build();
+ try {
+ Response response = client.newCall(request).execute();
+ emitter.onSuccess(response);
+ } catch(IOException e) {
+ emitter.onError(e);
+ }
+ })
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
response -> {
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 64fc1fda4..ece184035 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -17,6 +17,8 @@ import de.danoeh.antennapod.R;
public class RatingDialog {
+ private RatingDialog(){}
+
private static final String TAG = RatingDialog.class.getSimpleName();
private static final int AFTER_DAYS = 7;
@@ -54,7 +56,7 @@ public class RatingDialog {
}
}
- public static void rateNow() {
+ private static void rateNow() {
Context context = mContext.get();
if(context == null) {
return;
@@ -67,11 +69,11 @@ public class RatingDialog {
saveRated();
}
- public static boolean rated() {
+ private static boolean rated() {
return mPreferences.getBoolean(KEY_RATED, false);
}
- public static void saveRated() {
+ private static void saveRated() {
mPreferences
.edit()
.putBoolean(KEY_RATED, true)
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 7d6a66a54..4b8601ec6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.dialog;
import android.content.Context;
-import android.support.design.widget.Snackbar;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
@@ -9,7 +8,6 @@ import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
-import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
@@ -26,7 +24,7 @@ public abstract class SleepTimerDialog {
private static final String TAG = SleepTimerDialog.class.getSimpleName();
- private Context context;
+ private final Context context;
private MaterialDialog dialog;
private EditText etxtTime;
@@ -63,11 +61,11 @@ public abstract class SleepTimerDialog {
dialog = builder.build();
View view = dialog.getView();
- etxtTime = (EditText) view.findViewById(R.id.etxtTime);
- spTimeUnit = (Spinner) view.findViewById(R.id.spTimeUnit);
- cbShakeToReset = (CheckBox) view.findViewById(R.id.cbShakeToReset);
- cbVibrate = (CheckBox) view.findViewById(R.id.cbVibrate);
- chAutoEnable = (CheckBox) view.findViewById(R.id.chAutoEnable);
+ 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);
etxtTime.setText(SleepTimerPreferences.lastTimerValue());
etxtTime.addTextChangedListener(new TextWatcher() {
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 6a975fe49..cf9a2907b 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -52,7 +52,7 @@ public class VariableSpeedDialog {
builder.neutralText(R.string.close_label);
builder.onPositive((dialog, which) -> {
if (Build.VERSION.SDK_INT >= 16) { // just to be safe
- UserPreferences.enableSonic(true);
+ UserPreferences.enableSonic();
if(showSpeedSelector) {
showSpeedSelectorDialog(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 f14ebbdaf..ee2373da8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -25,25 +25,25 @@ public class AddFeedFragment extends Fragment {
/**
* Preset value for url text field.
*/
- public static final String ARG_FEED_URL = "feedurl";
+ private static final String ARG_FEED_URL = "feedurl";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.addfeed, container, false);
- final EditText etxtFeedurl = (EditText) root.findViewById(R.id.etxtFeedurl);
+ final EditText etxtFeedurl = root.findViewById(R.id.etxtFeedurl);
Bundle args = getArguments();
if (args != null && args.getString(ARG_FEED_URL) != null) {
etxtFeedurl.setText(args.getString(ARG_FEED_URL));
}
- Button butSearchITunes = (Button) root.findViewById(R.id.butSearchItunes);
- Button butBrowserGpoddernet = (Button) root.findViewById(R.id.butBrowseGpoddernet);
- Button butSearchFyyd = (Button) root.findViewById(R.id.butSearchFyyd);
- Button butOpmlImport = (Button) root.findViewById(R.id.butOpmlImport);
- Button butConfirm = (Button) root.findViewById(R.id.butConfirm);
+ Button butSearchITunes = root.findViewById(R.id.butSearchItunes);
+ Button butBrowserGpoddernet = root.findViewById(R.id.butBrowseGpoddernet);
+ Button butSearchFyyd = root.findViewById(R.id.butSearchFyyd);
+ Button butOpmlImport = root.findViewById(R.id.butOpmlImport);
+ Button butConfirm = root.findViewById(R.id.butConfirm);
final MainActivity activity = (MainActivity) getActivity();
activity.getSupportActionBar().setTitle(R.string.add_feed_label);
@@ -66,4 +66,15 @@ public class AddFeedFragment extends Fragment {
return root;
}
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+
+ // So, we certainly *don't* have an options menu,
+ // but unless we say we do, old options menus sometimes
+ // persist. mfietz thinks this causes the ActionBar to be invalidated
+ setHasOptionsMenu(true);
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index bbfd1688d..ef522d3b3 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -4,6 +4,8 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
+import android.os.Handler;
+import android.support.design.widget.Snackbar;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.LinearLayoutManager;
@@ -36,22 +38,22 @@ 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;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
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.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Shows unread or recently published episodes
@@ -69,24 +71,24 @@ public class AllEpisodesFragment extends Fragment {
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
- protected RecyclerView recyclerView;
- protected AllEpisodesRecycleAdapter listAdapter;
+ RecyclerView recyclerView;
+ AllEpisodesRecycleAdapter listAdapter;
private ProgressBar progLoading;
- protected List<FeedItem> episodes;
+ List<FeedItem> episodes;
private List<Downloader> downloaderList;
private boolean itemsLoaded = false;
private boolean viewsCreated = false;
private boolean isUpdatingFeeds;
- protected boolean isMenuInvalidationAllowed = false;
+ boolean isMenuInvalidationAllowed = false;
- protected Subscription subscription;
+ Disposable disposable;
private LinearLayoutManager layoutManager;
- protected boolean showOnlyNewEpisodes() { return false; }
- protected String getPrefName() { return DEFAULT_PREF_NAME; }
+ boolean showOnlyNewEpisodes() { return false; }
+ String getPrefName() { return DEFAULT_PREF_NAME; }
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -123,8 +125,8 @@ public class AllEpisodesFragment extends Fragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
}
@@ -165,7 +167,7 @@ public class AllEpisodesFragment extends Fragment {
}
}
- protected void resetViewState() {
+ void resetViewState() {
viewsCreated = false;
listAdapter = null;
}
@@ -273,24 +275,24 @@ public class AllEpisodesFragment extends Fragment {
if(item.getItemId() == R.id.share_item) {
return true; // avoids that the position is reset when we need it in the submenu
}
- int pos = listAdapter.getPosition();
- if(pos < 0) {
- return false;
- }
- FeedItem selectedItem = itemAccess.getItem(pos);
+ FeedItem selectedItem = listAdapter.getSelectedItem();
if (selectedItem == null) {
- Log.i(TAG, "Selected item at position " + pos + " was null, ignoring selection");
+ Log.i(TAG, "Selected item was null, ignoring selection");
return super.onContextItemSelected(item);
}
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
+ // Mark as seen contains UI logic specific to All/New/FavoriteSegments,
+ // e.g., Undo with Snackbar,
+ // and is handled by this class rather than the generic FeedItemMenuHandler
+ // Undo is useful for Mark as seen, given there is no UI to undo it otherwise,
+ // i.e., there is context menu item for Mark as new
+ if (R.id.mark_as_seen_item == item.getItemId()) {
+ markItemAsSeenWithUndo(selectedItem);
return true;
}
+
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
}
@Override
@@ -299,15 +301,15 @@ public class AllEpisodesFragment extends Fragment {
R.layout.all_episodes_fragment);
}
- protected View onCreateViewHelper(LayoutInflater inflater,
- ViewGroup container,
- Bundle savedInstanceState,
- int fragmentResource) {
+ View onCreateViewHelper(LayoutInflater inflater,
+ ViewGroup container,
+ Bundle savedInstanceState,
+ int fragmentResource) {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(fragmentResource, container, false);
- recyclerView = (RecyclerView) root.findViewById(android.R.id.list);
+ recyclerView = root.findViewById(android.R.id.list);
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
@@ -317,7 +319,7 @@ public class AllEpisodesFragment extends Fragment {
recyclerView.setHasFixedSize(true);
recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build());
- progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
+ progLoading = root.findViewById(R.id.progLoading);
if (!itemsLoaded) {
progLoading.setVisibility(View.VISIBLE);
@@ -346,7 +348,7 @@ public class AllEpisodesFragment extends Fragment {
updateShowOnlyEpisodesListViewState();
}
- protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
+ private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() {
@Override
public int getCount() {
@@ -444,7 +446,7 @@ public class AllEpisodesFragment extends Fragment {
}
}
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
@@ -459,16 +461,16 @@ public class AllEpisodesFragment extends Fragment {
private void updateShowOnlyEpisodesListViewState() {
}
- protected void loadItems() {
- if(subscription != null) {
- subscription.unsubscribe();
+ void loadItems() {
+ if (disposable != null) {
+ disposable.dispose();
}
if (viewsCreated && !itemsLoaded) {
recyclerView.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
- subscription = Observable.fromCallable(this::loadData)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(this::loadData)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
recyclerView.setVisibility(View.VISIBLE);
@@ -483,8 +485,40 @@ public class AllEpisodesFragment extends Fragment {
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
- protected List<FeedItem> loadData() {
+ List<FeedItem> loadData() {
return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT);
}
+ void markItemAsSeenWithUndo(FeedItem item) {
+ if (item == null) {
+ return;
+ }
+
+ Log.d(TAG, "markItemAsSeenWithUndo(" + item.getId() + ")");
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ // we're marking it as unplayed since the user didn't actually play it
+ // but they don't want it considered 'NEW' anymore
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
+
+ final Handler h = new Handler(getActivity().getMainLooper());
+ final Runnable r = () -> {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
+ }
+ };
+
+ Snackbar snackbar = Snackbar.make(getView(), getString(R.string.marked_as_seen_label),
+ Snackbar.LENGTH_LONG);
+ snackbar.setAction(getString(R.string.undo), v -> {
+ DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
+ // don't forget to cancel the thing that's going to remove the media
+ h.removeCallbacks(r);
+ });
+ snackbar.show();
+ h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f));
+ }
+
}
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 1ba7ed557..4bba9b255 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
@@ -1,8 +1,6 @@
package de.danoeh.antennapod.fragment;
import android.content.Context;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.util.Log;
@@ -12,9 +10,6 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
-import com.joanzapata.iconify.IconDrawable;
-import com.joanzapata.iconify.fonts.FontAwesomeIcons;
-
import java.util.List;
import de.danoeh.antennapod.R;
@@ -22,15 +17,14 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter;
import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays all running downloads and provides a button to delete them
@@ -48,7 +42,7 @@ public class CompletedDownloadsFragment extends ListFragment {
private boolean viewCreated = false;
- private Subscription subscription;
+ private Disposable disposable;
@Override
public void onCreate(Bundle savedInstanceState) {
@@ -67,16 +61,16 @@ public class CompletedDownloadsFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
}
@Override
public void onDetach() {
super.onDetach();
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
}
@@ -85,8 +79,8 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onDestroyView();
listAdapter = null;
viewCreated = false;
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
}
@@ -140,18 +134,7 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onCreateOptionsMenu(menu, inflater);
if(items != null) {
inflater.inflate(R.menu.downloads_completed, menu);
- MenuItem episodeActions = menu.findItem(R.id.episode_actions);
- if(items.size() > 0) {
- int[] attrs = {R.attr.action_bar_icon_color};
- TypedArray ta = getActivity().obtainStyledAttributes(UserPreferences.getTheme(), attrs);
- int textColor = ta.getColor(0, Color.GRAY);
- ta.recycle();
- episodeActions.setIcon(new IconDrawable(getActivity(),
- FontAwesomeIcons.fa_gears).color(textColor).actionBarSize());
- episodeActions.setVisible(true);
- } else {
- episodeActions.setVisible(false);
- }
+ menu.findItem(R.id.episode_actions).setVisible(items.size() > 0);
}
}
@@ -160,7 +143,7 @@ public class CompletedDownloadsFragment extends ListFragment {
switch (item.getItemId()) {
case R.id.episode_actions:
EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment
- .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE);
+ .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE | EpisodesApplyActionFragment.ACTION_QUEUE);
((MainActivity) getActivity()).loadChildFragment(fragment);
return true;
default:
@@ -168,7 +151,7 @@ public class CompletedDownloadsFragment extends ListFragment {
}
}
- private DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() {
+ private final DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() {
@Override
public int getCount() {
return (items != null) ? items.size() : 0;
@@ -189,7 +172,7 @@ public class CompletedDownloadsFragment extends ListFragment {
}
};
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
@@ -199,21 +182,19 @@ public class CompletedDownloadsFragment extends ListFragment {
};
private void loadItems() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
if (items == null && viewCreated) {
setListShown(false);
}
- subscription = Observable.fromCallable(DBReader::getDownloadedItems)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(DBReader::getDownloadedItems)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
- if (result != null) {
- items = result;
- if (viewCreated && getActivity() != null) {
- onFragmentLoaded();
- }
+ items = result;
+ if (viewCreated && getActivity() != null) {
+ onFragmentLoaded();
}
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
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 1d3fcefba..5a061c7e6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -11,6 +11,7 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
@@ -22,7 +23,6 @@ import de.danoeh.antennapod.core.util.playback.Playable;
public class CoverFragment extends Fragment implements MediaplayerInfoContentFragment {
private static final String TAG = "CoverFragment";
- private static final String ARG_PLAYABLE = "arg.playable";
private Playable media;
@@ -49,9 +49,9 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
root = inflater.inflate(R.layout.cover_fragment, container, false);
- txtvPodcastTitle = (TextView) root.findViewById(R.id.txtvPodcastTitle);
- txtvEpisodeTitle = (TextView) root.findViewById(R.id.txtvEpisodeTitle);
- imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
+ txtvPodcastTitle = root.findViewById(R.id.txtvPodcastTitle);
+ txtvEpisodeTitle = root.findViewById(R.id.txtvEpisodeTitle);
+ imgvCover = root.findViewById(R.id.imgvCover);
return root;
}
@@ -61,9 +61,10 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra
txtvEpisodeTitle.setText(media.getEpisodeTitle());
Glide.with(this)
.load(media.getImageLocation())
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .dontAnimate()
- .fitCenter()
+ .apply(new RequestOptions()
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .dontAnimate()
+ .fitCenter())
.into(imgvCover);
} else {
Log.w(TAG, "loadMediaInfo was called while media was null");
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 0a710196a..5ab6bac63 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.fragment;
+import android.app.AlertDialog;
+import android.app.Dialog;
import android.content.res.TypedArray;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
@@ -10,19 +12,21 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import android.widget.TextView;
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.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Shows the download log
@@ -37,7 +41,7 @@ public class DownloadLogFragment extends ListFragment {
private boolean viewsCreated = false;
private boolean itemsLoaded = false;
- private Subscription subscription;
+ private Disposable disposable;
@Override
public void onStart() {
@@ -51,8 +55,8 @@ public class DownloadLogFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
}
@@ -82,7 +86,30 @@ public class DownloadLogFragment extends ListFragment {
getActivity().supportInvalidateOptionsMenu();
}
- private DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() {
+ @Override
+ public void onListItemClick(ListView l, View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+
+ DownloadStatus status = adapter.getItem(position);
+ String url = "unknown";
+ String message = getString(R.string.download_successful);
+ FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId());
+ if (media != null) {
+ url = media.getDownload_url();
+ }
+ if (!status.isSuccessful()) {
+ message = status.getReasonDetailed();
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.download_error_details);
+ builder.setMessage(getString(R.string.download_error_details_message, message, url));
+ builder.setPositiveButton(android.R.string.ok, null);
+ Dialog dialog = builder.show();
+ ((TextView) dialog.findViewById(android.R.id.message)).setTextIsSelectable(true);
+ }
+
+ private final DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() {
@Override
public int getCount() {
@@ -99,7 +126,7 @@ public class DownloadLogFragment extends ListFragment {
}
};
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
@@ -151,11 +178,11 @@ public class DownloadLogFragment extends ListFragment {
}
private void loadItems() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
- subscription = Observable.fromCallable(DBReader::getDownloadLog)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(DBReader::getDownloadLog)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
index 52a38ccb9..aa6029c84 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
@@ -25,7 +25,7 @@ public class DownloadsFragment extends Fragment {
public static final String ARG_SELECTED_TAB = "selected_tab";
public static final int POS_RUNNING = 0;
- public static final int POS_COMPLETED = 1;
+ private static final int POS_COMPLETED = 1;
public static final int POS_LOG = 2;
private static final String PREF_LAST_TAB_POSITION = "tab_position";
@@ -38,12 +38,12 @@ public class DownloadsFragment extends Fragment {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.pager_fragment, container, false);
- viewPager = (ViewPager)root.findViewById(R.id.viewpager);
+ viewPager = root.findViewById(R.id.viewpager);
DownloadsPagerAdapter pagerAdapter = new DownloadsPagerAdapter(getChildFragmentManager(), getResources());
viewPager.setAdapter(pagerAdapter);
// Give the TabLayout the ViewPager
- tabLayout = (TabLayout) root.findViewById(R.id.sliding_tabs);
+ tabLayout = root.findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
return root;
@@ -78,9 +78,9 @@ public class DownloadsFragment extends Fragment {
viewPager.setCurrentItem(lastPosition);
}
- public class DownloadsPagerAdapter extends FragmentPagerAdapter {
+ public static class DownloadsPagerAdapter extends FragmentPagerAdapter {
- Resources resources;
+ final Resources resources;
public DownloadsPagerAdapter(FragmentManager fm, Resources resources) {
super(fm);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
index e2fbd91f3..0610bfd24 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
@@ -21,10 +21,10 @@ public class EpisodesFragment extends Fragment {
public static final String TAG = "EpisodesFragment";
private static final String PREF_LAST_TAB_POSITION = "tab_position";
- public static final int POS_NEW_EPISODES = 0;
- public static final int POS_ALL_EPISODES = 1;
- public static final int POS_FAV_EPISODES = 2;
- public static final int TOTAL_COUNT = 3;
+ private static final int POS_NEW_EPISODES = 0;
+ private static final int POS_ALL_EPISODES = 1;
+ private static final int POS_FAV_EPISODES = 2;
+ private static final int TOTAL_COUNT = 3;
private TabLayout tabLayout;
@@ -46,11 +46,11 @@ public class EpisodesFragment extends Fragment {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.episodes_label);
View rootView = inflater.inflate(R.layout.pager_fragment, container, false);
- viewPager = (ViewPager)rootView.findViewById(R.id.viewpager);
+ viewPager = rootView.findViewById(R.id.viewpager);
viewPager.setAdapter(new EpisodesPagerAdapter(getChildFragmentManager(), getResources()));
// Give the TabLayout the ViewPager
- tabLayout = (TabLayout) rootView.findViewById(R.id.sliding_tabs);
+ tabLayout = rootView.findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
return rootView;
@@ -79,7 +79,7 @@ public class EpisodesFragment extends Fragment {
public static class EpisodesPagerAdapter extends FragmentPagerAdapter {
private final Resources resources;
- private AllEpisodesFragment[] fragments = {
+ private final AllEpisodesFragment[] fragments = {
new NewEpisodesFragment(),
new AllEpisodesFragment(),
new FavoriteEpisodesFragment()
@@ -106,7 +106,7 @@ public class EpisodesFragment extends Fragment {
case POS_ALL_EPISODES:
return resources.getString(R.string.all_episodes_short_label);
case POS_NEW_EPISODES:
- return resources.getString(R.string.new_label);
+ return resources.getString(R.string.new_episodes_label);
case POS_FAV_EPISODES:
return resources.getString(R.string.favorite_episodes_label);
default:
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 1e385728a..de2f04590 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -1,6 +1,9 @@
package de.danoeh.antennapod.fragment;
+import android.content.Intent;
+import android.os.Build;
import android.os.Bundle;
+import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
@@ -13,12 +16,17 @@ import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
+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.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import io.reactivex.Maybe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Fragment which is supposed to be displayed outside of the MediaplayerActivity
@@ -33,8 +41,8 @@ public class ExternalPlayerFragment extends Fragment {
private ImageButton butPlay;
private TextView mFeedName;
private ProgressBar mProgressBar;
-
private PlaybackController controller;
+ private Disposable disposable;
public ExternalPlayerFragment() {
super();
@@ -45,19 +53,26 @@ public class ExternalPlayerFragment extends Fragment {
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.external_player_fragment,
container, false);
- fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout);
- imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
- txtvTitle = (TextView) root.findViewById(R.id.txtvTitle);
- butPlay = (ImageButton) root.findViewById(R.id.butPlay);
- mFeedName = (TextView) root.findViewById(R.id.txtvAuthor);
- mProgressBar = (ProgressBar) root.findViewById(R.id.episodeProgress);
+ fragmentLayout = root.findViewById(R.id.fragmentLayout);
+ imgvCover = root.findViewById(R.id.imgvCover);
+ txtvTitle = root.findViewById(R.id.txtvTitle);
+ butPlay = root.findViewById(R.id.butPlay);
+ mFeedName = root.findViewById(R.id.txtvAuthor);
+ mProgressBar = root.findViewById(R.id.episodeProgress);
fragmentLayout.setOnClickListener(v -> {
Log.d(TAG, "layoutInfo was clicked");
if (controller != null && controller.getMedia() != null) {
- startActivity(PlaybackService.getPlayerActivityIntent(
- getActivity(), controller.getMedia()));
+ 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");
+ startActivity(intent, options.toBundle());
+ } else {
+ startActivity(intent);
+ }
}
});
return root;
@@ -68,10 +83,15 @@ public class ExternalPlayerFragment extends Fragment {
super.onActivityCreated(savedInstanceState);
controller = setupPlaybackController();
butPlay.setOnClickListener(v -> {
- if(controller != null) {
+ if (controller != null) {
controller.playPause();
}
});
+ loadMediaInfo();
+ }
+
+ public void connectToPlaybackService() {
+ controller.init();
}
private PlaybackController setupPlaybackController() {
@@ -112,9 +132,9 @@ public class ExternalPlayerFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
+ onPositionObserverUpdate();
+
controller.init();
- mProgressBar.setProgress((int)
- ((double) controller.getPosition() / controller.getDuration() * 100));
}
@Override
@@ -124,6 +144,9 @@ public class ExternalPlayerFragment extends Fragment {
if (controller != null) {
controller.release();
}
+ if (disposable != null) {
+ disposable.dispose();
+ }
}
@Override
@@ -144,7 +167,7 @@ public class ExternalPlayerFragment extends Fragment {
controller = setupPlaybackController();
if (butPlay != null) {
butPlay.setOnClickListener(v -> {
- if(controller != null) {
+ if (controller != null) {
controller.playPause();
}
});
@@ -154,50 +177,65 @@ public class ExternalPlayerFragment extends Fragment {
private boolean loadMediaInfo() {
Log.d(TAG, "Loading media info");
- if (controller != null && controller.serviceAvailable()) {
- Playable media = controller.getMedia();
- if (media != null) {
- txtvTitle.setText(media.getEpisodeTitle());
- mFeedName.setText(media.getFeedTitle());
- mProgressBar.setProgress((int)
- ((double) controller.getPosition() / controller.getDuration() * 100));
-
- Glide.with(getActivity())
- .load(media.getImageLocation())
+ if (controller == null) {
+ Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!");
+ return false;
+ }
+
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Maybe.create(emitter -> {
+ Playable media = controller.getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> updateUi((Playable) media),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ return true;
+ }
+
+ private void updateUi(Playable media) {
+ if (media != null) {
+ txtvTitle.setText(media.getEpisodeTitle());
+ mFeedName.setText(media.getFeedTitle());
+ onPositionObserverUpdate();
+
+ Glide.with(getActivity())
+ .load(media.getImageLocation())
+ .apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.fitCenter()
- .dontAnimate()
- .into(imgvCover);
+ .dontAnimate())
+ .into(imgvCover);
- fragmentLayout.setVisibility(View.VISIBLE);
- if (controller.isPlayingVideoLocally()) {
- butPlay.setVisibility(View.GONE);
- } else {
- butPlay.setVisibility(View.VISIBLE);
- }
- return true;
+ fragmentLayout.setVisibility(View.VISIBLE);
+ if (controller.isPlayingVideoLocally()) {
+ butPlay.setVisibility(View.GONE);
} else {
- Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
- return false;
+ butPlay.setVisibility(View.VISIBLE);
}
} else {
- Log.w(TAG, "loadMediaInfo was called while playbackService was null!");
- return false;
+ Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!");
}
}
- private String getPositionString(int position, int duration) {
- return Converter.getDurationStringLong(position) + " / "
- + Converter.getDurationStringLong(duration);
- }
-
public PlaybackController getPlaybackControllerTestingOnly() {
return controller;
}
- public void onPositionObserverUpdate() {
+ private void onPositionObserverUpdate() {
+ if (controller.getPosition() == PlaybackService.INVALID_TIME
+ || controller.getDuration() == PlaybackService.INVALID_TIME) {
+ return;
+ }
mProgressBar.setProgress((int)
((double) controller.getPosition() / controller.getDuration() * 100));
}
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 234c8377d..70f82c2ec 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -26,7 +26,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
public class FavoriteEpisodesFragment extends AllEpisodesFragment {
- public static final String TAG = "FavoriteEpisodesFrag";
+ private static final String TAG = "FavoriteEpisodesFrag";
private static final String PREF_NAME = "PrefFavoriteEpisodesFragment";
@@ -62,8 +62,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
Log.d(TAG, "remove(" + holder.getItemId() + ")");
- if (subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
FeedItem item = holder.getFeedItem();
if (item != null) {
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 7c1ec5ec1..dadc596e2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
@@ -28,9 +28,9 @@ import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.mfietz.fyydlin.FyydClient;
import de.mfietz.fyydlin.FyydResponse;
import de.mfietz.fyydlin.SearchHit;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
import static java.util.Collections.emptyList;
@@ -49,13 +49,13 @@ public class FyydSearchFragment extends Fragment {
private Button butRetry;
private TextView txtvEmpty;
- private FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient());
+ private final FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient());
/**
* List of podcasts retreived from the search
*/
private List<Podcast> searchResults;
- private Subscription subscription;
+ private Disposable disposable;
/**
* Constructor
@@ -75,7 +75,7 @@ public class FyydSearchFragment extends Fragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
- gridView = (GridView) root.findViewById(R.id.gridView);
+ gridView = root.findViewById(R.id.gridView);
adapter = new ItunesAdapter(getActivity(), new ArrayList<>());
gridView.setAdapter(adapter);
@@ -87,10 +87,10 @@ public class FyydSearchFragment extends Fragment {
intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title);
startActivity(intent);
});
- progressBar = (ProgressBar) root.findViewById(R.id.progressBar);
- txtvError = (TextView) root.findViewById(R.id.txtvError);
- butRetry = (Button) root.findViewById(R.id.butRetry);
- txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
+ progressBar = root.findViewById(R.id.progressBar);
+ txtvError = root.findViewById(R.id.txtvError);
+ butRetry = root.findViewById(R.id.butRetry);
+ txtvEmpty = root.findViewById(android.R.id.empty);
return root;
}
@@ -98,8 +98,8 @@ public class FyydSearchFragment extends Fragment {
@Override
public void onDestroy() {
super.onDestroy();
- if (subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
adapter = null;
}
@@ -141,12 +141,12 @@ public class FyydSearchFragment extends Fragment {
}
private void search(String query) {
- if (subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
showOnlyProgressBar();
- subscription = client.searchPodcasts(query)
- .subscribeOn(Schedulers.newThread())
+ disposable = client.searchPodcasts(query, 10)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
progressBar.setVisibility(View.GONE);
@@ -169,12 +169,12 @@ public class FyydSearchFragment extends Fragment {
progressBar.setVisibility(View.VISIBLE);
}
- void processSearchResult(FyydResponse response) {
+ private void processSearchResult(FyydResponse response) {
adapter.clear();
if (!response.getData().isEmpty()) {
adapter.clear();
searchResults = new ArrayList<>();
- for (SearchHit searchHit : response.getData().values()) {
+ for (SearchHit searchHit : response.getData()) {
Podcast podcast = Podcast.fromSearch(searchHit);
searchResults.add(podcast);
}
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 a0586fe16..4ee7a06ad 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -10,7 +10,6 @@ import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
@@ -34,15 +33,16 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
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.ShownotesProvider;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import de.danoeh.antennapod.core.util.playback.Timeline;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays the description of a Playable object in a Webview.
@@ -66,7 +66,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
private ShownotesProvider shownotesProvider;
private Playable media;
- private Subscription webViewLoader;
+ private Disposable webViewLoader;
/**
* URL that was selected via long-press.
@@ -112,15 +112,20 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
Bundle savedInstanceState) {
Log.d(TAG, "Creating view");
webvDescription = new WebView(getActivity().getApplicationContext());
- if (Build.VERSION.SDK_INT >= 11) {
- webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
+ webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+
TypedArray ta = getActivity().getTheme().obtainStyledAttributes(new int[]
{android.R.attr.colorBackground});
- int backgroundColor = ta.getColor(0, UserPreferences.getTheme() ==
- R.style.Theme_AntennaPod_Dark ? Color.BLACK : Color.WHITE);
+ boolean black = UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark
+ || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack;
+ int backgroundColor = ta.getColor(0, black ? Color.BLACK : Color.WHITE);
+
ta.recycle();
webvDescription.setBackgroundColor(backgroundColor);
+ if (!NetworkUtils.networkAvailable()) {
+ webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
+ // Use cached resources, even if they have expired
+ }
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
webvDescription.getSettings().setLoadWithOverviewMode(true);
@@ -162,7 +167,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
super.onDestroy();
Log.d(TAG, "Fragment destroyed");
if (webViewLoader != null) {
- webViewLoader.unsubscribe();
+ webViewLoader.dispose();
}
if (webvDescription != null) {
webvDescription.removeAllViews();
@@ -193,7 +198,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
} else if (args.containsKey(ARG_FEEDITEM_ID)) {
long id = getArguments().getLong(ARG_FEEDITEM_ID);
Observable.defer(() -> Observable.just(DBReader.getFeedItem(id)))
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(feedItem -> {
shownotesProvider = feedItem;
@@ -203,7 +208,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
}
- private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
+ private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
@@ -238,17 +243,11 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
ShareUtils.shareLink(getActivity(), selectedURL);
break;
case R.id.copy_url_item:
- if (android.os.Build.VERSION.SDK_INT >= 11) {
- ClipData clipData = ClipData.newPlainText(selectedURL,
- selectedURL);
- android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setPrimaryClip(clipData);
- } else {
- android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setText(selectedURL);
- }
+ ClipData clipData = ClipData.newPlainText(selectedURL,
+ selectedURL);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
Toast t = Toast.makeText(getActivity(),
R.string.copied_url_msg, Toast.LENGTH_SHORT);
t.show();
@@ -299,13 +298,13 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo
private void load() {
Log.d(TAG, "load()");
if(webViewLoader != null) {
- webViewLoader.unsubscribe();
+ webViewLoader.dispose();
}
if(shownotesProvider == null) {
return;
}
- webViewLoader = Observable.defer(() -> Observable.just(loadData()))
- .subscribeOn(Schedulers.newThread())
+ webViewLoader = Observable.fromCallable(this::loadData)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(data -> {
webvDescription.loadDataWithBaseURL(null, data, "text/html",
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 7939dcb23..bcca281d4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -31,6 +31,7 @@ import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
@@ -51,26 +52,27 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
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.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.LongList;
+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 de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays information about a FeedItem and actions.
@@ -133,7 +135,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private IconButton butAction2;
private Menu popupMenu;
- private Subscription subscription;
+ private Disposable disposable;
/**
* URL that was selected via long-press.
@@ -164,32 +166,36 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
super.onCreateView(inflater, container, savedInstanceState);
View layout = inflater.inflate(R.layout.feeditem_fragment, container, false);
- root = (ViewGroup) layout.findViewById(R.id.content_root);
+ root = layout.findViewById(R.id.content_root);
- LinearLayout header = (LinearLayout) root.findViewById(R.id.header);
+ LinearLayout header = root.findViewById(R.id.header);
if(feedItems.length > 0) {
header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
}
- txtvPodcast = (TextView) layout.findViewById(R.id.txtvPodcast);
+ txtvPodcast = layout.findViewById(R.id.txtvPodcast);
txtvPodcast.setOnClickListener(v -> openPodcast());
- txtvTitle = (TextView) layout.findViewById(R.id.txtvTitle);
+ txtvTitle = layout.findViewById(R.id.txtvTitle);
if(Build.VERSION.SDK_INT >= 23) {
txtvTitle.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
}
- txtvDuration = (TextView) layout.findViewById(R.id.txtvDuration);
- txtvPublished = (TextView) layout.findViewById(R.id.txtvPublished);
+ txtvDuration = layout.findViewById(R.id.txtvDuration);
+ txtvPublished = layout.findViewById(R.id.txtvPublished);
if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
}
- webvDescription = (WebView) layout.findViewById(R.id.webvDescription);
- if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- if (Build.VERSION.SDK_INT >= 11
- && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ webvDescription = layout.findViewById(R.id.webvDescription);
+ if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark ||
+ UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
webvDescription.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.black));
}
+ if (!NetworkUtils.networkAvailable()) {
+ webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
+ // Use cached resources, even if they have expired
+ }
webvDescription.getSettings().setUseWideViewPort(false);
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
@@ -210,12 +216,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
});
registerForContextMenu(webvDescription);
- imgvCover = (ImageView) layout.findViewById(R.id.imgvCover);
+ imgvCover = layout.findViewById(R.id.imgvCover);
imgvCover.setOnClickListener(v -> openPodcast());
- progbarDownload = (ProgressBar) layout.findViewById(R.id.progbarDownload);
- progbarLoading = (ProgressBar) layout.findViewById(R.id.progbarLoading);
- butAction1 = (IconButton) layout.findViewById(R.id.butAction1);
- butAction2 = (IconButton) layout.findViewById(R.id.butAction2);
+ progbarDownload = layout.findViewById(R.id.progbarDownload);
+ progbarLoading = layout.findViewById(R.id.progbarLoading);
+ butAction1 = layout.findViewById(R.id.butAction1);
+ butAction2 = layout.findViewById(R.id.butAction2);
butAction1.setOnClickListener(v -> {
if (item == null) {
@@ -280,8 +286,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onDestroyView() {
super.onDestroyView();
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
if (webvDescription != null && root != null) {
root.removeView(webvDescription);
@@ -335,13 +341,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
openPodcast();
return true;
default:
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
- return true;
- }
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
}
}
@@ -379,11 +379,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
Glide.with(getActivity())
.load(item.getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(imgvCover);
progbarDownload.setVisibility(View.GONE);
@@ -434,6 +435,16 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
butAction1Text = R.string.download_label;
}
}
+
+ FeedItem.State state = item.getState();
+ if (butAction2Text == R.string.delete_label && state == FeedItem.State.PLAYING && PlaybackService.isRunning) {
+ butAction2.setEnabled(false);
+ butAction2.setAlpha(0.5f);
+ } else {
+ butAction2.setEnabled(true);
+ butAction2.setAlpha(1.0f);
+ }
+
if(butAction1Icon != null && butAction1Text != 0) {
butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
Iconify.addIcons(butAction1);
@@ -450,7 +461,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
- private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
+ private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
@@ -483,17 +494,11 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
ShareUtils.shareLink(getActivity(), selectedURL);
break;
case R.id.copy_url_item:
- if (android.os.Build.VERSION.SDK_INT >= 11) {
- ClipData clipData = ClipData.newPlainText(selectedURL,
- selectedURL);
- android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setPrimaryClip(clipData);
- } else {
- android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setText(selectedURL);
- }
+ ClipData clipData = ClipData.newPlainText(selectedURL,
+ selectedURL);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
Toast t = Toast.makeText(getActivity(),
R.string.copied_url_msg, Toast.LENGTH_SHORT);
t.show();
@@ -558,7 +563,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
@@ -568,12 +573,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
};
private void load() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
progbarLoading.setVisibility(View.VISIBLE);
- subscription = Observable.fromCallable(this::loadInBackground)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(this::loadInBackground)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
progbarLoading.setVisibility(View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
index a118673a6..d9e318069 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java
@@ -4,10 +4,7 @@ import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Color;
import android.graphics.LightingColorFilter;
-import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v4.view.MenuItemCompat;
@@ -22,15 +19,14 @@ import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ImageView;
-import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.Iconify;
-import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import com.joanzapata.iconify.widget.IconTextView;
import org.apache.commons.lang3.Validate;
@@ -39,6 +35,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.FeedInfoActivity;
+import de.danoeh.antennapod.activity.FeedSettingsActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
@@ -56,7 +53,6 @@ import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.glide.FastBlurTransformation;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -72,10 +68,10 @@ import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.FeedMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Displays a list of FeedItems.
@@ -89,9 +85,9 @@ public class ItemlistFragment extends ListFragment {
| EventDistributor.PLAYER_STATUS_UPDATE;
public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem";
- public static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
+ private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
- protected FeedItemlistAdapter adapter;
+ private FeedItemlistAdapter adapter;
private ContextMenu contextMenu;
private AdapterView.AdapterContextMenuInfo lastMenuInfo = null;
@@ -115,7 +111,7 @@ public class ItemlistFragment extends ListFragment {
private TextView txtvInformation;
- private Subscription subscription;
+ private Disposable disposable;
/**
* Creates new ItemlistFragment which shows the Feeditems of a specific
@@ -166,8 +162,8 @@ public class ItemlistFragment extends ListFragment {
super.onPause();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
}
@@ -223,13 +219,6 @@ public class ItemlistFragment extends ListFragment {
menu.findItem(R.id.share_link_item).setVisible(false);
menu.findItem(R.id.visit_website_item).setVisible(false);
}
- int[] attrs = { R.attr.action_bar_icon_color };
- TypedArray ta = getActivity().obtainStyledAttributes(UserPreferences.getTheme(), attrs);
- int textColor = ta.getColor(0, Color.GRAY);
- ta.recycle();
-
- menu.findItem(R.id.episode_actions).setIcon(new IconDrawable(getActivity(),
- FontAwesomeIcons.fa_gears).color(textColor).actionBarSize());
isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@@ -342,22 +331,7 @@ public class ItemlistFragment extends ListFragment {
return super.onContextItemSelected(item);
}
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
- } catch (DownloadRequestException e) {
- // context menu doesn't contain download functionality
- return true;
- }
- }
-
-
- @Override
- public void setListAdapter(ListAdapter adapter) {
- // This workaround prevents the ListFragment from setting a list adapter when its state is restored.
- // This is only necessary on API 10 because addFooterView throws an internal exception in this case.
- if (Build.VERSION.SDK_INT > 10 || insideOnFragmentLoaded) {
- super.setListAdapter(adapter);
- }
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
}
@Override
@@ -417,7 +391,7 @@ public class ItemlistFragment extends ListFragment {
}
}
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
@@ -440,13 +414,10 @@ public class ItemlistFragment extends ListFragment {
}
- private boolean insideOnFragmentLoaded = false;
-
private void onFragmentLoaded() {
if(!isVisible()) {
return;
}
- insideOnFragmentLoaded = true;
if (adapter == null) {
setListAdapter(null);
setupHeaderView();
@@ -463,9 +434,6 @@ public class ItemlistFragment extends ListFragment {
if (feed != null && feed.getNextPageLink() == null && listFooter != null) {
getListView().removeFooterView(listFooter.getRoot());
}
-
- insideOnFragmentLoaded = false;
-
}
private void refreshHeaderView() {
@@ -509,13 +477,14 @@ public class ItemlistFragment extends ListFragment {
View header = inflater.inflate(R.layout.feeditemlist_header, lv, false);
lv.addHeaderView(header);
- txtvTitle = (TextView) header.findViewById(R.id.txtvTitle);
- TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor);
- imgvBackground = (ImageView) header.findViewById(R.id.imgvBackground);
- imgvCover = (ImageView) header.findViewById(R.id.imgvCover);
- ImageButton butShowInfo = (ImageButton) header.findViewById(R.id.butShowInfo);
- txtvInformation = (TextView) header.findViewById(R.id.txtvInformation);
- txtvFailure = (IconTextView) header.findViewById(R.id.txtvFailure);
+ txtvTitle = header.findViewById(R.id.txtvTitle);
+ TextView txtvAuthor = header.findViewById(R.id.txtvAuthor);
+ imgvBackground = header.findViewById(R.id.imgvBackground);
+ imgvCover = header.findViewById(R.id.imgvCover);
+ ImageButton butShowInfo = header.findViewById(R.id.butShowInfo);
+ ImageButton butShowSettings = header.findViewById(R.id.butShowSettings);
+ txtvInformation = header.findViewById(R.id.txtvInformation);
+ txtvFailure = header.findViewById(R.id.txtvFailure);
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
@@ -526,10 +495,12 @@ public class ItemlistFragment extends ListFragment {
loadFeedImage();
- butShowInfo.setOnClickListener(v -> {
+ butShowInfo.setOnClickListener(v -> showFeedInfo());
+ imgvCover.setOnClickListener(v -> showFeedInfo());
+ butShowSettings.setOnClickListener(v -> {
if (viewsCreated && itemsLoaded) {
- Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
- startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
+ Intent startIntent = new Intent(getActivity(), FeedSettingsActivity.class);
+ startIntent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID,
feed.getId());
startActivity(startIntent);
}
@@ -537,23 +508,34 @@ public class ItemlistFragment extends ListFragment {
headerCreated = true;
}
+ private void showFeedInfo() {
+ if (viewsCreated && itemsLoaded) {
+ Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
+ startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
+ feed.getId());
+ startActivity(startIntent);
+ }
+ }
+
private void loadFeedImage() {
Glide.with(getActivity())
.load(feed.getImageLocation())
- .placeholder(R.color.image_readability_tint)
- .error(R.color.image_readability_tint)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .transform(new FastBlurTransformation(getActivity()))
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.image_readability_tint)
+ .error(R.color.image_readability_tint)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .transform(new FastBlurTransformation())
+ .dontAnimate())
.into(imgvBackground);
Glide.with(getActivity())
.load(feed.getImageLocation())
- .placeholder(R.color.light_gray)
- .error(R.color.light_gray)
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .fitCenter()
- .dontAnimate()
+ .apply(new RequestOptions()
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate())
.into(imgvCover);
}
@@ -583,7 +565,7 @@ public class ItemlistFragment extends ListFragment {
}
}
- private FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
+ private final FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
@Override
public FeedItem getItem(int position) {
@@ -629,11 +611,11 @@ public class ItemlistFragment extends ListFragment {
private void loadItems() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
- subscription = Observable.fromCallable(this::loadData)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(this::loadData)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
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 43dedad25..a0e2ca22a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -36,13 +36,14 @@ import de.danoeh.antennapod.adapter.itunes.ItunesAdapter;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+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 okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast;
@@ -69,13 +70,13 @@ public class ItunesSearchFragment extends Fragment {
*/
private List<Podcast> searchResults;
private List<Podcast> topList;
- private Subscription subscription;
+ private Disposable disposable;
/**
* Replace adapter data with provided search results from SearchTask.
* @param result List of Podcast objects containing search results
*/
- void updateData(List<Podcast> result) {
+ private void updateData(List<Podcast> result) {
this.searchResults = result;
adapter.clear();
if (result != null && result.size() > 0) {
@@ -109,7 +110,7 @@ public class ItunesSearchFragment extends Fragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View root = inflater.inflate(R.layout.fragment_itunes_search, container, false);
- gridView = (GridView) root.findViewById(R.id.gridView);
+ gridView = root.findViewById(R.id.gridView);
adapter = new ItunesAdapter(getActivity(), new ArrayList<>());
gridView.setAdapter(adapter);
@@ -127,7 +128,7 @@ public class ItunesSearchFragment extends Fragment {
} else {
gridView.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
- subscription = Observable.create((Observable.OnSubscribe<String>) subscriber -> {
+ disposable = Single.create((SingleOnSubscribe<String>) emitter -> {
OkHttpClient client = AntennapodHttpClient.getHttpClient();
Request.Builder httpReq = new Request.Builder()
.url(podcast.feedUrl)
@@ -139,17 +140,16 @@ public class ItunesSearchFragment extends Fragment {
JSONObject result = new JSONObject(resultString);
JSONObject results = result.getJSONArray("results").getJSONObject(0);
String feedUrl = results.getString("feedUrl");
- subscriber.onNext(feedUrl);
+ emitter.onSuccess(feedUrl);
} else {
String prefix = getString(R.string.error_msg_prefix);
- subscriber.onError(new IOException(prefix + response));
+ emitter.onError(new IOException(prefix + response));
}
} catch (IOException | JSONException e) {
- subscriber.onError(e);
+ emitter.onError(e);
}
- subscriber.onCompleted();
})
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(feedUrl -> {
progressBar.setVisibility(View.GONE);
@@ -170,10 +170,10 @@ public class ItunesSearchFragment extends Fragment {
});
}
});
- progressBar = (ProgressBar) root.findViewById(R.id.progressBar);
- txtvError = (TextView) root.findViewById(R.id.txtvError);
- butRetry = (Button) root.findViewById(R.id.butRetry);
- txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
+ progressBar = root.findViewById(R.id.progressBar);
+ txtvError = root.findViewById(R.id.txtvError);
+ butRetry = root.findViewById(R.id.butRetry);
+ txtvEmpty = root.findViewById(android.R.id.empty);
loadToplist();
@@ -183,8 +183,8 @@ public class ItunesSearchFragment extends Fragment {
@Override
public void onDestroy() {
super.onDestroy();
- if (subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
adapter = null;
}
@@ -228,15 +228,15 @@ public class ItunesSearchFragment extends Fragment {
}
private void loadToplist() {
- if (subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
gridView.setVisibility(View.GONE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
- subscription = Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
+ disposable = Single.create((SingleOnSubscribe<List<Podcast>>) emitter -> {
String lang = Locale.getDefault().getLanguage();
String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json";
OkHttpClient client = AntennapodHttpClient.getHttpClient();
@@ -268,15 +268,14 @@ public class ItunesSearchFragment extends Fragment {
}
else {
String prefix = getString(R.string.error_msg_prefix);
- subscriber.onError(new IOException(prefix + response));
+ emitter.onError(new IOException(prefix + response));
}
} catch (IOException | JSONException e) {
- subscriber.onError(e);
+ emitter.onError(e);
}
- subscriber.onNext(results);
- subscriber.onCompleted();
+ emitter.onSuccess(results);
})
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(podcasts -> {
progressBar.setVisibility(View.GONE);
@@ -293,15 +292,15 @@ public class ItunesSearchFragment extends Fragment {
}
private void search(String query) {
- if (subscription != null) {
- subscription.unsubscribe();
+ if (disposable != null) {
+ disposable.dispose();
}
gridView.setVisibility(View.GONE);
txtvError.setVisibility(View.GONE);
butRetry.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
- subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> {
+ disposable = Single.create((SingleOnSubscribe<List<Podcast>>) subscriber -> {
String encodedQuery = null;
try {
encodedQuery = URLEncoder.encode(query, "UTF-8");
@@ -341,10 +340,9 @@ public class ItunesSearchFragment extends Fragment {
} catch (IOException | JSONException e) {
subscriber.onError(e);
}
- subscriber.onNext(podcasts);
- subscriber.onCompleted();
+ subscriber.onSuccess(podcasts);
})
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(podcasts -> {
progressBar.setVisibility(View.GONE);
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 183c10f3d..6695ba427 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -1,14 +1,10 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.os.Handler;
-import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -18,10 +14,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.core.event.FeedItemEvent;
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.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
@@ -76,33 +69,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder;
-
- Log.d(TAG, "remove(" + holder.getItemId() + ")");
- if (subscription != null) {
- subscription.unsubscribe();
- }
- FeedItem item = holder.getFeedItem();
- // we're marking it as unplayed since the user didn't actually play it
- // but they don't want it considered 'NEW' anymore
- DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
-
- final Handler h = new Handler(getActivity().getMainLooper());
- final Runnable r = () -> {
- FeedMedia media = item.getMedia();
- if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
- DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
- }
- };
-
- Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_seen_label),
- Snackbar.LENGTH_LONG);
- snackbar.setAction(getString(R.string.undo), v -> {
- DBWriter.markItemPlayed(FeedItem.NEW, item.getId());
- // don't forget to cancel the thing that's going to remove the media
- h.removeCallbacks(r);
- });
- snackbar.show();
- h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f));
+ markItemAsSeenWithUndo(holder.getFeedItem());
}
@Override
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 441f0096c..c2a9200c8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -30,10 +30,10 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
public class PlaybackHistoryFragment extends ListFragment {
@@ -50,7 +50,7 @@ public class PlaybackHistoryFragment extends ListFragment {
private List<Downloader> downloaderList;
- private Subscription subscription;
+ private Disposable disposable;
@Override
public void onAttach(Context context) {
@@ -107,16 +107,16 @@ public class PlaybackHistoryFragment extends ListFragment {
public void onStop() {
super.onStop();
EventDistributor.getInstance().unregister(contentUpdate);
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
}
@Override
public void onDetach() {
super.onDetach();
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
}
@@ -199,7 +199,7 @@ public class PlaybackHistoryFragment extends ListFragment {
}
}
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
@@ -224,7 +224,7 @@ public class PlaybackHistoryFragment extends ListFragment {
getActivity().supportInvalidateOptionsMenu();
}
- private FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
+ private final FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() {
@Override
public int getItemDownloadProgressPercent(FeedItem item) {
@@ -269,11 +269,11 @@ public class PlaybackHistoryFragment extends ListFragment {
};
private void loadItems() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
- subscription = Observable.fromCallable(this::loadData)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(this::loadData)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
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 fccb86076..faeabf75c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -21,7 +21,6 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
-import android.widget.Toast;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
@@ -46,7 +45,6 @@ import de.danoeh.antennapod.core.service.download.Downloader;
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.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
@@ -55,10 +53,10 @@ import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.greenrobot.event.EventBus;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Shows all items in the queue
@@ -86,7 +84,7 @@ public class QueueFragment extends Fragment {
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
- private Subscription subscription;
+ private Disposable disposable;
private LinearLayoutManager layoutManager;
private ItemTouchHelper itemTouchHelper;
@@ -109,7 +107,6 @@ public class QueueFragment extends Fragment {
@Override
public void onResume() {
super.onResume();
- recyclerView.setAdapter(recyclerAdapter);
loadItems(true);
EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().registerSticky(this);
@@ -121,8 +118,8 @@ public class QueueFragment extends Fragment {
saveScrollPosition();
EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
}
@@ -328,6 +325,15 @@ public class QueueFragment extends Fragment {
case R.id.queue_sort_feed_title_desc:
QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_DESC, true);
return true;
+ case R.id.queue_sort_random:
+ QueueSorter.sort(getActivity(), QueueSorter.Rule.RANDOM, true);
+ return true;
+ case R.id.queue_sort_smart_shuffle_asc:
+ QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_ASC, true);
+ return true;
+ case R.id.queue_sort_smart_shuffle_desc:
+ QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_DESC, true);
+ return true;
default:
return false;
}
@@ -363,13 +369,7 @@ public class QueueFragment extends Fragment {
DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
return true;
default:
- try {
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
- return true;
- }
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
}
}
@@ -380,8 +380,8 @@ public class QueueFragment extends Fragment {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.queue_label);
View root = inflater.inflate(R.layout.queue_fragment, container, false);
- infoBar = (TextView) root.findViewById(R.id.info_bar);
- recyclerView = (RecyclerView) root.findViewById(R.id.recyclerView);
+ infoBar = root.findViewById(R.id.info_bar);
+ recyclerView = root.findViewById(R.id.recyclerView);
RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator();
if (animator instanceof SimpleItemAnimator) {
((SimpleItemAnimator) animator).setSupportsChangeAnimations(false);
@@ -395,24 +395,36 @@ public class QueueFragment extends Fragment {
itemTouchHelper = new ItemTouchHelper(
new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
+ // Position tracking whilst dragging
+ int dragFrom = -1;
+ int dragTo = -1;
+
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
+ int fromPosition = viewHolder.getAdapterPosition();
+ int toPosition = target.getAdapterPosition();
+
+ // Update tracked position
+ if(dragFrom == -1) {
+ dragFrom = fromPosition;
+ }
+ dragTo = toPosition;
+
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
- Log.d(TAG, "move(" + from + ", " + to + ")");
+ Log.d(TAG, "move(" + from + ", " + to + ") in memory");
if(from >= queue.size() || to >= queue.size()) {
return false;
}
queue.add(to, queue.remove(from));
recyclerAdapter.notifyItemMoved(from, to);
- DBWriter.moveQueueItem(from, to, true);
return true;
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
final int position = viewHolder.getAdapterPosition();
Log.d(TAG, "remove(" + position + ")");
@@ -459,19 +471,32 @@ public class QueueFragment extends Fragment {
RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
+ // Check if drag finished
+ if(dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) {
+ reallyMoved(dragFrom, dragTo);
+ }
+
+ dragFrom = dragTo = -1;
+
if (viewHolder instanceof QueueRecyclerAdapter.ItemTouchHelperViewHolder) {
QueueRecyclerAdapter.ItemTouchHelperViewHolder itemViewHolder =
(QueueRecyclerAdapter.ItemTouchHelperViewHolder) viewHolder;
itemViewHolder.onItemClear();
}
}
+
+ private void reallyMoved(int from, int to) {
+ // Write drag operation to database
+ Log.d(TAG, "Write to database move(" + from + ", " + to + ")");
+ DBWriter.moveQueueItem(from, to, true);
+ }
}
);
itemTouchHelper.attachToRecyclerView(recyclerView);
- txtvEmpty = (TextView) root.findViewById(android.R.id.empty);
+ txtvEmpty = root.findViewById(android.R.id.empty);
txtvEmpty.setVisibility(View.GONE);
- progLoading = (ProgressBar) root.findViewById(R.id.progLoading);
+ progLoading = root.findViewById(R.id.progLoading);
progLoading.setVisibility(View.VISIBLE);
return root;
@@ -507,19 +532,23 @@ public class QueueFragment extends Fragment {
private void refreshInfoBar() {
String info = queue.size() + getString(R.string.episodes_suffix);
if(queue.size() > 0) {
- long duration = 0;
+ long timeLeft = 0;
+ float playbackSpeed = Float.valueOf(UserPreferences.getPlaybackSpeed());
for(FeedItem item : queue) {
if(item.getMedia() != null) {
- duration += item.getMedia().getDuration();
+ timeLeft +=
+ (long) ((item.getMedia().getDuration() - item.getMedia().getPosition())
+ / playbackSpeed);
}
}
info += " \u2022 ";
- info += Converter.getDurationStringLocalized(getActivity(), duration);
+ info += getString(R.string.time_left_label);
+ info += Converter.getDurationStringLocalized(getActivity(), timeLeft);
}
infoBar.setText(info);
}
- private QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() {
+ private final QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() {
@Override
public int getCount() {
return queue != null ? queue.size() : 0;
@@ -579,7 +608,7 @@ public class QueueFragment extends Fragment {
}
};
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((arg & EVENTS) != 0) {
@@ -594,16 +623,16 @@ public class QueueFragment extends Fragment {
private void loadItems(final boolean restoreScrollPosition) {
Log.d(TAG, "loadItems()");
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
if (queue == null) {
recyclerView.setVisibility(View.GONE);
txtvEmpty.setVisibility(View.GONE);
progLoading.setVisibility(View.VISIBLE);
}
- subscription = Observable.fromCallable(DBReader::getQueue)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(DBReader::getQueue)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(items -> {
if(items != null) {
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 ba526edb3..66c59b7f7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -75,7 +75,7 @@ public class RunningDownloadsFragment extends ListFragment {
}
- private DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() {
+ private final DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() {
@Override
public int getCount() {
return (downloaderList != null) ? downloaderList.size() : 0;
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 f64b4c20a..8322a5573 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -24,10 +24,10 @@ import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.SearchResult;
import de.danoeh.antennapod.core.storage.FeedSearcher;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Performs a search operation on all feeds or one specific feed and displays the search result.
@@ -44,7 +44,7 @@ public class SearchFragment extends ListFragment {
private boolean viewCreated = false;
private boolean itemsLoaded = false;
- private Subscription subscription;
+ private Disposable disposable;
/**
* Create a new SearchFragment that searches all feeds.
@@ -85,8 +85,8 @@ public class SearchFragment extends ListFragment {
@Override
public void onStop() {
super.onStop();
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
EventDistributor.getInstance().unregister(contentUpdate);
}
@@ -94,8 +94,8 @@ public class SearchFragment extends ListFragment {
@Override
public void onDetach() {
super.onDetach();
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
}
@@ -205,14 +205,14 @@ public class SearchFragment extends ListFragment {
private void search() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
if (viewCreated && !itemsLoaded) {
setListShown(false);
}
- subscription = Observable.fromCallable(this::performSearch)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(this::performSearch)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (result != null) {
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 9626e6c2e..5f09be8ce 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.fragment;
import android.content.DialogInterface;
-import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
@@ -26,11 +25,12 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Fragment for displaying feed subscriptions
@@ -48,7 +48,7 @@ public class SubscriptionFragment extends Fragment {
private int mPosition = -1;
- private Subscription subscription;
+ private Disposable disposable;
public SubscriptionFragment() {
}
@@ -68,7 +68,7 @@ public class SubscriptionFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_subscriptions, container, false);
- subscriptionGridLayout = (GridView) root.findViewById(R.id.subscriptions_grid);
+ subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid);
registerForContextMenu(subscriptionGridLayout);
return root;
}
@@ -94,17 +94,17 @@ public class SubscriptionFragment extends Fragment {
@Override
public void onDestroy() {
super.onDestroy();
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
}
private void loadSubscriptions() {
- if(subscription != null) {
- subscription.unsubscribe();
+ if(disposable != null) {
+ disposable.dispose();
}
- subscription = Observable.fromCallable(DBReader::getNavDrawerData)
- .subscribeOn(Schedulers.newThread())
+ disposable = Observable.fromCallable(DBReader::getNavDrawerData)
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
navDrawerData = result;
@@ -152,18 +152,39 @@ public class SubscriptionFragment extends Fragment {
Feed feed = (Feed)selectedObject;
switch(item.getItemId()) {
case R.id.mark_all_seen_item:
- Observable.fromCallable(() -> DBWriter.markFeedSeen(feed.getId()))
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> loadSubscriptions(),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
+ ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(getActivity(),
+ R.string.mark_all_seen_label,
+ R.string.mark_all_seen_confirmation_msg) {
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ dialog.dismiss();
+
+ Observable.fromCallable(() -> DBWriter.markFeedSeen(feed.getId()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> loadSubscriptions(),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+ };
+ markAllSeenConfirmationDialog.createNewDialog().show();
return true;
case R.id.mark_all_read_item:
- Observable.fromCallable(() -> DBWriter.markFeedRead(feed.getId()))
- .subscribeOn(Schedulers.newThread())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> loadSubscriptions(),
- error -> Log.e(TAG, Log.getStackTraceString(error)));
+ ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
+ R.string.mark_all_read_label,
+ R.string.mark_all_read_confirmation_msg) {
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ dialog.dismiss();
+ Observable.fromCallable(() -> DBWriter.markFeedRead(feed.getId()))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> loadSubscriptions(),
+ error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+ };
+ markAllReadConfirmationDialog.createNewDialog().show();
return true;
case R.id.rename_item:
new RenameFeedDialog(getActivity(), feed).show();
@@ -190,8 +211,8 @@ public class SubscriptionFragment extends Fragment {
remover.skipOnCompletion = true;
int playerStatus = PlaybackPreferences.getCurrentPlayerStatus();
if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) {
- getActivity().sendBroadcast(new Intent(
- PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ IntentUtils.sendLocalBroadcast(getContext(), PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+
}
}
remover.executeAsync();
@@ -210,7 +231,7 @@ public class SubscriptionFragment extends Fragment {
loadSubscriptions();
}
- private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
@Override
public void update(EventDistributor eventDistributor, Integer arg) {
if ((EVENTS & arg) != 0) {
@@ -220,7 +241,7 @@ public class SubscriptionFragment extends Fragment {
}
};
- private SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() {
+ private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() {
@Override
public int getCount() {
if (navDrawerData != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java
index aff5069c6..4dc114f9b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java
@@ -20,7 +20,7 @@ import de.danoeh.antennapod.R;
*/
public class GpodnetMainFragment extends Fragment {
- public static final String TAG = "GpodnetMainFragment";
+ private static final String TAG = "GpodnetMainFragment";
private static final String PREF_LAST_TAB_POSITION = "tab_position";
private TabLayout tabLayout;
@@ -31,12 +31,12 @@ public class GpodnetMainFragment extends Fragment {
super.onCreateView(inflater, container, savedInstanceState);
View root = inflater.inflate(R.layout.pager_fragment, container, false);
- viewPager = (ViewPager)root.findViewById(R.id.viewpager);
+ viewPager = root.findViewById(R.id.viewpager);
GpodnetPagerAdapter pagerAdapter = new GpodnetPagerAdapter(getChildFragmentManager(), getResources());
viewPager.setAdapter(pagerAdapter);
// Give the TabLayout the ViewPager
- tabLayout = (TabLayout) root.findViewById(R.id.sliding_tabs);
+ tabLayout = root.findViewById(R.id.sliding_tabs);
tabLayout.setupWithViewPager(viewPager);
return root;
@@ -71,7 +71,7 @@ public class GpodnetMainFragment extends Fragment {
private static final int POS_TAGS = 1;
private static final int POS_SUGGESTIONS = 2;
- Resources resources;
+ final Resources resources;
public GpodnetPagerAdapter(FragmentManager fm, Resources resources) {
super(fm);
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 15e9c9943..49851ebb4 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
@@ -78,10 +78,10 @@ public abstract class PodcastListFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.gpodnet_podcast_list, container, false);
- gridView = (GridView) root.findViewById(R.id.gridView);
- progressBar = (ProgressBar) root.findViewById(R.id.progressBar);
- txtvError = (TextView) root.findViewById(R.id.txtvError);
- butRetry = (Button) root.findViewById(R.id.butRetry);
+ gridView = root.findViewById(R.id.gridView);
+ progressBar = root.findViewById(R.id.progressBar);
+ txtvError = root.findViewById(R.id.txtvError);
+ butRetry = root.findViewById(R.id.butRetry);
gridView.setOnItemClickListener((parent, view, position, id) ->
onPodcastSelected((GpodnetPodcast) gridView.getAdapter().getItem(position)));
@@ -91,7 +91,7 @@ public abstract class PodcastListFragment extends Fragment {
return root;
}
- protected void onPodcastSelected(GpodnetPodcast selection) {
+ private void onPodcastSelected(GpodnetPodcast selection) {
Log.d(TAG, "Selected podcast: " + selection.toString());
Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class);
intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, selection.getUrl());
@@ -101,7 +101,7 @@ public abstract class PodcastListFragment extends Fragment {
protected abstract List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException;
- protected final void loadData() {
+ final void loadData() {
AsyncTask<Void, Void, List<GpodnetPodcast>> loaderTask = new AsyncTask<Void, Void, List<GpodnetPodcast>>() {
volatile Exception exception = null;
@@ -160,10 +160,6 @@ public abstract class PodcastListFragment extends Fragment {
}
};
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- loaderTask.execute();
- }
+ loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java
index 33a35fa90..4f963756c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java
@@ -1,11 +1,11 @@
package de.danoeh.antennapod.fragment.gpodnet;
+import java.util.List;
+
import de.danoeh.antennapod.core.gpoddernet.GpodnetService;
import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast;
-import java.util.List;
-
/**
*
*/
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 613e06805..10bd636dd 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
@@ -73,7 +73,7 @@ public class SearchListFragment extends PodcastListFragment {
return service.searchPodcasts(query, 0);
}
- public void changeQuery(String query) {
+ private void changeQuery(String query) {
Validate.notNull(query);
this.query = query;
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 d2c7f32dd..1e46b1ac5 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
@@ -135,11 +135,7 @@ public class TagListFragment extends ListFragment {
}
}
};
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- loadTask.execute();
- }
+ loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
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 57b7c359c..ffdfa9516 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -17,7 +17,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
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.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
@@ -87,7 +87,7 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.add_to_queue_item, false);
}
- if (!showExtendedMenu || selectedItem.getLink() == null) {
+ if (!showExtendedMenu || !ShareUtils.hasLinkToShare(selectedItem)) {
mi.setItemVisibility(R.id.visit_website_item, false);
mi.setItemVisibility(R.id.share_link_item, false);
mi.setItemVisibility(R.id.share_link_with_position_item, false);
@@ -155,10 +155,10 @@ public class FeedItemMenuHandler {
}
public static boolean onMenuItemClicked(Context context, int menuItemId,
- FeedItem selectedItem) throws DownloadRequestException {
+ FeedItem selectedItem) {
switch (menuItemId) {
case R.id.skip_episode_item:
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
break;
case R.id.remove_item:
DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
@@ -217,7 +217,7 @@ public class FeedItemMenuHandler {
DBWriter.setFeedItemAutoDownload(selectedItem, false);
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(selectedItem.getLink());
+ Uri uri = Uri.parse(FeedItemUtil.getLinkWithFallback(selectedItem));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
if(IntentUtils.isCallable(context, intent)) {
context.startActivity(intent);
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 ab7d0e7c6..bd4fe9bcf 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -30,6 +30,9 @@ import de.danoeh.antennapod.core.util.ShareUtils;
* Handles interactions with the FeedItemMenu.
*/
public class FeedMenuHandler {
+
+ private FeedMenuHandler(){ }
+
private static final String TAG = "FeedMenuHandler";
public static boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) {
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 ac703e13e..7b9fcad9b 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
@@ -19,8 +19,9 @@ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuIte
public static void adjustTextColor(Context context, SearchView sv) {
if(Build.VERSION.SDK_INT < 14) {
- EditText searchEditText = (EditText) sv.findViewById(R.id.search_src_text);
- if(UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark) {
+ 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);
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java
new file mode 100644
index 000000000..b810cbfa6
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java
@@ -0,0 +1,48 @@
+package de.danoeh.antennapod.preferences;
+
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.Typeface;
+import android.os.Build;
+import android.support.v14.preference.SwitchPreference;
+import android.support.v7.preference.PreferenceViewHolder;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.widget.TextView;
+
+import de.danoeh.antennapod.R;
+
+public class MasterSwitchPreference extends SwitchPreference {
+
+ public MasterSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @TargetApi(Build.VERSION_CODES.LOLLIPOP)
+ public MasterSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ public MasterSwitchPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public MasterSwitchPreference(Context context) {
+ super(context);
+ }
+
+
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+
+ TypedValue typedValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(R.attr.master_switch_background, typedValue, true);
+ holder.itemView.setBackgroundColor(typedValue.data);
+
+ TextView title = (TextView) holder.findViewById(android.R.id.title);
+ if (title != null) {
+ title.setTypeface(title.getTypeface(), Typeface.BOLD);
+ }
+ }
+} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java
new file mode 100644
index 000000000..50e76838c
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java
@@ -0,0 +1,107 @@
+package de.danoeh.antennapod.preferences;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.support.v7.preference.Preference;
+import android.text.InputFilter;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.EditText;
+
+import de.danoeh.antennapod.R;
+
+public class NumberPickerPreference extends Preference {
+ private Context context;
+ private int defaultValue = 0;
+ private int minValue = 0;
+ private int maxValue = Integer.MAX_VALUE;
+
+ public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init(context, attrs);
+ }
+
+ public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init(context, attrs);
+ }
+
+ public NumberPickerPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init(context, attrs);
+ }
+
+ public NumberPickerPreference(Context context) {
+ super(context);
+ this.context = context;
+ }
+
+ private void init(Context context, AttributeSet attrs) {
+ this.context = context;
+
+ for (int i = 0; i < attrs.getAttributeCount(); i++) {
+ String name = attrs.getAttributeName(i);
+ String value = attrs.getAttributeValue(i);
+ switch (name) {
+ case "defaultValue":
+ defaultValue = Integer.parseInt(value);
+ break;
+ case "minValue":
+ minValue = Integer.parseInt(value);
+ break;
+ case "maxValue":
+ maxValue = Integer.parseInt(value);
+ break;
+ }
+ }
+ }
+
+ @Override
+ protected void onClick() {
+ super.onClick();
+
+ View view = View.inflate(context, R.layout.numberpicker, null);
+ EditText number = view.findViewById(R.id.number);
+ number.setText(getSharedPreferences().getString(getKey(), ""+defaultValue));
+ number.setFilters(new InputFilter[]{(source, start, end, dest, dstart, dend) -> {
+ try {
+ String newVal = dest.toString().substring(0, dstart) + dest.toString().substring(dend);
+ newVal = newVal.substring(0, dstart) + source.toString() + newVal.substring(dstart);
+ int input = Integer.parseInt(newVal);
+ if (input >= minValue && input <= maxValue) {
+ return null;
+ }
+ } catch (NumberFormatException nfe) {
+ nfe.printStackTrace();
+ }
+ return "";
+ }});
+
+ AlertDialog dialog = new AlertDialog.Builder(context)
+ .setTitle(getTitle())
+ .setView(view)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {
+ try {
+ String numberString = number.getText().toString();
+ int value = Integer.parseInt(numberString);
+
+ if (value < minValue || value > maxValue) {
+ return;
+ }
+
+ getSharedPreferences().edit().putString(getKey(), "" + value).apply();
+
+ if (getOnPreferenceChangeListener() != null) {
+ getOnPreferenceChangeListener().onPreferenceChange(this, value);
+ }
+ } catch (NumberFormatException e) {
+ // Do not set value
+ }
+ })
+ .create();
+ dialog.show();
+ dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index 1ca0d0109..31b2cbcb2 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -16,28 +16,28 @@ import android.net.Uri;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
import android.os.Build;
-import android.preference.CheckBoxPreference;
-import android.preference.EditTextPreference;
-import android.preference.ListPreference;
-import android.preference.Preference;
-import android.preference.PreferenceManager;
-import android.preference.PreferenceScreen;
+import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
-import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AlertDialog;
-import android.text.Editable;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.preference.CheckBoxPreference;
+import android.support.v7.preference.ListPreference;
+import android.support.v7.preference.Preference;
+import android.support.v7.preference.PreferenceFragmentCompat;
+import android.support.v7.preference.PreferenceManager;
+import android.support.v7.preference.PreferenceScreen;
import android.text.Html;
-import android.text.TextWatcher;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.Log;
-import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import com.afollestad.materialdialogs.MaterialDialog;
+import com.bytehamster.lib.preferencesearch.SearchConfiguration;
+import com.bytehamster.lib.preferencesearch.SearchPreference;
import org.apache.commons.lang3.ArrayUtils;
@@ -46,7 +46,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
-import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.concurrent.TimeUnit;
@@ -55,13 +54,13 @@ import de.danoeh.antennapod.CrashReportWriter;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AboutActivity;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
+import de.danoeh.antennapod.activity.ImportExportActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.MediaplayerActivity;
+import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
import de.danoeh.antennapod.activity.StatisticsActivity;
import de.danoeh.antennapod.asynctask.ExportWorker;
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.export.html.HtmlWriter;
import de.danoeh.antennapod.core.export.opml.OpmlWriter;
@@ -69,16 +68,19 @@ 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.util.flattr.FlattrUtils;
+import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
import de.danoeh.antennapod.dialog.ProxyDialog;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import static de.danoeh.antennapod.activity.PreferenceActivity.PARAM_RESOURCE;
/**
* Sets up a preference UI that lets the user change user preferences.
@@ -88,19 +90,28 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
private static final String TAG = "PreferenceController";
- private static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
+ private static final String PREF_SCREEN_USER_INTERFACE = "prefScreenInterface";
+ private static final String PREF_SCREEN_PLAYBACK = "prefScreenPlayback";
+ private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork";
+ private static final String PREF_SCREEN_INTEGRATIONS = "prefScreenIntegrations";
+ private static final String PREF_SCREEN_STORAGE = "prefScreenStorage";
+ private static final String PREF_SCREEN_AUTODL = "prefAutoDownloadSettings";
+ private static final String PREF_SCREEN_FLATTR = "prefFlattrSettings";
+ private static final String PREF_SCREEN_GPODDER = "prefGpodderSettings";
+
private static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
private static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
private static final String PREF_OPML_EXPORT = "prefOpmlExport";
+ private static final String PREF_OPML_IMPORT = "prefOpmlImport";
private static final String PREF_HTML_EXPORT = "prefHtmlExport";
private static final String STATISTICS = "statistics";
+ private static final String IMPORT_EXPORT = "importExport";
private static final String PREF_ABOUT = "prefAbout";
private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
- private static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings";
private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
- public static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher";
- public static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher";
+ private static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher";
+ private static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher";
private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
@@ -126,7 +137,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
};
private CheckBoxPreference[] selectedNetworks;
- private Subscription subscription;
+ private Disposable disposable;
public PreferenceController(PreferenceUI ui) {
this.ui = ui;
@@ -134,30 +145,51 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
.registerOnSharedPreferenceChangeListener(this);
}
- /**
- * Returns the preference activity that should be used on this device.
- *
- * @return PreferenceActivity if the API level is greater than 10, PreferenceActivityGingerbread otherwise.
- */
- public static Class<? extends Activity> getPreferenceActivity() {
- if (Build.VERSION.SDK_INT > 10) {
- return PreferenceActivity.class;
- } else {
- return PreferenceActivityGingerbread.class;
- }
- }
-
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
- if(key.equals(UserPreferences.PREF_SONIC)) {
- CheckBoxPreference prefSonic = (CheckBoxPreference) ui.findPreference(UserPreferences.PREF_SONIC);
- if(prefSonic != null) {
- prefSonic.setChecked(sharedPreferences.getBoolean(UserPreferences.PREF_SONIC, false));
- }
+
+ }
+
+
+
+ public void onCreate(int screen) {
+ switch (screen) {
+ case R.xml.preferences:
+ setupMainScreen();
+ break;
+ case R.xml.preferences_network:
+ setupNetworkScreen();
+ break;
+ case R.xml.preferences_autodownload:
+ setupAutoDownloadScreen();
+ buildAutodownloadSelectedNetworksPreference();
+ setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
+ buildEpisodeCleanupPreference();
+ break;
+ case R.xml.preferences_playback:
+ setupPlaybackScreen();
+ PreferenceControllerFlavorHelper.setupFlavoredUI(ui);
+ buildSmartMarkAsPlayedPreference();
+ break;
+ case R.xml.preferences_integrations:
+ setupIntegrationsScreen();
+ break;
+ case R.xml.preferences_flattr:
+ setupFlattrScreen();
+ break;
+ case R.xml.preferences_gpodder:
+ setupGpodderScreen();
+ break;
+ case R.xml.preferences_storage:
+ setupStorageScreen();
+ break;
+ case R.xml.preferences_user_interface:
+ setupInterfaceScreen();
+ break;
}
}
- public void onCreate() {
+ private void setupInterfaceScreen() {
final Activity activity = ui.getActivity();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
@@ -172,22 +204,64 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
);
}
- ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
- preference -> {
- FlattrUtils.revokeAccessToken(activity);
- checkItemVisibility();
+ ui.findPreference(UserPreferences.PREF_THEME)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ Intent i = new Intent(activity, MainActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ activity.finish();
+ activity.startActivity(i);
+ return true;
+ }
+ );
+ ui.findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
+ .setOnPreferenceClickListener(preference -> {
+ showDrawerPreferencesDialog();
return true;
- }
- );
- ui.findPreference(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener(
- preference -> {
- activity.startActivity(new Intent(activity, AboutActivity.class));
+ });
+
+ ui.findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS)
+ .setOnPreferenceClickListener(preference -> {
+ showNotificationButtonsDialog();
return true;
- }
- );
- ui.findPreference(PreferenceController.STATISTICS).setOnPreferenceClickListener(
+ });
+
+ ui.findPreference(UserPreferences.PREF_BACK_BUTTON_BEHAVIOR)
+ .setOnPreferenceChangeListener((preference, newValue) -> {
+ if (newValue.equals("page")) {
+ final Context context = ui.getActivity();
+ final String[] navTitles = context.getResources().getStringArray(R.array.back_button_go_to_pages);
+ final String[] navTags = context.getResources().getStringArray(R.array.back_button_go_to_pages_tags);
+ final String choice[] = { UserPreferences.getBackButtonGoToPage() };
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.back_button_go_to_page_title);
+ builder.setSingleChoiceItems(navTitles, ArrayUtils.indexOf(navTags, UserPreferences.getBackButtonGoToPage()), (dialogInterface, i) -> {
+ if (i >= 0) {
+ choice[0] = navTags[i];
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialogInterface, i) -> UserPreferences.setBackButtonGoToPage(choice[0]));
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ return true;
+ } else {
+ return true;
+ }
+ });
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ ui.findPreference(UserPreferences.PREF_EXPANDED_NOTIFICATION).setVisible(false);
+ }
+ }
+
+ private void setupStorageScreen() {
+ final Activity activity = ui.getActivity();
+
+ ui.findPreference(PreferenceController.IMPORT_EXPORT).setOnPreferenceClickListener(
preference -> {
- activity.startActivity(new Intent(activity, StatisticsActivity.class));
+ activity.startActivity(new Intent(activity, ImportExportActivity.class));
return true;
}
);
@@ -195,6 +269,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
preference -> export(new OpmlWriter()));
ui.findPreference(PreferenceController.PREF_HTML_EXPORT).setOnPreferenceClickListener(
preference -> export(new HtmlWriter()));
+ ui.findPreference(PreferenceController.PREF_OPML_IMPORT).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
+ return true;
+ });
ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
preference -> {
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
@@ -228,130 +307,70 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
return true;
}
);
- ui.findPreference(UserPreferences.PREF_THEME)
- .setOnPreferenceChangeListener(
- (preference, newValue) -> {
- Intent i = new Intent(activity, MainActivity.class);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- } else {
- i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- }
- activity.finish();
- activity.startActivity(i);
- return true;
+ ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ int newValue = Integer.parseInt((String) o) * 1024 * 1024;
+ if (newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
}
- );
- ui.findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
- .setOnPreferenceClickListener(preference -> {
- showDrawerPreferencesDialog();
- return true;
- });
+ return true;
+ }
+ return false;
+ }
+ );
+ }
- ui.findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS)
- .setOnPreferenceClickListener(preference -> {
- showNotificationButtonsDialog();
+ private void setupIntegrationsScreen() {
+ final AppCompatActivity activity = ui.getActivity();
+
+ ui.findPreference(PREF_SCREEN_FLATTR).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_flattr, activity);
+ return true;
+ });
+ ui.findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_gpodder, activity);
+ return true;
+ });
+ }
+
+ private void setupFlattrScreen() {
+ final AppCompatActivity activity = ui.getActivity();
+
+ ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
+ preference -> {
+ FlattrUtils.revokeAccessToken(activity);
+ checkFlattrItemVisibility();
return true;
- });
+ }
+ );
- ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
+ ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS)
.setOnPreferenceClickListener(preference -> {
- showUpdateIntervalTimePreferencesDialog();
- return true;
- });
+ AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
+ new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
+ @Override
+ public void onCancelled() {
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
- (preference, newValue) -> {
- if (newValue instanceof Boolean) {
- boolean enabled = (Boolean) newValue;
- ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(enabled);
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(enabled);
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(enabled);
- setSelectedNetworksEnabled(enabled && UserPreferences.isEnableAutodownloadWifiFilter());
- }
- return true;
- });
- ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
- .setOnPreferenceChangeListener(
- (preference, newValue) -> {
- if (newValue instanceof Boolean) {
- setSelectedNetworksEnabled((Boolean) newValue);
- return true;
- } else {
- return false;
- }
- }
- );
- ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
- .setOnPreferenceChangeListener(
- (preference, o) -> {
- if (o instanceof String) {
- try {
- int value = Integer.parseInt((String) o);
- if (1 <= value && value <= 50) {
- setParallelDownloadsText(value);
- return true;
- }
- } catch (NumberFormatException e) {
- return false;
}
- }
- return false;
- }
- );
- // validate and set correct value: number of downloads between 1 and 50 (inclusive)
- final EditText ev = ((EditTextPreference) ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)).getEditText();
- ev.addTextChangedListener(new TextWatcher() {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
- }
- @Override
- public void afterTextChanged(Editable s) {
- if (s.length() > 0) {
- try {
- int value = Integer.parseInt(s.toString());
- if (value <= 0) {
- ev.setText("1");
- } else if (value > 50) {
- ev.setText("50");
- }
- } catch (NumberFormatException e) {
- ev.setText("6");
- }
- ev.setSelection(ev.getText().length());
- }
- }
- });
- ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE)
- .setOnPreferenceChangeListener(
- (preference, o) -> {
- if (o instanceof String) {
- setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
- }
- return true;
- }
- );
- ui.findPreference(PreferenceController.PREF_PLAYBACK_SPEED_LAUNCHER)
- .setOnPreferenceClickListener(preference -> {
- VariableSpeedDialog.showDialog(activity);
- return true;
- });
- ui.findPreference(PreferenceController.PREF_PLAYBACK_REWIND_DELTA_LAUNCHER)
- .setOnPreferenceClickListener(preference -> {
- MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND);
- return true;
- });
- ui.findPreference(PreferenceController.PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)
- .setOnPreferenceClickListener(preference -> {
- MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD);
+ @Override
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
+ UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
+ checkFlattrItemVisibility();
+ }
+ });
return true;
});
+ }
+
+ private void setupGpodderScreen() {
+ final AppCompatActivity activity = ui.getActivity();
+
ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION)
.setOnPreferenceClickListener(preference -> {
AuthenticationDialog dialog = new AuthenticationDialog(activity,
@@ -399,45 +418,127 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
return true;
});
+ }
- ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS)
+ private void setupPlaybackScreen() {
+ final Activity activity = ui.getActivity();
+
+ ui.findPreference(PreferenceController.PREF_PLAYBACK_SPEED_LAUNCHER)
.setOnPreferenceClickListener(preference -> {
- AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
- new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
- @Override
- public void onCancelled() {
+ VariableSpeedDialog.showDialog(activity);
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_PLAYBACK_REWIND_DELTA_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND);
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD);
+ return true;
+ });
+ if (!PictureInPictureUtil.supportsPictureInPicture(activity)) {
+ ListPreference behaviour = (ListPreference) ui.findPreference(UserPreferences.PREF_VIDEO_BEHAVIOR);
+ behaviour.setEntries(R.array.video_background_behavior_options_without_pip);
+ behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip);
+ }
+ }
- }
+ private void setupAutoDownloadScreen() {
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ checkAutodownloadItemVisibility((Boolean) newValue);
+ }
+ return true;
+ });
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ setSelectedNetworksEnabled((Boolean) newValue);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ );
+ ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE)
+ .setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
+ }
+ return true;
+ }
+ );
+ }
- @Override
- public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
- UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
- checkItemVisibility();
- }
- });
+ private void setupNetworkScreen() {
+ final AppCompatActivity activity = ui.getActivity();
+ ui.findPreference(PREF_SCREEN_AUTODL).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_autodownload, activity);
+ return true;
+ });
+ ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
+ .setOnPreferenceClickListener(preference -> {
+ showUpdateIntervalTimePreferencesDialog();
return true;
});
- ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
- (preference, o) -> {
- if (o instanceof String) {
- int newValue = Integer.parseInt((String) o) * 1024 * 1024;
- if (newValue != UserPreferences.getImageCacheSize()) {
- AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
- dialog.setTitle(android.R.string.dialog_alert_title);
- dialog.setMessage(R.string.pref_restart_required);
- dialog.setPositiveButton(android.R.string.ok, null);
- dialog.show();
+ ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
+ .setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof Integer) {
+ setParallelDownloadsText((Integer) o);
+ }
+ return true;
}
- return true;
- }
- return false;
- }
- );
+ );
+ // validate and set correct value: number of downloads between 1 and 50 (inclusive)
ui.findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
ProxyDialog dialog = new ProxyDialog(ui.getActivity());
dialog.createDialog().show();
return true;
});
+ }
+
+ private void setupMainScreen() {
+ final AppCompatActivity activity = ui.getActivity();
+ setupSearch();
+ ui.findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_user_interface, activity);
+ return true;
+ });
+ ui.findPreference(PREF_SCREEN_PLAYBACK).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_playback, activity);
+ return true;
+ });
+ ui.findPreference(PREF_SCREEN_NETWORK).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_network, activity);
+ return true;
+ });
+ ui.findPreference(PREF_SCREEN_INTEGRATIONS).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_integrations, activity);
+ return true;
+ });
+ ui.findPreference(PREF_SCREEN_STORAGE).setOnPreferenceClickListener(preference -> {
+ openScreen(R.xml.preferences_storage, activity);
+ return true;
+ });
+
+ ui.findPreference(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, AboutActivity.class));
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.STATISTICS).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, StatisticsActivity.class));
+ return true;
+ }
+ );
ui.findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
return true;
@@ -469,11 +570,76 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
return true;
});
- PreferenceControllerFlavorHelper.setupFlavoredUI(ui);
- buildEpisodeCleanupPreference();
- buildSmartMarkAsPlayedPreference();
- buildAutodownloadSelectedNetworsPreference();
- setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
+ }
+
+ private void setupSearch() {
+ final AppCompatActivity activity = ui.getActivity();
+
+ SearchPreference searchPreference = (SearchPreference) ui.findPreference("searchPreference");
+ SearchConfiguration config = searchPreference.getSearchConfiguration();
+ config.setActivity(activity);
+ config.setFragmentContainerViewId(R.id.content);
+ config.setBreadcrumbsEnabled(true);
+
+ config.index()
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface))
+ .addFile(R.xml.preferences_user_interface);
+ config.index()
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_playback))
+ .addFile(R.xml.preferences_playback);
+ config.index()
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_network))
+ .addFile(R.xml.preferences_network);
+ config.index()
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_storage))
+ .addFile(R.xml.preferences_storage);
+ config.index()
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_network))
+ .addBreadcrumb(R.string.automation)
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_autodownload))
+ .addFile(R.xml.preferences_autodownload);
+ config.index()
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_integrations))
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_gpodder))
+ .addFile(R.xml.preferences_gpodder);
+ config.index()
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_integrations))
+ .addBreadcrumb(getTitleOfPage(R.xml.preferences_flattr))
+ .addFile(R.xml.preferences_flattr);
+ }
+
+ public PreferenceFragmentCompat openScreen(int preferences, AppCompatActivity activity) {
+ PreferenceFragmentCompat prefFragment = new PreferenceActivity.MainFragment();
+ Bundle args = new Bundle();
+ args.putInt(PARAM_RESOURCE, preferences);
+ prefFragment.setArguments(args);
+ activity.getSupportFragmentManager().beginTransaction()
+ .replace(R.id.content, prefFragment)
+ .addToBackStack(TAG).commit();
+ return prefFragment;
+ }
+
+ public static int getTitleOfPage(int preferences) {
+ switch (preferences) {
+ case R.xml.preferences_network:
+ return R.string.network_pref;
+ case R.xml.preferences_autodownload:
+ return R.string.pref_automatic_download_title;
+ case R.xml.preferences_playback:
+ return R.string.playback_pref;
+ case R.xml.preferences_storage:
+ return R.string.storage_pref;
+ case R.xml.preferences_user_interface:
+ return R.string.user_interface_label;
+ case R.xml.preferences_integrations:
+ return R.string.integrations_label;
+ case R.xml.preferences_flattr:
+ return R.string.flattr_label;
+ case R.xml.preferences_gpodder:
+ return R.string.gpodnet_main_label;
+ default:
+ return R.string.settings_label;
+ }
}
private boolean export(ExportWriter exportWriter) {
@@ -485,11 +651,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
final AlertDialog.Builder alert = new AlertDialog.Builder(context)
.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
- subscription = observable.subscribeOn(Schedulers.newThread())
+ disposable = observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(output -> {
- alert.setTitle(R.string.opml_export_success_title);
- String message = context.getString(R.string.opml_export_success_sum) + output.toString();
+ alert.setTitle(R.string.export_success_title);
+ String message = context.getString(R.string.export_success_sum, output.toString());
alert.setMessage(message);
alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
@@ -515,7 +681,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
alert.setTitle(R.string.export_error_label);
alert.setMessage(error.getMessage());
alert.show();
- }, () -> progressDialog.dismiss());
+ }, progressDialog::dismiss);
return true;
}
@@ -529,23 +695,42 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
}
- public void onResume() {
- checkItemVisibility();
- setUpdateIntervalText();
- setParallelDownloadsText(UserPreferences.getParallelDownloads());
- setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize());
- setDataFolderText();
- GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
- updateGpodnetPreferenceScreen();
+ public void onResume(int screen) {
+ switch (screen) {
+ case R.xml.preferences_network:
+ setUpdateIntervalText();
+ setParallelDownloadsText(UserPreferences.getParallelDownloads());
+ break;
+ case R.xml.preferences_autodownload:
+ setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize());
+ checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload());
+ break;
+ case R.xml.preferences_storage:
+ setDataFolderText();
+ break;
+ case R.xml.preferences_integrations:
+ setIntegrationsItemVisibility();
+ return;
+ case R.xml.preferences_flattr:
+ checkFlattrItemVisibility();
+ break;
+ case R.xml.preferences_gpodder:
+ GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
+ updateGpodnetPreferenceScreen();
+ break;
+ case R.xml.preferences_playback:
+ checkSonicItemVisibility();
+ break;
+ }
}
- public void onPause() {
+ public void unregisterGpodnet() {
GpodnetPreferences.unregisterOnSharedPreferenceChangeListener(gpoddernetListener);
}
- public void onStop() {
- if(subscription != null) {
- subscription.unsubscribe();
+ public void unsubscribeExportSubscription() {
+ if (disposable != null) {
+ disposable.dispose();
}
}
@@ -698,27 +883,32 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
}
+ private void setIntegrationsItemVisibility() {
+ ui.findPreference(PreferenceController.PREF_SCREEN_FLATTR).setEnabled(FlattrUtils.hasAPICredentials());
+ }
+
@SuppressWarnings("deprecation")
- private void checkItemVisibility() {
+ private void checkFlattrItemVisibility() {
boolean hasFlattrToken = FlattrUtils.hasToken();
- ui.findPreference(PreferenceController.PREF_FLATTR_SETTINGS).setEnabled(FlattrUtils.hasAPICredentials());
ui.findPreference(PreferenceController.PREF_FLATTR_AUTH).setEnabled(!hasFlattrToken);
ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setEnabled(hasFlattrToken);
ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS).setEnabled(hasFlattrToken);
+ }
- boolean autoDownload = UserPreferences.isEnableAutodownload();
+ private void checkAutodownloadItemVisibility(boolean autoDownload) {
ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(autoDownload);
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(autoDownload);
ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(autoDownload);
+ ui.findPreference(UserPreferences.PREF_EPISODE_CLEANUP).setEnabled(autoDownload);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_MOBILE).setEnabled(autoDownload);
setSelectedNetworksEnabled(autoDownload && UserPreferences.isEnableAutodownloadWifiFilter());
+ }
- ui.findPreference(PREF_SEND_CRASH_REPORT).setEnabled(CrashReportWriter.getFile().exists());
-
- if (Build.VERSION.SDK_INT >= 16) {
- ui.findPreference(UserPreferences.PREF_SONIC).setEnabled(true);
- } else {
- Preference prefSonic = ui.findPreference(UserPreferences.PREF_SONIC);
- prefSonic.setSummary("[Android 4.1+]\n" + prefSonic.getSummary());
+ private void checkSonicItemVisibility() {
+ if (Build.VERSION.SDK_INT < 16) {
+ ListPreference p = (ListPreference) ui.findPreference(UserPreferences.PREF_MEDIA_PLAYER);
+ p.setEntries(R.array.media_player_options_no_sonic);
+ p.setEntryValues(R.array.media_player_values_no_sonic);
}
}
@@ -778,7 +968,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
}
}
- private void buildAutodownloadSelectedNetworsPreference() {
+ private static String blankIfNull(String val) {
+ return val == null ? "" : val;
+ }
+
+ private void buildAutodownloadSelectedNetworksPreference() {
final Activity activity = ui.getActivity();
if (selectedNetworks != null) {
@@ -788,67 +982,63 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
WifiManager wifiservice = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
List<WifiConfiguration> networks = wifiservice.getConfiguredNetworks();
- if (networks != null) {
- Collections.sort(networks, new Comparator<WifiConfiguration>() {
- @Override
- public int compare(WifiConfiguration x, WifiConfiguration y) {
- return x.SSID.compareTo(y.SSID);
+ if (networks == null) {
+ Log.e(TAG, "Couldn't get list of configure Wi-Fi networks");
+ return;
+ }
+ Collections.sort(networks, (x, y) ->
+ blankIfNull(x.SSID).compareTo(blankIfNull(y.SSID)));
+ selectedNetworks = new CheckBoxPreference[networks.size()];
+ List<String> prefValues = Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks());
+ PreferenceScreen prefScreen = ui.getPreferenceScreen();
+ Preference.OnPreferenceClickListener clickListener = preference -> {
+ if (preference instanceof CheckBoxPreference) {
+ String key = preference.getKey();
+ List<String> prefValuesList = new ArrayList<>(
+ Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks())
+ );
+ boolean newValue = ((CheckBoxPreference) preference)
+ .isChecked();
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
+
+ int index = prefValuesList.indexOf(key);
+ if (index >= 0 && !newValue) {
+ // remove network
+ prefValuesList.remove(index);
+ } else if (index < 0 && newValue) {
+ prefValuesList.add(key);
}
- });
- selectedNetworks = new CheckBoxPreference[networks.size()];
- List<String> prefValues = Arrays.asList(UserPreferences
- .getAutodownloadSelectedNetworks());
- PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN);
- Preference.OnPreferenceClickListener clickListener = preference -> {
- if (preference instanceof CheckBoxPreference) {
- String key = preference.getKey();
- List<String> prefValuesList = new ArrayList<>(
- Arrays.asList(UserPreferences
- .getAutodownloadSelectedNetworks())
- );
- boolean newValue = ((CheckBoxPreference) preference)
- .isChecked();
- Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
-
- int index = prefValuesList.indexOf(key);
- if (index >= 0 && !newValue) {
- // remove network
- prefValuesList.remove(index);
- } else if (index < 0 && newValue) {
- prefValuesList.add(key);
- }
- UserPreferences.setAutodownloadSelectedNetworks(
- prefValuesList.toArray(new String[prefValuesList.size()])
- );
- return true;
- } else {
- return false;
- }
- };
- // create preference for each known network. attach listener and set
- // value
- for (int i = 0; i < networks.size(); i++) {
- WifiConfiguration config = networks.get(i);
-
- CheckBoxPreference pref = new CheckBoxPreference(activity);
- String key = Integer.toString(config.networkId);
- pref.setTitle(config.SSID);
- pref.setKey(key);
- pref.setOnPreferenceClickListener(clickListener);
- pref.setPersistent(false);
- pref.setChecked(prefValues.contains(key));
- selectedNetworks[i] = pref;
- prefScreen.addPreference(pref);
+ UserPreferences.setAutodownloadSelectedNetworks(
+ prefValuesList.toArray(new String[prefValuesList.size()])
+ );
+ return true;
+ } else {
+ return false;
}
- } else {
- Log.e(TAG, "Couldn't get list of configure Wi-Fi networks");
+ };
+ // create preference for each known network. attach listener and set
+ // value
+ for (int i = 0; i < networks.size(); i++) {
+ WifiConfiguration config = networks.get(i);
+
+ CheckBoxPreference pref = new CheckBoxPreference(activity);
+ String key = Integer.toString(config.networkId);
+ pref.setTitle(config.SSID);
+ pref.setKey(key);
+ pref.setOnPreferenceClickListener(clickListener);
+ pref.setPersistent(false);
+ pref.setChecked(prefValues.contains(key));
+ selectedNetworks[i] = pref;
+ prefScreen.addPreference(pref);
}
}
private void clearAutodownloadSelectedNetworsPreference() {
if (selectedNetworks != null) {
- PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN);
+ PreferenceScreen prefScreen = ui.getPreferenceScreen();
for (CheckBoxPreference network : selectedNetworks) {
if (network != null) {
@@ -1013,11 +1203,16 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
public interface PreferenceUI {
+ void setFragment(PreferenceFragmentCompat fragment);
+ PreferenceFragmentCompat getFragment();
+
/**
* Finds a preference based on its key.
*/
Preference findPreference(CharSequence key);
- Activity getActivity();
+ PreferenceScreen getPreferenceScreen();
+
+ AppCompatActivity getActivity();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java
deleted file mode 100644
index 10c11b88e..000000000
--- a/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package de.danoeh.antennapod.preferences;
-
-import android.annotation.TargetApi;
-import android.content.Context;
-import android.os.Build;
-import android.preference.CheckBoxPreference;
-import android.util.AttributeSet;
-
-import de.danoeh.antennapod.R;
-
-public class SwitchCompatPreference extends CheckBoxPreference {
-
- public SwitchCompatPreference(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- init();
- }
-
- @TargetApi(Build.VERSION_CODES.LOLLIPOP)
- public SwitchCompatPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- init();
- }
-
- public SwitchCompatPreference(Context context, AttributeSet attrs) {
- super(context, attrs);
- init();
- }
-
- public SwitchCompatPreference(Context context) {
- super(context);
- init();
- }
-
- private void init() {
- setWidgetLayoutResource(R.layout.preference_switch_layout);
- }
-} \ No newline at end of file
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
index a373c5353..c9bd973cb 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
+++ b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java
@@ -22,8 +22,8 @@ public class SPAReceiver extends BroadcastReceiver{
private static final String TAG = "SPAReceiver";
public static final String ACTION_SP_APPS_QUERY_FEEDS = "de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS";
- public static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE = "de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE";
- public static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA = "feeds";
+ private static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE = "de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE";
+ private static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA = "feeds";
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
deleted file mode 100644
index b5bfb1ae4..000000000
--- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java
+++ /dev/null
@@ -1,245 +0,0 @@
-package de.danoeh.antennapod.service;
-
-import android.app.PendingIntent;
-import android.app.Service;
-import android.appwidget.AppWidgetManager;
-import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.os.Build;
-import android.os.IBinder;
-import android.util.Log;
-import android.view.KeyEvent;
-import android.view.View;
-import android.widget.RemoteViews;
-
-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.receiver.MediaButtonReceiver;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.fragment.QueueFragment;
-import de.danoeh.antennapod.receiver.PlayerWidget;
-
-/**
- * Updates the state of the player widget
- */
-public class PlayerWidgetService extends Service {
- private static final String TAG = "PlayerWidgetService";
-
- private PlaybackService playbackService;
-
- /**
- * Controls write access to playbackservice reference
- */
- private Object psLock;
-
- /**
- * True while service is updating the widget
- */
- private volatile boolean isUpdating;
-
- public PlayerWidgetService() {
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- Log.d(TAG, "Service created");
- isUpdating = false;
- psLock = new Object();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.d(TAG, "Service is about to be destroyed");
- if (playbackService != null) {
- Playable playable = playbackService.getPlayable();
- if (playable != null && playable instanceof FeedMedia) {
- FeedMedia media = (FeedMedia) playable;
- if (media.hasAlmostEnded()) {
- Log.d(TAG, "smart mark as read");
- FeedItem item = media.getItem();
- DBWriter.markItemPlayed(item, FeedItem.PLAYED, false);
- DBWriter.removeQueueItem(this, item, false);
- DBWriter.addItemToPlaybackHistory(media);
- if (item.getFeed().getPreferences().getCurrentAutoDelete() &&
- (!item.isTagged(FeedItem.TAG_FAVORITE) || !UserPreferences.shouldFavoriteKeepEpisode())) {
- Log.d(TAG, "Delete " + media.toString());
- DBWriter.deleteFeedMediaOfItem(this, media.getId());
- }
- }
- }
- }
-
- try {
- unbindService(mConnection);
- } catch (IllegalArgumentException e) {
- Log.w(TAG, "IllegalArgumentException when trying to unbind service");
- }
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- if (!isUpdating) {
- if (playbackService == null && PlaybackService.isRunning) {
- bindService(new Intent(this, PlaybackService.class),
- mConnection, 0);
- } else {
- startViewUpdaterIfNotRunning();
- }
- } else {
- Log.d(TAG, "Service was called while updating. Ignoring update request");
- }
- return Service.START_NOT_STICKY;
- }
-
- private void updateViews() {
- isUpdating = true;
-
- ComponentName playerWidget = new ComponentName(this, PlayerWidget.class);
- AppWidgetManager manager = AppWidgetManager.getInstance(this);
- RemoteViews views = new RemoteViews(getPackageName(),
- R.layout.player_widget);
- PendingIntent startMediaplayer = PendingIntent.getActivity(this, 0,
- PlaybackService.getPlayerActivityIntent(this), 0);
-
- Intent startApp = new Intent(getBaseContext(), MainActivity.class);
- startApp.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startApp.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, QueueFragment.TAG);
- PendingIntent startAppPending = PendingIntent.getActivity(getBaseContext(), 0, startApp, PendingIntent.FLAG_UPDATE_CURRENT);
-
- boolean nothingPlaying = false;
- if (playbackService != null) {
- final Playable media = playbackService.getPlayable();
- if (media != null) {
- PlayerStatus status = playbackService.getStatus();
- views.setOnClickPendingIntent(R.id.layout_left, startMediaplayer);
-
- views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle());
-
- String progressString = getProgressString();
- if (progressString != null) {
- views.setViewVisibility(R.id.txtvProgress, View.VISIBLE);
- views.setTextViewText(R.id.txtvProgress, progressString);
- }
-
- if (status == PlayerStatus.PLAYING) {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause_white_24dp);
- if (Build.VERSION.SDK_INT >= 15) {
- views.setContentDescription(R.id.butPlay, getString(R.string.pause_label));
- }
- } else {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
- if (Build.VERSION.SDK_INT >= 15) {
- views.setContentDescription(R.id.butPlay, getString(R.string.play_label));
- }
- }
- views.setOnClickPendingIntent(R.id.butPlay,
- createMediaButtonIntent());
- } else {
- nothingPlaying = true;
- }
- } else {
- nothingPlaying = true;
- }
-
- if (nothingPlaying) {
- // start the app if they click anything
- views.setOnClickPendingIntent(R.id.layout_left, startAppPending);
- views.setOnClickPendingIntent(R.id.butPlay, startAppPending);
- views.setViewVisibility(R.id.txtvProgress, View.INVISIBLE);
- views.setTextViewText(R.id.txtvTitle,
- this.getString(R.string.no_media_playing_label));
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
- }
-
- manager.updateAppWidget(playerWidget, views);
- isUpdating = false;
- }
-
- /**
- * Creates an intent which fakes a mediabutton press
- */
- private PendingIntent createMediaButtonIntent() {
- KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
- Intent startingIntent = new Intent(
- MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER);
- startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
-
- return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
- }
-
- private String getProgressString() {
- int position = playbackService.getCurrentPosition();
- int duration = playbackService.getDuration();
- if (position > 0 && duration > 0) {
- return Converter.getDurationStringLong(position) + " / "
- + Converter.getDurationStringLong(duration);
- } else {
- return null;
- }
- }
-
- private ServiceConnection mConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- Log.d(TAG, "Connection to service established");
- synchronized (psLock) {
- if(service instanceof PlaybackService.LocalBinder) {
- playbackService = ((PlaybackService.LocalBinder) service).getService();
- startViewUpdaterIfNotRunning();
- }
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- synchronized (psLock) {
- playbackService = null;
- Log.d(TAG, "Disconnected from service");
- }
- }
-
- };
-
- private void startViewUpdaterIfNotRunning() {
- if (!isUpdating) {
- ViewUpdater updateThread = new ViewUpdater(this);
- updateThread.start();
- }
- }
-
- class ViewUpdater extends Thread {
- private static final String THREAD_NAME = "ViewUpdater";
- private PlayerWidgetService service;
-
- public ViewUpdater(PlayerWidgetService service) {
- super();
- setName(THREAD_NAME);
- this.service = service;
-
- }
-
- @Override
- public void run() {
- synchronized (psLock) {
- service.updateViews();
- }
- }
-
- }
-
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
index 75cbd8b5a..03958508d 100644
--- a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
+++ b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
@@ -33,7 +33,7 @@ public class SPAUtil {
* sent before.
*/
public static synchronized boolean sendSPAppsQueryFeedsIntent(Context context) {
- if (context == null) throw new IllegalArgumentException("context = null");
+ assert context != null : "context = null";
final Context appContext = context.getApplicationContext();
if (appContext == null) {
Log.wtf(TAG, "Unable to get application context");
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 f930c912a..e79389fb3 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
@@ -25,6 +25,8 @@ public class AspectRatioVideoView extends VideoView {
private int mVideoWidth;
private int mVideoHeight;
+ private float mAvailableWidth = -1;
+ private float mAvailableHeight = -1;
public AspectRatioVideoView(Context context) {
this(context, null);
@@ -48,8 +50,13 @@ public class AspectRatioVideoView extends VideoView {
return;
}
- float heightRatio = (float) mVideoHeight / (float) getHeight();
- float widthRatio = (float) mVideoWidth / (float) getWidth();
+ if (mAvailableWidth < 0 || mAvailableHeight < 0) {
+ mAvailableWidth = getWidth();
+ mAvailableHeight = getHeight();
+ }
+
+ float heightRatio = (float) mVideoHeight / mAvailableHeight;
+ float widthRatio = (float) mVideoWidth / mAvailableWidth;
int scaledHeight;
int scaledWidth;
@@ -94,4 +101,15 @@ public class AspectRatioVideoView extends VideoView {
invalidate();
}
+ /**
+ * Sets the maximum size that the view might expand to
+ * @param width
+ * @param height
+ */
+ public void setAvailableSize(float width, float height) {
+ mAvailableWidth = width;
+ mAvailableHeight = height;
+ requestLayout();
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java
index 27b6ee2bc..7ce33e11f 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java
@@ -1,13 +1,13 @@
package de.danoeh.antennapod.view;
import android.content.Context;
+import android.support.v7.widget.AppCompatImageView;
import android.util.AttributeSet;
-import android.widget.ImageView;
/**
* From http://stackoverflow.com/a/19449488/6839
*/
-public class SquareImageView extends ImageView {
+public class SquareImageView extends AppCompatImageView {
public SquareImageView(Context context) {
super(context);
@@ -26,6 +26,7 @@ public class SquareImageView extends ImageView {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
+ //noinspection SuspiciousNameCombination
setMeasuredDimension(width, width);
}
diff --git a/app/src/main/play/ar/listing/fulldescription b/app/src/main/play/ar/listing/fulldescription
index 87b477fdc..27abb5532 100644
--- a/app/src/main/play/ar/listing/fulldescription
+++ b/app/src/main/play/ar/listing/fulldescription
@@ -1,4 +1,4 @@
-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 your favourites and delay settings).<br>
+AntennaPod هو مدير للبودكاست واللاعب الذي يتيح لك الوصول الفوري إلى ملايين البودكاست المجانية والمدفوعة ، من البودكاسترات المستقلة إلى دور النشر الكبيرة مثل بي بي سي ، إن بي آر وسي إن إن. يمكنك إضافة واستيراد وتصدير خلاصاتهم الخالية من المتاعب باستخدام قاعدة بيانات بودكاست iTunes أو ملفات OPML أو عناوين URL RSS بسيطة. يمكنك توفير الجهد واستخدام طاقة البطارية واستخدام البيانات المتنقلة مع عناصر تحكم تلقائية قوية لتنزيل الحلقات (حدد الأوقات والفترات الزمنية وشبكات WiFi) وحذف الحلقات (استنادًا إلى إعداداتك المفضلة وتأخير الإعدادات).<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. You can even show your love to the content creators with our Flattr integration.
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
diff --git a/app/src/main/play/bg/listing/fulldescription b/app/src/main/play/bg/listing/fulldescription
new file mode 100644
index 000000000..16a005e22
--- /dev/null
+++ b/app/src/main/play/bg/listing/fulldescription
@@ -0,0 +1,43 @@
+AntennaPod е подкаст мениджър и плейър, който ви дава незабавен достъп до милиони безплатни и платени подкасти, от независими подкасти до големи издателства като BBC, NPR и CNN. Добавете, импортирайте и експортирайте своите емисии безпроблемно, като използвате базата данни на iTunes, OPML файлове или обикновени URL адреси. Спестете усилие, захранване на батерията и мобилни данни чрез мощни контролни механизми за автоматизиране на изтеглянето на епизоди (задайте часове, интервали и WiFi мрежи) и изтриването на епизоди (базирани на предпочитаните от вас и настройките за отлагане).<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. You can even show your love to the content creators with our Flattr integration.
+
+Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
+
+<b>Всички функции:</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>
+&#8226; Support content creators with Flattr integration including automatic flattring
+
+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>Присъединете се към общността на AntennaPod!</b><br>
+AntennaPod се разработва активно от доброволци. Можете да допринесете с код или с коментар!
+
+GitHub е мястото за заявки за функции, отчети за грешки и участие:<br>
+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
+
+Имате въпрос или искате да дадете отзиви?
+https://twitter.com/@AntennaPod
+
+Transifex е мястото за помощ при преводите:<br>
+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 \ No newline at end of file
diff --git a/app/src/main/play/ca/listing/fulldescription b/app/src/main/play/ca/listing/fulldescription
index 87b477fdc..0e8d76e5e 100644
--- a/app/src/main/play/ca/listing/fulldescription
+++ b/app/src/main/play/ca/listing/fulldescription
@@ -1,43 +1,43 @@
-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 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. You can even show your love to the content creators with our Flattr integration.
-
-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>
-&#8226; Support content creators with Flattr integration including automatic flattring
-
-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 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>
+L'AntennaPod és un gestor i reproductor de podcasts que us dóna accés instantani a milions de podcasts gratuïts i de pagament, fets per podcasters independents o grans emissores com la BBC, l'NPR i la CNN. Afegiu, importeu i exporteu les seves subscripcions sense complicacions utilitzant la base de dades de podcasts de l'iTunes, fitxers OPML o enllaços RSS. Estalvieu esforços, bateria i consum de dades al mòbil amb potents controls d'automatització per a baixar episodis (especifiqueu hores, intervals i xarxes WiFi) i esborrar-los (basats en la vostra configuració sobre favorits i demores).<br>
+Però el més important: baixeu, transmeteu o afegiu episodis a la cua i gaudiu-ne a la vostra manera amb velocitats de reproducció ajustables, capacitat per a usar capítols i un temporitzador per a dormir. Fins i tot podeu demostrar la vostra estima pels creadors de continguts amb la nostra integració de Flattr.
+
+Fet per entusiastes del podcàsting, l'AntennaPod és lliure en tots els sentits del terme: codi obert, sense cost ni anuncis.
+
+<b>Totes les funcions:</b><br>
+IMPORTA, ORGANITZA I REPRODUEIX<br>
+&#8226; Afegeix i importa canals mitjançant els directoris iTunes i gPodder.net, fitxers OPML i enllaços RSS o Atom<br>
+&#8226; Gestiona la reproducció des de qualsevol banda: giny de la pantalla d'inici, notificacions del sistema i controls d'auriculars i Bluetooth.<br>
+&#8226; Gaudiu escoltant a la vostra manera amb velocitat de reproducció ajustable, capacitat per a usar capítols (MP3, VorbisComment i Podlove), record de la posició de reproducció i un temporitzador per a dormir avançat (agiteu per a reiniciar, abaixar el volum i reduir la velocitat de reproducció).<br>
+&#8226; Accediu a subscripcions i episodis protegits per contrasenya.<br>
+&#8226; Aprofiteu les subscripcions paginades (www.podlove.org/paged-feeds).
+
+FEU SEGUIMENT, COMPARTIU I AGRAÏU<br>
+&#8226; Seguiu el millor del millor marcant episodis com a favorits.<br>
+&#8226; Trobeu l'episodi que busqueu mitjançant l'historial de reproducció o la cerca (de títols i descripcions).<br>
+&#8226; Compartiu episodis i subscripcions mitjançant xarxes socials i correu electrònic, els serveis de gPodder.net i exportant a OPML.<br>
+&#8226; Recolzeu els creadors de continguts amb la integració de Flattr, fins i tot de forma automàtica.
+
+CONTROLEU EL SISTEMA<br>
+&#8226; Prengueu el control de les baixades automàtiques: trieu les subscripcions, exclogueu xarxes mòbils, trieu xarxes WiFi específiques, requeriu que el telèfon s'estigui carregat i establiu les hores o intervals.<br>
+&#8226; Gestioneu l'emmagatzematge ajustant la quantitat d'episodis en emmagatzematge temporal, l'esborrat intel·ligent (basat en els vostres favorits i l'estat de reproducció) i triant la vostra ubicació preferida.<br>
+&#8226; Feu servir l'AntennaPod en la vostra llengua (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH).<br>
+&#8226; Adapteu-vos al vostre entorn fent servir el tema clar o el fosc.<br>
+&#8226; Feu còpies de seguretat de les vostres subscripcions amb la integració amb gPodder.net i l'exportació a OPML.
+
+<b>Uniu-vos a la comunitat d'AntennaPod!</b><br>
+L'AntennaPod el desenvolupen voluntaris. Podeu col·laborar, amb codi o comentaris.
+
+Podeu proposar noves característiques, informar d'errors i aportar codi a GitHub:<br>
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>
+Podeu compartir les vostres idees, els vostres moments de podcàsting favorits i la gratitud amb tots els voluntaris al nostre grup de Google:<br>
https://groups.google.com/forum/#!forum/antennapod
-Have a question or want to give us feedback?
+Teniu preguntes o voleu comentar-nos alguna cosa?
https://twitter.com/@AntennaPod
-Transifex is the place to help with translations:<br>
+Ens podeu ajudar amb les traduccions a Transifex:<br>
https://www.transifex.com/antennapod/antennapod
-Check out our Beta Testing programme to get the latest features first:<br>
+Consulteu el nostre programa de proves beta per a obtenir els primers<br> les últimes característiques:
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/ca/listing/video b/app/src/main/play/ca/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/ca/listing/video
+++ /dev/null
diff --git a/app/src/main/play/contactPhone b/app/src/main/play/contactPhone
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/contactPhone
+++ /dev/null
diff --git a/app/src/main/play/cs-CZ/listing/video b/app/src/main/play/cs-CZ/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/cs-CZ/listing/video
+++ /dev/null
diff --git a/app/src/main/play/da-DK/listing/video b/app/src/main/play/da-DK/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/da-DK/listing/video
+++ /dev/null
diff --git a/app/src/main/play/de-DE/listing/video b/app/src/main/play/de-DE/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/de-DE/listing/video
+++ /dev/null
diff --git a/app/src/main/play/de/listing/fulldescription b/app/src/main/play/de/listing/fulldescription
index e5d7ec72a..0e09c5c29 100644
--- a/app/src/main/play/de/listing/fulldescription
+++ b/app/src/main/play/de/listing/fulldescription
@@ -1,4 +1,4 @@
-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 von Episoden (basierend auf deinen Favoriten und weiteren Einstellungen).<br>
+AntennaPod ist ein Podcast-Manager und -Player, der Dir unmittelbar Zugriff auf Millionen von freien und kostenpflichtigen Podcasts ermöglicht, angefangen von unabhängigen Podcastern bis hin zu großen Rundfunkanstalten 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 von Episoden (basierend auf deinen Favoriten und weiteren Einstellungen).<br>
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. Mit Flattr kannst du den Podcastern sogar deine Wertschätzung zeigen.
AntennaPod ist, von Podcast-Enthusiasten gemacht, frei im Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
@@ -24,7 +24,7 @@ STEUER DAS SYSTEM<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>Trete der AntennaPod-Community bei!</b><br>
+<b>Tritt der AntennaPod-Community bei!</b><br>
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).
diff --git a/app/src/main/play/el/listing/fulldescription b/app/src/main/play/el/listing/fulldescription
index 87b477fdc..f0ec3ae38 100644
--- a/app/src/main/play/el/listing/fulldescription
+++ b/app/src/main/play/el/listing/fulldescription
@@ -1,11 +1,11 @@
-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 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. You can even show your love to the content creators with our Flattr integration.
+Το Antennapod είναι μία εφαρμογή διαχείρισης και εκτέλεσης podcasts που σας δίνει άμεση πρόσβαση σε εκατομμύρια δωρεάν και επί πληρωμή podcasts, από ανεξάρτητους podcasters έως μεγάλους εκδοτικούς οίκους όπως το BBC, NPR και CNN. Μπορείτε εύκολα να προσθέσετε, να εισάγετε και να εξάγετε τις ροές τους χρησιμοποιώντας τη βάση δεδομένων podcast του iTunes, αρχεία OPML ή απλά RSS URLs. Γλυτώστε προσπάθεια, μπαταρία και χρήση δεδομένων κινητής τηλεφωνίας με ισχυρές ρυθμίσεις αυτοματισμών για λήψη επεισοδίων (ορίστε τους χρόνους, τα διαστήματα και τα δίκτυα WiFi) και σβήστε επεισόδια (με βάση τα αγαπημένα σας και τις ρυθμίσεις καθυστέρησης).<br>
+Αλλά το πλέον σημαντικό: Κατεβάστε, κάντε stream ή βάλτε στη σειρά επεισόδια και απολαύστε τα όπως επιθυμήτε με ρυθμιζόμενη ταχύτητα αναπαραγωγής, υποστήριξη κεφάλαιων και χρονόμετρο απενεργοποίησης. Μπορείτε ακόμη και να δείξετε την αγάπη σας στους δημιουργούς περιεχομένου μέσω της ενσωμάτωσης του Flattr.
-Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
+Φτιαγμένο από λάτρη των podcast, το AntennaPod είναι ελεύθερο με όλες τις έννοιες της λέξης: ανοικτού λογισμικού, χωρίς κόστη, χωρίς διαφημίσεις.
-<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>
+<b>Όλα τα χαρακτηριστικά:</b><br>
+ΕΙΣΑΓΩΓΉ, ΟΡΓΆΝΩΣΗ ΚΑΙ ΕΚΤΈΛΕΣΗ<br>
+&#8226; Προσθέστε και εισάγετε ροές μέσω των φακέλων του iTunes και του gPodder.net, αρχείων OPML και RSS ή συνδέσμους Atom<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>
@@ -24,20 +24,20 @@ CONTROL THE SYSTEM<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 under active development by volunteers. You can contribute too, with code or with comment!
+<b>Ενταχθείτε στη κοινότητα του AntennaPod!</b><br>
+Το AntennaPod βρίσκεται υπό ενεργή ανάπτυξη από εθελοντές. Μπορείτε και εσείς να συνεισφέρετε, με κώδικα ή με κάποιο σχόλιο.
-GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+Το Github είναι το μέρος να επισκεφθείτε για να ζητήσετε καινούρια χαρακτηριστικά, να αναφέρετε σφάλματα και για συνεισφορά κώδικα:<br>
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>
+Το Google Group μας είναι το μέρος να μοιραστείτε τις ιδέες σας, τις αγαπημένες σας στιγμές ενασχόλησης με τα podcasts και ευγνωμοσύνη σε όλους τους εθελοντές:<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 είναι το μέρος για να βοηθήσετε με τις μεταφράσεις:<br>
https://www.transifex.com/antennapod/antennapod
-Check out our Beta Testing programme to get the latest features first:<br>
+Ελέγξτε το πρόγραμμά μας Beta Testing για να λαμβάνετε τα τελευταία χαρακτηριστικά πρώτοι:<br>
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/en-US/listing/featureGraphic/feature-graphic.png b/app/src/main/play/en-US/listing/featureGraphic/feature-graphic.png
new file mode 100644
index 000000000..3b5261b28
--- /dev/null
+++ b/app/src/main/play/en-US/listing/featureGraphic/feature-graphic.png
Binary files differ
diff --git a/app/src/main/play/en-US/listing/video b/app/src/main/play/en-US/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/en-US/listing/video
+++ /dev/null
diff --git a/app/src/main/play/es-ES/listing/video b/app/src/main/play/es-ES/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/es-ES/listing/video
+++ /dev/null
diff --git a/app/src/main/play/es/listing/fulldescription b/app/src/main/play/es/listing/fulldescription
index 2f1c8861a..96f914f26 100644
--- a/app/src/main/play/es/listing/fulldescription
+++ b/app/src/main/play/es/listing/fulldescription
@@ -1,4 +1,4 @@
-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 (a horas o intervalos específicos, o redes WiFi) y de borrado de episodios (basado en favoritos y ajustes de tiempo).<br>
+AntennaPod es un gestor y reproductor de podcast que te da acceso instantáneo a millones de podcast gratuitos y pagos, 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. Ahorre esfuerzo, energía de la batería y uso de datos móviles con potentes controles de automatización para descargar episodios (especifique horarios, intervalos y redes WiFi) y elimine episodios (según sus preferencias y configuraciones de demora).<br>
Y lo más importante: descarga, escucha en stream y disfrutalos como quieras con velocidad de reproducción variable, soporte para capítulos y temporizador de sueño. Incluso puedes mostrar tu gratitud a los creadores de contenido mediante Flattr.
Hecho por entusiastas del podcasting, AntennaPod es libre, gratuito y sin publicidad.
diff --git a/app/src/main/play/fa/listing/fulldescription b/app/src/main/play/fa/listing/fulldescription
index 87b477fdc..0a005f962 100644
--- a/app/src/main/play/fa/listing/fulldescription
+++ b/app/src/main/play/fa/listing/fulldescription
@@ -1,5 +1,5 @@
-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 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. You can even show your love to the content creators with our Flattr integration.
+AntennaPod یک نرم افزار مدیریت و پخش پادکست میباشد که به شما امکان دسترسی سریع به میلیون ها پادکست رایگان و پرداخت شده ، از طریق انتشارات بزرگ و مستقل مانند بی بی سی، NPR و CNN را می دهد. همچنین شما میتوانید از پایگاه داده iTunes podcast، فایل های OPML یا URL های ساده RSS، استفاده نمایید ، آنها را اضافه کنید ،اطلاعالت خود را وارد و یا از انها فایل پشتیبان تهیه کنید.. صرفه جویی در زمان جستجو ، قدرت باتری و استفاده از داده های تلفن همراه با استفاده از ترفندهای قدرتمند اتوماسیون برای دانلود قسمت ها (تعیین زمان، فواصل و شبکه های WiFi) و حذف قسمت (بر اساس علاقه مندی های خود و تنظیمات تاخیر)<br>
+ما مهمتر از همه: دانلود، پخش یا پخش قسمت ها و لذت بردن از آنهاست، سرعت پخش قابل تنظیم، به هر سرعتی که شما علاقه دارید ، با پشتیبانی فصل ( قسمتها ) و یک تایمر برای خواب .حتی شما میتوانید علاقه خود را با سازندگان محتوا با استفاده از Flattr integration ما ، نشان دهید.
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
diff --git a/app/src/main/play/fr-FR/listing/video b/app/src/main/play/fr-FR/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/fr-FR/listing/video
+++ /dev/null
diff --git a/app/src/main/play/he_IL/listing/fulldescription b/app/src/main/play/he_IL/listing/fulldescription
index b4ac8ca6c..4ccee5452 100644
--- a/app/src/main/play/he_IL/listing/fulldescription
+++ b/app/src/main/play/he_IL/listing/fulldescription
@@ -1,43 +1,43 @@
-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 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. You can even show your love to the content creators with our Flattr integration.
+היישומון אנטנה־פּוֹד הוא נגן ומנהל פודקאסטים שמעניק לך גישה ישירה למיליונים של פודקאסטים בחינם ובתשלום, החל ממגישי פודקאסטים עצמאיים ועד למפיצים גדולים כגון BBC,‏ NPR ו־CNN. ניתן להוסיף, לייבא ולייצא את ההזנות שלהם בקלות יחסית באמצעות מסד נתוני הפודקאסטים של iTunes, קובצי OPML או כתובות של RSS. מאפשר לך לחסוך במאמץ, סוללה ותקשורת נתונים עם פקדי אוטומציה להורדה של פרקים (לפי זמנים, הפרשי זמן ורשתות אלחוטיות) ומחיקה של פרקים (על בסיס הגדרות המועדפים וההשהיה שלך).<br>
+אבל הכי חשוב: ניתן להוריד, להזרים או לסדר רשימות של פרקים וליהנות מהם בכל דרך שמתאימה לך עם מהירויות נגינה משתנות, תמיכה במקטעים ומתזמן שינה. ניתן אפילו להביע את חיבתך ליוצרי התוכן עם שילוב של Flattr ביישומון.
מיוצרת על ידי חובבי פודקאסטים, אנטנהפוד הינה תוכנה חינמית בכל מובן המילה: קוד פתוח, ללא עלות וללא פרסומות.
-<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>
-&#8226; Support content creators with Flattr integration including automatic flattring
-
-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 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>
+<b>כל התכונות:</b><br>
+ייבוא, ארגון ונגינה<br>
+&#8226; ניתן להוסיף ולייבא הזנות דרך הספריות של iTunes ושל gPodder.net, קובצי OPML וקישורי RSS או Atom<br>
+&#8226; ניתן לנהל את הנגינה מכל מקום: וידג׳ט על מסך הבית, התרעות המערכת ופקדי שקע אוזניות ובלוטות׳<br>
+&#8226; פשוט ליהנות בדרך שלך עם מהירות נגינה משתנה, תמיכה במקטעים (MP3, VorbisComment ו־Podlove), שמירת מיקום הנגינה ומתזמן שינה מתקדם (ניתן לנער כדי לאפס, להנמיך את עצמך השמע ולהאט את מהירות הנגינה)<br>
+&#8226; גישה להזנות ולפרקים המוגנים בססמה<br>
+&#8226; ניתן להשתמש בעימודי ההזנות שלנו (www.podlove.org/paged-feeds)
+
+מעקב, שיתוף והבעת הערכה<br>
+&#8226; מעקב אחר הטובים שבטובים על ידי סימון פרקים כמועדפים<br>
+&#8226; ניתן לאתר פרק אחד דרך היסטוריית הנגינה או על ידי חיפוש (כותרות והערות פרק)<br>
+&#8226; ניתן לשתף פרקים והזנות דרך אפשרויות מתקדמות ברשתות חברתיות ודוא״ל, שירותי gPodder.net ודרך ייצוא OPML<br>
+&#8226; ניתן לתמוך בעורכי תוכן עם שילוב של Flattr לתוך המערכת לרבות תרומה אוטומטית
+
+שליטה במערכת<br>
+&#8226; ניתן לשלוט על הורדה אוטומטית: לבחור הזנות, להחריג רשתות סלולריות, לבחור רשתות אלחוטיות מסוימות, לדרוש מהטלפון להיות בטעינה ולהגדיר מועדים או מרווחי זמן<br>
+&#8226; ניתן לנהל את האחסון על ידי הגדרת כמות הפרקים שנשמרים במטמון, מחיקה חכמה (בהתבסס על המועדפים ומצב הנגינה שלך) ובחירת המיקום המועדף עליך<br>
+&#8226; ניתן להשתמש באנטנה־פּוֹד בשפה שלך (HE, EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
+&#8226; התאמה לסביבה שלך באמצעות ערכות עיצוב בהירה וכהה<br>
+&#8226; גיבוי התמיכה שלך עם שילוב מול gPodder.net וייצוא של OPML
+
+<b>מזמינים אותך להצטרף לקהילת אנטנה־פּוֹד!</b><br>
+את תהליכי הפיתוח הפעילים של אנטנה־פּוֹד מובילים מתנדבים. ניתן לתרום גם כן, עם קוד או עם הערה!
+
+GitHub הוא המקום בו אנו מרכזים את בקשות התכונות, דיווחי התקלות ותרומות הקוד:<br>
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>
+הקבוצה שלנו ב־Google היא המקום לשתף את הרעיונות שלך, רגעי הפודקאסט המועדפים עליך ואת הערכתך לכל המתנדבים:<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 הוא המקום לסייע בתרגומים:<br>
https://www.transifex.com/antennapod/antennapod
-Check out our Beta Testing programme to get the latest features first:<br>
+מזמינים אותך לחקור את תכנית הבדיקות שלנו כדי לקבל את התכונות העדכניות ביותר לפני כולם:<br>
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/hi-IN/listing/video b/app/src/main/play/hi-IN/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/hi-IN/listing/video
+++ /dev/null
diff --git a/app/src/main/play/it-IT/listing/video b/app/src/main/play/it-IT/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/it-IT/listing/video
+++ /dev/null
diff --git a/app/src/main/play/it/listing/fulldescription b/app/src/main/play/it/listing/fulldescription
index 307f86117..3d0425660 100644
--- a/app/src/main/play/it/listing/fulldescription
+++ b/app/src/main/play/it/listing/fulldescription
@@ -1,36 +1,36 @@
-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 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. You can even show your love to the content creators with our Flattr integration.
+AntennaPod è un gestore e player di podcast che ti dà accesso istantaneo a milioni di podcast gratuiti e a pagamento, da podcaster indipendenti a grandi case editrici come BBC, NPR e CNN. Aggiungi, Importa ed esporta facilmente i Feed dal database di iTunes, file OPML oppure semplici collegamenti RSS. Risparmia fatica, batteria e dati con potenti controlli automatici per scaricare gli episodi (specifica orari, intervalli e reti WiFi) ed eliminare gli episodi.<br>
+Ma soprattutto: Scarica, fai Stream o metti in coda gli episodi e goditeli come preferisci cambiando la velocità di riproduzione, saltando tra capitoli e impostando lo sleep timer. Puoi persino mostrare il tuo amore attraverso l'integrazione con Flattr.
-Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
+Creato da amatori del podcast, AntennaPod è <i>free</i> in tutti i sensi: open-source, nessun costo, nessuna pubblicità.
<b>Tutte le funzioni:</b><br>
IMPORTA, ORGANIZZA E RIPRODUCI<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; Aggiungi ed importa i feed tramite i database di iTunes e gPodder.net, file OPML e link RSS o Atom<br>
+&#8226; Gestisci la riproduzione in ogni modo: attraverso il widget per la home, le notifiche di sistema oppure tramite i controlli sulle cuffie, sia cablate che bluetooth<br>
+&#8226; Goditi l'ascolto a modo tuo attraverso la velocità di riproduzione regolabile, il supporto ai capitoli (MP3, VorbisComment e Podlove), la memoria della posizione di riproduzione e un timer di riproduzione avanzato (scuoti per reimpostare, abbassamento del volume e rallentamento di riproduzione)<br>
&#8226; Accedi a feed ed episodi protetti da password<br>
-&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
+&#8226; Approfitta dei <i>paged feeds</i> (www.podlove.org/paged-feeds)
-TIENI TRACCIA, CONDIVIDI & APPREZZA<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>
-&#8226; Support content creators with Flattr integration including automatic flattring
+TIENI TRACCIA, CONDIVIDI E APPREZZA<br>
+&#8226; Tieni traccia degli episodi migliori aggiungendoli ai preferiti<br>
+&#8226; Trova episodi specifici nella cronologia di riproduzione o cercando tra titoli e descrizioni<br>
+&#8226; Condividi episodi e feed attraverso le opzioni avanzate di condivisione verso social e emali, i servizi online di gPodder.net e l'esportazione in file OPML<br>
+&#8226; Supporta i creatori attraverso Flattr consentendo anche il <i>flattring</i> automatico
CONTROLLA IL SISTEMA<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; Prendi il controllo dei download automatici: scegli i Feed, escludi le reti cellulari, seleziona reti WiFi specifiche, attiva solo a telefono in carica e imposta orari e intervalli.<br>
+&#8226; Gestisci la memoria impostando il numero massimo di episodi scaricati, l'eliminazione automatica (basata sui tuoi preferiti e lo stato di riproduzione) e selezionando la tua posizione preferita in memoria<br>
&#8226; Utilizza AntennaPod nella tua lingua (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
+&#8226; Adatta all'ambiente usando il tema chiaro e quello scuro<br>
+&#8226; Fai il backup delle tue iscrizione con gPodder.net e l'esportazione OPML
<b>Unisciti alla comunità di AntennaPod!</b><br>
-AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
+AntennaPod è in fase attiva di sviluppo da parte di volontari. Anche te puoi contribuire, con codice o commenti!
-GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+Per chiedere nuove funzioni, contribuire con del codice o segnalare problemi, puoi trovarci su GitHub:
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>
+Il nostro Gruppo Google è il posto giusto per condividere le tue idee, i tuoi più bei momenti di podcasting e per mostrare gratitudine ai volontari<br>
https://groups.google.com/forum/#!forum/antennapod
Hai una domanda o vuoi fornirci un feedback?
diff --git a/app/src/main/play/it_IT/listing/fulldescription b/app/src/main/play/it_IT/listing/fulldescription
index eb2abb40d..31fa64d69 100644
--- a/app/src/main/play/it_IT/listing/fulldescription
+++ b/app/src/main/play/it_IT/listing/fulldescription
@@ -1,28 +1,28 @@
AntennaPod è un riproduttore e gestore di podcast che ti da accesso immediato a milioni di podcast gratuiti e a pagamento, dai podcaster indipendenti alle più grandi emittenti come BBC, NPR e CNN. Aggiungi, importa e esporta in modo semplici usando il database di podcast di iTunes, da file OPML o da semplici URL RSS. Risparmia fatica, batteria e dati con il potente controllo automatizzato per il download di episodi (orari specifici, intervalli e reti WiFi) e l'eliminazione degli episodi.<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. You can even show your love to the content creators with our Flattr integration.
+Ancora più importante: scarica, ascolta in streaming o metti in coda gli episodi e goditeli quando e come vuoi grazie alla velocità di riproduzione personalizzabile, al supporto per i capitoli e al timer di spegnimento. Puoi anche ringraziare i creatori di contenuti grazie all'integrazione con Flattr.
Creato da amanti dei podcast, AntennaPod è libero in tutti i sensi: open source, gratis, senza pubblicità.
<b>Funzioni:</b><br>
IMPORTA, ORGANIZZA E RIPRODUCI<br>
&#8226; Aggiungi e importa feed via iTunes, gPodder.net, file OPML e link RSS o Atom<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; Gestisci la riproduzione da dove vuoi: widget sulla schermata Home, notifiche di sistema e controlli sugli auricolari e via bluetooth. <br>
+&#8226; Divertiti ad ascoltare nel modo che preferisci grazie alla velocità di riproduzione variabile, il supporto ai capitoli (MP3, VorbisComment e Podlove), la memorizzazione dell'avanzamento di riproduzione e un timer di spegnimento avanzato (scuoti per resettare, volume basso e rallentamento della velocità)<br>
&#8226; Accedi a feed e episodi protetti da password<br>
-&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
+&#8226; Sfrutta i vantaggi dei paged feeds (www.podlove.org/paged-feeds)
TIENI TRACCIA, CONDIVIDI & APPREZZA<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>
+&#8226; Tieni traccia del meglio del meglio segnando gli episodi come Preferiti<br>
+&#8226; Trova l'episodio specifico grazie alla cronologia di riproduzione o al sistema di ricerca (titoli e note dell'episodio)<br>
+&#8226; Condividi gli episodi e i feed tramite le avanzate opzioni social ed email, il servizio gPodder.net e l'esportazione in OPML<br>
&#8226; Supporta i creatori di contenuti tramite l'integrazione con Flattr e il flattring automatico
CONTROLLA IL SISTEMA<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; Prendi il controllo grazie ai download automatici: scegli i feed, escludi le reti mobili, seleziona reti WiFi specifiche, richiedi che il telefono sia in carica e imposta tempi e intervalli di aggiornamento<br>
+&#8226; Gestisci lo spazio impostando l'occupazione massima, la cancellazione intelligente (basata sui Preferiti e sullo stato di riproduzione) e selezionando la posizione che preferisci <br>
&#8226; Usa AntennaPod nella tua lingua (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
+&#8226; Adattati all'ambiente in cui ti trovi con i temi Chiaro e Scuro.<br>
+&#8226; Salva le sottoscrizioni grazie all'integrazione con gPodder.net e l'esportazione OPML
<b>Entra nella community di AntennaPod!</b><br>
AntennaPod è sviluppato da volontari. Anche tu puoi contribuire, con il codice o con dei commenti!
@@ -30,7 +30,7 @@ AntennaPod è sviluppato da volontari. Anche tu puoi contribuire, con il codice
GitHub è il posto nel quale fare richieste, segnalare bug e contribuire allo sviluppo:<br>
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>
+Il nostro gruppo Google è il luogo ideale dove condividere le idee, stimolare momenti di podcasting e ringraziare tutti i volontari:<br>
https://groups.google.com/forum/#!forum/antennapod
Hai una domanda o vuoi darci un feedback?
diff --git a/app/src/main/play/iw-IL/listing/video b/app/src/main/play/iw-IL/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/iw-IL/listing/video
+++ /dev/null
diff --git a/app/src/main/play/ja-JP/listing/video b/app/src/main/play/ja-JP/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/ja-JP/listing/video
+++ /dev/null
diff --git a/app/src/main/play/ko-KR/listing/video b/app/src/main/play/ko-KR/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/ko-KR/listing/video
+++ /dev/null
diff --git a/app/src/main/play/ms_MY/listing/fulldescription b/app/src/main/play/ms_MY/listing/fulldescription
new file mode 100644
index 000000000..87b477fdc
--- /dev/null
+++ b/app/src/main/play/ms_MY/listing/fulldescription
@@ -0,0 +1,43 @@
+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 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. You can even show your love to the content creators with our Flattr integration.
+
+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>
+&#8226; Support content creators with Flattr integration including automatic flattring
+
+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 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>
+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>
+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 \ No newline at end of file
diff --git a/app/src/main/play/nb_NO/listing/fulldescription b/app/src/main/play/nb_NO/listing/fulldescription
index 11bc6fd9f..460a880fe 100644
--- a/app/src/main/play/nb_NO/listing/fulldescription
+++ b/app/src/main/play/nb_NO/listing/fulldescription
@@ -18,7 +18,7 @@ FØLG MED, DEL OG SETT PRIS PÅ<br>
&#8226; Støtt innholdsskapere med Flattr-integrasjon gjennom automatisk &#171;flattring&#187;
KONTROLLER SYSTEMET<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; Ta kontroll over automatiske nedlastninger: velg feeds, ekskluder mobil tilkobling, spesifiser Wifi-nettverk, krev at telefonen er tilkoblet lader, sett tidspunk eller intervaller<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; Bruk AntennaPod i ditt språk (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
&#8226; Tilpass appens stil ved å bruke det mørke eller lyse temaet<br>
@@ -33,11 +33,11 @@ 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?
+Har du spørsmål eller vil du gi oss tilbakemelding?
https://twitter.com/@AntennaPod
-Transifex is the place to help with translations:<br>
+Transifex er stedet for å hjelpe til med oversettinger:<br>
https://www.transifex.com/antennapod/antennapod
-Check out our Beta Testing programme to get the latest features first:<br>
+Sjekk ut vårt program for betatesting for å få de nyeste funksjonene først:<br>
https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
diff --git a/app/src/main/play/nl-NL/listing/video b/app/src/main/play/nl-NL/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/nl-NL/listing/video
+++ /dev/null
diff --git a/app/src/main/play/pl-PL/listing/video b/app/src/main/play/pl-PL/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/pl-PL/listing/video
+++ /dev/null
diff --git a/app/src/main/play/pt-BR/listing/video b/app/src/main/play/pt-BR/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/pt-BR/listing/video
+++ /dev/null
diff --git a/app/src/main/play/pt-PT/listing/video b/app/src/main/play/pt-PT/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/pt-PT/listing/video
+++ /dev/null
diff --git a/app/src/main/play/ro/listing/video b/app/src/main/play/ro/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/ro/listing/video
+++ /dev/null
diff --git a/app/src/main/play/ru-RU/listing/video b/app/src/main/play/ru-RU/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/ru-RU/listing/video
+++ /dev/null
diff --git a/app/src/main/play/sl_SI/listing/fulldescription b/app/src/main/play/sl_SI/listing/fulldescription
new file mode 100644
index 000000000..87b477fdc
--- /dev/null
+++ b/app/src/main/play/sl_SI/listing/fulldescription
@@ -0,0 +1,43 @@
+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 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. You can even show your love to the content creators with our Flattr integration.
+
+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>
+&#8226; Support content creators with Flattr integration including automatic flattring
+
+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 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>
+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>
+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 \ No newline at end of file
diff --git a/app/src/main/play/sv-SE/listing/video b/app/src/main/play/sv-SE/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/sv-SE/listing/video
+++ /dev/null
diff --git a/app/src/main/play/tr-TR/listing/video b/app/src/main/play/tr-TR/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/tr-TR/listing/video
+++ /dev/null
diff --git a/app/src/main/play/uk/listing/video b/app/src/main/play/uk/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/uk/listing/video
+++ /dev/null
diff --git a/app/src/main/play/vi/listing/fulldescription b/app/src/main/play/vi/listing/fulldescription
index 87b477fdc..9519b5da1 100644
--- a/app/src/main/play/vi/listing/fulldescription
+++ b/app/src/main/play/vi/listing/fulldescription
@@ -3,7 +3,7 @@ But most importantly: Download, stream or queue episodes and enjoy them the way
Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
-<b>All features:</b><br>
+Mọi tính năng:
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>
@@ -17,14 +17,14 @@ KEEP TRACK, SHARE & APPRECIATE<br>
&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
&#8226; Support content creators with Flattr integration including automatic flattring
-CONTROL THE SYSTEM<br>
+ĐIỀU KHIỂN HỆ THỐNG
&#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>
+Tham gia cộng đồng AntennaPod!
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>
@@ -33,7 +33,7 @@ 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?
+Có một thắc mắc hay muốn phản hồi cho chúng tôi?
https://twitter.com/@AntennaPod
Transifex is the place to help with translations:<br>
diff --git a/app/src/main/play/zh-CN/listing/video b/app/src/main/play/zh-CN/listing/video
deleted file mode 100644
index e69de29bb..000000000
--- a/app/src/main/play/zh-CN/listing/video
+++ /dev/null
diff --git a/app/src/main/res/layout-v14/authentication_dialog.xml b/app/src/main/res/layout-v14/authentication_dialog.xml
deleted file mode 100644
index 00e74c9e1..000000000
--- a/app/src/main/res/layout-v14/authentication_dialog.xml
+++ /dev/null
@@ -1,87 +0,0 @@
-<?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" >
-
- <LinearLayout
- 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_alignParentTop="true"
- android:layout_toLeftOf="@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_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label" />
- </RelativeLayout>
-
-</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout-v14/directory_chooser.xml b/app/src/main/res/layout-v14/directory_chooser.xml
deleted file mode 100644
index 14e2f6a38..000000000
--- a/app/src/main/res/layout-v14/directory_chooser.xml
+++ /dev/null
@@ -1,114 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout 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"
- tools:background="@android:color/darker_gray">
-
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="fill_parent"
- android:layout_height="48dp"
- android:layout_alignParentBottom="true" >
-
- <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_alignParentTop="true"
- android:layout_toLeftOf="@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_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label" />
- </RelativeLayout>
-
- <RelativeLayout
- android:id="@+id/directory_info"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true" >
-
- <ImageButton
- android:id="@+id/butNavUp"
- android:contentDescription="@string/navigate_upwards_label"
- android:layout_width="60dp"
- android:layout_height="60dp"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- android:background="?attr/selectableItemBackground"
- android:src="?attr/navigation_up"
- tools:src="@drawable/navigation_up"
- tools:background="@android:color/holo_green_dark" />
-
- <TextView
- android:id="@+id/txtvSelectedFolderLabel"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="8dp"
- android:layout_toRightOf="@id/butNavUp"
- android:text="@string/selected_folder_label"
- android:textStyle="bold"
- tools:background="@android:color/holo_green_dark">
- </TextView>
-
- <TextView
- android:id="@+id/txtvSelectedFolder"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_below="@id/txtvSelectedFolderLabel"
- android:layout_margin="8dp"
- android:layout_toRightOf="@id/butNavUp"
- android:ellipsize="start"
- android:scrollHorizontally="true"
- android:singleLine="true"
- tools:text="/path/to/selected/folder"
- tools:background="@android:color/holo_green_dark"/>
-
- <View
- android:id="@+id/divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_below="@id/butNavUp"
- android:background="@color/holo_blue_light" />
- </RelativeLayout>
-
- <ListView
- android:id="@+id/directory_list"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_above="@id/footer"
- android:layout_below="@id/directory_info" />
-
-</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout-v14/download_authentication_activity.xml b/app/src/main/res/layout-v14/download_authentication_activity.xml
deleted file mode 100644
index f6925dc3a..000000000
--- a/app/src/main/res/layout-v14/download_authentication_activity.xml
+++ /dev/null
@@ -1,98 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <TextView
- android:id="@+id/txtvTitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/authentication_notification_title"
- android:layout_alignParentTop="true"
- android:textSize="@dimen/text_size_large"
- android:layout_margin="16dp"
- android:textColor="@color/holo_blue_light"
- android:textStyle="italic"/>
-
- <TextView
- android:id="@+id/txtvDescription"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/authentication_notification_msg"
- android:layout_below="@id/txtvTitle"
- android:textSize="@dimen/text_size_medium"
- android:textColor="?android:attr/textColorSecondary"
- android:layout_margin="16dp"/>
-
- <EditText
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:id="@+id/etxtUsername"
- android:hint="@string/username_label"
- android:layout_below="@id/txtvDescription"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <EditText
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:id="@+id/etxtPassword"
- android:hint="@string/password_label"
- android:inputType="textPassword"
- android:layout_below="@id/etxtUsername"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="fill_parent"
- android:layout_height="48dp"
- android:focusableInTouchMode="true"
- android:layout_alignParentBottom="true">
-
- <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_alignParentTop="true"
- android:layout_toLeftOf="@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_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label"/>
- </RelativeLayout>
-
-
-</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout-v14/opml_selection.xml b/app/src/main/res/layout-v14/opml_selection.xml
deleted file mode 100644
index 3133debd1..000000000
--- a/app/src/main/res/layout-v14/opml_selection.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout 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" >
-
- <RelativeLayout
- android:id="@+id/footer"
- android:layout_width="fill_parent"
- android:layout_height="48dp"
- android:layout_alignParentBottom="true" >
-
- <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_alignParentTop="true"
- android:layout_toLeftOf="@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_alignParentTop="true"
- android:layout_toRightOf="@id/horizontal_divider"
- android:background="?android:attr/selectableItemBackground"
- android:text="@string/confirm_label" />
- </RelativeLayout>
-
- <ListView
- android:id="@+id/feedlist"
- android:layout_width="match_parent"
- android:layout_height="0dp"
- android:layout_above="@id/footer"
- android:layout_alignParentTop="true"
- tools:listitem="@android:layout/simple_list_item_multiple_choice" >
- </ListView>
-
-</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout-v14/time_dialog.xml b/app/src/main/res/layout-v14/time_dialog.xml
deleted file mode 100644
index ba4249268..000000000
--- a/app/src/main/res/layout-v14/time_dialog.xml
+++ /dev/null
@@ -1,67 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center">
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="horizontal" >
-
- <EditText
- android:id="@+id/etxtTime"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:layout_margin="8dp"
- android:ems="2"
- android:hint="@string/enter_time_here_label"
- android:inputType="number"
- android:maxLength="2" >
-
- <requestFocus />
- </EditText>
-
- <Spinner
- android:id="@+id/spTimeUnit"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="8dp" />
- </LinearLayout>
-
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/timer_about_to_expire_label"
- android:textSize="16sp" />
-
- <CheckBox
- android:id="@+id/cbShakeToReset"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/shake_to_reset_label" />
-
- <CheckBox
- android:id="@+id/cbVibrate"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/timer_vibration_label" />
-
- <CheckBox
- android:id="@+id/chAutoEnable"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/auto_enable_label" />
-
- </LinearLayout>
-
-</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/about.xml b/app/src/main/res/layout/about.xml
index 02e232d9a..42f5e1245 100644
--- a/app/src/main/res/layout/about.xml
+++ b/app/src/main/res/layout/about.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/webvContainer"
+ android:id="@+id/webViewContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<WebView
- android:id="@+id/webvAbout"
+ android:id="@+id/webViewAbout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml
index 5336fb8ce..89f900a1f 100644
--- a/app/src/main/res/layout/all_episodes_fragment.xml
+++ b/app/src/main/res/layout/all_episodes_fragment.xml
@@ -10,10 +10,12 @@
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:scrollbarStyle="outsideOverlay"
+ android:clipToPadding="false"
android:paddingTop="@dimen/list_vertical_padding"
android:paddingBottom="@dimen/list_vertical_padding"
- android:clipToPadding="false"/>
+ android:scrollbarStyle="outsideOverlay"
+ tools:itemCount="13"
+ tools:listitem="@layout/new_episodes_listitem" />
<ProgressBar
android:id="@+id/progLoading"
@@ -22,7 +24,7 @@
android:layout_gravity="center"
android:indeterminateOnly="true"
android:visibility="gone"
- tools:visibility="visible"
+ tools:visibility="gone"
tools:layout_width="match_parent"
tools:layout_height="64dp"
tools:background="@android:color/holo_red_light"/>
diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml
index 852b6e922..f03e4c03f 100644
--- a/app/src/main/res/layout/audio_controls.xml
+++ b/app/src/main/res/layout/audio_controls.xml
@@ -21,6 +21,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:text="1.00x"/>
</LinearLayout>
@@ -35,6 +36,7 @@
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:gravity="center"
android:text="-"
android:textStyle="bold"
@@ -48,6 +50,7 @@
android:layout_height="32dp"
android:minWidth="0dp"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:gravity="center"
android:text="+"
android:textStyle="bold"
@@ -60,7 +63,9 @@
android:layout_width="match_parent"
android:layout_height="32dp"
android:layout_toRightOf="@id/butDecSpeed"
+ android:layout_toEndOf="@id/butDecSpeed"
android:layout_toLeftOf="@id/butIncSpeed"
+ android:layout_toStartOf="@id/butIncSpeed"
android:layout_centerVertical="true"
android:max="40"/>
@@ -79,6 +84,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="-12dp"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:orientation="horizontal"
android:gravity="center">
@@ -101,6 +107,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:orientation="horizontal"
android:gravity="center">
diff --git a/app/src/main/res/layout/authentication_dialog.xml b/app/src/main/res/layout/authentication_dialog.xml
index e18ab42eb..187045c63 100644
--- a/app/src/main/res/layout/authentication_dialog.xml
+++ b/app/src/main/res/layout/authentication_dialog.xml
@@ -1,14 +1,13 @@
<?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:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:orientation="vertical">
+ android:orientation="vertical" >
<EditText
android:id="@+id/etxtUsername"
@@ -39,30 +38,54 @@
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" >
- </LinearLayout>
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dip"
+ android:layout_alignParentTop="true"
+ android:background="?android:attr/dividerVertical" />
- <LinearLayout
- style="@android:style/ButtonBar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
+ <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/butConfirm"
- android:layout_width="0dp"
+ android:id="@+id/butCancel"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginRight="8dp"
- android:text="@string/confirm_label"
- android:layout_weight="1"/>
+ 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/butCancel"
- android:text="@string/cancel_label"
- android:layout_width="0dp"
+ android:id="@+id/butConfirm"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"/>
- </LinearLayout>
+ 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>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml
index 9ad1ff9c6..b1e93a195 100644
--- a/app/src/main/res/layout/cover_fragment.xml
+++ b/app/src/main/res/layout/cover_fragment.xml
@@ -16,6 +16,7 @@
android:scaleType="fitCenter"
app:layout_aspectRatio="100%"
app:layout_widthPercent="82%"
+ android:transitionName="coverTransition"
tools:src="@android:drawable/sym_def_app_icon" />
<LinearLayout
@@ -37,6 +38,7 @@
android:maxLines="2"
android:ellipsize="end"
android:text="Podcast"
+ android:textIsSelectable="true"
android:textColor="?android:attr/textColorSecondary" />
</LinearLayout>
@@ -60,6 +62,7 @@
android:maxLines="2"
android:ellipsize="end"
android:text="Episode"
+ android:textIsSelectable="true"
android:textColor="?android:attr/textColorPrimary" />
</LinearLayout>
diff --git a/app/src/main/res/layout/directory_chooser.xml b/app/src/main/res/layout/directory_chooser.xml
index 635a73cf4..5b9269607 100644
--- a/app/src/main/res/layout/directory_chooser.xml
+++ b/app/src/main/res/layout/directory_chooser.xml
@@ -2,30 +2,57 @@
<RelativeLayout 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:layout_height="match_parent"
+ tools:background="@android:color/darker_gray">
- <LinearLayout
+ <RelativeLayout
android:id="@+id/footer"
- style="@android:style/ButtonBar"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:orientation="horizontal" >
+ android:layout_width="fill_parent"
+ android:layout_height="48dp"
+ android:layout_alignParentBottom="true" >
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="0px"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/confirm_label" />
+ <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="0px"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
+ 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" />
- </LinearLayout>
+
+ <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>
<RelativeLayout
android:id="@+id/directory_info"
@@ -39,6 +66,7 @@
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:background="?attr/selectableItemBackground"
android:src="?attr/navigation_up"
@@ -54,9 +82,10 @@
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:layout_toRightOf="@id/butNavUp"
+ android:layout_toEndOf="@id/butNavUp"
android:text="@string/selected_folder_label"
android:textStyle="bold"
- tools:background="@android:color/holo_blue_bright">
+ tools:background="@android:color/holo_green_dark">
</TextView>
<TextView
@@ -64,14 +93,16 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_below="@id/txtvSelectedFolderLabel"
android:layout_margin="8dp"
android:layout_toRightOf="@id/butNavUp"
+ android:layout_toEndOf="@id/butNavUp"
android:ellipsize="start"
android:scrollHorizontally="true"
android:singleLine="true"
tools:text="/path/to/selected/folder"
- tools:background="@android:color/holo_blue_bright"/>
+ tools:background="@android:color/holo_green_dark"/>
<View
android:id="@+id/divider"
diff --git a/app/src/main/res/layout/download_authentication_activity.xml b/app/src/main/res/layout/download_authentication_activity.xml
index 27604973a..3414a5e00 100644
--- a/app/src/main/res/layout/download_authentication_activity.xml
+++ b/app/src/main/res/layout/download_authentication_activity.xml
@@ -48,28 +48,55 @@
android:focusableInTouchMode="true"
android:cursorVisible="true"/>
- <LinearLayout
+ <RelativeLayout
android:id="@+id/footer"
- style="@android:style/ButtonBar"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:orientation="horizontal">
+ android:layout_height="48dp"
+ android:focusableInTouchMode="true"
+ android:layout_alignParentBottom="true">
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="0px"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/confirm_label"/>
+ <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="0px"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
+ 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"/>
- </LinearLayout>
+
+ <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>
</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/downloaded_episodeslist_item.xml b/app/src/main/res/layout/downloaded_episodeslist_item.xml
index 760b6b9db..66ae6c180 100644
--- a/app/src/main/res/layout/downloaded_episodeslist_item.xml
+++ b/app/src/main/res/layout/downloaded_episodeslist_item.xml
@@ -1,9 +1,8 @@
<?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="@dimen/listitem_threeline_height"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
@@ -14,68 +13,76 @@
android:layout_gravity="center_vertical"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_green_dark"/>
+
- <RelativeLayout
+ <LinearLayout
android:layout_width="0dp"
- android:layout_height="@dimen/thumbnail_length_downloaded_item"
+ android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
+ android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1"
+ android:orientation="vertical"
tools:background="@android:color/holo_red_dark">
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.ListItemPrimaryTitle"
- android:layout_width="0dp"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true"
- android:layout_alignParentTop="true"
- android:layout_centerVertical="true"
android:layout_marginBottom="4dp"
tools:text="Downloaded episode title"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_green_dark"/>
- <TextView
- android:id="@+id/txtvSize"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:layout_alignParentLeft="true"
- android:layout_alignParentBottom="true"
- tools:text="23 MB"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_red_dark" >
- <TextView
- android:id="@+id/txtvPublished"
- style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentRight="true"
- android:layout_alignParentBottom="true"
- android:layout_marginLeft="8dp"
- tools:text="Jan 23"
- tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvSize"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ tools:text="23 MB"
+ tools:background="@android:color/holo_green_dark"/>
+
+ <View
+ android:layout_width="0dip"
+ android:layout_height="1dip"
+ android:layout_weight="1" />
+
+ <ImageView
+ android:id="@+id/imgvInPlaylist"
+ android:layout_width="@dimen/enc_icons_size"
+ android:layout_height="@dimen/enc_icons_size"
+ android:contentDescription="@string/in_queue_label"
+ android:src="?attr/stat_playlist"
+ android:visibility="visible"
+ tools:src="@drawable/ic_list_white_24dp"
+ tools:background="@android:color/holo_red_light"/>
- <ImageView
- android:id="@+id/imgvInPlaylist"
- android:layout_width="@dimen/enc_icons_size"
- android:layout_height="@dimen/enc_icons_size"
- android:layout_toLeftOf="@id/txtvPublished"
- android:layout_alignParentBottom="true"
- android:contentDescription="@string/in_queue_label"
- android:src="?attr/stat_playlist"
- android:visibility="visible"
- tools:src="@drawable/ic_list_white_24dp"
- tools:background="@android:color/holo_red_light" />
+ <TextView
+ android:id="@+id/txtvPublished"
+ style="@style/AntennaPod.TextView.ListItemSecondaryTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="Jan 23"
+ tools:background="@android:color/holo_green_dark"/>
- </RelativeLayout>
+ </LinearLayout>
+ </LinearLayout>
<include layout="@layout/vertical_list_divider"/>
diff --git a/app/src/main/res/layout/downloadlist_item.xml b/app/src/main/res/layout/downloadlist_item.xml
index e7694502b..668ec817a 100644
--- a/app/src/main/res/layout/downloadlist_item.xml
+++ b/app/src/main/res/layout/downloadlist_item.xml
@@ -2,13 +2,13 @@
<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="@dimen/listitem_threeline_height"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
<LinearLayout
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
@@ -17,6 +17,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:ellipsize="end"
android:lines="1"
@@ -48,6 +49,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
@@ -60,6 +62,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorPrimary"
diff --git a/app/src/main/res/layout/downloadlog_item.xml b/app/src/main/res/layout/downloadlog_item.xml
index 7b4773bca..505102ea4 100644
--- a/app/src/main/res/layout/downloadlog_item.xml
+++ b/app/src/main/res/layout/downloadlog_item.xml
@@ -8,14 +8,16 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingBottom="8dp"
+ android:descendantFocusability="blocksDescendants"
tools:background="@android:color/darker_gray">
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/txtvIcon"
- android:layout_width="48dp"
- android:layout_height="48dp"
+ android:layout_width="48sp"
+ android:layout_height="48sp"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:textSize="48sp"
android:gravity="center" />
@@ -25,7 +27,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/txtvIcon"
android:layout_alignLeft="@id/txtvIcon"
+ android:layout_alignStart="@id/txtvIcon"
android:layout_alignRight="@id/txtvIcon"
+ android:layout_alignEnd="@id/txtvIcon"
android:layout_marginTop="8dp"
android:text="{fa-repeat}"
tools:text="↻" />
@@ -37,7 +41,9 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
tools:text="Media file"
tools:background="@android:color/holo_green_dark" />
@@ -49,8 +55,11 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/txtvIcon"
+ android:layout_toEndOf="@id/txtvIcon"
android:layout_toLeftOf="@id/txtvType"
+ android:layout_toStartOf="@id/txtvType"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
android:minLines="1"
android:maxLines="2"
@@ -63,8 +72,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/txtvIcon"
+ android:layout_toEndOf="@id/txtvIcon"
android:layout_below="@id/txtvTitle"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
tools:text="January 23"
tools:background="@android:color/holo_green_dark" />
@@ -75,7 +86,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/txtvDate"
android:layout_toRightOf="@id/txtvIcon"
+ android:layout_toEndOf="@id/txtvIcon"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:textColor="?android:attr/textColorTertiary"
android:textSize="@dimen/text_size_micro"
tools:text="@string/design_time_downloaded_log_failure_reason"
diff --git a/app/src/main/res/layout/ellipsize_start_listitem.xml b/app/src/main/res/layout/ellipsize_start_listitem.xml
index f737b60d3..1b6c48152 100644
--- a/app/src/main/res/layout/ellipsize_start_listitem.xml
+++ b/app/src/main/res/layout/ellipsize_start_listitem.xml
@@ -1,23 +1,22 @@
<?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:orientation="vertical"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- tools:background="@android:color/darker_gray">
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:background="@android:color/darker_gray">
<TextView
android:id="@+id/txtvTitle"
- android:textColor="?android:attr/textColorPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textSize="@dimen/text_size_small"
- android:lines="1"
- android:singleLine="true"
android:layout_margin="16dp"
android:ellipsize="start"
- tools:text="List item title"
- tools:background="@android:color/holo_green_dark"/>
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_small"
+ tools:background="@android:color/holo_green_dark"
+ tools:text="List item title" />
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml
index fb7abde55..dc890807c 100644
--- a/app/src/main/res/layout/external_player_fragment.xml
+++ b/app/src/main/res/layout/external_player_fragment.xml
@@ -1,89 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout
+<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fragmentLayout"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:visibility="gone">
+ android:layout_height="@dimen/external_player_height"
+ android:visibility="gone"
+ android:background="?attr/selectableItemBackground">
- <RelativeLayout
- android:layout_width="match_parent"
- android:layout_height="@dimen/external_player_height">
-
- <ImageView
- android:id="@+id/imgvCover"
- android:contentDescription="@string/cover_label"
- android:layout_width="@dimen/external_player_height"
- android:layout_height="@dimen/external_player_height"
- android:adjustViewBounds="true"
- android:cropToPadding="true"
- android:scaleType="centerCrop"
- tools:src="@drawable/ic_drag_vertical_white_48dp"
- tools:background="@android:color/holo_green_dark"
- android:layout_alignParentTop="true"
- android:layout_alignParentLeft="true"
- android:layout_alignParentStart="true"/>
+ <ImageView
+ android:id="@+id/imgvCover"
+ android:contentDescription="@string/cover_label"
+ android:layout_width="@dimen/external_player_height"
+ android:layout_height="@dimen/external_player_height"
+ android:adjustViewBounds="true"
+ android:cropToPadding="true"
+ android:scaleType="centerCrop"
+ tools:src="@drawable/ic_drag_vertical_white_48dp"
+ tools:background="@android:color/holo_green_dark"
+ android:transitionName="coverTransition"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"/>
- <ProgressBar
- android:id="@+id/episodeProgress"
- android:layout_width="match_parent"
- android:layout_height="4dp"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_toEndOf="@id/imgvCover"
- android:layout_alignParentTop="true"
- style="?attr/progressBarTheme"
- android:indeterminate="false"
- tools:progress="100"/>
-
- <ImageButton
- android:id="@+id/butPlay"
- android:layout_width="52dp"
- android:layout_height="52dp"
- android:layout_alignParentRight="true"
- android:layout_alignParentEnd="true"
- android:layout_below="@id/episodeProgress"
- android:layout_centerVertical="true"
- android:contentDescription="@string/pause_label"
- android:background="?attr/selectableItemBackground"
- tools:src="@drawable/ic_play_arrow_white_36dp"/>
+ <ProgressBar
+ android:id="@+id/episodeProgress"
+ android:layout_width="match_parent"
+ android:layout_height="4dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
+ android:layout_alignParentTop="true"
+ style="?attr/progressBarTheme"
+ android:indeterminate="false"
+ tools:progress="100"/>
- <TextView
- android:id="@+id/txtvTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:layout_marginBottom="26dp"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_toEndOf="@id/imgvCover"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="16dp"
- android:layout_toLeftOf="@id/butPlay"
- android:layout_toStartOf="@id/butPlay"
- style="@style/Base.TextAppearance.AppCompat.Body1"
- android:ellipsize="end"
- android:maxLines="1"
- tools:text="Episode title that is too long and will cause the text to wrap"/>
+ <ImageButton
+ android:id="@+id/butPlay"
+ android:layout_width="52dp"
+ android:layout_height="52dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
+ android:layout_below="@id/episodeProgress"
+ android:layout_centerVertical="true"
+ android:contentDescription="@string/pause_label"
+ android:background="?attr/selectableItemBackground"
+ android:src="?attr/av_play_big"
+ tools:src="@drawable/ic_play_arrow_white_36dp"/>
- <TextView
- android:id="@+id/txtvAuthor"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/episodeProgress"
- android:layout_marginTop="26dp"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_toEndOf="@id/imgvCover"
- android:layout_marginLeft="16dp"
- android:layout_marginStart="16dp"
- android:layout_toLeftOf="@id/butPlay"
- android:layout_toStartOf="@id/butPlay"
- style="@style/TextAppearance.AppCompat.Body1"
- android:textColor="?android:attr/textColorSecondary"
- android:ellipsize="end"
- android:maxLines="1"
- tools:text="Episode author that is too long and will cause the text to wrap"/>
+ <TextView
+ android:id="@+id/txtvTitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true"
+ android:layout_marginBottom="26dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_toLeftOf="@id/butPlay"
+ android:layout_toStartOf="@id/butPlay"
+ style="@style/Base.TextAppearance.AppCompat.Body1"
+ android:ellipsize="end"
+ android:maxLines="1"
+ tools:text="Episode title that is too long and will cause the text to wrap"/>
- </RelativeLayout>
+ <TextView
+ android:id="@+id/txtvAuthor"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/episodeProgress"
+ android:layout_marginTop="26dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_toLeftOf="@id/butPlay"
+ android:layout_toStartOf="@id/butPlay"
+ style="@style/TextAppearance.AppCompat.Body1"
+ android:textColor="?android:attr/textColorSecondary"
+ android:ellipsize="end"
+ android:maxLines="1"
+ tools:text="Episode author that is too long and will cause the text to wrap"/>
-</LinearLayout>
+</RelativeLayout>
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index 4b545e3cc..50061c4d8 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -6,48 +6,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <RelativeLayout
- android:id="@+id/header"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:layout_marginLeft="16dp"
- android:layout_marginRight="16dp"
- android:layout_marginTop="8dp">
-
- <ImageView
- android:id="@+id/imgvCover"
- android:contentDescription="@string/cover_label"
- android:layout_width="80dp"
- android:layout_height="80dp"
- android:layout_alignParentLeft="true"
- android:layout_alignParentTop="true"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark"/>
-
- <TextView
- android:id="@+id/txtvTitle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerVertical="true"
- android:layout_marginLeft="8dp"
- android:layout_alignTop="@id/imgvCover"
- android:layout_toRightOf="@id/imgvCover"
- android:layout_alignBottom="@id/imgvCover"
- style="@style/AntennaPod.TextView.Heading"
- tools:text="Feed title"
- tools:background="@android:color/holo_green_dark"/>
-
- <View
- android:id="@+id/divider"
- android:layout_width="match_parent"
- android:layout_height="1dp"
- android:layout_below="@id/imgvCover"
- android:layout_marginTop="8dp"
- android:background="@color/holo_blue_light"/>
- </RelativeLayout>
+ <include layout="@layout/feeditemlist_header" />
<ScrollView
android:id="@+id/scrollView"
@@ -57,7 +16,8 @@
android:scrollbarStyle="outsideOverlay"
android:paddingLeft="16dp"
android:paddingRight="16dp"
- android:paddingBottom="8dp">
+ android:paddingBottom="8dp"
+ android:clipToPadding="false">
<LinearLayout
android:layout_width="match_parent"
@@ -76,7 +36,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_row="0"
app:layout_column="0"
@@ -86,9 +48,10 @@
tools:background="@android:color/holo_red_light"/>
<TextView
- android:id="@+id/txtvAuthor"
+ android:id="@+id/txtvDetailsAuthor"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textIsSelectable="true"
app:layout_row="0"
app:layout_column="1"
tools:text="Daniel Oeh"
@@ -99,6 +62,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
app:layout_row="1"
app:layout_column="0"
@@ -111,6 +75,7 @@
android:id="@+id/txtvLanguage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:textIsSelectable="true"
app:layout_row="1"
app:layout_column="1"
tools:text="English"
@@ -121,6 +86,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
app:layout_row="2"
app:layout_column="0"
android:lines="1"
@@ -133,6 +99,7 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingBottom="4dp"
+ android:background="?attr/selectableItemBackground"
app:layout_row="2"
app:layout_column="1"
app:layout_gravity="fill"
@@ -143,200 +110,6 @@
</android.support.v7.widget.GridLayout>
<TextView
- android:id="@+id/txtvSettings"
- style="@style/AntennaPod.TextView.Heading"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/podcast_settings_label"
- android:layout_marginTop="8dp"/>
-
- <android.support.v7.widget.GridLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- app:columnCount="2"
- app:rowCount="1">
-
- <TextView
- android:id="@+id/txtvFeedAutoDelete"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/auto_delete_label"
- app:layout_row="0"
- app:layout_column="0"
- app:layout_gravity="center_vertical"
- android:layout_marginRight="10dp" />
-
- <Spinner
- android:layout_width="wrap_content"
- android:layout_height="match_parent"
- android:id="@+id/spnAutoDelete"
- android:entries="@array/spnAutoDeleteItems"
- android:layout_marginTop="8dp"
- app:layout_row="0"
- app:layout_column="1"
- android:spinnerMode="dropdown"
- app:layout_gravity="center"
- android:dropDownWidth="wrap_content"
- android:clickable="true" />
- </android.support.v7.widget.GridLayout>
-
- <CheckBox
- android:id="@+id/cbxKeepUpdated"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/keep_updated"
- android:enabled="true"
- android:textColor="?android:attr/textColorPrimary"
- tools:background="@android:color/holo_red_light"
- android:checked="true" />
-
- <TextView
- android:id="@+id/txtvAuthentication"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/authentication_label"
- android:textSize="@dimen/text_size_medium"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <TextView
- android:id="@+id/txtvAuthenticationDescr"
- android:text="@string/authentication_descr"
- android:textSize="@dimen/text_size_small"
- android:textColor="?android:attr/textColorPrimary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"/>
-
- <android.support.v7.widget.GridLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- app:columnCount="2"
- app:rowCount="3"
- android:layout_gravity="center_horizontal">
-
- <TextView
- android:id="@+id/txtvUsername"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="8dp"
- android:layout_marginBottom="8dp"
- app:layout_row="0"
- app:layout_column="0"
- android:text="@string/username_label"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <EditText
- android:id="@+id/etxtUsername"
- android:layout_width="140sp"
- android:layout_height="wrap_content"
- app:layout_row="0"
- app:layout_column="1"
- android:hint="@string/username_label"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <TextView
- android:id="@+id/txtvPassword"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_marginRight="8dp"
- android:layout_marginBottom="8dp"
- app:layout_row="1"
- app:layout_column="0"
- android:text="@string/password_label"
- android:textColor="?android:attr/textColorPrimary"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <EditText
- android:id="@+id/etxtPassword"
- android:layout_width="140sp"
- android:layout_height="wrap_content"
- app:layout_row="1"
- app:layout_column="1"
- android:hint="@string/password_label"
- android:inputType="textPassword"/>
-
- </android.support.v7.widget.GridLayout>
-
- <TextView
- android:id="@+id/txtvAutoDownloadSettings"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/auto_download_settings_label"
- android:textSize="@dimen/text_size_medium"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <CheckBox
- android:id="@+id/cbxAutoDownload"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/auto_download_label"
- android:enabled="false"
- android:textColor="?android:attr/textColorPrimary"
- tools:background="@android:color/holo_red_light"
- android:checked="false" />
-
- <TextView
- android:id="@+id/txtvEpisodeFilters"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
- android:text="@string/episode_filters_label"
- android:textSize="@dimen/text_size_medium"
- android:textColor="?android:attr/textColorPrimary"/>
-
- <TextView
- android:id="@+id/txtvEpisodeFiltersDescription"
- android:text="@string/episode_filters_description"
- android:textSize="@dimen/text_size_small"
- android:textColor="?android:attr/textColorPrimary"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="8dp"/>
-
- <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/radio_filter_group"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:gravity="center"
- android:orientation="horizontal">
- <RadioButton android:id="@+id/radio_filter_include"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/episode_filters_include"
- android:onClick="onRadioButtonClicked"/>
- <RadioButton android:id="@+id/radio_filter_exclude"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/episode_filters_exclude"
- android:onClick="onRadioButtonClicked"/>
- </RadioGroup>
-
- <EditText
- android:id="@+id/etxtEpisodeFilterText"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:lines="8"
- android:minLines="1"
- android:maxLines="20"
- android:scrollbars="vertical"
- android:hint="@string/episode_filters_hint"
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:cursorVisible="true"/>
-
- <TextView
style="@style/AntennaPod.TextView.Heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -348,6 +121,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
+ android:textIsSelectable="true"
android:text="@string/design_time_lorem_ipsum"
tools:background="@android:color/holo_green_dark"/>
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index a6ce221db..78c0b3b16 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -32,6 +32,7 @@
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:contentDescription="@string/cover_label"
android:gravity="center_vertical"
@@ -45,6 +46,7 @@
android:layout_height="wrap_content"
android:layout_alignTop="@id/imgvCover"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
tools:text="Podcast title"
tools:background="@android:color/holo_green_dark" />
@@ -54,6 +56,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/txtvPodcast"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="end"
@@ -67,6 +70,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:layout_below="@id/txtvTitle"
tools:text="00:42:23"
tools:background="@android:color/holo_green_dark"/>
@@ -77,8 +81,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_below="@id/txtvTitle"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
@@ -98,7 +104,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:orientation="horizontal"
tools:background="@android:color/holo_blue_bright">
@@ -108,6 +116,7 @@
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"
@@ -123,6 +132,7 @@
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:ellipsize="end"
diff --git a/app/src/main/res/layout/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml
index 361b583c9..e1f451e9e 100644
--- a/app/src/main/res/layout/feeditemlist_header.xml
+++ b/app/src/main/res/layout/feeditemlist_header.xml
@@ -4,6 +4,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:background="@color/feed_image_bg"
tools:context="de.danoeh.antennapod.activity.MainActivity"
tools:background="@android:color/darker_gray">
@@ -18,10 +19,12 @@
android:layout_width="@dimen/thumbnail_length_onlinefeedview"
android:layout_height="@dimen/thumbnail_length_onlinefeedview"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_centerVertical="true"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/cover_label"
tools:src="@drawable/ic_stat_antenna_default"
@@ -29,18 +32,32 @@
<ImageButton
android:id="@+id/butShowInfo"
- android:layout_width="48dp"
- android:layout_height="48dp"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
- android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/show_info_label"
android:src="@drawable/ic_info_white_24dp"
tools:background="@android:color/holo_green_dark"/>
+ <ImageButton
+ android:id="@+id/butShowSettings"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:background="?attr/selectableItemBackground"
+ android:contentDescription="@string/show_feed_settings_label"
+ android:src="@drawable/ic_settings_white_24dp"
+ tools:background="@android:color/holo_green_dark"
+ android:layout_below="@+id/butShowInfo"
+ android:layout_marginBottom="16dp"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"/>
+
<TextView
android:id="@+id/txtvTitle"
style="@style/AntennaPod.TextView.Heading"
@@ -49,9 +66,12 @@
android:layout_alignParentTop="true"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_toLeftOf="@id/butShowInfo"
+ android:layout_toStartOf="@id/butShowInfo"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:ellipsize="end"
android:maxLines="2"
android:shadowColor="@color/black"
@@ -62,13 +82,18 @@
<TextView
android:id="@+id/txtvAuthor"
- android:layout_width="wrap_content"
+ android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_below="@id/txtvTitle"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
+ android:layout_toLeftOf="@id/butShowSettings"
+ android:layout_toStartOf="@id/butShowSettings"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:ellipsize="end"
android:lines="1"
android:shadowColor="@color/black"
@@ -76,7 +101,7 @@
android:textColor="@color/white"
android:textSize="@dimen/text_size_small"
tools:text="Podcast author"
- tools:background="@android:color/holo_green_dark" />
+ tools:background="@android:color/holo_green_dark"/>
<com.joanzapata.iconify.widget.IconTextView
android:id="@+id/txtvFailure"
diff --git a/app/src/main/res/layout/feeditemlist_item.xml b/app/src/main/res/layout/feeditemlist_item.xml
index d2b85e7df..adf0748eb 100644
--- a/app/src/main/res/layout/feeditemlist_item.xml
+++ b/app/src/main/res/layout/feeditemlist_item.xml
@@ -5,7 +5,7 @@
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:layout_width="match_parent"
- android:layout_height="@dimen/listitem_threeline_height"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
@@ -13,7 +13,10 @@
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_weight="1"
+ android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
tools:background="@android:color/holo_orange_dark">
<TextView
@@ -22,8 +25,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
- android:layout_marginTop="16dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
tools:text="NEW"
@@ -35,10 +38,11 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
- android:layout_marginBottom="8dp"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginBottom="4dp"
android:layout_toLeftOf="@id/statusUnread"
+ android:layout_toStartOf="@id/statusUnread"
tools:text="Episode title"
tools:background="@android:color/holo_green_dark" />
@@ -48,6 +52,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_below="@id/txtvItemname"
tools:text="00:42:23"
tools:background="@android:color/holo_green_dark" />
@@ -57,8 +62,10 @@
android:layout_width="@dimen/enc_icons_size"
android:layout_height="@dimen/enc_icons_size"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_below="@id/txtvItemname"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:contentDescription="@string/in_queue_label"
android:src="?attr/stat_playlist"
android:visibility="visible"
@@ -71,7 +78,9 @@
android:layout_height="@dimen/enc_icons_size"
android:layout_below="@id/txtvItemname"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_toLeftOf="@id/imgvInPlaylist"
+ android:layout_toStartOf="@id/imgvInPlaylist"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_hearing_white_18dp"
tools:background="@android:color/holo_red_light" />
@@ -83,7 +92,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/txtvItemname"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_toLeftOf="@id/imgvType"
+ android:layout_toStartOf="@id/imgvType"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
@@ -92,17 +103,18 @@
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
+ android:layout_alignBottom="@id/txtvPublished"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
+ android:layout_toStartOf="@id/txtvPublished"
android:layout_toLeftOf="@id/txtvPublished"
+ android:layout_toEndOf="@id/txtvLenSize"
android:layout_toRightOf="@id/txtvLenSize"
- android:layout_alignTop="@id/txtvPublished"
- android:layout_alignBottom="@id/txtvPublished"
- tools:background="@android:color/holo_blue_light"
+ android:layoutDirection="ltr"
+ android:indeterminate="false"
android:max="100"
android:progress="42"
- android:indeterminate="false"
- />
+ tools:background="@android:color/holo_blue_light" />
</RelativeLayout>
diff --git a/app/src/main/res/layout/feedsettings.xml b/app/src/main/res/layout/feedsettings.xml
new file mode 100644
index 000000000..9e5f2245b
--- /dev/null
+++ b/app/src/main/res/layout/feedsettings.xml
@@ -0,0 +1,218 @@
+<?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"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <include layout="@layout/feeditemlist_header" />
+
+ <ScrollView
+ android:id="@+id/scrollView"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"
+ android:scrollbarStyle="outsideOverlay"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingBottom="8dp"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <android.support.v7.widget.GridLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ app:columnCount="2"
+ app:rowCount="1">
+
+ <TextView
+ android:id="@+id/txtvFeedAutoDelete"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/auto_delete_label"
+ app:layout_row="0"
+ app:layout_column="0"
+ app:layout_gravity="center_vertical"
+ android:layout_marginRight="10dp"
+ android:layout_marginEnd="10dp" />
+
+ <Spinner
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:id="@+id/spnAutoDelete"
+ android:entries="@array/spnAutoDeleteItems"
+ android:layout_marginTop="8dp"
+ app:layout_row="0"
+ app:layout_column="1"
+ android:spinnerMode="dropdown"
+ app:layout_gravity="center"
+ android:dropDownWidth="wrap_content"
+ android:clickable="true" />
+ </android.support.v7.widget.GridLayout>
+
+ <CheckBox
+ android:id="@+id/cbxKeepUpdated"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/keep_updated"
+ android:enabled="true"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:background="@android:color/holo_red_light"
+ android:checked="true" />
+
+ <TextView
+ android:id="@+id/txtvAuthentication"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/authentication_label"
+ android:textSize="@dimen/text_size_medium"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/txtvAuthenticationDescr"
+ android:text="@string/authentication_descr"
+ android:textSize="@dimen/text_size_small"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"/>
+
+ <android.support.v7.widget.GridLayout
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ app:columnCount="2"
+ app:rowCount="3"
+ android:layout_gravity="center_horizontal">
+
+ <TextView
+ android:id="@+id/txtvUsername"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="8dp"
+ app:layout_row="0"
+ app:layout_column="0"
+ android:text="@string/username_label"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <EditText
+ android:id="@+id/etxtUsername"
+ android:layout_width="140sp"
+ android:layout_height="wrap_content"
+ app:layout_row="0"
+ app:layout_column="1"
+ android:hint="@string/username_label"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ <TextView
+ android:id="@+id/txtvPassword"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
+ android:layout_marginBottom="8dp"
+ app:layout_row="1"
+ app:layout_column="0"
+ android:text="@string/password_label"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <EditText
+ android:id="@+id/etxtPassword"
+ android:layout_width="140sp"
+ android:layout_height="wrap_content"
+ app:layout_row="1"
+ app:layout_column="1"
+ android:hint="@string/password_label"
+ android:inputType="textPassword"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ </android.support.v7.widget.GridLayout>
+
+ <TextView
+ android:id="@+id/txtvAutoDownloadSettings"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/auto_download_settings_label"
+ android:textSize="@dimen/text_size_medium"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <CheckBox
+ android:id="@+id/cbxAutoDownload"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/auto_download_label"
+ android:enabled="false"
+ android:textColor="?android:attr/textColorPrimary"
+ tools:background="@android:color/holo_red_light"
+ android:checked="false" />
+
+ <TextView
+ android:id="@+id/txtvEpisodeFilters"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"
+ android:text="@string/episode_filters_label"
+ android:textSize="@dimen/text_size_medium"
+ android:textColor="?android:attr/textColorPrimary"/>
+
+ <TextView
+ android:id="@+id/txtvEpisodeFiltersDescription"
+ android:text="@string/episode_filters_description"
+ android:textSize="@dimen/text_size_small"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="8dp"/>
+
+ <RadioGroup xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/radio_filter_group"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:gravity="center"
+ android:orientation="horizontal">
+ <RadioButton android:id="@+id/radio_filter_include"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/episode_filters_include" />
+ <RadioButton android:id="@+id/radio_filter_exclude"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/episode_filters_exclude" />
+ </RadioGroup>
+
+ <EditText
+ android:id="@+id/etxtEpisodeFilterText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:lines="8"
+ android:minLines="1"
+ android:maxLines="20"
+ android:scrollbars="vertical"
+ android:hint="@string/episode_filters_hint"
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:cursorVisible="true"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
index bbe8e65d6..27a8bbdca 100644
--- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
@@ -15,8 +15,10 @@
android:layout_width="@dimen/thumbnail_length_itemlist"
android:layout_height="@dimen/thumbnail_length_itemlist"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:adjustViewBounds="true"
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
@@ -30,6 +32,7 @@
android:layout_height="wrap_content"
android:layout_alignTop="@id/txtvTitle"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:orientation="horizontal">
<ImageView
@@ -37,6 +40,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="-4dp"
+ android:layout_marginEnd="-4dp"
android:src="?attr/feed" />
<TextView
@@ -56,7 +60,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:layout_toLeftOf="@id/subscribers_container"
+ android:layout_toStartOf="@id/subscribers_container"
android:layout_alignTop="@id/imgvCover"
android:maxLines="2"
android:includeFontPadding="false"
@@ -69,6 +75,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:layout_below="@id/txtvTitle"
android:textSize="14sp"
android:textColor="?android:attr/textColorSecondary"
diff --git a/app/src/main/res/layout/gpodnet_tag_listitem.xml b/app/src/main/res/layout/gpodnet_tag_listitem.xml
index 9e545e59d..a377f9ba1 100644
--- a/app/src/main/res/layout/gpodnet_tag_listitem.xml
+++ b/app/src/main/res/layout/gpodnet_tag_listitem.xml
@@ -13,6 +13,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:lines="1"
@@ -25,7 +26,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginEnd="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
tools:text="301"
diff --git a/app/src/main/res/layout/gpodnetauth_credentials.xml b/app/src/main/res/layout/gpodnetauth_credentials.xml
index a290b682b..f995ae4cc 100644
--- a/app/src/main/res/layout/gpodnetauth_credentials.xml
+++ b/app/src/main/res/layout/gpodnetauth_credentials.xml
@@ -59,6 +59,7 @@
android:layout_height="wrap_content"
android:layout_below="@id/etxtPassword"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:text="@string/gpodnetauth_login_butLabel"
android:layout_margin="8dp"/>
@@ -68,7 +69,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/etxtPassword"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_toLeftOf="@id/butLogin"
+ android:layout_toStartOf="@id/butLogin"
android:textColor="@color/download_failed_red"
android:textSize="@dimen/text_size_small"
android:maxLines="2"
@@ -84,7 +87,8 @@
android:layout_height="wrap_content"
android:visibility="gone"
android:layout_alignTop="@+id/butLogin"
- android:layout_toLeftOf="@+id/butLogin"/>
+ android:layout_toLeftOf="@+id/butLogin"
+ android:layout_toStartOf="@+id/butLogin"/>
<TextView
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/gpodnetauth_device.xml b/app/src/main/res/layout/gpodnetauth_device.xml
index 38455f02c..5840fe955 100644
--- a/app/src/main/res/layout/gpodnetauth_device.xml
+++ b/app/src/main/res/layout/gpodnetauth_device.xml
@@ -41,7 +41,8 @@
android:textColor="?android:attr/textColorSecondary"
android:layout_margin="8dp"
android:layout_alignTop="@+id/etxtDeviceID"
- android:layout_alignLeft="@+id/etxtCaption"/>
+ android:layout_alignLeft="@+id/etxtCaption"
+ android:layout_alignStart="@+id/etxtCaption"/>
<EditText
android:id="@+id/etxtDeviceID"
@@ -49,7 +50,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/etxtCaption"
android:layout_toRightOf="@id/txtvDeviceID"
+ android:layout_toEndOf="@id/txtvDeviceID"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_margin="8dp"/>
<Button
@@ -58,6 +61,7 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_below="@id/etxtDeviceID"
android:text="@string/gpodnetauth_device_butCreateNewDevice"/>
@@ -66,8 +70,10 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_below="@id/etxtDeviceID"
android:layout_toLeftOf="@id/butCreateNewDevice"
+ android:layout_toStartOf="@id/butCreateNewDevice"
android:textColor="@color/download_failed_red"
android:layout_margin="16dp"
android:textSize="@dimen/text_size_small"
@@ -80,6 +86,7 @@
android:layout_height="wrap_content"
android:layout_alignTop="@id/butCreateNewDevice"
android:layout_toLeftOf="@id/butCreateNewDevice"
+ android:layout_toStartOf="@id/butCreateNewDevice"
android:textColor="@color/download_failed_red"
android:textSize="@dimen/text_size_medium"
android:visibility="gone"
@@ -102,7 +109,9 @@
android:text="@string/gpodnetauth_device_butChoose"
android:layout_below="@+id/spinnerChooseDevice"
android:layout_alignLeft="@+id/butCreateNewDevice"
- android:layout_alignRight="@+id/butCreateNewDevice"/>
+ android:layout_alignStart="@+id/butCreateNewDevice"
+ android:layout_alignRight="@+id/butCreateNewDevice"
+ android:layout_alignEnd="@+id/butCreateNewDevice"/>
<Spinner
android:id="@+id/spinnerChooseDevice"
@@ -110,7 +119,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/txtvChooseExistingDevice"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_margin="8dp"
- android:layout_alignParentRight="true"/>
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"/>
</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/import_export_activity.xml b/app/src/main/res/layout/import_export_activity.xml
new file mode 100644
index 000000000..97ff34e41
--- /dev/null
+++ b/app/src/main/res/layout/import_export_activity.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:id="@+id/import_export_layout"
+ android:padding="8dp"
+ android:clipToPadding="false">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/import_export_warning"
+ android:gravity="center_horizontal"/>
+
+ <Button
+ android:text="@string/label_export"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/button_export"
+ android:layout_marginTop="24dp"/>
+
+ <Button
+ android:text="@string/label_import"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/button_import"/>
+ </LinearLayout>
+</ScrollView>
diff --git a/app/src/main/res/layout/itemdescription_listitem.xml b/app/src/main/res/layout/itemdescription_listitem.xml
index 51bc9a5eb..9d03e30a3 100644
--- a/app/src/main/res/layout/itemdescription_listitem.xml
+++ b/app/src/main/res/layout/itemdescription_listitem.xml
@@ -17,8 +17,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:textSize="14sp"
android:textColor="?android:textColorSecondary"
android:ellipsize="end"
@@ -31,8 +33,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/txtvPubDate"
+ android:layout_toStartOf="@id/txtvPubDate"
android:textSize="16sp"
android:textColor="?android:attr/textColorPrimary"
android:ellipsize="end"
diff --git a/app/src/main/res/layout/itunes_podcast_listitem.xml b/app/src/main/res/layout/itunes_podcast_listitem.xml
index 1e6e5a836..4848563b1 100644
--- a/app/src/main/res/layout/itunes_podcast_listitem.xml
+++ b/app/src/main/res/layout/itunes_podcast_listitem.xml
@@ -16,8 +16,10 @@
android:layout_width="@dimen/thumbnail_length_itemlist"
android:layout_height="@dimen/thumbnail_length_itemlist"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:adjustViewBounds="true"
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
@@ -29,6 +31,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:layout_centerVertical="true"
android:orientation="vertical">
diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml
index c05132b42..6cabcdff2 100644
--- a/app/src/main/res/layout/main.xml
+++ b/app/src/main/res/layout/main.xml
@@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ <RelativeLayout
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml
index 0f68b503e..c9e93e149 100644
--- a/app/src/main/res/layout/mediaplayerinfo_activity.xml
+++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml
@@ -47,6 +47,7 @@
android:paddingTop="8dp"
android:layout_alignParentBottom="true"
android:background="?attr/overlay_drawable"
+ android:layoutDirection="ltr"
android:orientation="vertical">
@@ -59,8 +60,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
@@ -71,8 +74,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:text="@string/position_default_label"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_micro"
@@ -84,9 +89,13 @@
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_toLeftOf="@id/txtvLength"
+ android:layout_toStartOf="@id/txtvLength"
android:layout_toRightOf="@id/txtvPosition"
+ android:layout_toEndOf="@id/txtvPosition"
android:max="500"
tools:background="@android:color/holo_green_dark" />
@@ -106,13 +115,15 @@
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:layout_centerHorizontal="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/pause_label"
- android:src="?attr/av_pause"
+ android:src="?attr/av_play"
android:scaleType="fitCenter"
- tools:src="@drawable/ic_pause_white_36dp"
+ tools:src="@drawable/ic_play_arrow_white_24dp"
tools:background="@android:color/holo_green_dark" />
<ImageButton
@@ -120,7 +131,9 @@
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toLeftOf="@id/butPlay"
+ android:layout_toStartOf="@id/butPlay"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/rewind_label"
android:src="?attr/av_rew_big"
@@ -134,7 +147,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/butRev"
android:layout_alignLeft="@id/butRev"
+ android:layout_alignStart="@id/butRev"
android:layout_alignRight="@id/butRev"
+ android:layout_alignEnd="@id/butRev"
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
@@ -147,11 +162,13 @@
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toLeftOf="@id/butRev"
+ android:layout_toStartOf="@id/butRev"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/set_playback_speed_label"
android:src="?attr/av_fast_forward"
android:textSize="@dimen/text_size_medium"
android:textAllCaps="false"
+ android:maxLines="1"
tools:visibility="gone"
tools:background="@android:color/holo_green_dark" />
@@ -160,6 +177,7 @@
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toLeftOf="@id/butRev"
+ android:layout_toStartOf="@id/butRev"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/cast_disconnect_label"
android:src="?attr/ic_cast_disconnect"
@@ -174,7 +192,9 @@
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toRightOf="@id/butPlay"
+ android:layout_toEndOf="@id/butPlay"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/fast_forward_label"
android:src="?attr/av_ff_big"
@@ -188,7 +208,9 @@
android:layout_height="wrap_content"
android:layout_below="@id/butFF"
android:layout_alignLeft="@id/butFF"
+ android:layout_alignStart="@id/butFF"
android:layout_alignRight="@id/butFF"
+ android:layout_alignEnd="@id/butFF"
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
@@ -201,6 +223,7 @@
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
android:layout_toRightOf="@id/butFF"
+ android:layout_toEndOf="@id/butFF"
android:background="?attr/selectableItemBackground"
android:scaleType="fitCenter"
android:src="?attr/av_skip_big"
diff --git a/app/src/main/res/layout/nav_feedlistitem.xml b/app/src/main/res/layout/nav_feedlistitem.xml
index 18b5255aa..9f19157fc 100644
--- a/app/src/main/res/layout/nav_feedlistitem.xml
+++ b/app/src/main/res/layout/nav_feedlistitem.xml
@@ -5,7 +5,6 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_iconwithtext_height"
- android:paddingRight="@dimen/listitem_threeline_verticalpadding"
tools:background="@android:color/darker_gray">
<ImageView
@@ -14,6 +13,7 @@
android:layout_width="@dimen/thumbnail_length_navlist"
android:layout_height="@dimen/thumbnail_length_navlist"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:cropToPadding="true"
@@ -21,6 +21,7 @@
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
+ android:layout_marginStart="@dimen/listitem_icon_leftpadding"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark"/>
@@ -29,10 +30,14 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/list_vertical_padding"
+ android:layout_marginStart="@dimen/list_vertical_padding"
+ android:layout_marginRight="@dimen/listitem_icon_rightpadding"
+ android:layout_marginEnd="@dimen/listitem_icon_rightpadding"
android:lines="1"
android:textColor="?android:attr/textColorTertiary"
android:textSize="@dimen/text_size_navdrawer"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
tools:text="23"
tools:background="@android:color/holo_green_dark"/>
@@ -42,7 +47,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/txtvCount"
+ android:layout_toStartOf="@id/txtvCount"
android:layout_marginLeft="@dimen/list_vertical_padding"
+ android:layout_marginStart="@dimen/list_vertical_padding"
android:layout_alignWithParentIfMissing="true"
android:lines="1"
android:text="{fa-exclamation-circle}"
@@ -63,8 +70,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
+ android:layout_marginStart="@dimen/listitem_iconwithtext_textleftpadding"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:layout_toLeftOf="@id/itxtvFailure"
+ android:layout_toStartOf="@id/itxtvFailure"
android:layout_alignWithParentIfMissing="true"
tools:text="Navigation feed item title"
tools:background="@android:color/holo_green_dark"/>
diff --git a/app/src/main/res/layout/nav_list.xml b/app/src/main/res/layout/nav_list.xml
index 7e72bb39b..2d044f548 100644
--- a/app/src/main/res/layout/nav_list.xml
+++ b/app/src/main/res/layout/nav_list.xml
@@ -15,7 +15,8 @@
android:layout_alignParentBottom="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/settings_label"
- android:orientation="horizontal">
+ android:orientation="horizontal"
+ android:focusable="true">
<ImageView
android:id="@+id/imgvCover"
@@ -23,6 +24,7 @@
android:layout_height="@dimen/thumbnail_length_navlist"
android:layout_marginBottom="4dp"
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
+ android:layout_marginStart="@dimen/listitem_icon_leftpadding"
android:layout_marginTop="4dp"
android:adjustViewBounds="true"
android:contentDescription="@string/cover_label"
@@ -31,13 +33,14 @@
android:scaleType="centerCrop"
android:src="?attr/ic_settings"
tools:background="@android:color/holo_orange_dark"
- tools:src="@android:drawable/sym_def_app_icon"></ImageView>
+ tools:src="@android:drawable/sym_def_app_icon" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="@string/settings_label"
@@ -69,5 +72,5 @@
android:paddingTop="@dimen/list_vertical_padding"
android:scrollbarStyle="outsideOverlay"
tools:background="@android:color/holo_purple"
- tools:listitem="@layout/nav_listitem"></ListView>
+ tools:listitem="@layout/nav_listitem" />
</RelativeLayout>
diff --git a/app/src/main/res/layout/nav_listitem.xml b/app/src/main/res/layout/nav_listitem.xml
index d62672c34..c140533e6 100644
--- a/app/src/main/res/layout/nav_listitem.xml
+++ b/app/src/main/res/layout/nav_listitem.xml
@@ -13,12 +13,14 @@
android:layout_width="@dimen/thumbnail_length_navlist"
android:layout_height="@dimen/thumbnail_length_navlist"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:cropToPadding="true"
android:scaleType="centerInside"
android:padding="4dp"
android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
+ android:layout_marginStart="@dimen/listitem_icon_leftpadding"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
tools:src="@drawable/ic_new_releases_white_24dp"
@@ -36,8 +38,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
+ android:layout_marginStart="@dimen/listitem_iconwithtext_textleftpadding"
android:layout_marginRight="48dp"
+ android:layout_marginEnd="48dp"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
tools:text="Navigation item title"
tools:background="@android:color/holo_green_dark"
/>
@@ -50,8 +55,11 @@
android:textColor="?android:attr/textColorTertiary"
android:textSize="@dimen/text_size_navdrawer"
android:layout_marginLeft="12dp"
+ android:layout_marginStart="12dp"
android:layout_marginRight="@dimen/listitem_icon_rightpadding"
+ android:layout_marginEnd="@dimen/listitem_icon_rightpadding"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
tools:text="23"
tools:background="@android:color/holo_green_dark"/>
diff --git a/app/src/main/res/layout/new_episodes_listitem.xml b/app/src/main/res/layout/new_episodes_listitem.xml
index 944711aec..150d692e7 100644
--- a/app/src/main/res/layout/new_episodes_listitem.xml
+++ b/app/src/main/res/layout/new_episodes_listitem.xml
@@ -10,10 +10,10 @@
<LinearLayout
android:id="@+id/content"
android:layout_width="match_parent"
- android:layout_height="@dimen/listitem_threeline_height"
+ android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
- tools:background="@android:color/darker_gray">
+ android:gravity="center_vertical">
<RelativeLayout
android:layout_width="wrap_content"
@@ -26,6 +26,7 @@
android:layout_gravity="center_vertical"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:background="@color/light_gray"
android:ellipsize="end"
@@ -37,24 +38,26 @@
android:layout_height="64dp"
android:layout_width="64dp"
android:layout_alignLeft="@id/txtvPlaceholder"
+ android:layout_alignStart="@id/txtvPlaceholder"
android:layout_alignTop="@id/txtvPlaceholder"
android:layout_alignRight="@id/txtvPlaceholder"
+ android:layout_alignEnd="@id/txtvPlaceholder"
android:layout_alignBottom="@id/txtvPlaceholder"
android:contentDescription="@string/cover_label"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark" />
+ tools:src="@tools:sample/avatars" />
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
- android:layout_height="match_parent"
+ android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
+ android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
- android:layout_weight="1"
- tools:background="@android:color/white" >
+ android:layout_weight="1">
<TextView
@@ -63,8 +66,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
- android:layout_marginLeft="8dp"/>
+ android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
+ tools:text="@sample/episodes.json/data/status_label"/>
<TextView
android:id="@+id/txtvTitle"
@@ -72,10 +78,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/statusUnread"
- tools:text="Episode title"
- tools:background="@android:color/holo_green_dark" />
+ android:layout_toStartOf="@id/statusUnread"
+ tools:text="@sample/episodes.json/data/title" />
<RelativeLayout
android:id="@+id/bottom_bar"
@@ -84,8 +91,9 @@
android:layout_below="@id/txtvTitle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentRight="true"
- tools:background="@android:color/holo_red_light" >
+ android:layout_alignParentEnd="true">
<TextView
android:id="@+id/txtvDuration"
@@ -93,19 +101,20 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
- tools:text="00:42:23"
- tools:background="@android:color/holo_blue_dark" />
+ android:layout_alignParentStart="true"
+ tools:text="@sample/episodes.json/data/duration" />
<ImageView
android:id="@+id/imgvInPlaylist"
android:layout_width="@dimen/enc_icons_size"
android:layout_height="@dimen/enc_icons_size"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:contentDescription="@string/in_queue_label"
android:src="?attr/stat_playlist"
- tools:src="@drawable/ic_list_grey600_24dp"
- tools:background="@android:color/black" />
+ tools:src="@sample/inplaylist" />
<TextView
android:id="@+id/txtvPublished"
@@ -114,17 +123,16 @@
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@id/imgvInPlaylist"
+ android:layout_toStartOf="@id/imgvInPlaylist"
android:ellipsize="end"
- tools:text="Jan 23"
- tools:background="@android:color/holo_green_dark" />
+ tools:text="@sample/episodes.json/data/published_at" />
<ProgressBar
android:id="@+id/pbar_progress"
- style="?android:attr/progressBarStyleHorizontal"
+ style="?attr/progressBarTheme"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="4dp"
android:layout_below="@id/txtvDuration"
- android:layout_marginTop="-2dp"
android:max="100" />
</RelativeLayout>
diff --git a/app/src/main/res/layout/numberpicker.xml b/app/src/main/res/layout/numberpicker.xml
new file mode 100644
index 000000000..813326bd6
--- /dev/null
+++ b/app/src/main/res/layout/numberpicker.xml
@@ -0,0 +1,16 @@
+<?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="32dp">
+
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="numberDecimal"
+ android:ems="10"
+ android:selectAllOnFocus="true"
+ android:id="@+id/number" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/onlinefeedview_header.xml b/app/src/main/res/layout/onlinefeedview_header.xml
index 491d955fb..4217322e4 100644
--- a/app/src/main/res/layout/onlinefeedview_header.xml
+++ b/app/src/main/res/layout/onlinefeedview_header.xml
@@ -10,6 +10,7 @@
android:layout_width="@dimen/thumbnail_length_onlinefeedview"
android:layout_height="@dimen/thumbnail_length_onlinefeedview"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
@@ -24,10 +25,13 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_marginBottom="8dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:ellipsize="end"
android:gravity="center_vertical"
android:maxLines="2"
@@ -41,7 +45,9 @@
android:layout_below="@id/txtvTitle"
android:layout_marginBottom="8dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:ellipsize="end"
android:lines="1"
android:textColor="?android:attr/textColorSecondary"
@@ -61,7 +67,9 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:layout_marginTop="8dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_micro" />
@@ -80,7 +88,9 @@
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
+ android:layout_marginEnd="16dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small"
tools:text="@string/design_time_lorem_ipsum"
diff --git a/app/src/main/res/layout/opml_import.xml b/app/src/main/res/layout/opml_import.xml
index 2a67e7ee1..ac75cb8b3 100644
--- a/app/src/main/res/layout/opml_import.xml
+++ b/app/src/main/res/layout/opml_import.xml
@@ -6,7 +6,7 @@
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:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="8dp"
android:paddingLeft="16dp"
diff --git a/app/src/main/res/layout/opml_selection.xml b/app/src/main/res/layout/opml_selection.xml
index d08ebd0bd..f8f37b5ac 100644
--- a/app/src/main/res/layout/opml_selection.xml
+++ b/app/src/main/res/layout/opml_selection.xml
@@ -4,28 +4,54 @@
android:layout_width="match_parent"
android:layout_height="match_parent" >
- <LinearLayout
+ <RelativeLayout
android:id="@+id/footer"
- style="@android:style/ButtonBar"
android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:layout_alignParentBottom="true"
- android:orientation="horizontal" >
+ android:layout_height="48dp"
+ android:layout_alignParentBottom="true" >
- <Button
- android:id="@+id/butConfirm"
- android:layout_width="0px"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="@string/confirm_label" />
+ <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="0px"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
+ 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" />
- </LinearLayout>
+
+ <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>
<ListView
android:id="@+id/feedlist"
diff --git a/app/src/main/res/layout/preference_switch_layout.xml b/app/src/main/res/layout/preference_switch_layout.xml
deleted file mode 100644
index 54fa74061..000000000
--- a/app/src/main/res/layout/preference_switch_layout.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<android.support.v7.widget.SwitchCompat
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@android:id/checkbox"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@null"
- android:clickable="false"
- android:focusable="false" /> \ No newline at end of file
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 8de80e355..6b41b68d5 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -9,7 +9,7 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="@dimen/listitem_threeline_height"
+ android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:orientation="horizontal"
android:gravity="center_vertical"
@@ -60,7 +60,9 @@
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_textleftpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_textrightpadding"
+ android:layout_marginEnd="@dimen/listitem_threeline_textrightpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1"
tools:background="@android:color/holo_red_dark">
@@ -73,9 +75,11 @@
android:layout_height="wrap_content"
android:lines="2"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="8dp"
- android:gravity="right|top"
+ android:layout_marginStart="8dp"
+ android:gravity="end|top"
android:text="Feb\n12"
tools:background="@android:color/holo_blue_light" />
@@ -85,7 +89,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/txtvPubDate"
+ android:layout_toStartOf="@id/txtvPubDate"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:text="Queue item title"
android:ellipsize="end"
@@ -98,7 +104,9 @@
android:layout_below="@id/txtvTitle"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
- android:layout_alignParentRight="true">
+ android:layout_alignParentStart="true"
+ android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true">
<TextView
android:id="@+id/txtvProgressLeft"
@@ -106,6 +114,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_marginBottom="0dp"
android:text="00:42:23"
tools:background="@android:color/holo_blue_light"/>
@@ -116,17 +125,18 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_marginBottom="0dp"
tools:text="Jan 23"
tools:background="@android:color/holo_green_dark" />
<ProgressBar
android:id="@+id/progressBar"
- style="?android:attr/progressBarStyleHorizontal"
+ style="?attr/progressBarTheme"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="4dp"
android:layout_below="@id/txtvProgressLeft"
- android:layout_marginTop="-2dp"
+ android:layoutDirection="ltr"
android:max="100"
tools:background="@android:color/holo_blue_light" />
diff --git a/app/src/main/res/layout/searchlist_item.xml b/app/src/main/res/layout/searchlist_item.xml
index a8b8e7b62..50374c737 100644
--- a/app/src/main/res/layout/searchlist_item.xml
+++ b/app/src/main/res/layout/searchlist_item.xml
@@ -2,18 +2,20 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="@dimen/listitem_threeline_height"
- tools:background="@android:color/darker_gray">
+ android:layout_height="wrap_content"
+ tools:background="@android:color/darker_gray"
+ android:paddingTop="@dimen/listitem_threeline_verticalpadding"
+ android:paddingBottom="@dimen/listitem_threeline_verticalpadding">
<ImageView
android:id="@+id/imgvFeedimage"
android:layout_width="@dimen/thumbnail_length_itemlist"
android:layout_height="@dimen/thumbnail_length_itemlist"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_centerVertical="true"
- android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
tools:src="@drawable/ic_stat_antenna_default"
@@ -23,9 +25,11 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
+ android:layout_marginStart="@dimen/listitem_iconwithtext_textleftpadding"
android:layout_marginRight="@dimen/listitem_threeline_verticalpadding"
- android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
+ android:layout_marginEnd="@dimen/listitem_threeline_verticalpadding"
android:layout_toRightOf="@id/imgvFeedimage"
+ android:layout_toEndOf="@id/imgvFeedimage"
android:orientation="vertical"
tools:background="@android:color/holo_red_dark">
diff --git a/app/src/main/res/layout/secondary_action.xml b/app/src/main/res/layout/secondary_action.xml
index b2aea03f8..1f4d9e4e6 100644
--- a/app/src/main/res/layout/secondary_action.xml
+++ b/app/src/main/res/layout/secondary_action.xml
@@ -9,5 +9,4 @@
android:focusable="false"
android:focusableInTouchMode="false"
tools:ignore="ContentDescription"
- tools:src="@drawable/ic_play_arrow_grey600_36dp"
- tools:background="@android:color/holo_green_dark" />
+ tools:src="@sample/secondaryaction" />
diff --git a/app/src/main/res/layout/simplechapter_item.xml b/app/src/main/res/layout/simplechapter_item.xml
index 21bbc9545..0d02eac1a 100644
--- a/app/src/main/res/layout/simplechapter_item.xml
+++ b/app/src/main/res/layout/simplechapter_item.xml
@@ -2,7 +2,7 @@
<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="@dimen/listitem_threeline_height"
+ android:layout_height="wrap_content"
android:orientation="horizontal"
tools:background="@android:color/darker_gray">
@@ -13,6 +13,7 @@
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:gravity="center_vertical"
tools:text="Start"
tools:background="@android:color/holo_green_dark" />
@@ -22,7 +23,9 @@
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/listitem_threeline_verticalpadding"
android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginRight="@dimen/listitem_threeline_horizontalpadding"
+ android:layout_marginEnd="@dimen/listitem_threeline_horizontalpadding"
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:layout_weight="1"
android:gravity="center_vertical"
diff --git a/app/src/main/res/layout/splash.xml b/app/src/main/res/layout/splash.xml
new file mode 100644
index 000000000..71b6cd15a
--- /dev/null
+++ b/app/src/main/res/layout/splash.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ProgressBar
+ style="?android:attr/progressBarStyle"
+ android:layout_width="24dp"
+ android:layout_height="24dp"
+ android:layout_alignParentBottom="true"
+ android:layout_centerHorizontal="true"
+ android:layout_margin="36dp"
+ android:id="@+id/progressBar"/>
+</RelativeLayout>
diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml
index 20e01bf32..b186add9e 100644
--- a/app/src/main/res/layout/statistics_listitem.xml
+++ b/app/src/main/res/layout/statistics_listitem.xml
@@ -4,7 +4,10 @@
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_iconwithtext_height"
+ android:paddingLeft="@dimen/listitem_threeline_verticalpadding"
+ android:paddingStart="@dimen/listitem_threeline_verticalpadding"
android:paddingRight="@dimen/listitem_threeline_verticalpadding"
+ android:paddingEnd="@dimen/listitem_threeline_verticalpadding"
tools:background="@android:color/darker_gray">
<ImageView
@@ -13,13 +16,13 @@
android:layout_width="@dimen/thumbnail_length_navlist"
android:layout_height="@dimen/thumbnail_length_navlist"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:adjustViewBounds="true"
android:cropToPadding="true"
android:scaleType="centerCrop"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
- android:layout_marginLeft="@dimen/listitem_icon_leftpadding"
tools:src="@drawable/ic_stat_antenna_default"
tools:background="@android:color/holo_green_dark"/>
@@ -28,10 +31,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/list_vertical_padding"
+ android:layout_marginStart="@dimen/list_vertical_padding"
android:lines="1"
android:textColor="?android:attr/textColorTertiary"
android:textSize="@dimen/text_size_navdrawer"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
tools:text="23"
tools:background="@android:color/holo_green_dark"/>
@@ -47,8 +52,11 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding"
+ android:layout_marginStart="@dimen/listitem_iconwithtext_textleftpadding"
android:layout_toRightOf="@id/imgvCover"
+ android:layout_toEndOf="@id/imgvCover"
android:layout_toLeftOf="@id/txtvTime"
+ android:layout_toStartOf="@id/txtvTime"
android:layout_alignWithParentIfMissing="true"
tools:text="Navigation feed item title"
tools:background="@android:color/holo_green_dark"/>
diff --git a/app/src/main/res/layout/subscription_item.xml b/app/src/main/res/layout/subscription_item.xml
index 8f0539dfa..502fa8672 100644
--- a/app/src/main/res/layout/subscription_item.xml
+++ b/app/src/main/res/layout/subscription_item.xml
@@ -11,7 +11,7 @@
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="centerCrop"
- tools:src="@drawable/ic_launcher">
+ tools:src="@mipmap/ic_launcher_round">
</de.danoeh.antennapod.view.SquareImageView>
<com.joanzapata.iconify.widget.IconTextView
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index 0290ce708..ba4249268 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -8,7 +8,7 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:orientation="horizontal">
+ android:orientation="horizontal" >
<EditText
android:id="@+id/etxtTime"
@@ -18,9 +18,11 @@
android:layout_margin="8dp"
android:ems="2"
android:hint="@string/enter_time_here_label"
-
android:inputType="number"
- android:maxLength="2" />
+ android:maxLength="2" >
+
+ <requestFocus />
+ </EditText>
<Spinner
android:id="@+id/spTimeUnit"
@@ -28,7 +30,6 @@
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp" />
-
</LinearLayout>
<LinearLayout
diff --git a/app/src/main/res/layout/videoplayer_activity.xml b/app/src/main/res/layout/videoplayer_activity.xml
index 4db663e19..ebea0c618 100644
--- a/app/src/main/res/layout/videoplayer_activity.xml
+++ b/app/src/main/res/layout/videoplayer_activity.xml
@@ -3,7 +3,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
- android:orientation="vertical">
+ android:orientation="vertical"
+ android:id="@+id/videoframe">
<de.danoeh.antennapod.view.AspectRatioVideoView
android:id="@+id/videoview"
@@ -24,6 +25,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
+ android:layoutDirection="ltr"
android:orientation="horizontal">
<ImageButton
@@ -67,6 +69,7 @@
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#80000000"
+ android:layoutDirection="ltr"
android:paddingTop="8dp">
<TextView
@@ -74,10 +77,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_marginTop="4dp"
android:text="@string/position_default_label"
android:textColor="@color/white"
@@ -88,10 +94,13 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
+ android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
+ android:layout_marginStart="8dp"
android:layout_marginRight="8dp"
+ android:layout_marginEnd="8dp"
android:layout_marginTop="4dp"
android:text="@string/position_default_label"
android:textColor="@color/white"
@@ -102,7 +111,10 @@
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/txtvLength"
+ android:layout_toStartOf="@+id/txtvLength"
android:layout_toRightOf="@+id/txtvPosition"
+ android:layout_toEndOf="@+id/txtvPosition"
+ android:layout_centerInParent="true"
android:max="500" />
</RelativeLayout>
diff --git a/app/src/main/res/menu/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml
index 7398b9118..28493c5b6 100644
--- a/app/src/main/res/menu/allepisodes_context.xml
+++ b/app/src/main/res/menu/allepisodes_context.xml
@@ -7,6 +7,12 @@
android:menuCategory="container"
android:title="@string/skip_episode_label" />
+
+ <item
+ android:id="@+id/mark_as_seen_item"
+ android:menuCategory="container"
+ android:title="@string/mark_as_seen_label" />
+
<item
android:id="@+id/mark_read_item"
android:menuCategory="container"
diff --git a/app/src/main/res/menu/downloads_completed.xml b/app/src/main/res/menu/downloads_completed.xml
index dc2996893..a88d93913 100644
--- a/app/src/main/res/menu/downloads_completed.xml
+++ b/app/src/main/res/menu/downloads_completed.xml
@@ -6,7 +6,8 @@
<item
android:id="@+id/episode_actions"
android:menuCategory="container"
- android:title="@string/episode_actions"
+ android:title="@string/batch_edit"
+ android:icon="?attr/checkbox_multiple"
custom:showAsAction="always">
</item>
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index 0646dc70f..3882cdff1 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -12,7 +12,8 @@
<item
android:id="@+id/episode_actions"
android:menuCategory="container"
- android:title="@string/episode_actions"
+ android:icon="?attr/checkbox_multiple"
+ android:title="@string/batch_edit"
custom:showAsAction="always">
</item>
<item
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 530eb3400..98c7478a6 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -41,6 +41,14 @@
android:title="@string/visit_website_label"
android:visible="false">
</item>
+
+ <item
+ android:id="@+id/player_go_to_picture_in_picture"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/player_go_to_picture_in_picture"
+ android:visible="false">
+ </item>
+
<item
android:id="@+id/share_item"
android:icon="?attr/social_share"
diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml
index a5fe85865..7b82cbef3 100644
--- a/app/src/main/res/menu/queue.xml
+++ b/app/src/main/res/menu/queue.xml
@@ -90,6 +90,25 @@
android:title="@string/descending"/>
</menu>
</item>
+
+ <item
+ android:id="@+id/queue_sort_random"
+ android:title="@string/random">
+ </item>
+
+ <item
+ android:id="@+id/queue_sort_smart_shuffle"
+ android:title="@string/smart_shuffle">
+
+ <menu>
+ <item
+ android:id="@+id/queue_sort_smart_shuffle_asc"
+ android:title="@string/ascending"/>
+ <item
+ android:id="@+id/queue_sort_smart_shuffle_desc"
+ android:title="@string/descending"/>
+ </menu>
+ </item>
</menu>
</item>
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index e81115627..f45847e54 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -1,347 +1,58 @@
<?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">
+ xmlns:android="http://schemas.android.com/apk/res/android">
- <PreferenceCategory android:title="@string/user_interface_label">
- <com.afollestad.materialdialogs.prefs.MaterialListPreference
- android:entryValues="@array/theme_values"
- android:entries="@array/theme_options"
- android:title="@string/pref_set_theme_title"
- android:key="prefTheme"
- android:summary="@string/pref_set_theme_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
- <PreferenceScreen
- android:key="prefDrawerSettings"
- android:summary="@string/pref_nav_drawer_sum"
- android:title="@string/pref_nav_drawer_title">
- <Preference
- android:key="prefHiddenDrawerItems"
- android:summary="@string/pref_nav_drawer_items_sum"
- android:title="@string/pref_nav_drawer_items_title" />
- <com.afollestad.materialdialogs.prefs.MaterialListPreference
- android:entryValues="@array/nav_drawer_feed_order_values"
- android:entries="@array/nav_drawer_feed_order_options"
- 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"/>
- <com.afollestad.materialdialogs.prefs.MaterialListPreference
- 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"/>
- </PreferenceScreen>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefExpandNotify"
- android:summary="@string/pref_expandNotify_sum"
- android:title="@string/pref_expandNotify_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefPersistNotify"
- android:summary="@string/pref_persistNotify_sum"
- android:title="@string/pref_persistNotify_title"/>
- <Preference
- android:key="prefCompactNotificationButtons"
- android:summary="@string/pref_compact_notification_buttons_sum"
- android:title="@string/pref_compact_notification_buttons_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefLockscreenBackground"
- android:summary="@string/pref_lockscreen_background_sum"
- android:title="@string/pref_lockscreen_background_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefShowDownloadReport"
- android:summary="@string/pref_showDownloadReport_sum"
- android:title="@string/pref_showDownloadReport_title"/>
- </PreferenceCategory>
+ <com.bytehamster.lib.preferencesearch.SearchPreference
+ android:key="searchPreference" />
- <PreferenceCategory android:title="@string/queue_label">
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefQueueAddToFront"
- android:summary="@string/pref_queueAddToFront_sum"
- android:title="@string/pref_queueAddToFront_title"/>
- </PreferenceCategory>
+ <Preference
+ android:key="prefScreenInterface"
+ android:title="@string/user_interface_label"
+ android:icon="?attr/ic_cellphone_text" />
- <PreferenceCategory android:title="@string/playback_pref">
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="false"
- android:key="prefSonic"
- android:summary="@string/pref_sonic_message"
- android:title="@string/pref_sonic_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefPauseOnHeadsetDisconnect"
- android:summary="@string/pref_pauseOnDisconnect_sum"
- android:title="@string/pref_pauseOnHeadsetDisconnect_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:dependency="prefPauseOnHeadsetDisconnect"
- android:key="prefUnpauseOnHeadsetReconnect"
- android:summary="@string/pref_unpauseOnHeadsetReconnect_sum"
- android:title="@string/pref_unpauseOnHeadsetReconnect_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:dependency="prefPauseOnHeadsetDisconnect"
- android:key="prefUnpauseOnBluetoothReconnect"
- android:summary="@string/pref_unpauseOnBluetoothReconnect_sum"
- android:title="@string/pref_unpauseOnBluetoothReconnect_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefHardwareForwardButtonSkips"
- android:summary="@string/pref_hardwareForwardButtonSkips_sum"
- android:title="@string/pref_hardwareForwardButtonSkips_title"/>
- <Preference
- android:key="prefPlaybackFastForwardDeltaLauncher"
- android:summary="@string/pref_fast_forward_sum"
- android:title="@string/pref_fast_forward" />
- <Preference
- android:key="prefPlaybackRewindDeltaLauncher"
- android:summary="@string/pref_rewind_sum"
- android:title="@string/pref_rewind" />
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefHardwarePreviousButtonRestarts"
- android:summary="@string/pref_hardwarePreviousButtonRestarts_sum"
- android:title="@string/pref_hardwarePreviousButtonRestarts_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefFollowQueue"
- android:summary="@string/pref_followQueue_sum"
- android:title="@string/pref_followQueue_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefSkipKeepsEpisode"
- android:summary="@string/pref_skip_keeps_episodes_sum"
- android:title="@string/pref_skip_keeps_episodes_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefFavoriteKeepsEpisode"
- android:summary="@string/pref_favorite_keeps_episodes_sum"
- android:title="@string/pref_favorite_keeps_episodes_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefAutoDelete"
- android:summary="@string/pref_auto_delete_sum"
- android:title="@string/pref_auto_delete_title"/>
- <com.afollestad.materialdialogs.prefs.MaterialListPreference
- android:defaultValue="30"
- android:entries="@array/smart_mark_as_played_values"
- 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"/>
- <Preference
- android:key="prefPlaybackSpeedLauncher"
- android:summary="@string/pref_playback_speed_sum"
- android:title="@string/pref_playback_speed_title" />
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefPauseForFocusLoss"
- android:summary="@string/pref_pausePlaybackForFocusLoss_sum"
- android:title="@string/pref_pausePlaybackForFocusLoss_title" />
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefResumeAfterCall"
- android:summary="@string/pref_resumeAfterCall_sum"
- android:title="@string/pref_resumeAfterCall_title"/>
+ <Preference
+ android:key="prefScreenPlayback"
+ android:title="@string/playback_pref"
+ android:icon="?attr/av_play" />
- </PreferenceCategory>
- <PreferenceCategory android:title="@string/network_pref">
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="true"
- android:enabled="true"
- android:key="prefEnqueueDownloaded"
- android:summary="@string/pref_enqueue_downloaded_summary"
- android:title="@string/pref_enqueue_downloaded_title" />
- <Preference
- android:key="prefAutoUpdateIntervall"
- android:summary="@string/pref_autoUpdateIntervallOrTime_sum"
- android:title="@string/pref_autoUpdateIntervallOrTime_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefMobileUpdate"
- android:summary="@string/pref_mobileUpdate_sum"
- android:title="@string/pref_mobileUpdate_title"/>
- <com.afollestad.materialdialogs.prefs.MaterialListPreference
- 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"/>
- <com.afollestad.materialdialogs.prefs.MaterialEditTextPreference
- android:defaultValue="4"
- android:inputType="number"
- android:key="prefParallelDownloads"
- android:title="@string/pref_parallel_downloads_title"
- app:useStockLayout="true"/>
- <PreferenceScreen
- android:summary="@string/pref_automatic_download_sum"
- android:key="prefAutoDownloadSettings"
- android:title="@string/pref_automatic_download_title">
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:key="prefEnableAutoDl"
- android:title="@string/pref_automatic_download_title"
- android:defaultValue="false"/>
- <com.afollestad.materialdialogs.prefs.MaterialListPreference
- android:defaultValue="25"
- android:entries="@array/episode_cache_size_entries"
- android:key="prefEpisodeCacheSize"
- android:title="@string/pref_episode_cache_title"
- android:entryValues="@array/episode_cache_size_values"
- app:useStockLayout="true"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:key="prefEnableAutoDownloadOnBattery"
- android:title="@string/pref_automatic_download_on_battery_title"
- android:summary="@string/pref_automatic_download_on_battery_sum"
- android:defaultValue="true"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:key="prefEnableAutoDownloadOnMobile"
- android:title="@string/pref_autodl_allow_on_mobile_title"
- android:summary="@string/pref_autodl_allow_on_mobile_sum"
- android:defaultValue="false"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:key="prefEnableAutoDownloadWifiFilter"
- android:title="@string/pref_autodl_wifi_filter_title"
- android:summary="@string/pref_autodl_wifi_filter_sum"/>
- </PreferenceScreen>
- <Preference
- android:key="prefProxy"
- android:summary="@string/pref_proxy_sum"
- android:title="@string/pref_proxy_title" />
-
- </PreferenceCategory>
+ <Preference
+ android:key="prefScreenNetwork"
+ android:title="@string/network_pref"
+ android:icon="?attr/ic_swap" />
- <PreferenceCategory android:title="@string/services_label">
- <PreferenceScreen
- android:key="prefFlattrSettings"
- android:title="@string/flattr_label">
- <PreferenceScreen
- android:key="pref_flattr_authenticate"
- android:summary="@string/pref_flattr_auth_sum"
- android:title="@string/pref_flattr_auth_title">
- <intent android:action=".activities.FlattrAuthActivity"/>
- </PreferenceScreen>
+ <Preference
+ android:key="prefScreenIntegrations"
+ android:title="@string/integrations_label"
+ android:icon="?attr/ic_unfav" />
- <Preference
- android:key="prefAutoFlattrPrefs"
- android:summary="@string/pref_auto_flattr_sum"
- android:title="@string/pref_auto_flattr_title" />
- <Preference
- android:key="prefRevokeAccess"
- android:summary="@string/pref_revokeAccess_sum"
- android:title="@string/pref_revokeAccess_title"/>
- </PreferenceScreen>
- <PreferenceScreen
- android:key="prefGpodderSettings"
- android:title="@string/gpodnet_main_label">
+ <Preference
+ android:key="prefScreenStorage"
+ android:title="@string/storage_pref"
+ android:icon="?attr/storage" />
- <PreferenceScreen
- android:key="pref_gpodnet_authenticate"
- android:title="@string/pref_gpodnet_authenticate_title"
- android:summary="@string/pref_gpodnet_authenticate_sum">
- <intent android:action=".activity.gpoddernet.GpodnetAuthenticationActivity"/>
- </PreferenceScreen>
- <Preference
- android:key="pref_gpodnet_setlogin_information"
- android:title="@string/pref_gpodnet_setlogin_information_title"
- android:summary="@string/pref_gpodnet_setlogin_information_sum"/>
- <Preference
- android:key="pref_gpodnet_sync"
- android:title="@string/pref_gpodnet_sync_changes_title"
- android:summary="@string/pref_gpodnet_sync_changes_sum"/>
- <Preference
- android:key="pref_gpodnet_force_full_sync"
- android:title="@string/pref_gpodnet_full_sync_title"
- android:summary="@string/pref_gpodnet_full_sync_sum"/>
- <Preference
- android:key="pref_gpodnet_logout"
- android:title="@string/pref_gpodnet_logout_title"/>
- <Preference
- android:key="pref_gpodnet_hostname"
- android:title="@string/pref_gpodnet_sethostname_title"/>
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:key="pref_gpodnet_notifications"
- android:title="@string/pref_gpodnet_notifications_title"
- android:summary="@string/pref_gpodnet_notifications_sum"
- android:defaultValue="true"/>
- </PreferenceScreen>
- </PreferenceCategory>
- <PreferenceCategory android:title="@string/storage_pref">
- <Preference
- android:title="@string/choose_data_directory"
- android:key="prefChooseDataDir"/>
- <ListPreference
- android:entryValues="@array/image_cache_size_values"
- android:entries="@array/image_cache_size_options"
- android:title="@string/pref_image_cache_size_title"
- android:key="prefImageCacheSize"
- android:summary="@string/pref_image_cache_size_sum"
- android:defaultValue="100"/>
- </PreferenceCategory>
- <PreferenceCategory android:title="@string/other_pref">
- <Preference
- android:key="prefOpmlExport"
- android:title="@string/opml_export_label"/>
- <Preference
- android:key="prefHtmlExport"
- android:title="@string/html_export_label"/>
- <Preference
+ <Preference
android:key="statistics"
- android:title="@string/statistics_label"/>
- </PreferenceCategory>
+ android:title="@string/statistics_label"
+ android:icon="?attr/statistics" />
+
<PreferenceCategory android:title="@string/project_pref">
<Preference
android:key="prefFaq"
- android:title="@string/pref_faq"/>
+ android:title="@string/pref_faq"
+ android:icon="?attr/ic_question_answer" />
+
<Preference
android:key="prefKnownIssues"
- android:title="@string/pref_known_issues"/>
+ android:title="@string/pref_known_issues"
+ android:icon="?attr/ic_known_issues" />
<Preference
android:key="prefSendCrashReport"
android:title="@string/crash_report_title"
- android:summary="@string/crash_report_sum"/>
+ android:summary="@string/crash_report_sum"
+ android:icon="?attr/ic_bug" />
<Preference
android:key="prefAbout"
- android:title="@string/about_pref"/>
- </PreferenceCategory>
-
- <PreferenceCategory android:title="@string/experimental_pref">
- <de.danoeh.antennapod.preferences.SwitchCompatPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefCast"
- android:summary="@string/pref_cast_message"
- android:title="@string/pref_cast_title"/>
+ android:title="@string/about_pref"
+ android:icon="?attr/action_about" />
</PreferenceCategory>
-
</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_autodownload.xml b/app/src/main/res/xml/preferences_autodownload.xml
new file mode 100644
index 000000000..b5e3182f0
--- /dev/null
+++ b/app/src/main/res/xml/preferences_autodownload.xml
@@ -0,0 +1,41 @@
+<?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"
+ xmlns:search="http://schemas.android.com/apk/com.bytehamster.lib.preferencesearch">
+
+ <de.danoeh.antennapod.preferences.MasterSwitchPreference
+ android:key="prefEnableAutoDl"
+ android:title="@string/pref_automatic_download_title"
+ search:summary="@string/pref_automatic_download_sum"
+ android:defaultValue="false"/>
+ <ListPreference
+ android:defaultValue="25"
+ android:entries="@array/episode_cache_size_entries"
+ android:key="prefEpisodeCacheSize"
+ android:title="@string/pref_episode_cache_title"
+ android:entryValues="@array/episode_cache_size_values"
+ app:useStockLayout="true"/>
+ <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"/>
+ <SwitchPreference
+ android:key="prefEnableAutoDownloadOnBattery"
+ android:title="@string/pref_automatic_download_on_battery_title"
+ android:summary="@string/pref_automatic_download_on_battery_sum"
+ android:defaultValue="true"/>
+ <SwitchPreference
+ android:key="prefEnableAutoDownloadOnMobile"
+ android:title="@string/pref_autodl_allow_on_mobile_title"
+ android:summary="@string/pref_autodl_allow_on_mobile_sum"
+ android:defaultValue="false"/>
+ <SwitchPreference
+ android:key="prefEnableAutoDownloadWifiFilter"
+ android:title="@string/pref_autodl_wifi_filter_title"
+ android:summary="@string/pref_autodl_wifi_filter_sum"/>
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_flattr.xml b/app/src/main/res/xml/preferences_flattr.xml
new file mode 100644
index 000000000..6b4c38a0b
--- /dev/null
+++ b/app/src/main/res/xml/preferences_flattr.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceScreen
+ android:key="pref_flattr_authenticate"
+ android:summary="@string/pref_flattr_auth_sum"
+ android:title="@string/pref_flattr_auth_title">
+ <intent android:action=".activities.FlattrAuthActivity"/>
+ </PreferenceScreen>
+
+ <Preference
+ android:key="prefAutoFlattrPrefs"
+ android:summary="@string/pref_auto_flattr_sum"
+ android:title="@string/pref_auto_flattr_title"/>
+ <Preference
+ android:key="prefRevokeAccess"
+ android:summary="@string/pref_revokeAccess_sum"
+ android:title="@string/pref_revokeAccess_title"/>
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_gpodder.xml b/app/src/main/res/xml/preferences_gpodder.xml
new file mode 100644
index 000000000..5789f5f84
--- /dev/null
+++ b/app/src/main/res/xml/preferences_gpodder.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <PreferenceScreen
+ android:key="pref_gpodnet_authenticate"
+ android:title="@string/pref_gpodnet_authenticate_title"
+ android:summary="@string/pref_gpodnet_authenticate_sum">
+ <intent android:action=".activity.gpoddernet.GpodnetAuthenticationActivity"/>
+ </PreferenceScreen>
+ <Preference
+ android:key="pref_gpodnet_setlogin_information"
+ android:title="@string/pref_gpodnet_setlogin_information_title"
+ android:summary="@string/pref_gpodnet_setlogin_information_sum"/>
+ <Preference
+ android:key="pref_gpodnet_sync"
+ android:title="@string/pref_gpodnet_sync_changes_title"
+ android:summary="@string/pref_gpodnet_sync_changes_sum"/>
+ <Preference
+ android:key="pref_gpodnet_force_full_sync"
+ android:title="@string/pref_gpodnet_full_sync_title"
+ android:summary="@string/pref_gpodnet_full_sync_sum"/>
+ <Preference
+ android:key="pref_gpodnet_logout"
+ android:title="@string/pref_gpodnet_logout_title"/>
+ <Preference
+ android:key="pref_gpodnet_hostname"
+ android:title="@string/pref_gpodnet_sethostname_title"/>
+ <SwitchPreference
+ android:key="pref_gpodnet_notifications"
+ android:title="@string/pref_gpodnet_notifications_title"
+ android:summary="@string/pref_gpodnet_notifications_sum"
+ android:defaultValue="true"/>
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_integrations.xml b/app/src/main/res/xml/preferences_integrations.xml
new file mode 100644
index 000000000..c0fd299ec
--- /dev/null
+++ b/app/src/main/res/xml/preferences_integrations.xml
@@ -0,0 +1,16 @@
+<?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">
+
+ <Preference
+ android:key="prefFlattrSettings"
+ android:title="@string/flattr_label"
+ android:summary="@string/flattr_summary" />
+
+ <Preference
+ android:key="prefGpodderSettings"
+ android:title="@string/gpodnet_main_label"
+ android:summary="@string/gpodnet_summary" />
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_network.xml b/app/src/main/res/xml/preferences_network.xml
new file mode 100644
index 000000000..c37a99465
--- /dev/null
+++ b/app/src/main/res/xml/preferences_network.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:numberpicker="http://schemas.android.com/apk/de.danoeh.antennapod"
+ xmlns:search="http://schemas.android.com/apk/com.bytehamster.lib.preferencesearch">
+ <PreferenceCategory android:title="@string/automation">
+ <Preference
+ android:key="prefAutoUpdateIntervall"
+ android:summary="@string/pref_autoUpdateIntervallOrTime_sum"
+ android:title="@string/pref_autoUpdateIntervallOrTime_title"/>
+ <Preference
+ android:summary="@string/pref_automatic_download_sum"
+ android:key="prefAutoDownloadSettings"
+ android:title="@string/pref_automatic_download_title"
+ search:ignore="true" />
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/download_pref_details">
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefMobileUpdate"
+ android:summary="@string/pref_mobileUpdate_sum"
+ android:title="@string/pref_mobileUpdate_title"/>
+ <de.danoeh.antennapod.preferences.NumberPickerPreference
+ android:defaultValue="4"
+ numberpicker:minValue="1"
+ numberpicker:maxValue="50"
+ android:key="prefParallelDownloads"
+ android:title="@string/pref_parallel_downloads_title"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefShowDownloadReport"
+ android:summary="@string/pref_showDownloadReport_sum"
+ android:title="@string/pref_showDownloadReport_title"/>
+ <Preference
+ android:key="prefProxy"
+ android:summary="@string/pref_proxy_sum"
+ android:title="@string/pref_proxy_title"/>
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml
new file mode 100644
index 000000000..9182df600
--- /dev/null
+++ b/app/src/main/res/xml/preferences_playback.xml
@@ -0,0 +1,130 @@
+<?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">
+
+ <PreferenceCategory android:title="@string/interruptions">
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefPauseOnHeadsetDisconnect"
+ android:summary="@string/pref_pauseOnDisconnect_sum"
+ android:title="@string/pref_pauseOnHeadsetDisconnect_title"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:dependency="prefPauseOnHeadsetDisconnect"
+ android:key="prefUnpauseOnHeadsetReconnect"
+ android:summary="@string/pref_unpauseOnHeadsetReconnect_sum"
+ android:title="@string/pref_unpauseOnHeadsetReconnect_title"/>
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:dependency="prefPauseOnHeadsetDisconnect"
+ android:key="prefUnpauseOnBluetoothReconnect"
+ android:summary="@string/pref_unpauseOnBluetoothReconnect_sum"
+ android:title="@string/pref_unpauseOnBluetoothReconnect_title"/>
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefPauseForFocusLoss"
+ android:summary="@string/pref_pausePlaybackForFocusLoss_sum"
+ android:title="@string/pref_pausePlaybackForFocusLoss_title"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefResumeAfterCall"
+ android:summary="@string/pref_resumeAfterCall_sum"
+ android:title="@string/pref_resumeAfterCall_title"/>
+ <ListPreference
+ android:defaultValue="stop"
+ android:entries="@array/video_background_behavior_options"
+ 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"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/buttons">
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefHardwareForwardButtonSkips"
+ android:summary="@string/pref_hardwareForwardButtonSkips_sum"
+ android:title="@string/pref_hardwareForwardButtonSkips_title"/>
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefHardwarePreviousButtonRestarts"
+ android:summary="@string/pref_hardwarePreviousButtonRestarts_sum"
+ android:title="@string/pref_hardwarePreviousButtonRestarts_title"/>
+ <Preference
+ android:key="prefPlaybackFastForwardDeltaLauncher"
+ android:summary="@string/pref_fast_forward_sum"
+ android:title="@string/pref_fast_forward"/>
+ <Preference
+ android:key="prefPlaybackRewindDeltaLauncher"
+ android:summary="@string/pref_rewind_sum"
+ android:title="@string/pref_rewind"/>
+ <Preference
+ android:key="prefPlaybackSpeedLauncher"
+ android:summary="@string/pref_playback_speed_sum"
+ android:title="@string/pref_playback_speed_title"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/queue_label">
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ 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"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefFollowQueue"
+ android:summary="@string/pref_followQueue_sum"
+ android:title="@string/pref_followQueue_title"/>
+ <ListPreference
+ android:defaultValue="30"
+ android:entries="@array/smart_mark_as_played_values"
+ 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"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefSkipKeepsEpisode"
+ android:summary="@string/pref_skip_keeps_episodes_sum"
+ android:title="@string/pref_skip_keeps_episodes_title"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/media_player">
+ <ListPreference
+ android:defaultValue="sonic"
+ android:entries="@array/media_player_options"
+ android:key="prefMediaPlayer"
+ android:title="@string/media_player"
+ android:summary="@string/pref_media_player_message"
+ android:entryValues="@array/media_player_values"
+ app:useStockLayout="true"/>
+ </PreferenceCategory>
+
+ <PreferenceCategory android:title="@string/experimental_pref">
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefCast"
+ android:summary="@string/pref_cast_message"
+ android:title="@string/pref_cast_title"/>
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_storage.xml b/app/src/main/res/xml/preferences_storage.xml
new file mode 100644
index 000000000..fe48cc99c
--- /dev/null
+++ b/app/src/main/res/xml/preferences_storage.xml
@@ -0,0 +1,43 @@
+<?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">
+
+ <Preference
+ android:title="@string/choose_data_directory"
+ android:key="prefChooseDataDir"/>
+ <ListPreference
+ android:entryValues="@array/image_cache_size_values"
+ android:entries="@array/image_cache_size_options"
+ android:title="@string/pref_image_cache_size_title"
+ android:key="prefImageCacheSize"
+ android:summary="@string/pref_image_cache_size_sum"
+ android:defaultValue="100"/>
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefAutoDelete"
+ android:summary="@string/pref_auto_delete_sum"
+ android:title="@string/pref_auto_delete_title"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefFavoriteKeepsEpisode"
+ android:summary="@string/pref_favorite_keeps_episodes_sum"
+ android:title="@string/pref_favorite_keeps_episodes_title"/>
+
+ <PreferenceCategory android:title="@string/import_export_pref">
+ <Preference
+ android:key="prefOpmlExport"
+ android:title="@string/opml_export_label"/>
+ <Preference
+ android:key="prefOpmlImport"
+ android:title="@string/opml_import_label"/>
+ <Preference
+ android:key="prefHtmlExport"
+ android:title="@string/html_export_label"/>
+ <Preference
+ android:key="importExport"
+ android:title="@string/import_export"/>
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml
new file mode 100644
index 000000000..1d970a5f7
--- /dev/null
+++ b/app/src/main/res/xml/preferences_user_interface.xml
@@ -0,0 +1,70 @@
+<?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">
+
+ <PreferenceCategory android:title="@string/appearance">
+ <ListPreference
+ android:entryValues="@array/theme_values"
+ android:entries="@array/theme_options"
+ android:title="@string/pref_set_theme_title"
+ android:key="prefTheme"
+ android:summary="@string/pref_set_theme_sum"
+ android:defaultValue="0"
+ app:useStockLayout="true"/>
+ <Preference
+ android:key="prefHiddenDrawerItems"
+ android:summary="@string/pref_nav_drawer_items_sum"
+ android:title="@string/pref_nav_drawer_items_title"/>
+ <ListPreference
+ android:entryValues="@array/nav_drawer_feed_order_values"
+ android:entries="@array/nav_drawer_feed_order_options"
+ 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"/>
+ <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"/>
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/external_elements">
+ <SwitchPreference
+ android:defaultValue="false"
+ android:enabled="true"
+ android:key="prefExpandNotify"
+ android:summary="@string/pref_expandNotify_sum"
+ android:title="@string/pref_expandNotify_title"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefPersistNotify"
+ android:summary="@string/pref_persistNotify_sum"
+ android:title="@string/pref_persistNotify_title"/>
+ <Preference
+ android:key="prefCompactNotificationButtons"
+ android:summary="@string/pref_compact_notification_buttons_sum"
+ android:title="@string/pref_compact_notification_buttons_title"/>
+ <SwitchPreference
+ android:defaultValue="true"
+ android:enabled="true"
+ android:key="prefLockscreenBackground"
+ android:summary="@string/pref_lockscreen_background_sum"
+ android:title="@string/pref_lockscreen_background_title"/>
+ </PreferenceCategory>
+ <PreferenceCategory android:title="@string/behavior">
+ <ListPreference
+ android:entryValues="@array/back_button_behavior_values"
+ android:entries="@array/back_button_behavior_options"
+ 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"/>
+ </PreferenceCategory>
+</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
index 400727c46..cc3a24e62 100644
--- a/app/src/main/templates/about.html
+++ b/app/src/main/templates/about.html
@@ -8,30 +8,52 @@
font-family: 'Roboto-Light';
src: url('file:///android_asset/Roboto-Light.ttf');
}
+
+ html, body {
+ background: @background@;
+ margin: 0;
+ padding: 0;
+ }
* {
- color: %s;
+ color: @fontcolor@;
font-family: roboto-Light;
font-size: 12pt;
}
- header {
+ img#logo {
display: block;
margin-left: auto;
margin-right: auto;
- padding-bottom: 500px;
+ max-height: 200px;
+ max-height: 50vh;
+ max-width: 100%;
+ height: auto;
+ width: auto;
}
-
- versiontag {
- color: gray;
+
+ 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 {
@@ -44,67 +66,117 @@
<title>About AntennaPod</title>
</head>
<body>
-<div id="header" align="center">
- <img src="file:///android_asset/logo.png" alt="Logo" width="100px" height="100px"/>
-
- <p>AntennaPod, Version @versionname@</p>
- <p>Commit: @commit@</p>
+<div id="logobackground">
+<img id="logo" src="file:///android_asset/logo.png" alt="Logo"/>
+</div>
- <p>Created by Daniel Oeh</p>
+<h1>AntennaPod</h1>
- <p>Copyright &copy; 2012-@year@ AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a></p>
+<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>
- <p>Licensed under the MIT License <a href="LICENSE.txt">(View)</a></p>
+<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>
</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>flattr4j <a href="http://www.shredzone.org/projects/flattr4j/wiki">(Link)</a></h2>
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/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java b/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
index 7b07d3f84..eab085479 100644
--- a/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
+++ b/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
@@ -20,6 +20,7 @@ import android.support.v4.view.accessibility.AccessibilityEventCompat;
import android.support.v7.app.MediaRouteControllerDialog;
import android.support.v7.graphics.Palette;
import android.support.v7.media.MediaRouter;
+import android.support.v7.widget.AppCompatImageView;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
@@ -34,16 +35,17 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
public class CustomMRControllerDialog extends MediaRouteControllerDialog {
public static final String TAG = "CustomMRContrDialog";
@@ -59,7 +61,7 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
private boolean viewsCreated = false;
- private Subscription fetchArtSubscription;
+ private Disposable fetchArtSubscription;
private MediaControllerCompat mediaController;
private MediaControllerCompat.Callback mediaControllerCallback;
@@ -68,7 +70,7 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
this(context, 0);
}
- public CustomMRControllerDialog(Context context, int theme) {
+ private CustomMRControllerDialog(Context context, int theme) {
super(context, theme);
mediaRouter = MediaRouter.getInstance(getContext());
token = mediaRouter.getMediaSessionToken();
@@ -203,7 +205,7 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
* http://stackoverflow.com/questions/18077325/scale-image-to-fill-imageview-width-and-keep-aspect-ratio
*/
if (landscape) {
- artView = new ImageView(getContext()) {
+ artView = new AppCompatImageView(getContext()) {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredWidth = widthMeasureSpec;
@@ -234,7 +236,7 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
MarginLayoutParamsCompat.setMarginStart(artParams,
getContext().getResources().getDimensionPixelSize(R.dimen.media_router_controller_playback_control_horizontal_spacing));
} else {
- artView = new ImageView(getContext()) {
+ artView = new AppCompatImageView(getContext()) {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int desiredHeight = heightMeasureSpec;
@@ -326,7 +328,7 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
@Override
public void onDetachedFromWindow() {
if (fetchArtSubscription != null) {
- fetchArtSubscription.unsubscribe();
+ fetchArtSubscription.dispose();
fetchArtSubscription = null;
}
super.onDetachedFromWindow();
@@ -395,11 +397,11 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
}
if (fetchArtSubscription != null) {
- fetchArtSubscription.unsubscribe();
+ fetchArtSubscription.dispose();
}
fetchArtSubscription = Observable.fromCallable(() -> fetchArt(description))
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
fetchArtSubscription = null;
@@ -462,10 +464,10 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
} else if (iconUri != null) {
try {
art = Glide.with(getContext().getApplicationContext())
- .load(iconUri.toString())
.asBitmap()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
+ .load(iconUri.toString())
+ .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
+ .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get();
} catch (InterruptedException | ExecutionException e) {
Log.e(TAG, "Image art load failed", e);
diff --git a/artwork/feature-graphic.svg b/artwork/feature-graphic.svg
new file mode 100644
index 000000000..69e7902ec
--- /dev/null
+++ b/artwork/feature-graphic.svg
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1024"
+ height="500"
+ viewBox="0 0 270.93333 132.29167"
+ version="1.1"
+ id="svg8"
+ inkscape:version="0.92.2 2405546, 2018-03-11"
+ sodipodi:docname="feature-graphic.svg"
+ inkscape:export-filename="../app/src/main/play/en-US/listing/featureGraphic/feature-graphic.png"
+ inkscape:export-xdpi="96"
+ inkscape:export-ydpi="96">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#000000"
+ borderopacity="1"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="0.62493324"
+ inkscape:cx="475.1951"
+ inkscape:cy="288.63643"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="px"
+ inkscape:window-width="1600"
+ inkscape:window-height="835"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ borderlayer="true"
+ inkscape:showpageshadow="false" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-164.70832)">
+ <path
+ style="fill:#42a5f5;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 241.57724,93.688693 153.83631,322.70237 129.26786,237.27975 Z"
+ id="path4549"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="fill:#90caf9;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 129.26786,237.27975 v 0 l 77.66392,-84.70466 -101.47643,-10.54534 z"
+ id="path4547"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#64b5f6;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 111.88095,149.96725 v 0 L 179.16071,374.10712 67.657738,209.68749 Z"
+ id="path4545"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#0277bd;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M -5.2916667,329.12796 -5.6696427,217.24701 127.37797,308.71725 Z"
+ id="path4537"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="fill:#90caf9;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 71.059523,210.44344 -3.401785,-0.75595 -79.741849,5.15409 173.172371,131.21195 z"
+ id="path4539"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
+ <path
+ style="fill:#2196f3;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -5.6696427,217.24701 v 0 L 71.059523,210.44344 110.36905,157.90475 -14.363095,139.00594 Z"
+ id="path4541"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccc" />
+ <path
+ style="fill:#2196f3;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 293.68749,172.26785 -76.35118,94.87201 58.5863,58.9643 z"
+ id="path4553"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="fill:#64b5f6;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="M 184.64136,236.90177 378.4505,95.09124 217.33631,267.13986 Z"
+ id="path4555"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ style="fill:#1976d2;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 216.58035,147.69939 77.48513,10.58333 -109.42412,78.61905 z"
+ id="path4557"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ id="path4603"
+ d="m 131.14882,195.24411 c -6.94441,0 -12.5,5.55559 -12.5,12.5 v 9.72225 a 4.1666667,4.1666667 0 0 0 4.16667,4.16667 h 4.16666 V 210.5219 h -5.55553 v -2.77779 a 9.722222,9.722222 0 0 1 9.7222,-9.72221 9.722222,9.722222 0 0 1 9.72226,9.72221 v 2.77779 h -5.55559 v 11.11113 h 4.16666 a 4.1666667,4.1666667 0 0 0 4.16667,-4.16667 v -9.72225 c 0,-6.94441 -5.59722,-12.5 -12.5,-12.5 z"
+ inkscape:connector-curvature="0"
+ style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.38888884" />
+ <path
+ id="path4614"
+ d="m 131.1488,253.95301 a 3.9473708,3.9473708 0 0 1 3.94736,3.94737 v 7.89475 a 3.9473708,3.9473708 0 0 1 -3.94736,3.94736 3.9473708,3.9473708 0 0 1 -3.94737,-3.94736 v -7.89475 a 3.9473708,3.9473708 0 0 1 3.94737,-3.94737 m 9.21056,11.84212 c 0,4.64474 -3.43425,8.47365 -7.89474,9.11841 v 4.03947 h -2.6316 v -4.03947 c -4.46054,-0.64476 -7.89474,-4.47367 -7.89474,-9.11841 h 2.6316 a 6.5789516,6.5789516 0 0 0 6.57892,6.57891 6.5789516,6.5789516 0 0 0 6.57896,-6.57891 z"
+ inkscape:connector-curvature="0"
+ style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.31579018" />
+ <path
+ id="path4625"
+ d="m 211.96825,213.93341 a 3.5025768,3.5025768 0 0 1 3.50256,3.50257 c 0,1.8959 -1.57454,3.50263 -3.50256,3.50263 -1.89591,0 -3.50257,-1.60673 -3.50257,-3.50263 a 3.5025768,3.5025768 0 0 1 3.50257,-3.50257 m -3.50257,-17.99487 a 25.000044,25.000044 0 0 1 25,25.00007 h -4.54692 a 20.45312,20.45312 0 0 0 -20.45308,-20.45315 v -4.54692 m 0,9.09384 a 15.906197,15.906197 0 0 1 15.90617,15.90623 h -4.54692 A 11.359274,11.359274 0 0 0 208.46568,209.5793 Z"
+ inkscape:connector-curvature="0"
+ style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.60668647" />
+ <path
+ id="path4655"
+ d="m 163.79069,220.93857 h 20.58824 v -2.94119 h -20.58824 m 20.58824,-13.23528 h -5.88239 v -8.82353 h -8.82352 v 8.82353 h -5.88233 l 10.29409,10.29408 z"
+ inkscape:connector-curvature="0"
+ style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.47058702" />
+ <path
+ style="fill:#1565c0;fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:0.60962572"
+ d="m 184.64136,236.90177 87.50149,77.8631 -118.30654,7.9375 z"
+ id="path4551"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc" />
+ <path
+ id="path4636"
+ d="m 227.91014,264.36965 v -4.8611 a 1.3888889,1.3888889 0 0 0 -1.38887,-1.38887 H 209.8546 a 1.3888889,1.3888889 0 0 0 -1.38892,1.38887 v 13.88893 a 1.3888889,1.3888889 0 0 0 1.38892,1.38887 h 16.66667 a 1.3888889,1.3888889 0 0 0 1.38887,-1.38887 v -4.86116 l 5.55554,5.55559 v -15.27779 z"
+ inkscape:connector-curvature="0"
+ style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.38888884" />
+ <path
+ id="path4666"
+ d="m 174.08479,263.04394 a 3.4090929,3.4090929 0 0 0 -3.40909,3.40909 3.4090929,3.4090929 0 0 0 3.40909,3.40909 3.4090929,3.4090929 0 0 0 3.40909,-3.40909 3.4090929,3.4090929 0 0 0 -3.40909,-3.40909 m 0,9.09093 a 5.6818216,5.6818216 0 0 1 -5.68179,-5.68184 5.6818216,5.6818216 0 0 1 5.68179,-5.68184 5.6818216,5.6818216 0 0 1 5.68183,5.68184 5.6818216,5.6818216 0 0 1 -5.68183,5.68184 m 0,-14.20459 c -5.68179,0 -10.53407,3.53411 -12.49998,8.52275 1.96591,4.98863 6.81819,8.52271 12.49998,8.52271 5.68183,0 10.53411,-3.53408 12.50002,-8.52271 -1.96591,-4.98864 -6.81819,-8.52275 -12.50002,-8.52275 z"
+ inkscape:connector-curvature="0"
+ style="opacity:0.5;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.13636422;stroke-opacity:1" />
+ <g
+ transform="matrix(10.48853,0,0,10.48853,9.606285,-2891.1165)"
+ id="layer1-3"
+ inkscape:label="Ebene 1">
+ <path
+ sodipodi:nodetypes="ccsccscccsccscccsccsccccccccccccccccccccccccccccc"
+ inkscape:connector-curvature="0"
+ id="path845"
+ d="m 4.3909501,292.40513 v 0.28377 c 1.0037008,0.0216 1.6560447,0.3661 2.2523926,0.96964 0.5963478,0.60354 0.9467125,1.47696 0.9641258,2.5345 h 0.2782303 c 0.00673,-1.19521 -0.4584817,-2.15822 -1.0382469,-2.73639 -0.5797652,-0.57816 -1.3486655,-1.05093 -2.4565018,-1.05152 z m 0,0.82805 v 0.29924 c 0.7443971,0.0312 1.1668558,0.18401 1.6809974,0.70029 0.5141415,0.51628 0.7305748,1.18454 0.7450816,1.96033 h 0.2948245 c -0.00762,-1.06881 -0.4691861,-1.81182 -0.8291591,-2.17052 -0.3599732,-0.35872 -0.8621337,-0.78563 -1.8917444,-0.78934 z m -5.665e-4,0.87009 5.665e-4,0.27491 c 0.3438797,0.006 0.7199551,0.1209 1.0476503,0.48898 0.3276951,0.36807 0.4289716,0.88778 0.4419601,1.32588 h 0.282102 c -0.00135,-0.57972 -0.2415301,-1.20375 -0.5332283,-1.51395 -0.2916983,-0.31021 -0.6385502,-0.57378 -1.2390371,-0.57582 z m 0.024339,1.16492 c -0.3517826,1.2e-4 -0.6368407,0.28543 -0.636666,0.63722 1.22e-4,0.23202 0.1264212,0.44563 0.3296724,0.55757 l -2.6086157,5.39256 0.4463853,0.29041 0.5260374,-1.08194 4.6640899,1.33914 0.071908,0.13941 0.496721,-0.21739 -3.0267779,-5.84062 c 0.2273717,-0.10308 0.3735565,-0.3295 0.3739235,-0.57914 1.749e-4,-0.35179 -0.2848831,-0.6371 -0.6366657,-0.63722 z m -0.022127,1.80822 0.4574483,0.88834 -1.1306216,0.50944 z m 0.5481634,1.06479 0.7876737,1.52889 -2.1401047,-0.9138 z m -1.4486778,0.80427 2.2402234,0.95196 -3.1020179,0.83801 z m 2.438262,1.11679 1.0758604,2.08978 -4.2741251,-1.22466 z"
+ style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.28320846;stroke-opacity:1" />
+ </g>
+ </g>
+</svg>
diff --git a/artwork/ic_launcher.svg b/artwork/ic_launcher.svg
new file mode 100644
index 000000000..cd65ace03
--- /dev/null
+++ b/artwork/ic_launcher.svg
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="48"
+ height="48"
+ viewBox="0 0 12.7 12.7"
+ version="1.1"
+ id="svg8"
+ inkscape:version="0.92.2 2405546, 2018-03-11"
+ sodipodi:docname="ic_launcher_foreground.svg">
+ <defs
+ id="defs2" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11.313709"
+ inkscape:cx="23.22737"
+ inkscape:cy="21.198035"
+ inkscape:document-units="mm"
+ inkscape:current-layer="layer1"
+ showgrid="false"
+ units="px"
+ inkscape:window-width="1600"
+ inkscape:window-height="835"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="1"
+ borderlayer="true"
+ inkscape:showpageshadow="false"
+ inkscape:measure-start="22.5502,25.5372"
+ inkscape:measure-end="22.0529,26.5828" />
+ <metadata
+ id="metadata5">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title></dc:title>
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Ebene 1"
+ inkscape:groupmode="layer"
+ id="layer1"
+ transform="translate(0,-284.29998)">
+ <circle
+ style="opacity:1;fill:#008ab8;fill-opacity:1;stroke:none;stroke-width:0.00865707;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path905"
+ cx="6.2915349"
+ cy="290.91675"
+ r="5.4972386" />
+ <path
+ style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.99999994;stroke-opacity:1"
+ d="m 21.751953,8.4707031 v 1.0019531 c 3.544035,0.076347 5.84744,1.2927418 7.953125,3.4238278 2.105685,2.131087 3.342811,5.215084 3.404297,8.949219 h 0.982422 C 34.11557,17.625445 32.472913,14.225068 30.425781,12.183594 28.378649,10.14212 25.663687,8.4727819 21.751953,8.4707031 Z m 0,2.9238279 v 1.056641 c 2.628442,0.110362 4.12013,0.649714 5.935547,2.472656 1.815417,1.822942 2.579636,4.182587 2.630859,6.921875 h 1.041016 c -0.02691,-3.773934 -1.656681,-6.397467 -2.927734,-7.664062 -1.271054,-1.266596 -3.044166,-2.773995 -6.679688,-2.78711 z m -0.002,3.072266 0.002,0.970703 c 1.214228,0.02268 2.542138,0.426896 3.699219,1.726562 1.157081,1.299667 1.514685,3.134718 1.560547,4.681641 h 0.996093 C 28.003013,19.798749 27.154977,17.595307 26.125,16.5 25.095023,15.404693 23.8703,14.474016 21.75,14.466797 Z m 0.08594,4.113281 c -1.242133,4.62e-4 -2.248664,1.007867 -2.248047,2.25 4.31e-4,0.819298 0.446389,1.573535 1.164062,1.96875 l -9.210937,19.041016 1.576172,1.02539 1.857421,-3.820312 16.46875,4.728516 0.253907,0.492187 1.753906,-0.767578 L 22.763672,22.875 c 0.802842,-0.363971 1.319016,-1.16343 1.320312,-2.044922 6.17e-4,-1.242133 -1.005913,-2.249537 -2.248046,-2.25 z m -0.07813,6.384766 1.615235,3.136718 -3.992188,1.798829 z m 1.935547,3.759765 2.78125,5.398438 -7.55664,-3.226563 z m -5.115234,2.839844 7.910156,3.361328 -10.953125,2.958985 z M 27.1875,35.507812 30.986328,42.886719 15.894531,38.5625 Z"
+ id="path845"
+ transform="matrix(0.26458333,0,0,0.26458333,0,284.29998)"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccsccscccsccscccsccsccccccccccccccccccccccccccccc" />
+ </g>
+</svg>
diff --git a/build.gradle b/build.gradle
index d0fee89c9..838bc7674 100644
--- a/build.gradle
+++ b/build.gradle
@@ -4,12 +4,11 @@ buildscript {
repositories {
jcenter()
mavenCentral()
+ google()
}
dependencies {
- classpath "com.android.tools.build:gradle:2.3.3"
- classpath "me.tatarka:gradle-retrolambda:3.7.0"
- classpath "me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2"
- classpath "com.github.triplet.gradle:play-publisher:1.1.4"
+ classpath 'com.android.tools.build:gradle:3.1.0'
+ classpath "com.github.triplet.gradle:play-publisher:1.2.0"
// Exclude the version that the android plugin depends on.
configurations.classpath.exclude group: "com.android.tools.external.lombok"
}
@@ -17,6 +16,7 @@ buildscript {
allprojects {
repositories {
+ google()
jcenter()
}
}
@@ -28,48 +28,51 @@ project.ext.preDexLibs = !project.hasProperty("disablePreDex")
subprojects {
project.plugins.whenPluginAdded { plugin ->
- if ("com.android.build.gradle.AppPlugin".equals(plugin.class.name)) {
+ if ("com.android.build.gradle.AppPlugin" == plugin.class.name) {
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
- } else if ("com.android.build.gradle.LibraryPlugin".equals(plugin.class.name)) {
+ } else if ("com.android.build.gradle.LibraryPlugin" == plugin.class.name) {
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
}
}
}
project.ext {
- compileSdkVersion = 25
- buildToolsVersion = "25.0.3"
- minSdkVersion = 10
- targetSdkVersion = 25
+ compileSdkVersion = 26
+ buildToolsVersion = "27.0.3"
+ minSdkVersion = 14
+ targetSdkVersion = 26
- supportVersion = "25.3.1"
+ supportVersion = "27.1.1"
+ awaitilityVersion = "3.1.2"
commonsioVersion = "2.5"
commonslangVersion = "3.6"
+ commonstextVersion = "1.3"
eventbusVersion = "2.4.0"
flattr4jVersion = "2.14"
- glideVersion = "3.8.0"
- glideOkhttpIntegrationVersion = "1.5.0"
+ glideVersion = "4.8.0"
+ glideOkhttpIntegrationVersion = "4.8.0"
iconifyVersion = "2.2.2"
- jsoupVersion = "1.10.3"
+ jsoupVersion = "1.11.2"
materialDialogsVersion = "0.9.0.2"
okhttpVersion = "3.9.0"
- okioVersion = "1.13.0"
- recyclerviewFlexibledividerVersion = "1.2.6"
+ okioVersion = "1.14.0"
+ recyclerviewFlexibledividerVersion = "1.4.0"
robotiumSoloVersion = "5.6.3"
- rxAndroidVersion = "1.2.1"
- rxJavaVersion = "1.3.2"
- rxJavaRulesVersion = "1.3.2.0"
- triangleLabelViewVersion = "1.1.0"
+ rxAndroidVersion = "2.1.0"
+ rxJavaVersion = "2.2.2"
+ rxJavaRulesVersion = "1.3.3.0"
+ triangleLabelViewVersion = "1.1.2"
+ exoPlayerVersion = "2.7.3"
audioPlayerVersion = "v1.0.17"
castCompanionLibVer = "2.9.1"
playServicesVersion = "8.4.0"
- wearableSupportVersion = "2.0.3"
+ wearableSupportVersion = "2.2.0"
}
-task wrapper(type: Wrapper) {
- gradleVersion = "4.2"
+wrapper {
+ gradleVersion = "4.10.2"
}
// free build hack: common functions
diff --git a/ci/wait_for_emulator.sh b/ci/wait_for_emulator.sh
deleted file mode 100644
index 317883878..000000000
--- a/ci/wait_for_emulator.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-
-bootanim=""
-failcounter=0
-until [[ "$bootanim" =~ "stopped" ]]; do
- bootanim=`adb -e shell getprop init.svc.bootanim 2>&1`
- echo "$bootanim"
- if [[ "$bootanim" =~ "not found" ]]; then
- let "failcounter += 1"
- if [[ $failcounter -gt 3 ]]; then
- echo "Failed to start emulator"
- exit 1
- fi
- fi
- sleep 1
-done
-echo "Done"
diff --git a/circle.yml b/circle.yml
deleted file mode 100644
index 01055725a..000000000
--- a/circle.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-general:
- artifacts:
- - app/build/outputs/apk
-machine:
- environment:
- GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
- java:
- version: oraclejdk8
-dependencies:
- cache_directories:
- - ~/.android
- - ~/android
- pre:
- - echo y | android update sdk --no-ui --all --filter "tool,extra-android-m2repository,extra-android-support,extra-google-google_play_services,extra-google-m2repository,android-25"
- - echo y | android update sdk --no-ui --all --filter "build-tools-25.0.3"
- override:
- - echo override dependencies
-
-test:
- override:
- - ./gradlew assembleDebug -PdisablePreDex:
- timeout: 1800
diff --git a/contributers.template.py b/contributers.template.py
index 3127baa0c..0f4d78698 100755
--- a/contributers.template.py
+++ b/contributers.template.py
@@ -6,7 +6,15 @@ TRANSIFEX_USER = ""
TRANSIFEX_PW = ""
print('DEVELOPERS\n==========\n')
-p = subprocess.Popen("git log --format='%aN' | sort -fu", shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
+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='')
@@ -17,6 +25,7 @@ language_codes = {
"af": "Afrikaans",
"ak_GH": "Akan (Ghana)",
"ak": "Akan",
+ "ast_ES": "Asturian (Spain)",
"sq_AL": "Albanian (Albania)",
"sq": "Albanian",
"am_ET": "Amharic (Ethiopia)",
diff --git a/core/build.gradle b/core/build.gradle
index f70083ec1..588d3b163 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -1,5 +1,4 @@
apply plugin: "com.android.library"
-apply plugin: "me.tatarka.retrolambda"
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -11,7 +10,7 @@ android {
versionCode 1
versionName "1.0"
testApplicationId "de.danoeh.antennapod.core.tests"
- testInstrumentationRunner "de.danoeh.antennapod.core.tests.AntennaPodTestRunner"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
@@ -30,11 +29,13 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
- publishNonDefault true
+ flavorDimensions "market"
productFlavors {
free {
+ dimension "market"
}
play {
+ dimension "market"
}
}
@@ -46,36 +47,57 @@ repositories {
}
dependencies {
- compile "com.android.support:support-v4:$supportVersion"
- compile "com.android.support:appcompat-v7:$supportVersion"
- compile "org.apache.commons:commons-lang3:$commonslangVersion"
- compile ("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
+ implementation "com.android.support:support-v4:$supportVersion"
+ implementation "com.android.support:appcompat-v7:$supportVersion"
+ implementation "com.android.support:preference-v14:$supportVersion"
+ implementation "com.android.support:percent:$supportVersion"
+ implementation "org.apache.commons:commons-lang3:$commonslangVersion"
+ implementation "org.apache.commons:commons-text:$commonstextVersion"
+ implementation ("org.shredzone.flattr4j:flattr4j-core:$flattr4jVersion") {
exclude group: "org.json", module: "json"
}
- compile "commons-io:commons-io:$commonsioVersion"
- compile "com.jayway.android.robotium:robotium-solo:$robotiumSoloVersion"
- compile "org.jsoup:jsoup:$jsoupVersion"
- compile "com.github.bumptech.glide:glide:$glideVersion"
- compile "com.github.bumptech.glide:okhttp3-integration:$glideOkhttpIntegrationVersion@aar"
- compile "com.squareup.okhttp3:okhttp:$okhttpVersion"
- compile "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
- compile "com.squareup.okio:okio:$okioVersion"
- compile "com.nineoldandroids:library:2.4.0"
- compile "de.greenrobot:eventbus:$eventbusVersion"
- compile "io.reactivex:rxandroid:$rxAndroidVersion"
+ 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"
+ implementation "com.github.bumptech.glide:okhttp3-integration:$glideOkhttpIntegrationVersion@aar"
+ implementation "com.squareup.okhttp3:okhttp:$okhttpVersion"
+ implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion"
+ implementation "com.squareup.okio:okio:$okioVersion"
+ implementation "de.greenrobot:eventbus:$eventbusVersion"
+ implementation "io.reactivex.rxjava2:rxandroid:$rxAndroidVersion"
+ implementation "io.reactivex.rxjava2:rxjava:$rxJavaVersion"
+ implementation "org.awaitility:awaitility:$awaitilityVersion"
- compile "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
+ implementation "com.google.android.exoplayer:exoplayer:$exoPlayerVersion"
+ implementation "com.github.AntennaPod:AntennaPod-AudioPlayer:$audioPlayerVersion"
// Add casting features
// free build hack: skip some dependencies
if (!doFreeBuild()) {
- playCompile "com.google.android.libraries.cast.companionlibrary:ccl:$castCompanionLibVer"
- compile "com.android.support:mediarouter-v7:$supportVersion"
- playCompile "com.google.android.gms:play-services-cast:$playServicesVersion"
- compile "com.google.android.support:wearable:$wearableSupportVersion"
+ playApi "com.google.android.libraries.cast.companionlibrary:ccl:$castCompanionLibVer"
+ api "com.android.support:mediarouter-v7:$supportVersion"
+ playApi "com.google.android.gms:play-services-cast:$playServicesVersion"
+ api "com.google.android.support:wearable:$wearableSupportVersion"
} else {
System.out.println("core: free build hack, skipping some dependencies")
}
+
+ testImplementation 'junit:junit:4.12'
+ androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
+ androidTestImplementation 'com.android.support.test:runner:1.0.2'
+ androidTestImplementation 'com.android.support.test:rules:1.0.2'
+
+}
+
+tasks.withType(Test) {
+ testLogging {
+ exceptionFormat "full"
+ events "skipped", "passed", "failed"
+ showStandardStreams true
+ displayGranularity 2
+ }
}
allprojects {
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedImageMother.java b/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedImageMother.java
deleted file mode 100644
index f240c870e..000000000
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedImageMother.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-public class FeedImageMother {
-
- public static FeedImage anyFeedImage() {
- return new FeedImage(0, "image", null, "http://example.com/picture", false);
- }
-
-}
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/AntennaPodTestRunner.java b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/AntennaPodTestRunner.java
deleted file mode 100644
index fbb5459d4..000000000
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/AntennaPodTestRunner.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.danoeh.antennapod.core.tests;
-
-import android.test.InstrumentationTestRunner;
-import android.test.suitebuilder.TestSuiteBuilder;
-import junit.framework.TestSuite;
-
-public class AntennaPodTestRunner extends InstrumentationTestRunner {
-
- @Override
- public TestSuite getAllTests() {
- return new TestSuiteBuilder(AntennaPodTestRunner.class)
- .includeAllPackagesUnderHere()
- .build();
- }
-} \ No newline at end of file
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/service/download/DownloadServiceTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/service/download/DownloadServiceTest.java
deleted file mode 100644
index 94cfb3278..000000000
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/service/download/DownloadServiceTest.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package de.danoeh.antennapod.core.tests.util.service.download;
-
-import android.test.AndroidTestCase;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.service.download.DownloadService;
-
-public class DownloadServiceTest extends AndroidTestCase {
-
- public void testRemoveDuplicateImages() {
- List<FeedItem> items = new ArrayList<>();
- for (int i = 0; i < 50; i++) {
- FeedItem item = new FeedItem();
- String url = (i % 5 == 0) ? "dupe_url" : String.format("url_%d", i);
- item.setImage(new FeedImage(null, url, ""));
- items.add(item);
- }
- Feed feed = new Feed();
- feed.setItems(items);
-
- DownloadService.removeDuplicateImages(feed);
-
- assertEquals(50, items.size());
- for (int i = 0; i < items.size(); i++) {
- FeedItem item = items.get(i);
- String want = (i == 0) ? "dupe_url" : (i % 5 == 0) ? null : String.format("url_%d", i);
- assertEquals(want, item.getImageLocation());
- }
- }
-}
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java b/core/src/androidTest/java/de/danoeh/antennapod/core/util/DateUtilsTest.java
index 8adcc41c5..bef83b060 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/DateUtilsTest.java
+++ b/core/src/androidTest/java/de/danoeh/antennapod/core/util/DateUtilsTest.java
@@ -1,14 +1,21 @@
-package de.danoeh.antennapod.core.tests.util;
+package de.danoeh.antennapod.core.util;
import android.test.AndroidTestCase;
+import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;
-import de.danoeh.antennapod.core.util.DateUtils;
-
+/**
+ * Unit test for {@link DateUtils}.
+ *
+ * Note: It NEEDS to be run in android devices, i.e., it cannot be run in standard JDK, because
+ * the test invokes some android platform-specific behavior in the underlying
+ * {@link java.text.SimpleDateFormat} used by {@link DateUtils}.
+ *
+ */
public class DateUtilsTest extends AndroidTestCase {
public void testParseDateWithMicroseconds() throws Exception {
@@ -101,6 +108,12 @@ public class DateUtilsTest extends AndroidTestCase {
assertEquals(expected, actual);
}
+ /**
+ * Requires Android platform.
+ *
+ * Reason: Standard JDK cannot parse timezone <code>-08:00</code> (ISO 8601 format). It only accepts
+ * <code>-0800</code> (RFC 822 format)
+ */
public void testParseDateWithNoTimezonePadding() throws Exception {
GregorianCalendar exp = new GregorianCalendar(2017, 1, 22, 22, 28, 0);
exp.setTimeZone(TimeZone.getTimeZone("UTC"));
@@ -109,6 +122,12 @@ public class DateUtilsTest extends AndroidTestCase {
assertEquals(expected, actual);
}
+ /**
+ * Requires Android platform. Root cause: {@link DateUtils} implementation makes
+ * use of ISO 8601 time zone, which does not work on standard JDK.
+ *
+ * @see #testParseDateWithNoTimezonePadding()
+ */
public void testParseDateWithForCest() throws Exception {
GregorianCalendar exp1 = new GregorianCalendar(2017, 0, 28, 22, 0, 0);
exp1.setTimeZone(TimeZone.getTimeZone("UTC"));
@@ -130,4 +149,20 @@ public class DateUtilsTest extends AndroidTestCase {
Date actual = DateUtils.parse("Thu, 8 Oct 2014 09:00:00 GMT"); // actually a Wednesday
assertEquals(expected, actual);
}
+
+ public void testParseDateWithBadAbbreviation() {
+ GregorianCalendar exp1 = new GregorianCalendar(2014, 8, 8, 0, 0, 0);
+ exp1.setTimeZone(TimeZone.getTimeZone("GMT"));
+ Date expected = new Date(exp1.getTimeInMillis());
+ Date actual = DateUtils.parse("Mon, 8 Sept 2014 00:00:00 GMT"); // should be Sep
+ assertEquals(expected, actual);
+ }
+
+ public void testParseDateWithTwoTimezones() {
+ final GregorianCalendar exp1 = new GregorianCalendar(2015, Calendar.MARCH, 1, 1, 0, 0);
+ exp1.setTimeZone(TimeZone.getTimeZone("GMT-4"));
+ final Date expected = new Date(exp1.getTimeInMillis());
+ final Date actual = DateUtils.parse("Sun 01 Mar 2015 01:00:00 GMT-0400 (EDT)");
+ assertEquals(expected, actual);
+ }
}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java b/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
index 8818f6b46..9833e2cc9 100644
--- a/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
+++ b/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.feed;
/**
* Implements methods for FeedMedia that are flavor dependent.
*/
-public class FeedMediaFlavorHelper {
+class FeedMediaFlavorHelper {
private FeedMediaFlavorHelper(){}
static boolean instanceOfRemoteMedia(Object o) {
return false;
diff --git a/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index 4e6482fda..37109ddca 100644
--- a/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
+++ b/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -8,9 +8,9 @@ import android.support.v4.media.session.PlaybackStateCompat;
/**
* Class intended to work along PlaybackService and provide support for different flavors.
*/
-public class PlaybackServiceFlavorHelper {
+class PlaybackServiceFlavorHelper {
- private PlaybackService.FlavorHelperCallback callback;
+ private final PlaybackService.FlavorHelperCallback callback;
PlaybackServiceFlavorHelper(Context context, PlaybackService.FlavorHelperCallback callback) {
this.callback = callback;
diff --git a/core/src/main/AndroidManifest.xml b/core/src/main/AndroidManifest.xml
index ee035c9fa..1146f2a87 100644
--- a/core/src/main/AndroidManifest.xml
+++ b/core/src/main/AndroidManifest.xml
@@ -8,11 +8,11 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.VIBRATE"/>
+ <uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
- android:icon="@drawable/ic_launcher">
+ android:icon="@mipmap/ic_launcher">
<service
android:name=".service.download.DownloadService"
@@ -27,6 +27,7 @@
</service>
<service
android:name=".service.GpodnetSyncService"
+ android:permission="android.permission.BIND_JOB_SERVICE"
android:enabled="true" />
<receiver
@@ -52,9 +53,17 @@
</intent-filter>
</receiver>
- <receiver android:name=".receiver.FeedUpdateReceiver">
+ <receiver android:name=".receiver.FeedUpdateReceiver"
+ android:label="@string/feed_update_receiver_name"
+ android:exported="true"> <!-- allow feeds update to be triggered by external apps -->
</receiver>
+ <service
+ android:name=".service.FeedUpdateJobService"
+ android:permission="android.permission.BIND_JOB_SERVICE" >
+
+ </service>
+
</application>
</manifest>
diff --git a/core/src/main/java/android/support/v4/app/SafeJobIntentService.java b/core/src/main/java/android/support/v4/app/SafeJobIntentService.java
new file mode 100644
index 000000000..c07c409ee
--- /dev/null
+++ b/core/src/main/java/android/support/v4/app/SafeJobIntentService.java
@@ -0,0 +1,118 @@
+package android.support.v4.app;
+
+import android.app.job.JobParameters;
+import android.app.job.JobServiceEngine;
+import android.app.job.JobWorkItem;
+import android.content.Intent;
+import android.os.Build;
+import android.os.IBinder;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+
+public abstract class SafeJobIntentService extends JobIntentService {
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (Build.VERSION.SDK_INT >= 26) {
+ mJobImpl = new SafeJobServiceEngineImpl(this);
+ }
+ }
+
+ /**
+ * Implementation of a safe JobServiceEngine for interaction with JobIntentService.
+ */
+ @RequiresApi(26)
+ static final class SafeJobServiceEngineImpl extends JobServiceEngine
+ implements JobIntentService.CompatJobEngine {
+ static final String TAG = "JobServiceEngineImpl";
+
+ static final boolean DEBUG = false;
+
+ final JobIntentService mService;
+ final Object mLock = new Object();
+ JobParameters mParams;
+
+ final class WrapperWorkItem implements JobIntentService.GenericWorkItem {
+ final JobWorkItem mJobWork;
+
+ WrapperWorkItem(JobWorkItem jobWork) {
+ mJobWork = jobWork;
+ }
+
+ @Override
+ public Intent getIntent() {
+ return mJobWork.getIntent();
+ }
+
+ @Override
+ public void complete() {
+ synchronized (mLock) {
+ if (mParams != null) {
+ try {
+ mParams.completeWork(mJobWork);
+ } catch (SecurityException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+ }
+ }
+ }
+
+ SafeJobServiceEngineImpl(JobIntentService service) {
+ super(service);
+ mService = service;
+ }
+
+ @Override
+ public IBinder compatGetBinder() {
+ return getBinder();
+ }
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ if (DEBUG) Log.d(TAG, "onStartJob: " + params);
+ mParams = params;
+ // We can now start dequeuing work!
+ mService.ensureProcessorRunningLocked(false);
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ if (DEBUG) Log.d(TAG, "onStartJob: " + params);
+ boolean result = mService.doStopCurrentWork();
+ synchronized (mLock) {
+ // Once we return, the job is stopped, so its JobParameters are no
+ // longer valid and we should not be doing anything with them.
+ mParams = null;
+ }
+ return result;
+ }
+
+ /**
+ * Dequeue some work.
+ */
+ @Override
+ public JobIntentService.GenericWorkItem dequeueWork() {
+ JobWorkItem work = null;
+ synchronized (mLock) {
+ if (mParams == null) {
+ return null;
+ }
+ try {
+ work = mParams.dequeueWork();
+ } catch (SecurityException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+ if (work != null) {
+ work.getIntent().setExtrasClassLoader(mService.getClassLoader());
+ return new WrapperWorkItem(work);
+ } else {
+ return null;
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java
index 8362c4a4e..8ad70c328 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/UpdateManager.java
@@ -10,22 +10,16 @@ import android.util.Log;
import org.antennapod.audio.MediaPlayer;
-import java.io.File;
-import java.util.List;
-
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
-import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
/*
* This class's job is do perform maintenance tasks whenever the app has been updated
*/
-public class UpdateManager {
+class UpdateManager {
+
+ private UpdateManager(){}
- public static final String TAG = UpdateManager.class.getSimpleName();
+ private static final String TAG = UpdateManager.class.getSimpleName();
private static final String PREF_NAME = "app_version";
private static final String KEY_VERSION_CODE = "version_code";
@@ -55,41 +49,18 @@ public class UpdateManager {
}
}
- public static int getStoredVersionCode() {
+ private static int getStoredVersionCode() {
return prefs.getInt(KEY_VERSION_CODE, -1);
}
- public static void setCurrentVersionCode() {
+ private static void setCurrentVersionCode() {
prefs.edit().putInt(KEY_VERSION_CODE, currentVersionCode).apply();
}
private static void onUpgrade(final int oldVersionCode, final int newVersionCode) {
- if(oldVersionCode < 1030099) {
- // delete the now obsolete image cache
- // from now on, Glide will handle caching images
- new Thread() {
- public void run() {
- List<Feed> feeds = DBReader.getFeedList();
- for (Feed podcast : feeds) {
- List<FeedItem> episodes = DBReader.getFeedItemList(podcast);
- for (FeedItem episode : episodes) {
- FeedImage image = episode.getImage();
- if (image != null && image.isDownloaded() && image.getFile_url() != null) {
- File imageFile = new File(image.getFile_url());
- if (imageFile.exists()) {
- imageFile.delete();
- }
- image.setFile_url(null); // calls setDownloaded(false)
- DBWriter.setFeedImage(image);
- }
- }
- }
- }
- }.start();
- }
if(oldVersionCode < 1050004) {
if(MediaPlayer.isPrestoLibraryInstalled(context) && Build.VERSION.SDK_INT >= 16) {
- UserPreferences.enableSonic(true);
+ UserPreferences.enableSonic();
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/DBTaskLoader.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/DBTaskLoader.java
deleted file mode 100644
index 0f402f44a..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/DBTaskLoader.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package de.danoeh.antennapod.core.asynctask;
-
-import android.content.Context;
-import android.support.v4.content.AsyncTaskLoader;
-
-/**
- * Subclass of AsyncTaskLoader that is made for loading data with one of the DB*-classes.
- * This class will provide a useful default implementation that would otherwise always be necessary when interacting
- * with the DB*-classes with an AsyncTaskLoader.
- */
-public abstract class DBTaskLoader<D> extends AsyncTaskLoader<D> {
-
- public DBTaskLoader(Context context) {
- super(context);
- }
-
- @Override
- protected void onStopLoading() {
- super.onStopLoading();
- cancelLoad();
- }
-
- @Override
- protected void onStartLoading() {
- super.onStartLoading();
- // according to https://code.google.com/p/android/issues/detail?id=14944, this has to be called manually
- forceLoad();
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java
index 67c460e78..4504b2e7f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FeedRemover.java
@@ -1,9 +1,7 @@
package de.danoeh.antennapod.core.asynctask;
-import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
-import android.content.Intent;
import android.os.AsyncTask;
import java.util.concurrent.ExecutionException;
@@ -12,12 +10,13 @@ import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.IntentUtils;
/** Removes a feed in the background. */
public class FeedRemover extends AsyncTask<Void, Void, Void> {
- Context context;
- ProgressDialog dialog;
- Feed feed;
+ private final Context context;
+ private ProgressDialog dialog;
+ private final Feed feed;
public boolean skipOnCompletion = false;
public FeedRemover(Context context, Feed feed) {
@@ -42,7 +41,7 @@ public class FeedRemover extends AsyncTask<Void, Void, Void> {
dialog.dismiss();
}
if(skipOnCompletion) {
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE));
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
}
}
@@ -55,13 +54,8 @@ public class FeedRemover extends AsyncTask<Void, Void, Void> {
dialog.show();
}
- @SuppressLint("NewApi")
public void executeAsync() {
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- execute();
- }
+ executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
index d991006e5..318e404c8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrClickWorker.java
@@ -1,6 +1,5 @@
package de.danoeh.antennapod.core.asynctask;
-import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -27,6 +26,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
/**
* Performs a click action in a background thread.
@@ -39,7 +39,7 @@ import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
* to flattr something, a notification will be displayed.
*/
public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorker.ExitCode> {
- protected static final String TAG = "FlattrClickWorker";
+ private static final String TAG = "FlattrClickWorker";
private static final int NOTIFICATION_ID = 4;
@@ -176,7 +176,7 @@ public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorke
PendingIntent contentIntent = PendingIntent.getActivity(context, 0,
ClientConfig.flattrCallbacks.getFlattrAuthenticationActivityIntent(context), 0);
- Notification notification = new NotificationCompat.Builder(context)
+ Notification notification = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_ERROR)
.setStyle(new NotificationCompat.BigTextStyle().bigText(context.getString(R.string.no_flattr_token_notification_msg)))
.setContentIntent(contentIntent)
.setContentTitle(context.getString(R.string.no_flattr_token_title))
@@ -209,7 +209,7 @@ public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorke
+ context.getString(R.string.flattr_click_failure_count, failed);
}
- Notification notification = new NotificationCompat.Builder(context)
+ Notification notification = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_ERROR)
.setStyle(new NotificationCompat.BigTextStyle().bigText(subtext))
.setContentIntent(contentIntent)
.setContentTitle(title)
@@ -225,12 +225,7 @@ public class FlattrClickWorker extends AsyncTask<Void, Integer, FlattrClickWorke
/**
* Starts the FlattrClickWorker as an AsyncTask.
*/
- @TargetApi(11)
public void executeAsync() {
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- execute();
- }
+ executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrStatusFetcher.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrStatusFetcher.java
index 4c084eaaf..6d9ab2bd3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrStatusFetcher.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrStatusFetcher.java
@@ -18,12 +18,10 @@ import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
*/
public class FlattrStatusFetcher extends Thread {
- protected static final String TAG = "FlattrStatusFetcher";
- protected Context context;
+ private static final String TAG = "FlattrStatusFetcher";
public FlattrStatusFetcher(Context context) {
super();
- this.context = context;
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrTokenFetcher.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrTokenFetcher.java
index 2513d1abd..985cabbf8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrTokenFetcher.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/FlattrTokenFetcher.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.core.asynctask;
-import android.annotation.SuppressLint;
import android.app.ProgressDialog;
import android.content.Context;
import android.net.Uri;
@@ -23,12 +22,12 @@ import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
public class FlattrTokenFetcher extends AsyncTask<Void, Void, AccessToken> {
private static final String TAG = "FlattrTokenFetcher";
- Context context;
- AndroidAuthenticator auth;
- AccessToken token;
- Uri uri;
- ProgressDialog dialog;
- FlattrException exception;
+ private final Context context;
+ private final AndroidAuthenticator auth;
+ private AccessToken token;
+ private final Uri uri;
+ private ProgressDialog dialog;
+ private FlattrException exception;
public FlattrTokenFetcher(Context context, AndroidAuthenticator auth, Uri uri) {
super();
@@ -80,13 +79,8 @@ public class FlattrTokenFetcher extends AsyncTask<Void, Void, AccessToken> {
}
}
- @SuppressLint("NewApi")
public void executeAsync() {
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- execute();
- }
+ executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
index b14803751..c626a8189 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/dialog/ConfirmationDialog.java
@@ -15,9 +15,9 @@ public abstract class ConfirmationDialog {
private static final String TAG = ConfirmationDialog.class.getSimpleName();
- protected Context context;
- private int titleId;
- private String message;
+ private final Context context;
+ private final int titleId;
+ private final String message;
private int positiveText;
private int negativeText;
@@ -32,7 +32,7 @@ public abstract class ConfirmationDialog {
this.message = message;
}
- public void onCancelButtonPressed(DialogInterface dialog) {
+ private void onCancelButtonPressed(DialogInterface dialog) {
Log.d(TAG, "Dialog was cancelled");
dialog.dismiss();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
index d09f6802f..578007561 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
@@ -11,8 +11,8 @@ public class FavoritesEvent {
ADDED, REMOVED
}
- public final Action action;
- public final FeedItem item;
+ private final Action action;
+ private final FeedItem item;
private FavoritesEvent(Action action, FeedItem item) {
this.action = action;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
index 7ff241456..9db262857 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
@@ -17,7 +17,8 @@ public class FeedItemEvent {
UPDATE, DELETE_MEDIA
}
- @NonNull public final Action action;
+ @NonNull
+ private final Action action;
@NonNull public final List<FeedItem> items;
private FeedItemEvent(Action action, List<FeedItem> items) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java
index 864d0a405..4a591c996 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedMediaEvent.java
@@ -8,8 +8,8 @@ public class FeedMediaEvent {
UPDATE
}
- public final Action action;
- public final FeedMedia media;
+ private final Action action;
+ private final FeedMedia media;
private FeedMediaEvent(Action action, FeedMedia media) {
this.action = action;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java
new file mode 100644
index 000000000..b3241a8b6
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java
@@ -0,0 +1,13 @@
+package de.danoeh.antennapod.core.event;
+
+public class ServiceEvent {
+ public enum Action {
+ SERVICE_STARTED
+ }
+
+ public final Action action;
+
+ public ServiceEvent(Action action) {
+ this.action = action;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java
index b8807a686..1ca126469 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlSymbols.java
@@ -22,7 +22,7 @@ class HtmlSymbols extends CommonSymbols {
static final String ORDERED_LIST = "ol";
static final String LIST_ITEM = "li";
- static String HEADING = "h1";
+ static final String HEADING = "h1";
static final String LINK = "a";
static final String LINK_DESTINATION = "href";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java
index 40b0e23b8..86091720d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlSymbols.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.export.opml;
import de.danoeh.antennapod.core.export.CommonSymbols;
/** Contains symbols for reading and writing OPML documents. */
-public final class OpmlSymbols extends CommonSymbols {
+final class OpmlSymbols extends CommonSymbols {
public static final String OPML = "opml";
static final String OUTLINE = "outline";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java
index bb594ff87..f3dfdfdb6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java
@@ -7,19 +7,19 @@ import de.danoeh.antennapod.core.storage.PodDBAdapter;
public abstract class Chapter extends FeedComponent {
/** Defines starting point in milliseconds. */
- protected long start;
- protected String title;
- protected String link;
+ long start;
+ String title;
+ String link;
- public Chapter() {
+ Chapter() {
}
- public Chapter(long start) {
+ Chapter(long start) {
super();
this.start = start;
}
- public Chapter(long start, String title, FeedItem item, String link) {
+ Chapter(long start, String title, FeedItem item, String link) {
super();
this.start = start;
this.title = title;
@@ -27,11 +27,13 @@ public abstract class Chapter extends FeedComponent {
}
public static Chapter fromCursor(Cursor cursor, FeedItem item) {
+ int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
int indexChapterType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE);
+ long id = cursor.getLong(indexId);
String title = cursor.getString(indexTitle);
long start = cursor.getLong(indexStart);
String link = cursor.getString(indexLink);
@@ -49,6 +51,7 @@ public abstract class Chapter extends FeedComponent {
chapter = new VorbisCommentChapter(start, title, item, link);
break;
}
+ chapter.setId(id);
return chapter;
}
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
index 514a79fad..b769eaf55 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/EventDistributor.java
@@ -27,8 +27,8 @@ public class EventDistributor extends Observable {
public static final int DOWNLOAD_HANDLED = 64;
public static final int PLAYER_STATUS_UPDATE = 128;
- private Handler handler;
- private AbstractQueue<Integer> events;
+ private final Handler handler;
+ private final AbstractQueue<Integer> events;
private static EventDistributor instance;
@@ -52,7 +52,7 @@ public class EventDistributor extends Observable {
deleteObserver(el);
}
- public void addEvent(Integer i) {
+ private void addEvent(Integer i) {
events.offer(i);
handler.post(EventDistributor.this::processEventQueue);
}
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 746dd43c4..3395653f3 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
@@ -44,7 +44,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
* Name of the author
*/
private String author;
- private FeedImage image;
+ private String imageUrl;
private List<FeedItem> items;
/**
@@ -96,7 +96,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
* 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, FeedImage image, String fileUrl,
+ String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl,
String downloadUrl, boolean downloaded, FlattrStatus status, boolean paged, String nextPageLink,
String filter, boolean lastUpdateFailed) {
super(fileUrl, downloadUrl, downloaded);
@@ -111,7 +111,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
this.language = language;
this.type = type;
this.feedIdentifier = feedIdentifier;
- this.image = image;
+ this.imageUrl = imageUrl;
this.flattrStatus = status;
this.paged = paged;
this.nextPageLink = nextPageLink;
@@ -128,9 +128,9 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
* This constructor is used for test purposes and uses a default flattr status object.
*/
public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink,
- String author, String language, String type, String feedIdentifier, FeedImage image, String fileUrl,
+ 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, image,
+ this(id, lastUpdate, title, null, link, description, paymentLink, author, language, type, feedIdentifier, imageUrl,
fileUrl, downloadUrl, downloaded, new FlattrStatus(), false, null, null, false);
}
@@ -191,6 +191,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
int indexNextPageLink = cursor.getColumnIndex(PodDBAdapter.KEY_NEXT_PAGE_LINK);
int indexHide = cursor.getColumnIndex(PodDBAdapter.KEY_HIDE);
int indexLastUpdateFailed = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_UPDATE_FAILED);
+ int indexImageUrl = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE_URL);
Feed feed = new Feed(
cursor.getLong(indexId),
@@ -204,7 +205,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
cursor.getString(indexLanguage),
cursor.getString(indexType),
cursor.getString(indexFeedIdentifier),
- null,
+ cursor.getString(indexImageUrl),
cursor.getString(indexFileUrl),
cursor.getString(indexDownloadUrl),
cursor.getInt(indexDownloaded) > 0,
@@ -266,8 +267,8 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
public void updateFromOther(Feed other) {
// don't update feed's download_url, we do that manually if redirected
// see AntennapodHttpClient
- if (other.image != null) {
- this.image = other.image;
+ if (other.imageUrl != null) {
+ this.imageUrl = other.imageUrl;
}
if (other.feedTitle != null) {
feedTitle = other.feedTitle;
@@ -305,8 +306,10 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
if (super.compareWithOther(other)) {
return true;
}
- if(other.image != null && !TextUtils.equals(image.download_url, other.image.download_url)) {
- return true;
+ if (other.imageUrl != null) {
+ if (imageUrl == null || !TextUtils.equals(imageUrl, other.imageUrl)) {
+ return true;
+ }
}
if (!TextUtils.equals(feedTitle, other.feedTitle)) {
return true;
@@ -409,12 +412,12 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
this.description = description;
}
- public FeedImage getImage() {
- return image;
+ public String getImageUrl() {
+ return imageUrl;
}
- public void setImage(FeedImage image) {
- this.image = image;
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
}
public List<FeedItem> getItems() {
@@ -503,11 +506,7 @@ public class Feed extends FeedFile implements FlattrThing, ImageResource {
@Override
public String getImageLocation() {
- if (image != null) {
- return image.getImageLocation();
- } else {
- return null;
- }
+ return imageUrl;
}
public int getPageNr() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java
index 90b5e50b7..2610d253f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java
@@ -7,9 +7,9 @@ package de.danoeh.antennapod.core.feed;
*/
public abstract class FeedComponent {
- protected long id;
+ long id;
- public FeedComponent() {
+ FeedComponent() {
super();
}
@@ -26,7 +26,7 @@ public abstract class FeedComponent {
* FeedComponent. This method should only update attributes which where read from
* the feed.
*/
- public void updateFromOther(FeedComponent other) {
+ void updateFromOther(FeedComponent other) {
}
/**
@@ -36,7 +36,7 @@ public abstract class FeedComponent {
*
* @return true if attribute values are different, false otherwise
*/
- public boolean compareWithOther(FeedComponent other) {
+ boolean compareWithOther(FeedComponent other) {
return false;
}
@@ -50,7 +50,7 @@ public abstract class FeedComponent {
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (o == null || !(o instanceof FeedComponent)) return false;
FeedComponent that = (FeedComponent) o;
@@ -62,4 +62,4 @@ public abstract class FeedComponent {
public int hashCode() {
return (int) (id ^ (id >>> 32));
}
-} \ No newline at end of file
+}
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 d04d236e4..b790faadf 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
@@ -9,7 +9,7 @@ public class FeedEvent {
FILTER_CHANGED
}
- public final Action action;
+ private final Action action;
public final long feedId;
public FeedEvent(Action action, long feedId) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java
index ca9af058b..cc4dd230f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java
@@ -9,9 +9,9 @@ import java.io.File;
*/
public abstract class FeedFile extends FeedComponent {
- protected String file_url;
+ String file_url;
protected String download_url;
- protected boolean downloaded;
+ boolean downloaded;
/**
* Creates a new FeedFile object.
@@ -40,7 +40,7 @@ public abstract class FeedFile extends FeedComponent {
* FeedFile. This method should only update attributes which where read from
* the feed.
*/
- public void updateFromOther(FeedFile other) {
+ void updateFromOther(FeedFile other) {
super.updateFromOther(other);
this.download_url = other.download_url;
}
@@ -52,7 +52,7 @@ public abstract class FeedFile extends FeedComponent {
*
* @return true if attribute values are different, false otherwise
*/
- public boolean compareWithOther(FeedFile other) {
+ boolean compareWithOther(FeedFile other) {
if (super.compareWithOther(other)) {
return true;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java
index 35abb8de6..28161ac9b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java
@@ -9,8 +9,8 @@ public class FeedFilter {
private static final String TAG = "FeedFilter";
- private String includeFilter;
- private String excludeFilter;
+ private final String includeFilter;
+ private final String excludeFilter;
public FeedFilter() {
this("", "");
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
deleted file mode 100644
index f0c508830..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedImage.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.database.Cursor;
-
-import java.io.File;
-
-import de.danoeh.antennapod.core.asynctask.ImageResource;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-
-
-public class FeedImage extends FeedFile implements ImageResource {
- public static final int FEEDFILETYPE_FEEDIMAGE = 1;
-
- protected String title;
- protected FeedComponent owner;
-
- public FeedImage(FeedComponent owner, String download_url, String title) {
- super(null, download_url, false);
- this.download_url = download_url;
- this.title = title;
- this.owner = owner;
- }
-
- public FeedImage(long id, String title, String file_url,
- String download_url, boolean downloaded) {
- super(file_url, download_url, downloaded);
- this.id = id;
- this.title = title;
- }
-
- public FeedImage() {
- super();
- }
-
- public static FeedImage fromCursor(Cursor cursor) {
- int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
- int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
- int indexFileUrl = cursor.getColumnIndex(PodDBAdapter.KEY_FILE_URL);
- int indexDownloadUrl = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL);
- int indexDownloaded = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOADED);
-
- return new FeedImage(
- cursor.getLong(indexId),
- cursor.getString(indexTitle),
- cursor.getString(indexFileUrl),
- cursor.getString(indexDownloadUrl),
- cursor.getInt(indexDownloaded) > 0
- );
- }
-
-
- @Override
- public String getHumanReadableIdentifier() {
- if (owner != null && owner.getHumanReadableIdentifier() != null) {
- return owner.getHumanReadableIdentifier();
- } else {
- return download_url;
- }
- }
-
- @Override
- public int getTypeAsInt() {
- return FEEDFILETYPE_FEEDIMAGE;
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public FeedComponent getOwner() {
- return owner;
- }
-
- public void setOwner(FeedComponent owner) {
- this.owner = owner;
- }
-
- @Override
- public String getImageLocation() {
- if (file_url != null && downloaded) {
- return new File(file_url).getAbsolutePath();
- } else if(download_url != null) {
- return download_url;
- } else {
- return null;
- }
- }
-}
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 66c4b10d0..0f0343f25 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
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.core.feed;
import android.database.Cursor;
import android.support.annotation.Nullable;
+import android.text.TextUtils;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
@@ -59,7 +60,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
public static final int PLAYED = 1;
private String paymentLink;
- private FlattrStatus flattrStatus;
+ private final FlattrStatus flattrStatus;
/**
* Is true if the database contains any chapters that belong to this item. This attribute is only
@@ -74,7 +75,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
* in the database. The 'hasChapters' attribute should be used to check if this item has any chapters.
* */
private List<Chapter> chapters;
- private FeedImage image;
+ private String imageUrl;
/*
* 0: auto download disabled
@@ -87,7 +88,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
/**
* Any tags assigned to this item
*/
- private Set<String> tags = new HashSet<>();
+ private final Set<String> tags = new HashSet<>();
public FeedItem() {
this.state = UNPLAYED;
@@ -99,7 +100,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
* This constructor is used by DBReader.
* */
public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId,
- FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, int state,
+ FlattrStatus flattrStatus, boolean hasChapters, String imageUrl, int state,
String itemIdentifier, long autoDownload) {
this.id = id;
this.title = title;
@@ -109,7 +110,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
this.feedId = feedId;
this.flattrStatus = flattrStatus;
this.hasChapters = hasChapters;
- this.image = image;
+ this.imageUrl = imageUrl;
this.state = state;
this.itemIdentifier = itemIdentifier;
this.autoDownload = autoDownload;
@@ -157,9 +158,9 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
int indexRead = cursor.getColumnIndex(PodDBAdapter.KEY_READ);
int indexItemIdentifier = cursor.getColumnIndex(PodDBAdapter.KEY_ITEM_IDENTIFIER);
int indexAutoDownload = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD);
+ int indexImageUrl = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE_URL);
long id = cursor.getInt(indexId);
- assert(id > 0);
String title = cursor.getString(indexTitle);
String link = cursor.getString(indexLink);
Date pubDate = new Date(cursor.getLong(indexPubDate));
@@ -170,15 +171,16 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
int state = cursor.getInt(indexRead);
String itemIdentifier = cursor.getString(indexItemIdentifier);
long autoDownload = cursor.getLong(indexAutoDownload);
+ String imageUrl = cursor.getString(indexImageUrl);
return new FeedItem(id, title, link, pubDate, paymentLink, feedId, flattrStatus,
- hasChapters, null, state, itemIdentifier, autoDownload);
+ hasChapters, imageUrl, state, itemIdentifier, autoDownload);
}
public void updateFromOther(FeedItem other) {
super.updateFromOther(other);
- if (other.image != null) {
- this.image = other.image;
+ if (other.imageUrl != null) {
+ this.imageUrl = other.imageUrl;
}
if (other.title != null) {
title = other.title;
@@ -192,7 +194,7 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
if (other.link != null) {
link = other.link;
}
- if (other.pubDate != null && other.pubDate != pubDate) {
+ if (other.pubDate != null && other.pubDate.equals(pubDate)) {
pubDate = other.pubDate;
}
if (other.media != null) {
@@ -212,9 +214,6 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
chapters = other.chapters;
}
}
- if (image == null) {
- image = other.image;
- }
}
/**
@@ -373,7 +372,15 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
if (contentEncoded == null || description == null) {
DBReader.loadExtraInformationOfFeedItem(FeedItem.this);
}
- return (contentEncoded != null) ? contentEncoded : description;
+ if (TextUtils.isEmpty(contentEncoded)) {
+ return description;
+ } else if (TextUtils.isEmpty(description)) {
+ return contentEncoded;
+ } else if (description.length() > 1.25 * contentEncoded.length()) {
+ return description;
+ } else {
+ return contentEncoded;
+ }
};
}
@@ -381,8 +388,8 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
public String getImageLocation() {
if(media != null && media.hasEmbeddedPicture()) {
return media.getImageLocation();
- } else if (image != null) {
- return image.getImageLocation();
+ } else if (imageUrl != null) {
+ return imageUrl;
} else if (feed != null) {
return feed.getImageLocation();
} else {
@@ -418,29 +425,12 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr
* Returns the image of this item or the image of the feed if this item does
* not have its own image.
*/
- public FeedImage getImage() {
- return (hasItemImage()) ? image : feed.getImage();
- }
-
- public void setImage(FeedImage image) {
- this.image = image;
- if (image != null) {
- image.setOwner(this);
- }
+ public String getImageUrl() {
+ return (imageUrl != null) ? imageUrl : feed.getImageUrl();
}
- /**
- * Returns true if this FeedItem has its own image, false otherwise.
- */
- public boolean hasItemImage() {
- return image != null;
- }
-
- /**
- * Returns true if this FeedItem has its own image and the image has been downloaded.
- */
- public boolean hasItemImageDownloaded() {
- return image != null && image.isDownloaded();
+ public void setImageUrl(String imageUrl) {
+ this.imageUrl = imageUrl;
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
index 200153876..719383d23 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
@@ -8,6 +8,8 @@ import java.util.List;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.LongList;
+import static de.danoeh.antennapod.core.feed.FeedItem.TAG_FAVORITE;
+
public class FeedItemFilter {
private final String[] mProperties;
@@ -19,6 +21,7 @@ public class FeedItemFilter {
private boolean showDownloaded = false;
private boolean showNotDownloaded = false;
private boolean showHasMedia = false;
+ private boolean showIsFavorite = false;
public FeedItemFilter(String properties) {
this(TextUtils.split(properties, ","));
@@ -53,6 +56,9 @@ public class FeedItemFilter {
case "has_media":
showHasMedia = true;
break;
+ case "is_favorite":
+ showIsFavorite = true;
+ break;
}
}
}
@@ -88,6 +94,8 @@ public class FeedItemFilter {
if (showHasMedia && !item.hasMedia()) continue;
+ if (showIsFavorite && !item.isTagged(TAG_FAVORITE)) continue;
+
// If the item reaches here, it meets all criteria
result.add(item);
}
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 d4414227c..39994ec16 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
@@ -19,6 +19,7 @@ import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
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.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -34,7 +35,7 @@ public class FeedMedia extends FeedFile implements Playable {
public static final int PLAYABLE_TYPE_FEEDMEDIA = 1;
public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId";
- public static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
+ private static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
/**
* Indicates we've checked on the size of the item via the network
@@ -88,10 +89,10 @@ public class FeedMedia extends FeedFile implements Playable {
this.lastPlayedTime = lastPlayedTime;
}
- public FeedMedia(long id, FeedItem item, int duration, int position,
- long size, String mime_type, String file_url, String download_url,
- boolean downloaded, Date playbackCompletionDate, int played_duration,
- Boolean hasEmbeddedPicture, long lastPlayedTime) {
+ private FeedMedia(long id, FeedItem item, int duration, int position,
+ long size, String mime_type, String file_url, String download_url,
+ boolean downloaded, Date playbackCompletionDate, int played_duration,
+ Boolean hasEmbeddedPicture, long lastPlayedTime) {
this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded,
playbackCompletionDate, played_duration, lastPlayedTime);
this.hasEmbeddedPicture = hasEmbeddedPicture;
@@ -218,7 +219,7 @@ public class FeedMedia extends FeedFile implements Playable {
* currently being played and the current player status is playing.
*/
public boolean isCurrentlyPlaying() {
- return isPlaying() &&
+ return isPlaying() && PlaybackService.isRunning &&
((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING));
}
@@ -389,16 +390,19 @@ public class FeedMedia extends FeedFile implements Playable {
if (item == null && itemID != 0) {
item = DBReader.getFeedItem(itemID);
}
+ if (item == null || item.getChapters() != null) {
+ return;
+ }
// check if chapters are stored in db and not loaded yet.
- if (item != null && item.hasChapters() && item.getChapters() == null) {
+ if (item.hasChapters()) {
DBReader.loadChaptersOfFeedItem(item);
- } else if (item != null && item.getChapters() == null) {
+ } else {
if(localFileAvailable()) {
ChapterUtils.loadChaptersFromFileUrl(this);
} else {
ChapterUtils.loadChaptersFromStreamUrl(this);
}
- if (getChapters() != null && item != null) {
+ if (item.getChapters() != null) {
DBWriter.setFeedItem(item);
}
}
@@ -528,8 +532,8 @@ public class FeedMedia extends FeedFile implements Playable {
UserPreferences.isAutoFlattr() &&
item.getPaymentLink() != null &&
item.getFlattrStatus().getUnflattred() &&
- (completed && autoFlattrThreshold <= 1.0f ||
- played_duration >= autoFlattrThreshold * duration)) {
+ ((completed && autoFlattrThreshold <= 1.0f) ||
+ (played_duration >= autoFlattrThreshold * duration))) {
DBTasks.flattrItemIfLoggedIn(context, item);
}
}
@@ -551,15 +555,9 @@ public class FeedMedia extends FeedFile implements Playable {
public Callable<String> loadShownotes() {
return () -> {
if (item == null) {
- item = DBReader.getFeedItem(
- itemID);
+ item = DBReader.getFeedItem(itemID);
}
- if (item.getContentEncoded() == null || item.getDescription() == null) {
- DBReader.loadExtraInformationOfFeedItem(
- item);
-
- }
- return (item.getContentEncoded() != null) ? item.getContentEncoded() : item.getDescription();
+ return item.loadShownotes().call();
};
}
@@ -628,6 +626,9 @@ public class FeedMedia extends FeedFile implements Playable {
@Override
public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
if (FeedMediaFlavorHelper.instanceOfRemoteMedia(o)) {
return o.equals(this);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
index 66fc4024b..3285ad7cb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
@@ -33,7 +33,7 @@ public class FeedPreferences {
this(feedID, autoDownload, true, auto_delete_action, username, password, new FeedFilter());
}
- public FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, String username, String password, @NonNull FeedFilter filter) {
+ private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated, AutoDeleteAction auto_delete_action, String username, String password, @NonNull FeedFilter filter) {
this.feedID = feedID;
this.autoDownload = autoDownload;
this.keepUpdated = keepUpdated;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java
index 9aa8d3170..ea8eb7871 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SearchResult.java
@@ -1,11 +1,11 @@
package de.danoeh.antennapod.core.feed;
public class SearchResult {
- private FeedComponent component;
+ private final FeedComponent component;
/** Additional information (e.g. where it was found) */
private String subtitle;
/** Higher value means more importance */
- private int value;
+ private final int value;
public SearchResult(FeedComponent component, int value, String subtitle) {
super();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
index 5b54a2d59..5ab9868a6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.feed;
-import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
-
import java.util.concurrent.TimeUnit;
+import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
+
public class VorbisCommentChapter extends Chapter {
public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
index 835dee735..ec10b78aa 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideModule.java
@@ -2,31 +2,35 @@ package de.danoeh.antennapod.core.glide;
import android.content.Context;
+import android.support.annotation.NonNull;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
+import com.bumptech.glide.Registry;
+import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.load.engine.cache.InternalCacheDiskCacheFactory;
-import com.bumptech.glide.module.GlideModule;
+import com.bumptech.glide.module.AppGlideModule;
import java.io.InputStream;
+import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
* {@see com.bumptech.glide.integration.okhttp.OkHttpGlideModule}
*/
-public class ApGlideModule implements GlideModule {
+@GlideModule
+public class ApGlideModule extends AppGlideModule {
@Override
- public void applyOptions(Context context, GlideBuilder builder) {
- builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
+ public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
+ builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_ARGB_8888));
builder.setDiskCache(new InternalCacheDiskCacheFactory(context,
UserPreferences.getImageCacheSize()));
}
@Override
- public void registerComponents(Context context, Glide glide) {
- glide.register(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
+ public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
+ registry.append(String.class, InputStream.class, new ApOkHttpUrlLoader.Factory());
}
-
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java
index fc1acd0e1..d0061af99 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApGlideSettings.java
@@ -6,6 +6,7 @@ import com.bumptech.glide.load.engine.DiskCacheStrategy;
* The settings that AntennaPod will use for various Glide options
*/
public class ApGlideSettings {
+ private ApGlideSettings(){}
public static final DiskCacheStrategy AP_DISK_CACHE_STRATEGY = DiskCacheStrategy.ALL;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
index 8ca9faa0d..bd5276100 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ApOkHttpUrlLoader.java
@@ -1,12 +1,12 @@
package de.danoeh.antennapod.core.glide;
-import android.content.Context;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher;
-import com.bumptech.glide.load.data.DataFetcher;
-import com.bumptech.glide.load.model.GenericLoaderFactory;
+import com.bumptech.glide.load.Options;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
@@ -15,6 +15,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
+import com.bumptech.glide.load.model.MultiModelLoaderFactory;
+import com.bumptech.glide.signature.ObjectKey;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.service.download.HttpDownloader;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -27,7 +29,7 @@ import okhttp3.Response;
/**
* @see com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader
*/
-public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
+class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
private static final String TAG = ApOkHttpUrlLoader.class.getSimpleName();
@@ -37,7 +39,7 @@ public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
public static class Factory implements ModelLoaderFactory<String, InputStream> {
private static volatile OkHttpClient internalClient;
- private OkHttpClient client;
+ private final OkHttpClient client;
private static OkHttpClient getInternalClient() {
if (internalClient == null) {
@@ -56,19 +58,20 @@ public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
/**
* Constructor for a new Factory that runs requests using a static singleton client.
*/
- public Factory() {
+ Factory() {
this(getInternalClient());
}
/**
* Constructor for a new Factory that runs requests using given client.
*/
- public Factory(OkHttpClient client) {
+ Factory(OkHttpClient client) {
this.client = client;
}
+ @NonNull
@Override
- public ModelLoader<String, InputStream> build(Context context, GenericLoaderFactory factories) {
+ public ModelLoader<String, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
return new ApOkHttpUrlLoader(client);
}
@@ -80,24 +83,30 @@ public class ApOkHttpUrlLoader implements ModelLoader<String, InputStream> {
private final OkHttpClient client;
- public ApOkHttpUrlLoader(OkHttpClient client) {
+ private ApOkHttpUrlLoader(OkHttpClient client) {
this.client = client;
}
+ @Nullable
@Override
- public DataFetcher<InputStream> getResourceFetcher(String model, int width, int height) {
- Log.d(TAG, "getResourceFetcher() called with: " + "model = [" + model + "], width = ["
+ public LoadData<InputStream> buildLoadData(@NonNull String model, int width, int height, @NonNull Options options) {
+ Log.d(TAG, "buildLoadData() called with: " + "model = [" + model + "], width = ["
+ width + "], height = [" + height + "]");
if(TextUtils.isEmpty(model)) {
return null;
} else if(model.startsWith("/")) {
- return new AudioCoverFetcher(model);
+ return new LoadData<>(new ObjectKey(model), new AudioCoverFetcher(model));
} else {
GlideUrl url = new GlideUrl(model);
- return new OkHttpStreamFetcher(client, url);
+ return new LoadData<>(new ObjectKey(model), new OkHttpStreamFetcher(client, url));
}
}
+ @Override
+ public boolean handles(@NonNull String s) {
+ return true;
+ }
+
private static class NetworkAllowanceInterceptor implements Interceptor {
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java b/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java
index 48dadc492..479846655 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/AudioCoverFetcher.java
@@ -1,9 +1,10 @@
package de.danoeh.antennapod.core.glide;
import android.media.MediaMetadataRetriever;
-import android.util.Log;
+import android.support.annotation.NonNull;
import com.bumptech.glide.Priority;
+import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.data.DataFetcher;
import java.io.ByteArrayInputStream;
@@ -21,22 +22,20 @@ class AudioCoverFetcher implements DataFetcher<InputStream> {
this.path = path;
}
- @Override public String getId() {
- return path;
- }
-
- @Override public InputStream loadData(Priority priority) throws Exception {
+ @Override
+ public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(path);
byte[] picture = retriever.getEmbeddedPicture();
if (picture != null) {
- return new ByteArrayInputStream(picture);
+ callback.onDataReady(new ByteArrayInputStream(picture));
+ return;
}
} finally {
retriever.release();
}
- throw new IOException("Loading embedded cover did not work");
+ callback.onLoadFailed(new IOException("Loading embedded cover did not work"));
}
@Override public void cleanup() {
@@ -45,4 +44,16 @@ class AudioCoverFetcher implements DataFetcher<InputStream> {
@Override public void cancel() {
// cannot cancel
}
+
+ @NonNull
+ @Override
+ public Class<InputStream> getDataClass() {
+ return InputStream.class;
+ }
+
+ @NonNull
+ @Override
+ public DataSource getDataSource() {
+ return DataSource.LOCAL;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
index ee58c2f39..a740782d6 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
@@ -1,13 +1,15 @@
package de.danoeh.antennapod.core.glide;
-import android.content.Context;
import android.graphics.Bitmap;
import android.media.ThumbnailUtils;
+import android.support.annotation.NonNull;
import android.util.Log;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
+import java.security.MessageDigest;
+
public class FastBlurTransformation extends BitmapTransformation {
private static final String TAG = FastBlurTransformation.class.getSimpleName();
@@ -15,8 +17,8 @@ public class FastBlurTransformation extends BitmapTransformation {
private static final int STACK_BLUR_RADIUS = 1;
private static final int BLUR_IMAGE_WIDTH = 150;
- public FastBlurTransformation(Context context) {
- super(context);
+ public FastBlurTransformation() {
+ super();
}
@Override
@@ -33,11 +35,6 @@ public class FastBlurTransformation extends BitmapTransformation {
return result;
}
- @Override
- public String getId() {
- return "FastBlurTransformation[width=" + BLUR_IMAGE_WIDTH + "px,radius=" + STACK_BLUR_RADIUS +"]";
- }
-
private static Bitmap fastBlur(Bitmap bitmap, int radius) {
// Stack Blur v1.0 from
@@ -264,4 +261,8 @@ public class FastBlurTransformation extends BitmapTransformation {
return bitmap;
}
+ @Override
+ public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
+ messageDigest.update(TAG.getBytes());
+ }
} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java
index 4459fbd08..3af5e9080 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetService.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.core.gpoddernet;
import android.support.annotation.NonNull;
+
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceBadStatusCodeException.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceBadStatusCodeException.java
index 16f01f0f4..84c085ed2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceBadStatusCodeException.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceBadStatusCodeException.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.core.gpoddernet;
-public class GpodnetServiceBadStatusCodeException extends GpodnetServiceException {
- int statusCode;
+class GpodnetServiceBadStatusCodeException extends GpodnetServiceException {
+ private final int statusCode;
public GpodnetServiceBadStatusCodeException(String message, int statusCode) {
super(message);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceException.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceException.java
index ce704f7e3..78ddfc945 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceException.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/GpodnetServiceException.java
@@ -2,10 +2,10 @@ package de.danoeh.antennapod.core.gpoddernet;
public class GpodnetServiceException extends Exception {
- public GpodnetServiceException() {
+ GpodnetServiceException() {
}
- public GpodnetServiceException(String message) {
+ GpodnetServiceException(String message) {
super(message);
}
@@ -13,7 +13,7 @@ public class GpodnetServiceException extends Exception {
super(cause);
}
- public GpodnetServiceException(String message, Throwable cause) {
+ GpodnetServiceException(String message, Throwable cause) {
super(message, cause);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java
index 79eb33cb5..faf4264e5 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetDevice.java
@@ -4,10 +4,10 @@ import android.support.annotation.NonNull;
public class GpodnetDevice {
- private String id;
- private String caption;
- private DeviceType type;
- private int subscriptions;
+ private final String id;
+ private final String caption;
+ private final DeviceType type;
+ private final int subscriptions;
public GpodnetDevice(@NonNull String id,
String caption,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java
index 9627ecae6..330cde525 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeAction.java
@@ -131,7 +131,7 @@ public class GpodnetEpisodeAction {
return this.action;
}
- public String getActionString() {
+ private String getActionString() {
return this.action.name().toLowerCase();
}
@@ -169,7 +169,7 @@ public class GpodnetEpisodeAction {
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (o == null || !(o instanceof GpodnetEpisodeAction)) return false;
GpodnetEpisodeAction that = (GpodnetEpisodeAction) o;
@@ -199,16 +199,14 @@ public class GpodnetEpisodeAction {
}
public String writeToString() {
- StringBuilder result = new StringBuilder();
- result.append(this.podcast).append("\t");
- result.append(this.episode).append("\t");
- result.append(this.deviceId).append("\t");
- result.append(this.action).append("\t");
- result.append(this.timestamp.getTime()).append("\t");
- result.append(String.valueOf(this.started)).append("\t");
- result.append(String.valueOf(this.position)).append("\t");
- result.append(String.valueOf(this.total));
- return result.toString();
+ return this.podcast + "\t" +
+ this.episode + "\t" +
+ this.deviceId + "\t" +
+ this.action + "\t" +
+ this.timestamp.getTime() + "\t" +
+ String.valueOf(this.started) + "\t" +
+ String.valueOf(this.position) + "\t" +
+ String.valueOf(this.total);
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java
index 03c33c9a1..b6efab016 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetEpisodeActionPostResponse.java
@@ -21,9 +21,9 @@ public class GpodnetEpisodeActionPostResponse {
* URLs that should be updated. The key of the map is the original URL, the value of the map
* is the sanitized URL.
*/
- public final Map<String, String> updatedUrls;
+ private final Map<String, String> updatedUrls;
- public GpodnetEpisodeActionPostResponse(long timestamp, Map<String, String> updatedUrls) {
+ private GpodnetEpisodeActionPostResponse(long timestamp, Map<String, String> updatedUrls) {
this.timestamp = timestamp;
this.updatedUrls = updatedUrls;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java
index 191c0fa39..680dc1042 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetPodcast.java
@@ -3,13 +3,13 @@ package de.danoeh.antennapod.core.gpoddernet.model;
import android.support.annotation.NonNull;
public class GpodnetPodcast {
- private String url;
- private String title;
- private String description;
- private int subscribers;
- private String logoUrl;
- private String website;
- private String mygpoLink;
+ private final String url;
+ private final String title;
+ private final String description;
+ private final int subscribers;
+ private final String logoUrl;
+ private final String website;
+ private final String mygpoLink;
public GpodnetPodcast(@NonNull String url,
@NonNull String title,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java
index 6cc9b79a3..0f1961bef 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetSubscriptionChange.java
@@ -5,9 +5,9 @@ import android.support.annotation.NonNull;
import java.util.List;
public class GpodnetSubscriptionChange {
- private List<String> added;
- private List<String> removed;
- private long timestamp;
+ private final List<String> added;
+ private final List<String> removed;
+ private final long timestamp;
public GpodnetSubscriptionChange(@NonNull List<String> added,
@NonNull List<String> removed,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java
index 42a31afc5..40543592e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetTag.java
@@ -16,7 +16,7 @@ public class GpodnetTag implements Parcelable {
this.usage = usage;
}
- protected GpodnetTag(Parcel in) {
+ private GpodnetTag(Parcel in) {
title = in.readString();
tag = in.readString();
usage = in.readInt();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java
index 9bd1881e4..9f9c3bd74 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/gpoddernet/model/GpodnetUploadChangesResponse.java
@@ -22,9 +22,9 @@ public class GpodnetUploadChangesResponse {
* URLs that should be updated. The key of the map is the original URL, the value of the map
* is the sanitized URL.
*/
- public final Map<String, String> updatedUrls;
+ private final Map<String, String> updatedUrls;
- public GpodnetUploadChangesResponse(long timestamp, Map<String, String> updatedUrls) {
+ private GpodnetUploadChangesResponse(long timestamp, Map<String, String> updatedUrls) {
this.timestamp = timestamp;
this.updatedUrls = updatedUrls;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
index f14c9e36c..5b17dd338 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
@@ -7,6 +7,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@@ -23,29 +24,31 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService;
*/
public class GpodnetPreferences {
+ private GpodnetPreferences(){}
+
private static final String TAG = "GpodnetPreferences";
private static final String PREF_NAME = "gpodder.net";
- public static final String PREF_GPODNET_USERNAME = "de.danoeh.antennapod.preferences.gpoddernet.username";
- public static final String PREF_GPODNET_PASSWORD = "de.danoeh.antennapod.preferences.gpoddernet.password";
- public static final String PREF_GPODNET_DEVICEID = "de.danoeh.antennapod.preferences.gpoddernet.deviceID";
- public static final String PREF_GPODNET_HOSTNAME = "prefGpodnetHostname";
+ private static final String PREF_GPODNET_USERNAME = "de.danoeh.antennapod.preferences.gpoddernet.username";
+ private static final String PREF_GPODNET_PASSWORD = "de.danoeh.antennapod.preferences.gpoddernet.password";
+ private static final String PREF_GPODNET_DEVICEID = "de.danoeh.antennapod.preferences.gpoddernet.deviceID";
+ private static final String PREF_GPODNET_HOSTNAME = "prefGpodnetHostname";
- public static final String PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_sync_timestamp";
- public static final String PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_episode_actions_sync_timestamp";
- public static final String PREF_SYNC_ADDED = "de.danoeh.antennapod.preferences.gpoddernet.sync_added";
- public static final String PREF_SYNC_REMOVED = "de.danoeh.antennapod.preferences.gpoddernet.sync_removed";
- public static final String PREF_SYNC_EPISODE_ACTIONS = "de.danoeh.antennapod.preferences.gpoddernet.sync_queued_episode_actions";
+ private static final String PREF_LAST_SUBSCRIPTION_SYNC_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_sync_timestamp";
+ private static final String PREF_LAST_EPISODE_ACTIONS_SYNC_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_episode_actions_sync_timestamp";
+ private static final String PREF_SYNC_ADDED = "de.danoeh.antennapod.preferences.gpoddernet.sync_added";
+ private static final String PREF_SYNC_REMOVED = "de.danoeh.antennapod.preferences.gpoddernet.sync_removed";
+ private static final String PREF_SYNC_EPISODE_ACTIONS = "de.danoeh.antennapod.preferences.gpoddernet.sync_queued_episode_actions";
public static final String PREF_LAST_SYNC_ATTEMPT_TIMESTAMP = "de.danoeh.antennapod.preferences.gpoddernet.last_sync_attempt_timestamp";
- public static final String PREF_LAST_SYNC_ATTEMPT_RESULT = "de.danoeh.antennapod.preferences.gpoddernet.last_sync_attempt_result";
+ private static final String PREF_LAST_SYNC_ATTEMPT_RESULT = "de.danoeh.antennapod.preferences.gpoddernet.last_sync_attempt_result";
private static String username;
private static String password;
private static String deviceID;
private static String hostname;
- private static ReentrantLock feedListLock = new ReentrantLock();
+ private static final ReentrantLock feedListLock = new ReentrantLock();
private static Set<String> addedFeeds;
private static Set<String> removedFeeds;
@@ -316,9 +319,7 @@ public class GpodnetPreferences {
private static Set<String> readListFromString(String s) {
Set<String> result = new HashSet<>();
- for (String item : s.split(" ")) {
- result.add(item);
- }
+ Collections.addAll(result, s.split(" "));
return result;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java
index b7ed890f5..0f3a9fcb3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/SleepTimerPreferences.java
@@ -23,7 +23,6 @@ public class SleepTimerPreferences {
private static final String DEFAULT_VALUE = "15";
private static final int DEFAULT_TIME_UNIT = 1;
- private static Context context;
private static SharedPreferences prefs;
/**
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 20a349055..c44999c88 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
@@ -1,12 +1,9 @@
package de.danoeh.antennapod.core.preferences;
-import android.app.AlarmManager;
-import android.app.PendingIntent;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
-import android.os.SystemClock;
import android.preference.PreferenceManager;
+import android.support.annotation.IntRange;
import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
@@ -20,18 +17,17 @@ import java.io.IOException;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.Calendar;
import java.util.List;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.receiver.FeedUpdateReceiver;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
/**
* Provides access to preferences set by the user in the settings screen. A
@@ -40,44 +36,48 @@ import de.danoeh.antennapod.core.util.Converter;
* when called.
*/
public class UserPreferences {
+ private UserPreferences(){}
- public static final String IMPORT_DIR = "import/";
+ private static final String IMPORT_DIR = "import/";
private static final String TAG = "UserPreferences";
// User Interface
public static final String PREF_THEME = "prefTheme";
public static final String PREF_HIDDEN_DRAWER_ITEMS = "prefHiddenDrawerItems";
- public static final String PREF_DRAWER_FEED_ORDER = "prefDrawerFeedOrder";
- public static final String PREF_DRAWER_FEED_COUNTER = "prefDrawerFeedIndicator";
+ 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_PERSISTENT_NOTIFICATION = "prefPersistNotify";
+ 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";
- public static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport";
+ private static final String PREF_SHOW_DOWNLOAD_REPORT = "prefShowDownloadReport";
+ public static final String PREF_BACK_BUTTON_BEHAVIOR = "prefBackButtonBehavior";
+ private static final String PREF_BACK_BUTTON_GO_TO_PAGE = "prefBackButtonGoToPage";
// Queue
- public static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
+ private static final String PREF_QUEUE_ADD_TO_FRONT = "prefQueueAddToFront";
// Playback
public static final String PREF_PAUSE_ON_HEADSET_DISCONNECT = "prefPauseOnHeadsetDisconnect";
public static final String PREF_UNPAUSE_ON_HEADSET_RECONNECT = "prefUnpauseOnHeadsetReconnect";
- public static final String PREF_UNPAUSE_ON_BLUETOOTH_RECONNECT = "prefUnpauseOnBluetoothReconnect";
- public static final String PREF_HARDWARE_FOWARD_BUTTON_SKIPS = "prefHardwareForwardButtonSkips";
- public static final String PREF_HARDWARE_PREVIOUS_BUTTON_RESTARTS = "prefHardwarePreviousButtonRestarts";
+ private static final String PREF_UNPAUSE_ON_BLUETOOTH_RECONNECT = "prefUnpauseOnBluetoothReconnect";
+ private static final String PREF_HARDWARE_FOWARD_BUTTON_SKIPS = "prefHardwareForwardButtonSkips";
+ private static final String PREF_HARDWARE_PREVIOUS_BUTTON_RESTARTS = "prefHardwarePreviousButtonRestarts";
public static final String PREF_FOLLOW_QUEUE = "prefFollowQueue";
- public static final String PREF_SKIP_KEEPS_EPISODE = "prefSkipKeepsEpisode";
- public static final String PREF_FAVORITE_KEEPS_EPISODE = "prefFavoriteKeepsEpisode";
- public static final String PREF_AUTO_DELETE = "prefAutoDelete";
+ private static final String PREF_SKIP_KEEPS_EPISODE = "prefSkipKeepsEpisode";
+ private static final String PREF_FAVORITE_KEEPS_EPISODE = "prefFavoriteKeepsEpisode";
+ private static final String PREF_AUTO_DELETE = "prefAutoDelete";
public static final String PREF_SMART_MARK_AS_PLAYED_SECS = "prefSmartMarkAsPlayedSecs";
- public static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
- public static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
- public static final String PREF_RESUME_AFTER_CALL = "prefResumeAfterCall";
+ private static final String PREF_PLAYBACK_SPEED_ARRAY = "prefPlaybackSpeedArray";
+ private static final String PREF_PAUSE_PLAYBACK_FOR_FOCUS_LOSS = "prefPauseForFocusLoss";
+ private static final String PREF_RESUME_AFTER_CALL = "prefResumeAfterCall";
+ public static final String PREF_VIDEO_BEHAVIOR = "prefVideoBehavior";
// Network
- public static final String PREF_ENQUEUE_DOWNLOADED = "prefEnqueueDownloaded";
+ private static final String PREF_ENQUEUE_DOWNLOADED = "prefEnqueueDownloaded";
public static final String PREF_UPDATE_INTERVAL = "prefAutoUpdateIntervall";
- public static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
+ private static final String PREF_MOBILE_UPDATE = "prefMobileUpdate";
public static final String PREF_EPISODE_CLEANUP = "prefEpisodeCleanup";
public static final String PREF_PARALLEL_DOWNLOADS = "prefParallelDownloads";
public static final String PREF_EPISODE_CACHE_SIZE = "prefEpisodeCacheSize";
@@ -85,36 +85,35 @@ public class UserPreferences {
public static final String PREF_ENABLE_AUTODL_ON_BATTERY = "prefEnableAutoDownloadOnBattery";
public static final String PREF_ENABLE_AUTODL_WIFI_FILTER = "prefEnableAutoDownloadWifiFilter";
public static final String PREF_ENABLE_AUTODL_ON_MOBILE = "prefEnableAutoDownloadOnMobile";
- public static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
- public static final String PREF_PROXY_TYPE = "prefProxyType";
- public static final String PREF_PROXY_HOST = "prefProxyHost";
- public static final String PREF_PROXY_PORT = "prefProxyPort";
- public static final String PREF_PROXY_USER = "prefProxyUser";
- public static final String PREF_PROXY_PASSWORD = "prefProxyPassword";
+ private static final String PREF_AUTODL_SELECTED_NETWORKS = "prefAutodownloadSelectedNetworks";
+ private static final String PREF_PROXY_TYPE = "prefProxyType";
+ private static final String PREF_PROXY_HOST = "prefProxyHost";
+ private static final String PREF_PROXY_PORT = "prefProxyPort";
+ private static final String PREF_PROXY_USER = "prefProxyUser";
+ private static final String PREF_PROXY_PASSWORD = "prefProxyPassword";
// Services
- public static final String PREF_AUTO_FLATTR = "pref_auto_flattr";
- public static final String PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD = "prefAutoFlattrPlayedDurationThreshold";
- public static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
+ private static final String PREF_AUTO_FLATTR = "pref_auto_flattr";
+ private static final String PREF_AUTO_FLATTR_PLAYED_DURATION_THRESHOLD = "prefAutoFlattrPlayedDurationThreshold";
+ private static final String PREF_GPODNET_NOTIFICATIONS = "pref_gpodnet_notifications";
// Other
- public static final String PREF_DATA_FOLDER = "prefDataFolder";
+ private static final String PREF_DATA_FOLDER = "prefDataFolder";
public static final String PREF_IMAGE_CACHE_SIZE = "prefImageCacheSize";
// Mediaplayer
- public static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
+ public static final String PREF_MEDIA_PLAYER = "prefMediaPlayer";
+ private static final String PREF_PLAYBACK_SPEED = "prefPlaybackSpeed";
private static final String PREF_FAST_FORWARD_SECS = "prefFastForwardSecs";
private static final String PREF_REWIND_SECS = "prefRewindSecs";
- public static final String PREF_QUEUE_LOCKED = "prefQueueLocked";
- public static final String IMAGE_CACHE_DEFAULT_VALUE = "100";
- public static final int IMAGE_CACHE_SIZE_MINIMUM = 20;
- public static final String PREF_LEFT_VOLUME = "prefLeftVolume";
- public static final String PREF_RIGHT_VOLUME = "prefRightVolume";
+ private static final String PREF_QUEUE_LOCKED = "prefQueueLocked";
+ private static final String IMAGE_CACHE_DEFAULT_VALUE = "100";
+ private static final int IMAGE_CACHE_SIZE_MINIMUM = 20;
+ private static final String PREF_LEFT_VOLUME = "prefLeftVolume";
+ private static final String PREF_RIGHT_VOLUME = "prefRightVolume";
// Experimental
- public static final String PREF_SONIC = "prefSonic";
- public static final String PREF_STEREO_TO_MONO = "PrefStereoToMono";
- public static final String PREF_NORMALIZER = "prefNormalizer";
+ private static final String PREF_STEREO_TO_MONO = "PrefStereoToMono";
public static final String PREF_CAST_ENABLED = "prefCast"; //Used for enabling Chromecast support
public static final int EPISODE_CLEANUP_QUEUE = -1;
public static final int EPISODE_CLEANUP_NULL = -2;
@@ -124,10 +123,9 @@ public class UserPreferences {
private static final int NOTIFICATION_BUTTON_REWIND = 0;
private static final int NOTIFICATION_BUTTON_FAST_FORWARD = 1;
private static final int NOTIFICATION_BUTTON_SKIP = 2;
- private static int EPISODE_CACHE_SIZE_UNLIMITED = -1;
+ private static final int EPISODE_CACHE_SIZE_UNLIMITED = -1;
public static final int FEED_ORDER_COUNTER = 0;
public static final int FEED_ORDER_ALPHABETICAL = 1;
- public static final int FEED_ORDER_LAST_UPDATE = 2;
public static final int FEED_ORDER_MOST_PLAYED = 3;
public static final int FEED_COUNTER_SHOW_NEW_UNPLAYED_SUM = 0;
public static final int FEED_COUNTER_SHOW_NEW = 1;
@@ -166,6 +164,8 @@ public class UserPreferences {
int theme = getTheme();
if (theme == R.style.Theme_AntennaPod_Dark) {
return R.style.Theme_AntennaPod_Dark_NoTitle;
+ } else if (theme == R.style.Theme_AntennaPod_TrueBlack) {
+ return R.style.Theme_AntennaPod_TrueBlack_NoTitle;
} else {
return R.style.Theme_AntennaPod_Light_NoTitle;
}
@@ -182,8 +182,8 @@ public class UserPreferences {
String.valueOf(NOTIFICATION_BUTTON_SKIP)),
",");
List<Integer> notificationButtons = new ArrayList<>();
- for (int i=0; i<buttons.length; i++) {
- notificationButtons.add(Integer.parseInt(buttons[i]));
+ for (String button : buttons) {
+ notificationButtons.add(Integer.parseInt(button));
}
return notificationButtons;
}
@@ -513,9 +513,8 @@ public class UserPreferences {
.apply();
}
- public static void setVolume(int leftVolume, int rightVolume) {
- assert(0 <= leftVolume && leftVolume <= 100);
- assert(0 <= rightVolume && rightVolume <= 100);
+ public static void setVolume(@IntRange(from = 0, to = 100) int leftVolume,
+ @IntRange(from = 0, to = 100) int rightVolume) {
prefs.edit()
.putInt(PREF_LEFT_VOLUME, leftVolume)
.putInt(PREF_RIGHT_VOLUME, rightVolume)
@@ -603,6 +602,8 @@ public class UserPreferences {
return R.style.Theme_AntennaPod_Light;
case 1:
return R.style.Theme_AntennaPod_Dark;
+ case 2:
+ return R.style.Theme_AntennaPod_TrueBlack;
default:
return R.style.Theme_AntennaPod_Light;
}
@@ -642,13 +643,15 @@ public class UserPreferences {
}
public static boolean useSonic() {
- return prefs.getBoolean(PREF_SONIC, false);
+ return prefs.getString(PREF_MEDIA_PLAYER, "sonic").equals("sonic");
}
- public static void enableSonic(boolean enable) {
- prefs.edit()
- .putBoolean(PREF_SONIC, enable)
- .apply();
+ public static boolean useExoplayer() {
+ return prefs.getString(PREF_MEDIA_PLAYER, "sonic").equals("exoplayer");
+ }
+
+ public static void enableSonic() {
+ prefs.edit().putString(PREF_MEDIA_PLAYER, "sonic").apply();
}
public static boolean stereoToMono() {
@@ -661,6 +664,14 @@ public class UserPreferences {
.apply();
}
+ public static VideoBackgroundBehavior getVideoBackgroundBehavior() {
+ switch (prefs.getString(PREF_VIDEO_BEHAVIOR, "stop")) {
+ case "stop": return VideoBackgroundBehavior.STOP;
+ case "pip": return VideoBackgroundBehavior.PICTURE_IN_PICTURE;
+ case "continue": return VideoBackgroundBehavior.CONTINUE_PLAYING;
+ default: return VideoBackgroundBehavior.STOP;
+ }
+ }
public static EpisodeCleanupAlgorithm getEpisodeCleanupAlgorithm() {
int cleanupValue = Integer.parseInt(prefs.getString(PREF_EPISODE_CLEANUP, "-1"));
@@ -772,61 +783,18 @@ public class UserPreferences {
int[] timeOfDay = getUpdateTimeOfDay();
Log.d(TAG, "timeOfDay: " + Arrays.toString(timeOfDay));
if (timeOfDay.length == 2) {
- restartUpdateTimeOfDayAlarm(timeOfDay[0], timeOfDay[1]);
+ AutoUpdateManager.restartUpdateTimeOfDayAlarm(context, timeOfDay[0], timeOfDay[1]);
} else {
long milliseconds = getUpdateInterval();
long startTrigger = milliseconds;
if (now) {
startTrigger = TimeUnit.SECONDS.toMillis(10);
}
- restartUpdateIntervalAlarm(startTrigger, milliseconds);
+ AutoUpdateManager.restartUpdateIntervalAlarm(context, startTrigger, milliseconds);
}
}
/**
- * Sets the interval in which the feeds are refreshed automatically
- */
- public static void restartUpdateIntervalAlarm(long triggerAtMillis, long intervalMillis) {
- Log.d(TAG, "Restarting update alarm.");
- AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- Intent intent = new Intent(context, FeedUpdateReceiver.class);
- PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
- alarmManager.cancel(updateIntent);
- if (intervalMillis > 0) {
- alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + triggerAtMillis,
- updateIntent);
- Log.d(TAG, "Changed alarm to new interval " + TimeUnit.MILLISECONDS.toHours(intervalMillis) + " h");
- } else {
- Log.d(TAG, "Automatic update was deactivated");
- }
- }
-
- /**
- * Sets time of day the feeds are refreshed automatically
- */
- public static void restartUpdateTimeOfDayAlarm(int hoursOfDay, int minute) {
- Log.d(TAG, "Restarting update alarm.");
- AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
- PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0,
- new Intent(context, FeedUpdateReceiver.class), 0);
- alarmManager.cancel(updateIntent);
-
- Calendar now = Calendar.getInstance();
- Calendar alarm = (Calendar)now.clone();
- alarm.set(Calendar.HOUR_OF_DAY, hoursOfDay);
- alarm.set(Calendar.MINUTE, minute);
- if (alarm.before(now) || alarm.equals(now)) {
- alarm.add(Calendar.DATE, 1);
- }
- Log.d(TAG, "Alarm set for: " + alarm.toString() + " : " + alarm.getTimeInMillis());
- alarmManager.set(AlarmManager.RTC_WAKEUP,
- alarm.getTimeInMillis(),
- updateIntent);
- Log.d(TAG, "Changed alarm to new time of day " + hoursOfDay + ":" + minute);
- }
-
- /**
* Reads episode cache size as it is saved in the episode_cache_size_values array.
*/
public static int readEpisodeCacheSize(String valueFromPrefs) {
@@ -839,4 +807,33 @@ public class UserPreferences {
public static boolean isCastEnabled() {
return prefs.getBoolean(PREF_CAST_ENABLED, false);
}
+
+ public enum VideoBackgroundBehavior {
+ STOP, PICTURE_IN_PICTURE, CONTINUE_PLAYING
+ }
+
+ public enum BackButtonBehavior {
+ DEFAULT, OPEN_DRAWER, DOUBLE_TAP, SHOW_PROMPT, GO_TO_PAGE
+ }
+
+ public static BackButtonBehavior getBackButtonBehavior() {
+ switch (prefs.getString(PREF_BACK_BUTTON_BEHAVIOR, "default")) {
+ case "default": return BackButtonBehavior.DEFAULT;
+ case "drawer": return BackButtonBehavior.OPEN_DRAWER;
+ case "doubletap": return BackButtonBehavior.DOUBLE_TAP;
+ case "prompt": return BackButtonBehavior.SHOW_PROMPT;
+ case "page": return BackButtonBehavior.GO_TO_PAGE;
+ default: return BackButtonBehavior.DEFAULT;
+ }
+ }
+
+ public static String getBackButtonGoToPage() {
+ return prefs.getString(PREF_BACK_BUTTON_GO_TO_PAGE, "QueueFragment");
+ }
+
+ public static void setBackButtonGoToPage(String tag) {
+ prefs.edit()
+ .putString(PREF_BACK_BUTTON_GO_TO_PAGE, tag)
+ .apply();
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
index 9bbeb7c88..05e12f6df 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/FeedUpdateReceiver.java
@@ -7,8 +7,7 @@ import android.util.Log;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBTasks;
-import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.FeedUpdateUtils;
/**
* Refreshes all feeds when it receives an intent
@@ -21,11 +20,7 @@ public class FeedUpdateReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Received intent");
ClientConfig.initialize(context);
- if (NetworkUtils.networkAvailable() && NetworkUtils.isDownloadAllowed()) {
- DBTasks.refreshAllFeeds(context, null);
- } else {
- Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
- }
+ FeedUpdateUtils.startAutoUpdate(context, null);
UserPreferences.restartUpdateAlarm(false);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
index 9b4b91151..b191dbf8b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/MediaButtonReceiver.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.receiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.support.v4.content.ContextCompat;
import android.util.Log;
import android.view.KeyEvent;
@@ -29,7 +30,7 @@ public class MediaButtonReceiver extends BroadcastReceiver {
Intent serviceIntent = new Intent(context, PlaybackService.class);
serviceIntent.putExtra(EXTRA_KEYCODE, event.getKeyCode());
serviceIntent.putExtra(EXTRA_SOURCE, event.getSource());
- context.startService(serviceIntent);
+ ContextCompat.startForegroundService(context, serviceIntent);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java b/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
index f0d4014ed..7663cdbe4 100644
--- a/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/receiver/PlayerWidget.java
@@ -1,17 +1,16 @@
-package de.danoeh.antennapod.receiver;
-
-import java.util.Arrays;
+package de.danoeh.antennapod.core.receiver;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.text.TextUtils;
import android.util.Log;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.service.PlayerWidgetService;
+import java.util.Arrays;
+
+import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
+
public class PlayerWidget extends AppWidgetProvider {
private static final String TAG = "PlayerWidget";
@@ -22,17 +21,7 @@ public class PlayerWidget extends AppWidgetProvider {
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "onReceive");
super.onReceive(context, intent);
- // don't do anything if we're not enabled
- if (!isEnabled(context)) {
- return;
- }
-
- // these come from the PlaybackService when things should get updated
- if (TextUtils.equals(intent.getAction(), PlaybackService.FORCE_WIDGET_UPDATE)) {
- startUpdate(context);
- } else if (TextUtils.equals(intent.getAction(), PlaybackService.STOP_WIDGET_UPDATE)) {
- stopUpdate(context);
- }
+ PlayerWidgetJobService.updateWidget(context);
}
@Override
@@ -40,14 +29,13 @@ public class PlayerWidget extends AppWidgetProvider {
super.onEnabled(context);
Log.d(TAG, "Widget enabled");
setEnabled(context, true);
- startUpdate(context);
+ PlayerWidgetJobService.updateWidget(context);
}
@Override
- public void onUpdate(Context context, AppWidgetManager appWidgetManager,
- int[] appWidgetIds) {
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Log.d(TAG, "onUpdate() called with: " + "context = [" + context + "], appWidgetManager = [" + appWidgetManager + "], appWidgetIds = [" + Arrays.toString(appWidgetIds) + "]");
- startUpdate(context);
+ PlayerWidgetJobService.updateWidget(context);
}
@Override
@@ -55,20 +43,9 @@ public class PlayerWidget extends AppWidgetProvider {
super.onDisabled(context);
Log.d(TAG, "Widget disabled");
setEnabled(context, false);
- stopUpdate(context);
- }
-
- private void startUpdate(Context context) {
- Log.d(TAG, "startUpdate() called with: " + "context = [" + context + "]");
- context.startService(new Intent(context, PlayerWidgetService.class));
- }
-
- private void stopUpdate(Context context) {
- Log.d(TAG, "stopUpdate() called with: " + "context = [" + context + "]");
- context.stopService(new Intent(context, PlayerWidgetService.class));
}
- private boolean isEnabled(Context context) {
+ 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/FeedUpdateJobService.java b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateJobService.java
new file mode 100644
index 000000000..7d80d4e7c
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/FeedUpdateJobService.java
@@ -0,0 +1,35 @@
+package de.danoeh.antennapod.core.service;
+
+import android.app.job.JobParameters;
+import android.app.job.JobService;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.FeedUpdateUtils;
+
+@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+public class FeedUpdateJobService extends JobService {
+ private static final String TAG = "FeedUpdateJobService";
+
+ @Override
+ public boolean onStartJob(JobParameters params) {
+ Log.d(TAG, "Job started");
+ ClientConfig.initialize(getApplicationContext());
+
+ FeedUpdateUtils.startAutoUpdate(getApplicationContext(), () -> {
+ UserPreferences.restartUpdateAlarm(false);
+ jobFinished(params, false); // needsReschedule = false
+ });
+
+ return true;
+ }
+
+ @Override
+ public boolean onStopJob(JobParameters params) {
+ return true;
+ }
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
index e9312b929..5584991ca 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/GpodnetSyncService.java
@@ -3,11 +3,11 @@ package de.danoeh.antennapod.core.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
-import android.app.Service;
import android.content.Context;
import android.content.Intent;
-import android.os.IBinder;
+import android.support.annotation.NonNull;
import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.SafeJobIntentService;
import android.support.v4.util.ArrayMap;
import android.util.Log;
import android.util.Pair;
@@ -15,6 +15,7 @@ import android.util.Pair;
import java.util.Collection;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
@@ -37,30 +38,39 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.NetworkUtils;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
/**
* Synchronizes local subscriptions with gpodder.net service. The service should be started with ACTION_SYNC as an action argument.
* This class also provides static methods for starting the GpodnetSyncService.
*/
-public class GpodnetSyncService extends Service {
+public class GpodnetSyncService extends SafeJobIntentService {
+
private static final String TAG = "GpodnetSyncService";
private static final long WAIT_INTERVAL = 5000L;
- public static final String ARG_ACTION = "action";
+ private static final String ARG_ACTION = "action";
- public static final String ACTION_SYNC = "de.danoeh.antennapod.intent.action.sync";
- public static final String ACTION_SYNC_SUBSCRIPTIONS = "de.danoeh.antennapod.intent.action.sync_subscriptions";
- public static final String ACTION_SYNC_ACTIONS = "de.danoeh.antennapod.intent.action.sync_ACTIONS";
+ private static final String ACTION_SYNC = "de.danoeh.antennapod.intent.action.sync";
+ private static final String ACTION_SYNC_SUBSCRIPTIONS = "de.danoeh.antennapod.intent.action.sync_subscriptions";
+ private static final String ACTION_SYNC_ACTIONS = "de.danoeh.antennapod.intent.action.sync_ACTIONS";
private GpodnetService service;
- private boolean syncSubscriptions = false;
- private boolean syncActions = false;
+ private static final AtomicInteger syncActionCount = new AtomicInteger(0);
+ private static boolean syncSubscriptions = false;
+ private static boolean syncActions = false;
+
+ private static final int JOB_ID = -17000;
+
+ private static void enqueueWork(Context context, Intent intent) {
+ enqueueWork(context, GpodnetSyncService.class, JOB_ID, intent);
+ }
@Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- final String action = (intent != null) ? intent.getStringExtra(ARG_ACTION) : null;
+ protected void onHandleWork(@NonNull Intent intent) {
+ final String action = intent.getStringExtra(ARG_ACTION);
if (action != null) {
switch(action) {
case ACTION_SYNC:
@@ -78,24 +88,20 @@ public class GpodnetSyncService extends Service {
}
if(syncSubscriptions || syncActions) {
Log.d(TAG, String.format("Waiting %d milliseconds before uploading changes", WAIT_INTERVAL));
- syncWaiterThread.restart();
+ int syncActionId = syncActionCount.incrementAndGet();
+ try {
+ Thread.sleep(WAIT_INTERVAL);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ if (syncActionId == syncActionCount.get()) {
+ // onHandleWork was not called again in the meantime
+ sync();
+ }
}
} else {
Log.e(TAG, "Received invalid intent: action argument is null");
}
- return START_FLAG_REDELIVERY;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.d(TAG, "onDestroy");
- syncWaiterThread.interrupt();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- return null;
}
private synchronized GpodnetService tryLogin() throws GpodnetServiceException {
@@ -109,6 +115,7 @@ public class GpodnetSyncService extends Service {
private synchronized void sync() {
if (!GpodnetPreferences.loggedIn() || !NetworkUtils.networkAvailable()) {
+ stopForeground(true);
stopSelf();
return;
}
@@ -125,7 +132,6 @@ public class GpodnetSyncService extends Service {
}
syncActions = false;
}
- stopSelf();
}
private synchronized void syncSubscriptionChanges() {
@@ -222,14 +228,12 @@ public class GpodnetSyncService extends Service {
} catch (GpodnetServiceException e) {
e.printStackTrace();
updateErrorNotification(e);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
}
}
private synchronized void processEpisodeActions(List<GpodnetEpisodeAction> localActions,
- List<GpodnetEpisodeAction> remoteActions) throws DownloadRequestException {
+ List<GpodnetEpisodeAction> remoteActions) {
if(remoteActions.size() == 0) {
return;
}
@@ -321,7 +325,7 @@ public class GpodnetSyncService extends Service {
}
PendingIntent activityIntent = ClientConfig.gpodnetCallbacks.getGpodnetSyncServiceErrorNotificationPendingIntent(this);
- Notification notification = new NotificationCompat.Builder(this)
+ Notification notification = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_ERROR)
.setContentTitle(title)
.setContentText(description)
.setContentIntent(activityIntent)
@@ -333,69 +337,11 @@ public class GpodnetSyncService extends Service {
nm.notify(id, notification);
}
- private WaiterThread syncWaiterThread = new WaiterThread(WAIT_INTERVAL) {
- @Override
- public void onWaitCompleted() {
- sync();
- }
- };
-
- private abstract class WaiterThread {
- private long waitInterval;
- private Thread thread;
-
- private WaiterThread(long waitInterval) {
- this.waitInterval = waitInterval;
- reinit();
- }
-
- public abstract void onWaitCompleted();
-
- public void exec() {
- if (!thread.isAlive()) {
- thread.start();
- }
- }
-
- private void reinit() {
- if (thread != null && thread.isAlive()) {
- Log.d(TAG, "Interrupting waiter thread");
- thread.interrupt();
- }
- thread = new Thread() {
- @Override
- public void run() {
- try {
- Thread.sleep(waitInterval);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- if (!isInterrupted()) {
- synchronized (this) {
- onWaitCompleted();
- }
- }
- }
- };
- }
-
- public void restart() {
- reinit();
- exec();
- }
-
- public void interrupt() {
- if (thread != null && thread.isAlive()) {
- thread.interrupt();
- }
- }
- }
-
public static void sendSyncIntent(Context context) {
if (GpodnetPreferences.loggedIn()) {
Intent intent = new Intent(context, GpodnetSyncService.class);
intent.putExtra(ARG_ACTION, ACTION_SYNC);
- context.startService(intent);
+ enqueueWork(context, intent);
}
}
@@ -403,7 +349,7 @@ public class GpodnetSyncService extends Service {
if (GpodnetPreferences.loggedIn()) {
Intent intent = new Intent(context, GpodnetSyncService.class);
intent.putExtra(ARG_ACTION, ACTION_SYNC_SUBSCRIPTIONS);
- context.startService(intent);
+ enqueueWork(context, intent);
}
}
@@ -411,7 +357,7 @@ public class GpodnetSyncService extends Service {
if (GpodnetPreferences.loggedIn()) {
Intent intent = new Intent(context, GpodnetSyncService.class);
intent.putExtra(ARG_ACTION, ACTION_SYNC_ACTIONS);
- context.startService(intent);
+ enqueueWork(context, intent);
}
}
}
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
new file mode 100644
index 000000000..6dab9a561
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/PlayerWidgetJobService.java
@@ -0,0 +1,180 @@
+package de.danoeh.antennapod.core.service;
+
+import android.app.PendingIntent;
+import android.appwidget.AppWidgetManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.Build;
+import android.os.IBinder;
+import android.support.annotation.NonNull;
+import android.support.v4.app.SafeJobIntentService;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.widget.RemoteViews;
+
+import de.danoeh.antennapod.core.R;
+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.util.playback.Playable;
+
+/**
+ * Updates the state of the player widget
+ */
+public class PlayerWidgetJobService extends SafeJobIntentService {
+
+ private static final String TAG = "PlayerWidgetJobService";
+
+ private PlaybackService playbackService;
+ private final Object waitForService = new Object();
+
+ private static final int JOB_ID = -17001;
+
+ public static void updateWidget(Context context) {
+ enqueueWork(context, PlayerWidgetJobService.class, JOB_ID, new Intent(context, PlayerWidgetJobService.class));
+ }
+
+ @Override
+ protected void onHandleWork(@NonNull Intent intent) {
+ if (!PlayerWidget.isEnabled(getApplicationContext())) {
+ return;
+ }
+
+ synchronized (waitForService) {
+ if (PlaybackService.isRunning && playbackService == null) {
+ bindService(new Intent(this, PlaybackService.class), mConnection, 0);
+ while (playbackService == null) {
+ try {
+ waitForService.wait();
+ } catch (InterruptedException e) {
+ return;
+ }
+ }
+ }
+ }
+
+ updateViews();
+
+ if (playbackService != null) {
+ try {
+ unbindService(mConnection);
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "IllegalArgumentException when trying to unbind service");
+ }
+ }
+ }
+
+ private void updateViews() {
+
+ ComponentName playerWidget = new ComponentName(this, PlayerWidget.class);
+ AppWidgetManager manager = AppWidgetManager.getInstance(this);
+ RemoteViews views = new RemoteViews(getPackageName(), R.layout.player_widget);
+ PendingIntent startMediaplayer = PendingIntent.getActivity(this, 0,
+ PlaybackService.getPlayerActivityIntent(this), 0);
+
+ final PendingIntent startAppPending = PendingIntent.getActivity(this, 0,
+ PlaybackService.getPlayerActivityIntent(this),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ boolean nothingPlaying = false;
+ Playable media;
+ PlayerStatus status;
+ if (playbackService != null) {
+ media = playbackService.getPlayable();
+ status = playbackService.getStatus();
+ } else {
+ media = Playable.PlayableUtils.createInstanceFromPreferences(getApplicationContext());
+ status = PlayerStatus.STOPPED;
+ }
+
+ if (media != null) {
+ views.setOnClickPendingIntent(R.id.layout_left, startMediaplayer);
+
+ views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle());
+
+ String progressString;
+ if (playbackService != null) {
+ progressString = getProgressString(playbackService.getCurrentPosition(), playbackService.getDuration());
+ } else {
+ progressString = getProgressString(media.getPosition(), media.getDuration());
+ }
+
+ if (progressString != null) {
+ views.setViewVisibility(R.id.txtvProgress, View.VISIBLE);
+ views.setTextViewText(R.id.txtvProgress, progressString);
+ }
+
+ if (status == PlayerStatus.PLAYING) {
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause_white_24dp);
+ if (Build.VERSION.SDK_INT >= 15) {
+ views.setContentDescription(R.id.butPlay, getString(R.string.pause_label));
+ }
+ } else {
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
+ if (Build.VERSION.SDK_INT >= 15) {
+ views.setContentDescription(R.id.butPlay, getString(R.string.play_label));
+ }
+ }
+ views.setOnClickPendingIntent(R.id.butPlay, createMediaButtonIntent());
+ } else {
+ nothingPlaying = true;
+ }
+
+ if (nothingPlaying) {
+ // start the app if they click anything
+ views.setOnClickPendingIntent(R.id.layout_left, startAppPending);
+ views.setOnClickPendingIntent(R.id.butPlay, startAppPending);
+ views.setViewVisibility(R.id.txtvProgress, View.INVISIBLE);
+ views.setTextViewText(R.id.txtvTitle,
+ this.getString(R.string.no_media_playing_label));
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp);
+ }
+
+ manager.updateAppWidget(playerWidget, views);
+ }
+
+ /**
+ * Creates an intent which fakes a mediabutton press
+ */
+ private PendingIntent createMediaButtonIntent() {
+ KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
+ Intent startingIntent = new Intent(getBaseContext(), MediaButtonReceiver.class);
+ startingIntent.setAction(MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER);
+ startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event);
+
+ return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
+ }
+
+ private String getProgressString(int position, int duration) {
+ if (position > 0 && duration > 0) {
+ return Converter.getDurationStringLong(position) + " / "
+ + Converter.getDurationStringLong(duration);
+ } else {
+ return null;
+ }
+ }
+
+ private final ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ Log.d(TAG, "Connection to service established");
+ if (service instanceof PlaybackService.LocalBinder) {
+ synchronized (waitForService) {
+ playbackService = ((PlaybackService.LocalBinder) service).getService();
+ waitForService.notifyAll();
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ playbackService = null;
+ 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 eb28050f0..97007a214 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
@@ -40,12 +40,15 @@ import okhttp3.internal.http.StatusLine;
* Provides access to a HttpClient singleton.
*/
public class AntennapodHttpClient {
+
+ private AntennapodHttpClient(){}
+
private static final String TAG = "AntennapodHttpClient";
- public static final int CONNECTION_TIMEOUT = 30000;
- public static final int READ_TIMEOUT = 30000;
+ private static final int CONNECTION_TIMEOUT = 30000;
+ private static final int READ_TIMEOUT = 30000;
- public static final int MAX_CONNECTIONS = 8;
+ private static final int MAX_CONNECTIONS = 8;
private static volatile OkHttpClient httpClient = null;
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 de91916a9..48234c387 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
@@ -17,15 +17,15 @@ public class DownloadRequest implements Parcelable {
private String username;
private String password;
private String lastModified;
- private boolean deleteOnFailure;
+ private final boolean deleteOnFailure;
private final long feedfileId;
private final int feedfileType;
private final Bundle arguments;
- protected int progressPercent;
- protected long soFar;
- protected long size;
- protected int statusMsg;
+ private int progressPercent;
+ private long soFar;
+ private long size;
+ private int statusMsg;
public DownloadRequest(@NonNull String destination,
@NonNull String source,
@@ -53,7 +53,7 @@ public class DownloadRequest implements Parcelable {
this(destination, source, title, feedfileId, feedfileType, null, null, true, null);
}
- public DownloadRequest(Builder builder) {
+ private DownloadRequest(Builder builder) {
this.destination = builder.destination;
this.source = builder.source;
this.title = builder.title;
@@ -124,7 +124,7 @@ public class DownloadRequest implements Parcelable {
@Override
public boolean equals(Object o) {
if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ if (o == null || !(o instanceof DownloadRequest)) return false;
DownloadRequest that = (DownloadRequest) o;
@@ -211,10 +211,6 @@ public class DownloadRequest implements Parcelable {
this.size = size;
}
- public int getStatusMsg() {
- return statusMsg;
- }
-
public void setStatusMsg(int statusMsg) {
this.statusMsg = statusMsg;
}
@@ -254,15 +250,15 @@ public class DownloadRequest implements Parcelable {
}
public static class Builder {
- private String destination;
- private String source;
- private String title;
+ private final String destination;
+ private final String source;
+ private final String title;
private String username;
private String password;
private String lastModified;
private boolean deleteOnFailure = false;
- private long feedfileId;
- private int feedfileType;
+ private final long feedfileId;
+ private final int feedfileType;
private Bundle arguments;
public Builder(@NonNull String destination, @NonNull FeedFile item) {
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 6eefc296f..ae1e9de86 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,15 +7,12 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
import android.media.MediaMetadataRetriever;
import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.support.annotation.NonNull;
-import android.support.annotation.VisibleForTesting;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
@@ -23,7 +20,6 @@ import android.util.Pair;
import android.webkit.URLUtil;
import org.apache.commons.io.FileUtils;
-import org.apache.commons.lang3.StringUtils;
import org.xml.sax.SAXException;
import java.io.File;
@@ -32,10 +28,8 @@ import java.net.HttpURLConnection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
-import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService;
@@ -57,7 +51,6 @@ 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.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
@@ -77,6 +70,7 @@ import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeExceptio
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;
import de.greenrobot.event.EventBus;
/**
@@ -119,17 +113,12 @@ public class DownloadService extends Service {
private CompletionService<Downloader> downloadExecutor;
private FeedSyncThread feedSyncThread;
- /**
- * Number of threads of downloadExecutor.
- */
- private static final int NUM_PARALLEL_DOWNLOADS = 6;
-
private DownloadRequester requester;
private NotificationCompat.Builder notificationCompatBuilder;
- private int NOTIFICATION_ID = 2;
- private int REPORT_ID = 3;
+ private static final int NOTIFICATION_ID = 2;
+ private static final int REPORT_ID = 3;
/**
* Currently running downloads.
@@ -153,7 +142,7 @@ public class DownloadService extends Service {
private static final int SCHED_EX_POOL_SIZE = 1;
private ScheduledThreadPoolExecutor schedExecutor;
- private Handler postHandler = new Handler();
+ private final Handler postHandler = new Handler();
private final IBinder mBinder = new LocalBinder();
@@ -163,7 +152,7 @@ public class DownloadService extends Service {
}
}
- private Thread downloadCompletionThread = new Thread() {
+ private final Thread downloadCompletionThread = new Thread() {
private static final String TAG = "downloadCompletionThd";
@Override
@@ -296,6 +285,7 @@ public class DownloadService extends Service {
setupNotificationBuilders();
requester = DownloadRequester.getInstance();
+ startForeground(NOTIFICATION_ID, updateNotifications());
}
@Override
@@ -340,12 +330,9 @@ public class DownloadService extends Service {
}
private void setupNotificationBuilders() {
- Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.stat_notify_sync);
-
- notificationCompatBuilder = new NotificationCompat.Builder(this)
+ notificationCompatBuilder = new NotificationCompat.Builder(this, NotificationUtils.CHANNEL_ID_DOWNLOADING)
.setOngoing(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(this))
- .setLargeIcon(icon)
.setSmallIcon(R.drawable.stat_notify_sync);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notificationCompatBuilder.setVisibility(Notification.VISIBILITY_PUBLIC);
@@ -356,7 +343,7 @@ public class DownloadService extends Service {
/**
* Updates the contents of the service's notifications. Should be called
- * before setupNotificationBuilders.
+ * after setupNotificationBuilders.
*/
private Notification updateNotifications() {
if (notificationCompatBuilder == null) {
@@ -385,7 +372,7 @@ public class DownloadService extends Service {
return null;
}
- private BroadcastReceiver cancelDownloadReceiver = new BroadcastReceiver() {
+ private final BroadcastReceiver cancelDownloadReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -424,6 +411,8 @@ public class DownloadService extends Service {
"ACTION_ENQUEUE_DOWNLOAD intent needs request extra");
}
+ writeFileUrl(request);
+
Downloader downloader = getDownloader(request);
if (downloader != null) {
numberOfDownloads.incrementAndGet();
@@ -491,9 +480,7 @@ public class DownloadService extends Service {
if (status.isSuccessful()) {
successfulDownloads++;
} else if (!status.isCancelled()) {
- if (status.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE) {
- createReport = true;
- }
+ createReport = true;
failedDownloads++;
}
}
@@ -501,7 +488,7 @@ public class DownloadService extends Service {
if (createReport) {
Log.d(TAG, "Creating report");
// create notification object
- NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
+ 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(
@@ -510,10 +497,6 @@ public class DownloadService extends Service {
successfulDownloads, failedDownloads)
)
.setSmallIcon(R.drawable.stat_notify_sync_error)
- .setLargeIcon(
- BitmapFactory.decodeResource(getResources(),
- R.drawable.stat_notify_sync_error)
- )
.setContentIntent(
ClientConfig.downloadServiceCallbacks.getReportNotificationContentIntent(this)
)
@@ -533,14 +516,14 @@ public class DownloadService extends Service {
* 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.
*/
- void queryDownloadsAsync() {
+ private void queryDownloadsAsync() {
handler.post(DownloadService.this::queryDownloads);
}
/**
* Check if there's something else to download, otherwise stop
*/
- void queryDownloads() {
+ private void queryDownloads() {
Log.d(TAG, numberOfDownloads.get() + " downloads left");
if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) {
@@ -557,14 +540,13 @@ public class DownloadService extends Service {
final String resourceTitle = (downloadRequest.getTitle() != null) ?
downloadRequest.getTitle() : downloadRequest.getSource();
- NotificationCompat.Builder builder = new NotificationCompat.Builder(DownloadService.this);
+ 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_stat_authentication)
- .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_stat_authentication))
.setAutoCancel(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(DownloadService.this, downloadRequest));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
@@ -603,14 +585,14 @@ public class DownloadService extends Service {
private class FeedSyncThread extends Thread {
private static final String TAG = "FeedSyncThread";
- private BlockingQueue<DownloadRequest> completedRequests = new LinkedBlockingDeque<>();
- private CompletionService<Pair<DownloadRequest, FeedHandlerResult>> parserService = new ExecutorCompletionService<>(Executors.newSingleThreadExecutor());
- private ExecutorService dbService = Executors.newSingleThreadExecutor();
+ 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 final long WAIT_TIMEOUT = 3000;
+ private static final long WAIT_TIMEOUT = 3000;
/**
@@ -695,10 +677,6 @@ public class DownloadService extends Service {
Log.d(TAG, "Bundling " + results.size() + " feeds");
- for (Pair<DownloadRequest, FeedHandlerResult> result : results) {
- removeDuplicateImages(result.second.feed); // duplicate images have to removed because the DownloadRequester does not accept two downloads with the same download URL yet.
- }
-
// Save information of feed in DB
if (dbUpdateFuture != null) {
try {
@@ -765,7 +743,7 @@ public class DownloadService extends Service {
private class FeedParserTask implements Callable<Pair<DownloadRequest, FeedHandlerResult>> {
- private DownloadRequest request;
+ private final DownloadRequest request;
private FeedParserTask(DownloadRequest request) {
this.request = request;
@@ -873,22 +851,6 @@ public class DownloadService extends Service {
return true;
}
- /**
- * Delete files that aren't needed anymore
- */
- private void cleanup(Feed feed) {
- if (feed.getFile_url() != null) {
- if (new File(feed.getFile_url()).delete()) {
- Log.d(TAG, "Successfully deleted cache file.");
- } else {
- Log.e(TAG, "Failed to delete cache file.");
- }
- feed.setFile_url(null);
- } else {
- Log.d(TAG, "Didn't delete cache file: File url is not set.");
- }
- }
-
public void shutdown() {
isActive = false;
if (isCollectingRequests) {
@@ -906,6 +868,42 @@ public class DownloadService extends Service {
}
/**
+ * 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.
+ */
+ private void writeFileUrl(DownloadRequest request) {
+ if (request.getFeedfileType() != FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
+ return;
+ }
+
+ File dest = new File(request.getDestination());
+ if (!dest.exists()) {
+ try {
+ dest.createNewFile();
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to create file");
+ }
+ }
+
+ if (dest.exists()) {
+ Log.d(TAG, "Writing file url");
+ FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
+ if (media == null) {
+ Log.d(TAG, "No media");
+ return;
+ }
+ media.setFile_url(request.getDestination());
+ try {
+ DBWriter.setFeedMedia(media).get();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "writeFileUrl was interrupted");
+ } catch (ExecutionException e) {
+ Log.e(TAG, "ExecutionException in writeFileUrl: " + e.getMessage());
+ }
+ }
+ }
+
+ /**
* Handles failed downloads.
* <p/>
* If the file has been partially downloaded, this handler will set the file_url of the FeedFile to the location
@@ -913,10 +911,10 @@ public class DownloadService extends Service {
* <p/>
* Currently, this handler only handles FeedMedia objects, because Feeds and FeedImages are deleted if the download fails.
*/
- private class FailedDownloadHandler implements Runnable {
+ private static class FailedDownloadHandler implements Runnable {
- private DownloadRequest request;
- private DownloadStatus status;
+ private final DownloadRequest request;
+ private final DownloadStatus status;
FailedDownloadHandler(DownloadStatus status, DownloadRequest request) {
this.request = request;
@@ -929,23 +927,6 @@ public class DownloadService extends Service {
DBWriter.setFeedLastUpdateFailed(request.getFeedfileId(), true);
} else if (request.isDeleteOnFailure()) {
Log.d(TAG, "Ignoring failed download, deleteOnFailure=true");
- } else {
- File dest = new File(request.getDestination());
- if (dest.exists() && request.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) {
- Log.d(TAG, "File has been partially downloaded. Writing file url");
- FeedMedia media = DBReader.getFeedMedia(request.getFeedfileId());
- if (media == null) {
- return;
- }
- media.setFile_url(request.getDestination());
- try {
- DBWriter.setFeedMedia(media).get();
- } catch (InterruptedException e) {
- Log.e(TAG, "FailedDownloadHandler was interrupted");
- } catch (ExecutionException e) {
- Log.e(TAG, "ExecutionException in FailedDownloadHandler: " + e.getMessage());
- }
- }
}
}
}
@@ -955,7 +936,7 @@ public class DownloadService extends Service {
*/
private class MediaHandlerThread implements Runnable {
- private DownloadRequest request;
+ private final DownloadRequest request;
private DownloadStatus status;
MediaHandlerThread(@NonNull DownloadStatus status,
@@ -976,7 +957,9 @@ public class DownloadService extends Service {
media.checkEmbeddedPicture(); // enforce check
// check if file has chapters
- ChapterUtils.loadChaptersFromFileUrl(media);
+ if(media.getItem() != null && !media.getItem().hasChapters()) {
+ ChapterUtils.loadChaptersFromFileUrl(media);
+ }
// Get duration
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
@@ -1069,7 +1052,7 @@ public class DownloadService extends Service {
private long lastPost = 0;
- final Runnable postDownloaderTask = new Runnable() {
+ private final Runnable postDownloaderTask = new Runnable() {
@Override
public void run() {
List<Downloader> list = Collections.unmodifiableList(downloads);
@@ -1087,26 +1070,6 @@ public class DownloadService extends Service {
}
}
- /**
- * Checks if the FeedItems of this feed have images that point to the same URL. If two FeedItems
- * have an image that points to the same URL, the reference of the second item is removed, so
- * that every image reference is unique.
- */
- @VisibleForTesting
- public static void removeDuplicateImages(Feed feed) {
- Set<String> known = new HashSet<>();
- for (FeedItem item : feed.getItems()) {
- String url = item.hasItemImage() ? item.getImage().getDownload_url() : null;
- if (url != null) {
- if (known.contains(url)) {
- item.setImage(null);
- } else {
- known.add(url);
- }
- }
- }
- }
-
private static String compileNotificationString(List<Downloader> downloads) {
List<String> lines = new ArrayList<>(downloads.size());
for (Downloader downloader : downloads) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
index ed2b00dfe..5debc6d05 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
@@ -19,37 +19,37 @@ public class DownloadStatus {
// ----------------------------------- ATTRIBUTES STORED IN DB
/** Unique id for storing the object in database. */
- protected long id;
+ private long id;
/**
* A human-readable string which is shown to the user so that he can
* identify the download. Should be the title of the item/feed/media or the
* URL if the download has no other title.
*/
- protected String title;
- protected DownloadError reason;
+ private final String title;
+ private DownloadError reason;
/**
* A message which can be presented to the user to give more information.
* Should be null if Download was successful.
*/
- protected String reasonDetailed;
- protected boolean successful;
- protected Date completionDate;
- protected long feedfileId;
+ private String reasonDetailed;
+ private boolean successful;
+ private Date completionDate;
+ private final long feedfileId;
/**
* Is used to determine the type of the feedfile even if the feedfile does
* not exist anymore. The value should be FEEDFILETYPE_FEED,
* FEEDFILETYPE_FEEDIMAGE or FEEDFILETYPE_FEEDMEDIA
*/
- protected int feedfileType;
+ private final int feedfileType;
// ------------------------------------ NOT STORED IN DB
- protected boolean done;
- protected boolean cancelled;
+ private boolean done;
+ private boolean cancelled;
/** Constructor for restoring Download status entries from DB. */
- public DownloadStatus(long id, String title, long feedfileId,
- int feedfileType, boolean successful, DownloadError reason,
- Date completionDate, String reasonDetailed) {
+ private DownloadStatus(long id, String title, long feedfileId,
+ int feedfileType, boolean successful, DownloadError reason,
+ Date completionDate, String reasonDetailed) {
this.id = id;
this.title = title;
this.done = true;
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 d8042d202..445210d3a 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
@@ -14,14 +14,14 @@ import de.danoeh.antennapod.core.R;
public abstract class Downloader implements Callable<Downloader> {
private static final String TAG = "Downloader";
- protected volatile boolean finished;
+ private volatile boolean finished;
- protected volatile boolean cancelled;
+ volatile boolean cancelled;
- protected DownloadRequest request;
- protected DownloadStatus result;
+ final DownloadRequest request;
+ final DownloadStatus result;
- public Downloader(DownloadRequest request) {
+ Downloader(DownloadRequest request) {
super();
this.request = request;
this.request.setStatusMsg(R.string.download_pending);
@@ -33,7 +33,7 @@ public abstract class Downloader implements Callable<Downloader> {
public final Downloader call() {
WifiManager wifiManager = (WifiManager)
- ClientConfig.applicationCallbacks.getApplicationInstance().getSystemService(Context.WIFI_SERVICE);
+ ClientConfig.applicationCallbacks.getApplicationInstance().getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiManager.WifiLock wifiLock = null;
if (wifiManager != null) {
wifiLock = wifiManager.createWifiLock(TAG);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderCallback.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderCallback.java
deleted file mode 100644
index b0829f084..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloaderCallback.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package de.danoeh.antennapod.core.service.download;
-
-/**
- * Callback used by the Downloader-classes to notify the requester that the
- * download has completed.
- */
-public interface DownloaderCallback {
-
- void onDownloadCompleted(Downloader downloader);
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
index b409a419a..842ad0c60 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
@@ -20,7 +20,6 @@ import java.util.Date;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.DownloadError;
@@ -50,13 +49,8 @@ public class HttpDownloader extends Downloader {
if (request.isDeleteOnFailure() && fileExists) {
Log.w(TAG, "File already exists");
- if (request.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE) {
- onFail(DownloadError.ERROR_FILE_EXISTS, null);
- return;
- } else {
- onSuccess();
- return;
- }
+ onSuccess();
+ return;
}
OkHttpClient.Builder httpClientBuilder = AntennapodHttpClient.newBuilder();
@@ -93,7 +87,7 @@ public class HttpDownloader extends Downloader {
// add range header if necessary
- if (fileExists) {
+ if (fileExists && destination.length() > 0) {
request.setSoFar(destination.length());
httpReq.addHeader("Range", "bytes=" + request.getSoFar() + "-");
Log.d(TAG, "Adding range header: " + request.getSoFar());
@@ -310,13 +304,13 @@ public class HttpDownloader extends Downloader {
String encoded = ByteString.of(bytes).base64();
return "Basic " + encoded;
} catch (UnsupportedEncodingException e) {
- throw new AssertionError();
+ throw new AssertionError(e);
}
}
- private class BasicAuthorizationInterceptor implements Interceptor {
+ private static class BasicAuthorizationInterceptor implements Interceptor {
- private DownloadRequest downloadRequest;
+ private final DownloadRequest downloadRequest;
public BasicAuthorizationInterceptor(DownloadRequest downloadRequest) {
this.downloadRequest = downloadRequest;
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
new file mode 100644
index 000000000..e1d87e1d5
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -0,0 +1,250 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import android.content.Context;
+import android.net.Uri;
+import android.view.SurfaceHolder;
+
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.DefaultLoadControl;
+import com.google.android.exoplayer2.DefaultRenderersFactory;
+import com.google.android.exoplayer2.ExoPlaybackException;
+import com.google.android.exoplayer2.ExoPlayerFactory;
+import com.google.android.exoplayer2.PlaybackParameters;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.SeekParameters;
+import com.google.android.exoplayer2.SimpleExoPlayer;
+import com.google.android.exoplayer2.Timeline;
+import com.google.android.exoplayer2.audio.AudioAttributes;
+import com.google.android.exoplayer2.source.ExtractorMediaSource;
+import com.google.android.exoplayer2.source.MediaSource;
+import com.google.android.exoplayer2.source.TrackGroupArray;
+import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
+import com.google.android.exoplayer2.trackselection.TrackSelectionArray;
+import com.google.android.exoplayer2.upstream.DataSource;
+import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory;
+import com.google.android.exoplayer2.util.Util;
+
+import org.antennapod.audio.MediaPlayer;
+
+import de.danoeh.antennapod.core.util.playback.IPlayer;
+
+
+public class ExoPlayerWrapper implements IPlayer {
+ private final Context mContext;
+ private SimpleExoPlayer mExoPlayer;
+ private MediaSource mediaSource;
+ private MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener;
+ private MediaPlayer.OnCompletionListener audioCompletionListener;
+ private MediaPlayer.OnErrorListener audioErrorListener;
+
+ ExoPlayerWrapper(Context context) {
+ mContext = context;
+ mExoPlayer = createPlayer();
+ }
+
+ private SimpleExoPlayer createPlayer() {
+ SimpleExoPlayer p = ExoPlayerFactory.newSimpleInstance(new DefaultRenderersFactory(mContext),
+ new DefaultTrackSelector(), new DefaultLoadControl());
+ p.setSeekParameters(SeekParameters.PREVIOUS_SYNC);
+ p.addListener(new Player.EventListener() {
+ @Override
+ public void onTimelineChanged(Timeline timeline, Object manifest, int reason) {
+
+ }
+
+ @Override
+ public void onTracksChanged(TrackGroupArray trackGroups, TrackSelectionArray trackSelections) {
+
+ }
+
+ @Override
+ public void onLoadingChanged(boolean isLoading) {
+
+ }
+
+ @Override
+ public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
+ if (playbackState == Player.STATE_ENDED) {
+ audioCompletionListener.onCompletion(null);
+ }
+ }
+
+ @Override
+ public void onRepeatModeChanged(int repeatMode) {
+
+ }
+
+ @Override
+ public void onShuffleModeEnabledChanged(boolean shuffleModeEnabled) {
+
+ }
+
+ @Override
+ public void onPlayerError(ExoPlaybackException error) {
+ if (audioErrorListener != null) {
+ audioErrorListener.onError(null, 0, 0);
+ }
+ }
+
+ @Override
+ public void onPositionDiscontinuity(int reason) {
+
+ }
+
+ @Override
+ public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
+
+ }
+
+ @Override
+ public void onSeekProcessed() {
+ audioSeekCompleteListener.onSeekComplete(null);
+ }
+ });
+ return p;
+ }
+
+ @Override
+ public boolean canSetSpeed() {
+ return true;
+ }
+
+ @Override
+ public boolean canDownmix() {
+ return false;
+ }
+
+ @Override
+ public int getCurrentPosition() {
+ return (int) mExoPlayer.getCurrentPosition();
+ }
+
+ @Override
+ public float getCurrentSpeedMultiplier() {
+ return mExoPlayer.getPlaybackParameters().speed;
+ }
+
+ @Override
+ public int getDuration() {
+ if (mExoPlayer.getDuration() == C.TIME_UNSET) {
+ return PlaybackServiceMediaPlayer.INVALID_TIME;
+ }
+ return (int) mExoPlayer.getDuration();
+ }
+
+ @Override
+ public boolean isPlaying() {
+ return mExoPlayer.getPlayWhenReady();
+ }
+
+ @Override
+ public void pause() {
+ mExoPlayer.setPlayWhenReady(false);
+ }
+
+ @Override
+ public void prepare() throws IllegalStateException {
+ mExoPlayer.prepare(mediaSource);
+ }
+
+ @Override
+ public void release() {
+ if (mExoPlayer != null) {
+ mExoPlayer.release();
+ }
+ audioSeekCompleteListener = null;
+ audioCompletionListener = null;
+ audioErrorListener = null;
+ }
+
+ @Override
+ public void reset() {
+ mExoPlayer.release();
+ mExoPlayer = createPlayer();
+ }
+
+ @Override
+ public void seekTo(int i) throws IllegalStateException {
+ mExoPlayer.seekTo(i);
+ }
+
+ @Override
+ public void setAudioStreamType(int i) {
+ AudioAttributes a = mExoPlayer.getAudioAttributes();
+ AudioAttributes.Builder b = new AudioAttributes.Builder();
+ b.setContentType(i);
+ b.setFlags(a.flags);
+ b.setUsage(a.usage);
+ mExoPlayer.setAudioAttributes(b.build());
+ }
+
+ @Override
+ public void setDataSource(String s) throws IllegalArgumentException, IllegalStateException {
+ DataSource.Factory dataSourceFactory =
+ new DefaultDataSourceFactory(mContext, Util.getUserAgent(mContext, mContext.getPackageName()), null);
+ ExtractorMediaSource.Factory f = new ExtractorMediaSource.Factory(dataSourceFactory);
+ mediaSource = f.createMediaSource(Uri.parse(s));
+ }
+
+ @Override
+ public void setDisplay(SurfaceHolder sh) {
+ mExoPlayer.setVideoSurfaceHolder(sh);
+ }
+
+ @Override
+ public void setPlaybackSpeed(float v) {
+ PlaybackParameters params = mExoPlayer.getPlaybackParameters();
+ mExoPlayer.setPlaybackParameters(new PlaybackParameters(v, params.pitch));
+ }
+
+ @Override
+ public void setDownmix(boolean b) {
+
+ }
+
+ @Override
+ public void setVolume(float v, float v1) {
+ mExoPlayer.setVolume(v);
+ }
+
+ @Override
+ public void setWakeMode(Context context, int i) {
+
+ }
+
+ @Override
+ public void start() {
+ mExoPlayer.setPlayWhenReady(true);
+ }
+
+ @Override
+ public void stop() {
+ mExoPlayer.stop();
+ }
+
+ void setOnCompletionListener(MediaPlayer.OnCompletionListener audioCompletionListener) {
+ this.audioCompletionListener = audioCompletionListener;
+ }
+
+ void setOnSeekCompleteListener(MediaPlayer.OnSeekCompleteListener audioSeekCompleteListener) {
+ this.audioSeekCompleteListener = audioSeekCompleteListener;
+ }
+
+ void setOnErrorListener(MediaPlayer.OnErrorListener audioErrorListener) {
+ this.audioErrorListener = audioErrorListener;
+ }
+
+ int getVideoWidth() {
+ if (mExoPlayer.getVideoFormat() == null) {
+ return 0;
+ }
+ return mExoPlayer.getVideoFormat().width;
+ }
+
+ int getVideoHeight() {
+ if (mExoPlayer.getVideoFormat() == null) {
+ return 0;
+ }
+ return mExoPlayer.getVideoFormat().height;
+ }
+}
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 11cd21db5..c7948b157 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
@@ -1,7 +1,10 @@
package de.danoeh.antennapod.core.service.playback;
import android.content.Context;
+import android.media.AudioAttributes;
+import android.media.AudioFocusRequest;
import android.media.AudioManager;
+import android.os.Build;
import android.os.PowerManager;
import android.support.annotation.NonNull;
import android.telephony.TelephonyManager;
@@ -11,6 +14,7 @@ import android.view.SurfaceHolder;
import org.antennapod.audio.MediaPlayer;
+import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
@@ -32,7 +36,7 @@ import de.danoeh.antennapod.core.util.playback.VideoPlayer;
* Manages the MediaPlayer object of the PlaybackService.
*/
public class LocalPSMP extends PlaybackServiceMediaPlayer {
- public static final String TAG = "LclPlaybackSvcMPlayer";
+ private static final String TAG = "LclPlaybackSvcMPlayer";
private final AudioManager audioManager;
@@ -42,7 +46,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
private volatile boolean stream;
private volatile MediaType mediaType;
- private volatile AtomicBoolean startWhenPrepared;
+ private final AtomicBoolean startWhenPrepared;
private volatile boolean pausedBecauseOfTransientAudiofocusLoss;
private volatile Pair<Integer, Integer> videoSize;
@@ -165,8 +169,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
callback.onMediaChanged(false);
if (stream) {
mediaPlayer.setDataSource(media.getStreamUrl());
- } else {
+ } else if (new File(media.getLocalMediaUrl()).canRead()) {
mediaPlayer.setDataSource(media.getLocalMediaUrl());
+ } else {
+ throw new IOException("Unable to read local file " + media.getLocalMediaUrl());
}
setPlayerStatus(PlayerStatus.INITIALIZED, media);
@@ -199,9 +205,26 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
private void resumeSync() {
if (playerStatus == PlayerStatus.PAUSED || playerStatus == PlayerStatus.PREPARED) {
- int focusGained = audioManager.requestAudioFocus(
- audioFocusChangeListener, AudioManager.STREAM_MUSIC,
- AudioManager.AUDIOFOCUS_GAIN);
+ int focusGained;
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setUsage(AudioAttributes.USAGE_MEDIA)
+ .setContentType(AudioAttributes.CONTENT_TYPE_SPEECH)
+ .build();
+ AudioFocusRequest audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
+ .setAudioAttributes(audioAttributes)
+ .setOnAudioFocusChangeListener(audioFocusChangeListener)
+ .setAcceptsDelayedFocusGain(true)
+ .setWillPauseWhenDucked(true)
+ .build();
+ focusGained = audioManager.requestAudioFocus(audioFocusRequest);
+ } else {
+ focusGained = audioManager.requestAudioFocus(
+ audioFocusChangeListener, AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN);
+ }
+
if (focusGained == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
Log.d(TAG, "Audiofocus successfully requested");
Log.d(TAG, "Resuming/Starting playback");
@@ -256,7 +279,13 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
setPlayerStatus(PlayerStatus.PAUSED, media, getPosition());
if (abandonFocus) {
- audioManager.abandonAudioFocus(audioFocusChangeListener);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ AudioFocusRequest.Builder builder = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
+ .setOnAudioFocusChangeListener(audioFocusChangeListener);
+ audioManager.abandonAudioFocusRequest(builder.build());
+ } else {
+ audioManager.abandonAudioFocus(audioFocusChangeListener);
+ }
pausedBecauseOfTransientAudiofocusLoss = false;
}
if (stream && reinit) {
@@ -310,7 +339,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
Log.d(TAG, "Resource prepared");
- if (mediaType == MediaType.VIDEO) {
+ if (mediaType == MediaType.VIDEO && mediaPlayer instanceof ExoPlayerWrapper) {
+ ExoPlayerWrapper vp = (ExoPlayerWrapper) mediaPlayer;
+ videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
+ } else if(mediaType == MediaType.VIDEO && mediaPlayer instanceof VideoPlayer) {
VideoPlayer vp = (VideoPlayer) mediaPlayer;
videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
}
@@ -444,7 +476,8 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|| playerStatus == PlayerStatus.PAUSED
|| playerStatus == PlayerStatus.PREPARED) {
retVal = mediaPlayer.getDuration();
- } else if (media != null && media.getDuration() > 0) {
+ }
+ if (retVal <= 0 && media != null && media.getDuration() > 0) {
retVal = media.getDuration();
}
@@ -606,11 +639,30 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
public void shutdown() {
executor.shutdown();
if (mediaPlayer != null) {
+ try {
+ removeMediaPlayerErrorListener();
+ if (mediaPlayer.isPlaying()) {
+ mediaPlayer.stop();
+ }
+ } catch (Exception ignore) { }
mediaPlayer.release();
}
releaseWifiLockIfNecessary();
}
+ private void removeMediaPlayerErrorListener() {
+ if (mediaPlayer instanceof VideoPlayer) {
+ VideoPlayer vp = (VideoPlayer) mediaPlayer;
+ vp.setOnErrorListener((mp, what, extra) -> true);
+ } else if (mediaPlayer instanceof AudioPlayer) {
+ AudioPlayer ap = (AudioPlayer) mediaPlayer;
+ ap.setOnErrorListener((mediaPlayer, i, i1) -> true);
+ } else if (mediaPlayer instanceof ExoPlayerWrapper) {
+ ExoPlayerWrapper ap = (ExoPlayerWrapper) mediaPlayer;
+ ap.setOnErrorListener((mediaPlayer, i, i1) -> true);
+ }
+ }
+
/**
* Releases internally used resources. This method should only be called when the object is not used anymore.
* This method is executed on an internal executor service.
@@ -663,6 +715,10 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
Pair<Integer, Integer> res;
if (mediaPlayer == null || playerStatus == PlayerStatus.ERROR || mediaType != MediaType.VIDEO) {
res = null;
+ } else if (mediaPlayer instanceof ExoPlayerWrapper) {
+ ExoPlayerWrapper vp = (ExoPlayerWrapper) mediaPlayer;
+ videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
+ res = videoSize;
} else {
VideoPlayer vp = (VideoPlayer) mediaPlayer;
videoSize = new Pair<>(vp.getVideoWidth(), vp.getVideoHeight());
@@ -692,15 +748,19 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
if (mediaPlayer != null) {
mediaPlayer.release();
}
- if(media == null) {
+ if (media == null) {
mediaPlayer = null;
return;
}
- if (media.getMediaType() == MediaType.VIDEO) {
+
+ if (UserPreferences.useExoplayer()) {
+ mediaPlayer = new ExoPlayerWrapper(context);
+ } else if (media.getMediaType() == MediaType.VIDEO) {
mediaPlayer = new VideoPlayer();
} else {
mediaPlayer = new AudioPlayer(context);
}
+
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setWakeMode(context, PowerManager.PARTIAL_WAKE_LOCK);
setMediaPlayerListeners(mediaPlayer);
@@ -710,52 +770,49 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
@Override
public void onAudioFocusChange(final int focusChange) {
- executor.submit(new Runnable() {
- @Override
- public void run() {
- playerLock.lock();
-
- // If there is an incoming call, playback should be paused permanently
- TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
- final int callState = (tm != null) ? tm.getCallState() : 0;
- Log.i(TAG, "Call state:" + callState);
-
- if (focusChange == AudioManager.AUDIOFOCUS_LOSS ||
- (!UserPreferences.shouldResumeAfterCall() && callState != TelephonyManager.CALL_STATE_IDLE)) {
- Log.d(TAG, "Lost audio focus");
- pause(true, false);
- callback.shouldStop();
- } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
- Log.d(TAG, "Gained audio focus");
- if (pausedBecauseOfTransientAudiofocusLoss) { // we paused => play now
- resume();
- } else { // we ducked => raise audio level back
- setVolumeSync(UserPreferences.getLeftVolume(),
- UserPreferences.getRightVolume());
- }
- } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
- if (playerStatus == PlayerStatus.PLAYING) {
- if (!UserPreferences.shouldPauseForFocusLoss()) {
- Log.d(TAG, "Lost audio focus temporarily. Ducking...");
- final float DUCK_FACTOR = 0.25f;
- setVolumeSync(DUCK_FACTOR * UserPreferences.getLeftVolume(),
- DUCK_FACTOR * UserPreferences.getRightVolume());
- pausedBecauseOfTransientAudiofocusLoss = false;
- } else {
- Log.d(TAG, "Lost audio focus temporarily. Could duck, but won't, pausing...");
- pause(false, false);
- pausedBecauseOfTransientAudiofocusLoss = true;
- }
- }
- } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
- if (playerStatus == PlayerStatus.PLAYING) {
- Log.d(TAG, "Lost audio focus temporarily. Pausing...");
+ executor.submit(() -> {
+ playerLock.lock();
+
+ // If there is an incoming call, playback should be paused permanently
+ TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
+ final int callState = (tm != null) ? tm.getCallState() : 0;
+ Log.i(TAG, "Call state:" + callState);
+
+ if (focusChange == AudioManager.AUDIOFOCUS_LOSS ||
+ (!UserPreferences.shouldResumeAfterCall() && callState != TelephonyManager.CALL_STATE_IDLE)) {
+ Log.d(TAG, "Lost audio focus");
+ pause(true, false);
+ callback.shouldStop();
+ } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {
+ Log.d(TAG, "Gained audio focus");
+ if (pausedBecauseOfTransientAudiofocusLoss) { // we paused => play now
+ resume();
+ } else { // we ducked => raise audio level back
+ setVolumeSync(UserPreferences.getLeftVolume(),
+ UserPreferences.getRightVolume());
+ }
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {
+ if (playerStatus == PlayerStatus.PLAYING) {
+ if (!UserPreferences.shouldPauseForFocusLoss()) {
+ Log.d(TAG, "Lost audio focus temporarily. Ducking...");
+ final float DUCK_FACTOR = 0.25f;
+ setVolumeSync(DUCK_FACTOR * UserPreferences.getLeftVolume(),
+ DUCK_FACTOR * UserPreferences.getRightVolume());
+ pausedBecauseOfTransientAudiofocusLoss = false;
+ } else {
+ Log.d(TAG, "Lost audio focus temporarily. Could duck, but won't, pausing...");
pause(false, false);
pausedBecauseOfTransientAudiofocusLoss = true;
}
}
- playerLock.unlock();
+ } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {
+ if (playerStatus == PlayerStatus.PLAYING) {
+ Log.d(TAG, "Lost audio focus temporarily. Pausing...");
+ pause(false, false);
+ pausedBecauseOfTransientAudiofocusLoss = true;
+ }
}
+ playerLock.unlock();
});
}
};
@@ -784,7 +841,14 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
if (mediaPlayer != null) {
mediaPlayer.reset();
}
- audioManager.abandonAudioFocus(audioFocusChangeListener);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ AudioFocusRequest.Builder builder = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
+ .setOnAudioFocusChangeListener(audioFocusChangeListener);
+ audioManager.abandonAudioFocusRequest(builder.build());
+ } else {
+ audioManager.abandonAudioFocus(audioFocusChangeListener);
+ }
final Playable currentMedia = media;
Playable nextMedia = null;
@@ -880,6 +944,11 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
ap.setOnInfoListener(audioInfoListener);
ap.setOnSpeedAdjustmentAvailableChangedListener(audioSetSpeedAbilityListener);
+ } else if (mp instanceof ExoPlayerWrapper) {
+ ExoPlayerWrapper ap = (ExoPlayerWrapper) mp;
+ ap.setOnCompletionListener(audioCompletionListener);
+ ap.setOnSeekCompleteListener(audioSeekCompleteListener);
+ ap.setOnErrorListener(audioErrorListener);
} else {
Log.w(TAG, "Unknown media player: " + mp);
}
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 1661cc843..aea043a91 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
@@ -24,23 +24,22 @@ import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
+import android.support.v4.app.NotificationCompat;
import android.support.v4.media.MediaBrowserCompat;
import android.support.v4.media.MediaBrowserServiceCompat;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
-import android.support.v4.view.InputDeviceCompat;
-import android.support.v7.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
-import android.view.InputDevice;
import android.view.KeyEvent;
import android.view.SurfaceHolder;
import android.widget.Toast;
import com.bumptech.glide.Glide;
+import com.bumptech.glide.request.RequestOptions;
import com.bumptech.glide.request.target.Target;
import java.util.ArrayList;
@@ -49,6 +48,7 @@ import java.util.List;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.MessageEvent;
+import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -60,12 +60,15 @@ import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
+import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
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.util.IntList;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.QueueAccess;
+import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.greenrobot.event.EventBus;
@@ -74,8 +77,6 @@ import de.greenrobot.event.EventBus;
* Controls the MediaPlayer that plays a FeedMedia-file
*/
public class PlaybackService extends MediaBrowserServiceCompat {
- public static final String FORCE_WIDGET_UPDATE = "de.danoeh.antennapod.FORCE_WIDGET_UPDATE";
- public static final String STOP_WIDGET_UPDATE = "de.danoeh.antennapod.STOP_WIDGET_UPDATE";
/**
* Logging tag
*/
@@ -88,7 +89,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
/**
* True if cast session should disconnect.
*/
- public static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect";
+ private static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect";
/**
* True if media should be streamed.
*/
@@ -198,7 +199,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
/**
* Is true if the service was running, but paused due to headphone disconnect
*/
- public static boolean transientPause = false;
+ private static boolean transientPause = false;
/**
* Is true if a Cast Device is connected to the service.
*/
@@ -263,32 +264,24 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "Service created.");
isRunning = true;
- registerReceiver(autoStateUpdated, new IntentFilter(
- "com.google.android.gms.car.media.STATUS"));
- registerReceiver(headsetDisconnected, new IntentFilter(
- Intent.ACTION_HEADSET_PLUG));
- registerReceiver(shutdownReceiver, new IntentFilter(
- ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- registerReceiver(bluetoothStateUpdated, new IntentFilter(
- BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED));
- }
- 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));
+ NotificationCompat.Builder notificationBuilder = createBasicNotification();
+ startForeground(NOTIFICATION_ID, notificationBuilder.build());
+
+ registerReceiver(autoStateUpdated, new IntentFilter("com.google.android.gms.car.media.STATUS"));
+ registerReceiver(headsetDisconnected, new IntentFilter(Intent.ACTION_HEADSET_PLUG));
+ registerReceiver(shutdownReceiver, new IntentFilter(ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ registerReceiver(bluetoothStateUpdated, new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED));
+ 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));
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
flavorHelper = new PlaybackServiceFlavorHelper(PlaybackService.this, flavorHelperCallback);
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(prefListener);
- ComponentName eventReceiver = new ComponentName(getApplicationContext(),
- MediaButtonReceiver.class);
+ ComponentName eventReceiver = new ComponentName(getApplicationContext(), MediaButtonReceiver.class);
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(eventReceiver);
PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
@@ -311,7 +304,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
List<MediaSessionCompat.QueueItem> queueItems = new ArrayList<>();
try {
for (FeedItem feedItem : taskManager.getQueue()) {
- if(feedItem.getMedia() != null) {
+ if (feedItem.getMedia() != null) {
MediaDescriptionCompat mediaDescription = feedItem.getMedia().getMediaItem().getDescription();
queueItems.add(new MediaSessionCompat.QueueItem(mediaDescription, feedItem.getId()));
}
@@ -322,14 +315,34 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
flavorHelper.initializeMediaPlayer(PlaybackService.this);
-
mediaSession.setActive(true);
+
+ EventBus.getDefault().post(new ServiceEvent(ServiceEvent.Action.SERVICE_STARTED));
+ }
+
+ private NotificationCompat.Builder createBasicNotification() {
+ final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
+
+ final PendingIntent pIntent = PendingIntent.getActivity(this, 0,
+ PlaybackService.getPlayerActivityIntent(this),
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ return new NotificationCompat.Builder(
+ this, NotificationUtils.CHANNEL_ID_PLAYING)
+ .setContentTitle(getString(R.string.app_name))
+ .setContentText("Service is running") // Just in case the notification is not updated (should not occur)
+ .setOngoing(false)
+ .setContentIntent(pIntent)
+ .setWhen(0) // we don't need the time
+ .setSmallIcon(smallIcon)
+ .setPriority(NotificationCompat.PRIORITY_MIN);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Service is about to be destroyed");
+ stopForeground(true);
isRunning = false;
started = false;
currentMediaType = MediaType.UNKNOWN;
@@ -342,9 +355,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
unregisterReceiver(autoStateUpdated);
unregisterReceiver(headsetDisconnected);
unregisterReceiver(shutdownReceiver);
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- unregisterReceiver(bluetoothStateUpdated);
- }
+ unregisterReceiver(bluetoothStateUpdated);
unregisterReceiver(audioBecomingNoisy);
unregisterReceiver(skipCurrentEpisodeReceiver);
unregisterReceiver(pausePlayCurrentEpisodeReceiver);
@@ -354,6 +365,11 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.shutdown();
taskManager.shutdown();
}
+
+ private void stopService() {
+ stopForeground(true);
+ stopSelf();
+ }
@Override
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
@@ -379,10 +395,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
.setTitle(feed.getTitle())
.setDescription(feed.getDescription())
.setSubtitle(feed.getCustomTitle());
- if(feed.getImageLocation() != null) {
+ if (feed.getImageLocation() != null) {
builder.setIconUri(Uri.parse(feed.getImageLocation()));
}
- if(feed.getLink() != null) {
+ if (feed.getLink() != null) {
builder.setMediaUri(Uri.parse(feed.getLink()));
}
MediaDescriptionCompat description = builder.build();
@@ -405,13 +421,13 @@ public class PlaybackService extends MediaBrowserServiceCompat {
e.printStackTrace();
}
List<Feed> feeds = DBReader.getFeedList();
- for (Feed feed: feeds) {
+ for (Feed feed : feeds) {
mediaItems.add(createBrowsableMediaItemForFeed(feed));
}
- } else if (parentId.equals(getResources().getString(R.string.queue_label))){
+ } else if (parentId.equals(getResources().getString(R.string.queue_label))) {
// Child List
try {
- for (FeedItem feedItem: taskManager.getQueue()) {
+ for (FeedItem feedItem : taskManager.getQueue()) {
mediaItems.add(feedItem.getMedia().getMediaItem());
}
} catch (InterruptedException e) {
@@ -420,8 +436,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
} else if (parentId.startsWith("FeedId:")) {
Long feedId = Long.parseLong(parentId.split(":")[1]);
List<FeedItem> feedItems = DBReader.getFeedItemList(DBReader.getFeed(feedId));
- for (FeedItem feedItem: feedItems) {
- if(feedItem.getMedia() != null && feedItem.getMedia().getMediaItem() != null) {
+ for (FeedItem feedItem : feedItems) {
+ if (feedItem.getMedia() != null && feedItem.getMedia().getMediaItem() != null) {
mediaItems.add(feedItem.getMedia().getMediaItem());
}
}
@@ -432,7 +448,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "Received onBind event");
- if(intent.getAction() != null && TextUtils.equals(intent.getAction(), MediaBrowserServiceCompat.SERVICE_INTERFACE)) {
+ if (intent.getAction() != null && TextUtils.equals(intent.getAction(), MediaBrowserServiceCompat.SERVICE_INTERFACE)) {
return super.onBind(intent);
} else {
return mBinder;
@@ -446,23 +462,25 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.d(TAG, "OnStartCommand called");
final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1);
final boolean castDisconnect = intent.getBooleanExtra(EXTRA_CAST_DISCONNECT, false);
- final Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
+ Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
if (keycode == -1 && playable == null && !castDisconnect) {
Log.e(TAG, "PlaybackService was started with no arguments");
- stopSelf();
- return Service.START_REDELIVER_INTENT;
+ stopService();
+ return Service.START_NOT_STICKY;
}
if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
stopForeground(true);
} else {
-
if (keycode != -1) {
Log.d(TAG, "Received media button event");
- handleKeycode(keycode, intent.getIntExtra(MediaButtonReceiver.EXTRA_SOURCE,
- InputDeviceCompat.SOURCE_CLASS_NONE));
- } else if (!flavorHelper.castDisconnect(castDisconnect)) {
+ boolean handled = handleKeycode(keycode, true);
+ if (!handled) {
+ stopService();
+ return Service.START_NOT_STICKY;
+ }
+ } else if (!flavorHelper.castDisconnect(castDisconnect) && playable != null) {
started = true;
boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
true);
@@ -471,17 +489,22 @@ public class PlaybackService extends MediaBrowserServiceCompat {
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
//If the user asks to play External Media, the casting session, if on, should end.
flavorHelper.castDisconnect(playable instanceof ExternalMedia);
+ if (playable instanceof FeedMedia) {
+ playable = DBReader.getFeedMedia(((FeedMedia) playable).getId());
+ }
mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
}
+ setupNotification(playable);
}
- return Service.START_REDELIVER_INTENT;
+ return Service.START_NOT_STICKY;
}
/**
* Handles media button events
+ * return: keycode was handled
*/
- private void handleKeycode(int keycode, int source) {
+ private boolean handleKeycode(int keycode, boolean notificationButton) {
Log.d(TAG, "Handling keycode: " + keycode);
final PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
final PlayerStatus status = info.playerStatus;
@@ -497,24 +520,28 @@ public class PlaybackService extends MediaBrowserServiceCompat {
} else if (status == PlayerStatus.INITIALIZED) {
mediaPlayer.setStartWhenPrepared(true);
mediaPlayer.prepare();
+ } else if (mediaPlayer.getPlayable() == null) {
+ startPlayingFromPreferences();
}
- break;
+ return true;
case KeyEvent.KEYCODE_MEDIA_PLAY:
if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
mediaPlayer.resume();
} else if (status == PlayerStatus.INITIALIZED) {
mediaPlayer.setStartWhenPrepared(true);
mediaPlayer.prepare();
+ } else if (mediaPlayer.getPlayable() == null) {
+ startPlayingFromPreferences();
}
- break;
+ return true;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
if (status == PlayerStatus.PLAYING) {
mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
}
- break;
+ return true;
case KeyEvent.KEYCODE_MEDIA_NEXT:
- if(source == InputDevice.SOURCE_CLASS_NONE ||
+ if (notificationButton ||
UserPreferences.shouldHardwareButtonSkip()) {
// assume the skip command comes from a notification or the lockscreen
// a >| skip button should actually skip
@@ -524,22 +551,22 @@ public class PlaybackService extends MediaBrowserServiceCompat {
// user actually wants to fast-forward
seekDelta(UserPreferences.getFastForwardSecs() * 1000);
}
- break;
+ return true;
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
mediaPlayer.seekDelta(UserPreferences.getFastForwardSecs() * 1000);
- break;
+ return true;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- if(UserPreferences.shouldHardwarePreviousButtonRestart()) {
+ if (UserPreferences.shouldHardwarePreviousButtonRestart()) {
// user wants to restart current episode
mediaPlayer.seekTo(0);
} else {
// user wants to rewind current episode
mediaPlayer.seekDelta(-UserPreferences.getRewindSecs() * 1000);
}
- break;
+ return true;
case KeyEvent.KEYCODE_MEDIA_REWIND:
mediaPlayer.seekDelta(-UserPreferences.getRewindSecs() * 1000);
- break;
+ return true;
case KeyEvent.KEYCODE_MEDIA_STOP:
if (status == PlayerStatus.PLAYING) {
mediaPlayer.pause(true, true);
@@ -547,14 +574,23 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
stopForeground(true); // gets rid of persistent notification
- break;
+ return true;
default:
Log.d(TAG, "Unhandled key code: " + keycode);
if (info.playable != null && info.playerStatus == PlayerStatus.PLAYING) { // only notify the user about an unknown key event if it is actually doing something
String message = String.format(getResources().getString(R.string.unknown_media_key), keycode);
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
- break;
+ }
+ return false;
+ }
+
+ private void startPlayingFromPreferences() {
+ Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(getApplicationContext());
+ if (playable != null) {
+ mediaPlayer.playMediaObject(playable, false, true, true);
+ started = true;
+ PlaybackService.this.updateMediaSessionMetadata(playable);
}
}
@@ -567,17 +603,11 @@ public class PlaybackService extends MediaBrowserServiceCompat {
mediaPlayer.setVideoSurface(sh);
}
- /**
- * Called when the surface holder of the mediaplayer has to be changed.
- */
- private void resetVideoSurface() {
- taskManager.cancelPositionSaver();
- mediaPlayer.resetVideoSurface();
- }
-
public void notifyVideoSurfaceAbandoned() {
- stopForeground(!UserPreferences.isPersistNotify());
+ mediaPlayer.pause(true, false);
mediaPlayer.resetVideoSurface();
+ setupNotification(getPlayable());
+ stopForeground(!UserPreferences.isPersistNotify());
}
private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() {
@@ -611,7 +641,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onWidgetUpdaterTick() {
- updateWidget();
+ PlayerWidgetJobService.updateWidget(getBaseContext());
}
@Override
@@ -623,7 +653,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private final PlaybackServiceMediaPlayer.PSMPCallback mediaPlayerCallback = new PlaybackServiceMediaPlayer.PSMPCallback() {
@Override
public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
- currentMediaType = mediaPlayer.getCurrentMediaType();
+ if (mediaPlayer != null) {
+ currentMediaType = mediaPlayer.getCurrentMediaType();
+ } else {
+ currentMediaType = MediaType.UNKNOWN;
+ }
+
updateMediaSession(newInfo.playerStatus);
switch (newInfo.playerStatus) {
case INITIALIZED:
@@ -648,8 +683,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
break;
case STOPPED:
- //setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
- //stopSelf();
+ //writePlaybackPreferencesNoMediaPlaying();
+ //stopService();
break;
case PLAYING:
@@ -657,8 +692,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
setupNotification(newInfo);
started = true;
// set sleep timer if auto-enabled
- if(newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
- SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
+ if (newInfo.oldPlayerStatus != null && newInfo.oldPlayerStatus != PlayerStatus.SEEKING &&
+ SleepTimerPreferences.autoEnable() && !sleepTimerActive()) {
setSleepTimer(SleepTimerPreferences.timerMillis(), SleepTimerPreferences.shakeToReset(),
SleepTimerPreferences.vibrate());
}
@@ -666,21 +701,20 @@ public class PlaybackService extends MediaBrowserServiceCompat {
case ERROR:
writePlaybackPreferencesNoMediaPlaying();
+ stopService();
break;
}
- Intent statusUpdate = new Intent(ACTION_PLAYER_STATUS_CHANGED);
- // statusUpdate.putExtra(EXTRA_NEW_PLAYER_STATUS, newInfo.playerStatus.ordinal());
- sendBroadcast(statusUpdate);
- updateWidget();
+ IntentUtils.sendLocalBroadcast(getApplicationContext(), ACTION_PLAYER_STATUS_CHANGED);
+ PlayerWidgetJobService.updateWidget(getBaseContext());
bluetoothNotifyChange(newInfo, AVRCP_ACTION_PLAYER_STATUS_CHANGED);
bluetoothNotifyChange(newInfo, AVRCP_ACTION_META_CHANGED);
}
@Override
public void shouldStop() {
- stopSelf();
+ stopService();
}
@Override
@@ -729,7 +763,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
writePlaybackPreferencesNoMediaPlaying();
- stopSelf();
+ stopService();
return true;
}
@@ -801,7 +835,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
Log.e(TAG, "Error handling the queue in order to retrieve the next item", e);
return null;
}
- return (nextItem != null)? nextItem.getMedia() : null;
+ return (nextItem != null) ? nextItem.getMedia() : null;
}
@@ -816,7 +850,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
if (!isCasting) {
stopForeground(true);
}
- stopWidgetUpdater();
}
if (mediaType == null) {
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
@@ -830,7 +863,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
/**
* This method processes the media object after its playback ended, either because it completed
* or because a different media object was selected for playback.
- *
+ * <p>
* Even though these tasks aren't supposed to be resource intensive, a good practice is to
* usually call this method on a background thread.
*
@@ -910,7 +943,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
taskManager.setSleepTimer(waitingTime, shakeToReset, vibrate);
sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
EventBus.getDefault().post(new MessageEvent(getString(R.string.sleep_timer_enabled_label),
- () -> disableSleepTimer()));
+ this::disableSleepTimer));
}
public void disableSleepTimer() {
@@ -1014,22 +1047,17 @@ public class PlaybackService extends MediaBrowserServiceCompat {
editor.commit();
}
- /**
- * Send ACTION_PLAYER_STATUS_CHANGED without changing the status attribute.
- */
- private void postStatusUpdateIntent() {
- sendBroadcast(new Intent(ACTION_PLAYER_STATUS_CHANGED));
- }
-
private void sendNotificationBroadcast(int type, int code) {
Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION);
intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
intent.putExtra(EXTRA_NOTIFICATION_CODE, code);
+ intent.setPackage(getPackageName());
sendBroadcast(intent);
}
/**
* Updates the Media Session for the corresponding status.
+ *
* @param playerStatus the current {@link PlayerStatus}
*/
private void updateMediaSession(final PlayerStatus playerStatus) {
@@ -1069,7 +1097,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
} else {
state = PlaybackStateCompat.STATE_NONE;
}
- sessionState.setState(state, mediaPlayer.getPosition(), mediaPlayer.getPlaybackSpeed());
+ sessionState.setState(state, getCurrentPosition(), getCurrentPlaybackSpeed());
long capabilities = PlaybackStateCompat.ACTION_PLAY_PAUSE
| PlaybackStateCompat.ACTION_REWIND
| PlaybackStateCompat.ACTION_FAST_FORWARD
@@ -1105,8 +1133,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
// showRewindOnCompactNotification() corresponds to the "Set Lockscreen Buttons"
// Settings in UI.
// Hence, from user perspective, he/she is setting the buttons for Lockscreen
- return ( UserPreferences.showRewindOnCompactNotification() &&
- (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) );
+ return (UserPreferences.showRewindOnCompactNotification() &&
+ (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP));
}
/**
@@ -1138,10 +1166,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
builder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, imageLocation);
try {
Bitmap art = Glide.with(this)
- .load(imageLocation)
.asBitmap()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
+ .load(imageLocation)
+ .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
+ .submit(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get();
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, art);
} catch (Throwable tr) {
@@ -1158,7 +1186,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PendingIntent.FLAG_UPDATE_CURRENT));
try {
mediaSession.setMetadata(builder.build());
- } catch (OutOfMemoryError e) {
+ } catch (OutOfMemoryError e) {
Log.e(TAG, "Setting media session metadata", e);
builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, null);
mediaSession.setMetadata(builder.build());
@@ -1179,63 +1207,67 @@ public class PlaybackService extends MediaBrowserServiceCompat {
* Prepares notification and starts the service in the foreground.
*/
private void setupNotification(final PlaybackServiceMediaPlayer.PSMPInfo info) {
- final PendingIntent pIntent = PendingIntent.getActivity(this, 0,
- PlaybackService.getPlayerActivityIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT);
+ setupNotification(info.playable);
+ }
+ private synchronized void setupNotification(final Playable playable) {
if (notificationSetupThread != null) {
notificationSetupThread.interrupt();
}
+ if (playable == null) {
+ Log.d(TAG, "setupNotification: playable is null");
+ if (!started) {
+ stopService();
+ }
+ return;
+ }
Runnable notificationSetupTask = new Runnable() {
Bitmap icon = null;
@Override
public void run() {
Log.d(TAG, "Starting background work");
- if (android.os.Build.VERSION.SDK_INT >= 11) {
- if (info.playable != null) {
- int iconSize = getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- try {
- icon = Glide.with(PlaybackService.this)
- .load(info.playable.getImageLocation())
- .asBitmap()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .centerCrop()
- .into(iconSize, iconSize)
- .get();
- } catch (Throwable tr) {
- Log.e(TAG, "Error loading the media icon for the notification", tr);
- }
+
+ if (mediaPlayer == null) {
+ Log.d(TAG, "notificationSetupTask: mediaPlayer is null");
+ if (!started) {
+ stopService();
}
+ return;
+ }
+
+ int iconSize = getResources().getDimensionPixelSize(
+ android.R.dimen.notification_large_icon_width);
+ try {
+ icon = Glide.with(PlaybackService.this)
+ .asBitmap()
+ .load(playable.getImageLocation())
+ .apply(RequestOptions.diskCacheStrategyOf(ApGlideSettings.AP_DISK_CACHE_STRATEGY))
+ .apply(new RequestOptions().centerCrop())
+ .submit(iconSize, iconSize)
+ .get();
+ } catch (Throwable tr) {
+ Log.e(TAG, "Error loading the media icon for the notification", tr);
}
+
if (icon == null) {
icon = BitmapFactory.decodeResource(getApplicationContext().getResources(),
ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()));
}
- if (mediaPlayer == null) {
- return;
- }
PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
- final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
- if (!Thread.currentThread().isInterrupted() && started && info.playable != null) {
- String contentText = info.playable.getEpisodeTitle();
- String contentTitle = info.playable.getFeedTitle();
+ if (!Thread.currentThread().isInterrupted() && started) {
+ String contentText = playable.getEpisodeTitle();
+ String contentTitle = playable.getFeedTitle();
Notification notification;
// Builder is v7, even if some not overwritten methods return its parent's v4 interface
- NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(
- PlaybackService.this)
- .setContentTitle(contentTitle)
+ NotificationCompat.Builder notificationBuilder = createBasicNotification();
+ notificationBuilder.setContentTitle(contentTitle)
.setContentText(contentText)
- .setOngoing(false)
- .setContentIntent(pIntent)
- .setLargeIcon(icon)
- .setSmallIcon(smallIcon)
- .setWhen(0) // we don't need the time
- .setPriority(UserPreferences.getNotifyPriority()); // set notification priority
+ .setPriority(UserPreferences.getNotifyPriority())
+ .setLargeIcon(icon); // set notification priority
IntList compactActionList = new IntList();
int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction
@@ -1257,7 +1289,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
notificationBuilder.addAction(android.R.drawable.ic_media_rew,
getString(R.string.rewind_label),
rewindButtonPendingIntent);
- if(UserPreferences.showRewindOnCompactNotification()) {
+ if (UserPreferences.showRewindOnCompactNotification()) {
compactActionList.add(numActions);
}
numActions++;
@@ -1284,7 +1316,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
notificationBuilder.addAction(android.R.drawable.ic_media_ff,
getString(R.string.fast_forward_label),
ffButtonPendingIntent);
- if(UserPreferences.showFastForwardOnCompactNotification()) {
+ if (UserPreferences.showFastForwardOnCompactNotification()) {
compactActionList.add(numActions);
}
numActions++;
@@ -1295,7 +1327,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
notificationBuilder.addAction(android.R.drawable.ic_media_next,
getString(R.string.skip_episode_label),
skipButtonPendingIntent);
- if(UserPreferences.showSkipOnCompactNotification()) {
+ if (UserPreferences.showSkipOnCompactNotification()) {
compactActionList.add(numActions);
}
numActions++;
@@ -1303,7 +1335,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction(
KeyEvent.KEYCODE_MEDIA_STOP, numActions);
- notificationBuilder.setStyle(new android.support.v7.app.NotificationCompat.MediaStyle()
+ notificationBuilder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setMediaSession(mediaSession.getSessionToken())
.setShowActionsInCompactView(compactActionList.toArray())
.setShowCancelButton(true)
@@ -1348,9 +1380,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
*
* @param fromMediaPlayer if true, the information is gathered from the current Media Player
* and {@param playable} and {@param position} become irrelevant.
- * @param playable the playable for which the current position should be saved, unless
- * {@param fromMediaPlayer} is true.
- * @param position the position that should be saved, unless {@param fromMediaPlayer} is true.
+ * @param playable the playable for which the current position should be saved, unless
+ * {@param fromMediaPlayer} is true.
+ * @param position the position that should be saved, unless {@param fromMediaPlayer} is true.
*/
private synchronized void saveCurrentPosition(boolean fromMediaPlayer, Playable playable, int position) {
int duration;
@@ -1370,16 +1402,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
}
- private void stopWidgetUpdater() {
- taskManager.cancelWidgetUpdater();
- sendBroadcast(new Intent(STOP_WIDGET_UPDATE));
- }
-
- private void updateWidget() {
- PlaybackService.this.sendBroadcast(new Intent(
- FORCE_WIDGET_UPDATE));
- }
-
public boolean sleepTimerActive() {
return taskManager.isSleepTimerActive();
}
@@ -1418,7 +1440,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
String status = intent.getStringExtra("media_connection_status");
boolean isConnectedToCar = "media_connected".equals(status);
Log.d(TAG, "Received Auto Connection update: " + status);
- if(!isConnectedToCar) {
+ if (!isConnectedToCar) {
Log.d(TAG, "Car was unplugged during playback.");
pauseIfPauseOnDisconnect();
} else {
@@ -1446,6 +1468,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
+ if (isInitialStickyBroadcast ()) {
+ // Don't pause playback after we just started, just because the receiver
+ // delivers the current headset state (instead of a change)
+ return;
+ }
+
if (TextUtils.equals(intent.getAction(), Intent.ACTION_HEADSET_PLUG)) {
int state = intent.getIntExtra("state", -1);
if (state != -1) {
@@ -1467,13 +1495,11 @@ public class PlaybackService extends MediaBrowserServiceCompat {
private final BroadcastReceiver bluetoothStateUpdated = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- if (TextUtils.equals(intent.getAction(), BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
- if (state == BluetoothA2dp.STATE_CONNECTED) {
- Log.d(TAG, "Received bluetooth connection intent");
- unpauseIfPauseOnDisconnect(true);
- }
+ if (TextUtils.equals(intent.getAction(), BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
+ int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
+ if (state == BluetoothA2dp.STATE_CONNECTED) {
+ Log.d(TAG, "Received bluetooth connection intent");
+ unpauseIfPauseOnDisconnect(true);
}
}
}
@@ -1510,10 +1536,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
transientPause = false;
if (!bluetooth && UserPreferences.isUnpauseOnHeadsetReconnect()) {
mediaPlayer.resume();
- } else if (bluetooth && UserPreferences.isUnpauseOnBluetoothReconnect()){
+ } else if (bluetooth && UserPreferences.isUnpauseOnBluetoothReconnect()) {
// let the user know we've started playback again...
Vibrator v = (Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
- if(v != null) {
+ if (v != null) {
v.vibrate(500);
}
mediaPlayer.resume();
@@ -1526,7 +1552,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- stopSelf();
+ stopService();
}
}
@@ -1594,7 +1620,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return mediaPlayer.getPlayerStatus();
}
- public Playable getPlayable() { return mediaPlayer.getPlayable(); }
+ public Playable getPlayable() {
+ return mediaPlayer.getPlayable();
+ }
public boolean canSetSpeed() {
return mediaPlayer.canSetSpeed();
@@ -1609,6 +1637,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
public float getCurrentPlaybackSpeed() {
+ if(mediaPlayer == null) {
+ return 1.0f;
+ }
return mediaPlayer.getPlaybackSpeed();
}
@@ -1634,7 +1665,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
- public void seekDelta(final int d) {
+ private void seekDelta(final int d) {
mediaPlayer.seekDelta(d);
}
@@ -1650,6 +1681,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
* an invalid state.
*/
public int getDuration() {
+ if (mediaPlayer == null) {
+ return INVALID_TIME;
+ }
return mediaPlayer.getDuration();
}
@@ -1658,6 +1692,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
* is in an invalid state.
*/
public int getCurrentPosition() {
+ if (mediaPlayer == null) {
+ return INVALID_TIME;
+ }
return mediaPlayer.getPosition();
}
@@ -1689,19 +1726,19 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public void onPlayFromMediaId(String mediaId, Bundle extras) {
Log.d(TAG, "onPlayFromMediaId: mediaId: " + mediaId + " extras: " + extras.toString());
FeedMedia p = DBReader.getFeedMedia(Long.parseLong(mediaId));
- if(p != null) {
+ if (p != null) {
mediaPlayer.playMediaObject(p, !p.localFileAvailable(), true, true);
}
}
@Override
- public void onPlayFromSearch (String query, Bundle extras) {
+ public void onPlayFromSearch(String query, Bundle extras) {
Log.d(TAG, "onPlayFromSearch query=" + query + " extras=" + extras.toString());
- List<SearchResult> results = FeedSearcher.performSearch(getBaseContext(),query,0);
- for( SearchResult result : results) {
+ List<SearchResult> results = FeedSearcher.performSearch(getBaseContext(), query, 0);
+ for (SearchResult result : results) {
try {
- FeedMedia p = ((FeedItem)(result.getComponent())).getMedia();
+ FeedMedia p = ((FeedItem) (result.getComponent())).getMedia();
mediaPlayer.playMediaObject(p, !p.localFileAvailable(), true, true);
return;
} catch (Exception e) {
@@ -1711,7 +1748,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
}
onPlay();
- return;
}
@Override
@@ -1749,7 +1785,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onSkipToNext() {
Log.d(TAG, "onSkipToNext()");
- if(UserPreferences.shouldHardwareButtonSkip()) {
+ if (UserPreferences.shouldHardwareButtonSkip()) {
mediaPlayer.skip();
} else {
seekDelta(UserPreferences.getFastForwardSecs() * 1000);
@@ -1767,11 +1803,11 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public boolean onMediaButtonEvent(final Intent mediaButton) {
Log.d(TAG, "onMediaButtonEvent(" + mediaButton + ")");
if (mediaButton != null) {
- KeyEvent keyEvent = (KeyEvent) mediaButton.getExtras().get(Intent.EXTRA_KEY_EVENT);
+ KeyEvent keyEvent = mediaButton.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (keyEvent != null &&
keyEvent.getAction() == KeyEvent.ACTION_DOWN &&
- keyEvent.getRepeatCount() == 0){
- handleKeycode(keyEvent.getKeyCode(), keyEvent.getSource());
+ keyEvent.getRepeatCount() == 0) {
+ return handleKeycode(keyEvent.getKeyCode(), false);
}
}
return false;
@@ -1788,29 +1824,38 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
};
- private SharedPreferences.OnSharedPreferenceChangeListener prefListener =
+ private final SharedPreferences.OnSharedPreferenceChangeListener prefListener =
(sharedPreferences, key) -> {
if (UserPreferences.PREF_LOCKSCREEN_BACKGROUND.equals(key)) {
updateMediaSessionMetadata(getPlayable());
} else {
flavorHelper.onSharedPreference(key);
}
- };
+ };
interface FlavorHelperCallback {
PlaybackServiceMediaPlayer.PSMPCallback getMediaPlayerCallback();
+
void setMediaPlayer(PlaybackServiceMediaPlayer mediaPlayer);
+
PlaybackServiceMediaPlayer getMediaPlayer();
+
void setIsCasting(boolean isCasting);
+
void sendNotificationBroadcast(int type, int code);
+
void saveCurrentPosition(boolean fromMediaPlayer, Playable playable, int position);
+
void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info);
+
MediaSessionCompat getMediaSession();
+
Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
+
void unregisterReceiver(BroadcastReceiver receiver);
}
- private FlavorHelperCallback flavorHelperCallback = new FlavorHelperCallback() {
+ private final FlavorHelperCallback flavorHelperCallback = new FlavorHelperCallback() {
@Override
public PlaybackServiceMediaPlayer.PSMPCallback getMediaPlayerCallback() {
return PlaybackService.this.mediaPlayerCallback;
@@ -1853,7 +1898,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
UserPreferences.isPersistNotify()) &&
android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
PlaybackService.this.setupNotification(info);
- } else if (!UserPreferences.isPersistNotify()){
+ } else if (!UserPreferences.isPersistNotify()) {
PlaybackService.this.stopForeground(true);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
index 393019fd1..a2481b801 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
@@ -24,14 +24,14 @@ import de.danoeh.antennapod.core.util.playback.Playable;
* and remote (cast devices) playback.
*/
public abstract class PlaybackServiceMediaPlayer {
- public static final String TAG = "PlaybackSvcMediaPlayer";
+ private static final String TAG = "PlaybackSvcMediaPlayer";
/**
* Return value of some PSMP methods if the method call failed.
*/
static final int INVALID_TIME = -1;
- volatile PlayerStatus oldPlayerStatus;
+ private volatile PlayerStatus oldPlayerStatus;
volatile PlayerStatus playerStatus;
/**
@@ -39,8 +39,8 @@ public abstract class PlaybackServiceMediaPlayer {
*/
private WifiManager.WifiLock wifiLock;
- protected final PSMPCallback callback;
- protected final Context context;
+ final PSMPCallback callback;
+ final Context context;
PlaybackServiceMediaPlayer(@NonNull Context context,
@NonNull PSMPCallback callback){
@@ -279,7 +279,7 @@ public abstract class PlaybackServiceMediaPlayer {
final synchronized void acquireWifiLockIfNecessary() {
if (shouldLockWifi()) {
if (wifiLock == null) {
- wifiLock = ((WifiManager) context.getSystemService(Context.WIFI_SERVICE))
+ wifiLock = ((WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
wifiLock.setReferenceCounted(false);
}
@@ -365,7 +365,7 @@ public abstract class PlaybackServiceMediaPlayer {
* Holds information about a PSMP object.
*/
public static class PSMPInfo {
- public PlayerStatus oldPlayerStatus;
+ public final PlayerStatus oldPlayerStatus;
public PlayerStatus playerStatus;
public Playable playable;
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 0c7d5e718..3d97e862a 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
@@ -295,7 +295,7 @@ public class PlaybackServiceTaskManager {
/**
* Sleeps for a given time and then pauses playback.
*/
- protected class SleepTimer implements Runnable {
+ class SleepTimer implements Runnable {
private static final String TAG = "SleepTimer";
private static final long UPDATE_INTERVAL = 1000L;
private static final long NOTIFICATION_THRESHOLD = 10000;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java
index 8a222d7ec..4f2ae34f8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlayerStatus.java
@@ -12,7 +12,7 @@ public enum PlayerStatus {
INITIALIZING(9), // playback service is loading the Playable's metadata
INITIALIZED(10); // playback service was started, data source of media player was set.
- private int statusValue;
+ private final int statusValue;
private static final PlayerStatus[] fromOrdinalLookup;
static {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java
index fcd96826b..c0b1b3bc0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ShakeListener.java
@@ -7,14 +7,14 @@ import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.util.Log;
-public class ShakeListener implements SensorEventListener
+class ShakeListener implements SensorEventListener
{
private static final String TAG = ShakeListener.class.getSimpleName();
private Sensor mAccelerometer;
private SensorManager mSensorMgr;
- private PlaybackServiceTaskManager.SleepTimer mSleepTimer;
- private Context mContext;
+ private final PlaybackServiceTaskManager.SleepTimer mSleepTimer;
+ private final Context mContext;
public ShakeListener(Context context, PlaybackServiceTaskManager.SleepTimer sleepTimer) {
mContext = context;
@@ -22,7 +22,7 @@ public class ShakeListener implements SensorEventListener
resume();
}
- public void resume() {
+ private void resume() {
// only a precaution, the user should actually not be able to activate shake to reset
// when the accelerometer is not available
mSensorMgr = (SensorManager) mContext.getSystemService(Context.SENSOR_SERVICE);
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 a2ecd0a52..83aa4c780 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
@@ -1,7 +1,9 @@
package de.danoeh.antennapod.core.storage;
import android.database.Cursor;
+import android.support.annotation.Nullable;
import android.support.v4.util.ArrayMap;
+import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
@@ -13,13 +15,9 @@ import java.util.Map;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.ID3Chapter;
-import de.danoeh.antennapod.core.feed.SimpleChapter;
-import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.util.LongIntMap;
@@ -47,7 +45,7 @@ public final class DBReader {
/**
* Maximum size of the list returned by {@link #getDownloadLog()}.
*/
- public static final int DOWNLOAD_LOG_SIZE = 200;
+ private static final int DOWNLOAD_LOG_SIZE = 200;
private DBReader() {
@@ -78,7 +76,7 @@ public final class DBReader {
cursor = adapter.getAllFeedsCursor();
List<Feed> feeds = new ArrayList<>(cursor.getCount());
while (cursor.moveToNext()) {
- Feed feed = extractFeedFromCursorRow(adapter, cursor);
+ Feed feed = extractFeedFromCursorRow(cursor);
feeds.add(feed);
}
return feeds;
@@ -123,7 +121,7 @@ public final class DBReader {
loadFeedDataOfFeedItemList(items);
}
- public static void loadTagsOfFeedItemList(List<FeedItem> items) {
+ private static void loadTagsOfFeedItemList(List<FeedItem> items) {
LongList favoriteIds = getFavoriteIDList();
LongList queueIds = getQueueIDList();
@@ -144,7 +142,7 @@ public final class DBReader {
*
* @param items The FeedItems whose Feed-objects should be loaded.
*/
- public static void loadFeedDataOfFeedItemList(List<FeedItem> items) {
+ private static void loadFeedDataOfFeedItemList(List<FeedItem> items) {
List<Feed> feeds = getFeedList();
Map<Long, Feed> feedIndex = new ArrayMap<>(feeds.size());
@@ -204,25 +202,15 @@ public final class DBReader {
private static List<FeedItem> extractItemlistFromCursor(PodDBAdapter adapter, Cursor cursor) {
List<FeedItem> result = new ArrayList<>(cursor.getCount());
- LongList imageIds = new LongList(cursor.getCount());
LongList itemIds = new LongList(cursor.getCount());
if (cursor.moveToFirst()) {
do {
- int indexImage = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE);
- long imageId = cursor.getLong(indexImage);
- imageIds.add(imageId);
-
FeedItem item = FeedItem.fromCursor(cursor);
result.add(item);
itemIds.add(item.getId());
} while (cursor.moveToNext());
- Map<Long, FeedImage> images = getFeedImages(adapter, imageIds.toArray());
Map<Long, FeedMedia> medias = getFeedMedia(adapter, itemIds);
- for (int i = 0; i < result.size(); i++) {
- FeedItem item = result.get(i);
- long imageId = imageIds.get(i);
- FeedImage image = images.get(imageId);
- item.setImage(image);
+ for (FeedItem item : result) {
FeedMedia media = medias.get(item.getId());
item.setMedia(media);
if (media != null) {
@@ -256,25 +244,10 @@ public final class DBReader {
return result;
}
- private static Feed extractFeedFromCursorRow(PodDBAdapter adapter, Cursor cursor) {
- final FeedImage image;
- int indexImage = cursor.getColumnIndex(PodDBAdapter.KEY_IMAGE);
- long imageId = cursor.getLong(indexImage);
- if (imageId != 0) {
- image = getFeedImage(adapter, imageId);
- } else {
- image = null;
- }
-
+ private static Feed extractFeedFromCursorRow(Cursor cursor) {
Feed feed = Feed.fromCursor(cursor);
- if (image != null) {
- feed.setImage(image);
- image.setOwner(feed);
- }
-
FeedPreferences preferences = FeedPreferences.fromCursor(cursor);
feed.setPreferences(preferences);
-
return feed;
}
@@ -415,7 +388,7 @@ public final class DBReader {
}
}
- public static LongList getFavoriteIDList() {
+ private static LongList getFavoriteIDList() {
Log.d(TAG, "getFavoriteIDList() called");
PodDBAdapter adapter = PodDBAdapter.getInstance();
@@ -602,13 +575,14 @@ public final class DBReader {
}
}
+ @Nullable
static Feed getFeed(final long feedId, PodDBAdapter adapter) {
Feed feed = null;
Cursor cursor = null;
try {
cursor = adapter.getFeedCursor(feedId);
if (cursor.moveToNext()) {
- feed = extractFeedFromCursorRow(adapter, cursor);
+ feed = extractFeedFromCursorRow(cursor);
feed.setItems(getFeedItemList(feed));
} else {
Log.e(TAG, "getFeed could not find feed with id " + feedId);
@@ -666,7 +640,7 @@ public final class DBReader {
}
}
- static FeedItem getFeedItem(final String podcastUrl, final String episodeUrl, PodDBAdapter adapter) {
+ private static FeedItem getFeedItem(final String podcastUrl, final String episodeUrl, PodDBAdapter adapter) {
Log.d(TAG, "Loading feeditem with podcast url " + podcastUrl + " and episode url " + episodeUrl);
Cursor cursor = null;
try {
@@ -717,7 +691,7 @@ public final class DBReader {
if (cursor.moveToFirst()) {
String username = cursor.getString(0);
String password = cursor.getString(1);
- if (username != null && password != null) {
+ if (!TextUtils.isEmpty(username) && password != null) {
credentials = username + ":" + password;
} else {
credentials = "";
@@ -800,7 +774,7 @@ public final class DBReader {
}
}
- static void loadChaptersOfFeedItem(PodDBAdapter adapter, FeedItem item) {
+ private static void loadChaptersOfFeedItem(PodDBAdapter adapter, FeedItem item) {
Cursor cursor = null;
try {
cursor = adapter.getSimpleChaptersOfFeedItemCursor(item);
@@ -811,31 +785,8 @@ public final class DBReader {
}
item.setChapters(new ArrayList<>(chaptersCount));
while (cursor.moveToNext()) {
- int indexType = cursor.getColumnIndex(PodDBAdapter.KEY_CHAPTER_TYPE);
- int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
- int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
- int indexLink = cursor.getColumnIndex(PodDBAdapter.KEY_LINK);
-
- int chapterType = cursor.getInt(indexType);
- Chapter chapter = null;
- long start = cursor.getLong(indexStart);
- String title = cursor.getString(indexTitle);
- String link = cursor.getString(indexLink);
-
- switch (chapterType) {
- case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER:
- chapter = new SimpleChapter(start, title, item, link);
- break;
- case ID3Chapter.CHAPTERTYPE_ID3CHAPTER:
- chapter = new ID3Chapter(start, title, item, link);
- break;
- case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
- chapter = new VorbisCommentChapter(start, title, item, link);
- break;
- }
+ Chapter chapter = Chapter.fromCursor(cursor, item);
if (chapter != null) {
- int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
- chapter.setId(cursor.getLong(indexId));
item.getChapters().add(chapter);
}
}
@@ -865,62 +816,6 @@ public final class DBReader {
}
/**
- * Searches the DB for a FeedImage of the given id.
- *
- * @param imageId The id of the object
- * @return The found object
- */
- public static FeedImage getFeedImage(final long imageId) {
- Log.d(TAG, "getFeedImage() called with: " + "imageId = [" + imageId + "]");
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- try {
- return getFeedImage(adapter, imageId);
- } finally {
- adapter.close();
- }
- }
-
- /**
- * Searches the DB for a FeedImage of the given id.
- *
- * @param imageId The id of the object
- * @return The found object
- */
- private static FeedImage getFeedImage(PodDBAdapter adapter, final long imageId) {
- return getFeedImages(adapter, imageId).get(imageId);
- }
-
- /**
- * Searches the DB for a FeedImage of the given id.
- *
- * @param imageIds The ids of the images
- * @return Map that associates the id of an image with the image itself
- */
- private static Map<Long, FeedImage> getFeedImages(PodDBAdapter adapter, final long... imageIds) {
- String[] ids = new String[imageIds.length];
- for (int i = 0, len = imageIds.length; i < len; i++) {
- ids[i] = String.valueOf(imageIds[i]);
- }
- Cursor cursor = adapter.getImageCursor(ids);
- int imageCount = cursor.getCount();
- if (imageCount == 0) {
- cursor.close();
- return Collections.emptyMap();
- }
- Map<Long, FeedImage> result = new ArrayMap<>(imageCount);
- try {
- while (cursor.moveToNext()) {
- FeedImage image = FeedImage.fromCursor(cursor);
- result.put(image.getId(), image);
- }
- } finally {
- cursor.close();
- }
- return result;
- }
-
- /**
* Searches the DB for a FeedMedia of the given id.
*
* @param mediaId The id of the object
@@ -1049,14 +944,14 @@ public final class DBReader {
/**
* Simply sums up time of podcasts that are marked as played
*/
- public long totalTimeCountAll;
+ public final long totalTimeCountAll;
/**
* Respects speed, listening twice, ...
*/
- public long totalTime;
+ public final long totalTime;
- public List<StatisticsItem> feedTime;
+ public final List<StatisticsItem> feedTime;
public StatisticsData(long totalTime, long totalTimeCountAll, List<StatisticsItem> feedTime) {
this.totalTime = totalTime;
@@ -1066,26 +961,26 @@ public final class DBReader {
}
public static class StatisticsItem {
- public Feed feed;
- public long time;
+ public final Feed feed;
+ public final long time;
/**
* Respects speed, listening twice, ...
*/
- public long timePlayed;
+ public final long timePlayed;
/**
* Simply sums up time of podcasts that are marked as played
*/
- public long timePlayedCountAll;
- public long episodes;
+ public final long timePlayedCountAll;
+ public final long episodes;
/**
* Episodes that are actually played
*/
- public long episodesStarted;
+ public final long episodesStarted;
/**
* All episodes that are marked as played (or have position != 0)
*/
- public long episodesStartedIncludingMarked;
+ public final long episodesStartedIncludingMarked;
public StatisticsItem(Feed feed, long time, long timePlayed, long timePlayedCountAll,
long episodes, long episodesStarted, long episodesStartedIncludingMarked) {
@@ -1114,7 +1009,7 @@ public final class DBReader {
Cursor feedCursor = adapter.getFeedsInFlattrQueueCursor();
if (feedCursor.moveToFirst()) {
do {
- result.add(extractFeedFromCursorRow(adapter, feedCursor));
+ result.add(extractFeedFromCursorRow(feedCursor));
} while (feedCursor.moveToNext());
}
feedCursor.close();
@@ -1221,12 +1116,12 @@ public final class DBReader {
}
public static class NavDrawerData {
- public List<Feed> feeds;
- public int queueSize;
- public int numNewItems;
- public int numDownloadedItems;
- public LongIntMap feedCounters;
- public int reclaimableSpace;
+ public final List<Feed> feeds;
+ public final int queueSize;
+ public final int numNewItems;
+ public final int numDownloadedItems;
+ public final LongIntMap feedCounters;
+ public final int reclaimableSpace;
public NavDrawerData(List<Feed> feeds,
int queueSize,
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 148b530a7..dab8e19b5 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
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.storage;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
+import android.support.annotation.Nullable;
import android.util.Log;
import java.util.ArrayList;
@@ -17,7 +17,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
-import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.ClientConfig;
@@ -28,19 +27,18 @@ 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;
-import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DownloadError;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
import de.danoeh.antennapod.core.util.exception.MediaFileNotFoundException;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import static android.content.Context.MODE_PRIVATE;
-import static android.provider.Contacts.SettingsColumns.KEY;
/**
* Provides methods for doing common tasks that use DBReader and DBWriter.
@@ -48,13 +46,13 @@ import static android.provider.Contacts.SettingsColumns.KEY;
public final class DBTasks {
private static final String TAG = "DBTasks";
- public static final String PREF_NAME = "dbtasks";
+ private static final String PREF_NAME = "dbtasks";
private static final String PREF_LAST_REFRESH = "last_refresh";
/**
* Executor service used by the autodownloadUndownloadedEpisodes method.
*/
- private static ExecutorService autodownloadExec;
+ private static final ExecutorService autodownloadExec;
static {
autodownloadExec = Executors.newSingleThreadExecutor(r -> {
@@ -124,16 +122,13 @@ public final class DBTasks {
media);
}
}
- // Start playback Service
- Intent launchIntent = new Intent(context, PlaybackService.class);
- launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
- launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED,
- startWhenPrepared);
- launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
- shouldStream);
- launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY,
- true);
- context.startService(launchIntent);
+
+ new PlaybackServiceStarter(context, media)
+ .callEvenIfRunning(true)
+ .startWhenPrepared(startWhenPrepared)
+ .shouldStream(shouldStream)
+ .start();
+
if (showPlayer) {
// Launch media player
context.startActivity(PlaybackService.getPlayerActivityIntent(
@@ -143,55 +138,75 @@ public final class DBTasks {
} catch (MediaFileNotFoundException e) {
e.printStackTrace();
if (media.isPlaying()) {
- context.sendBroadcast(new Intent(
- PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
}
notifyMissingFeedMediaFile(context, media);
}
}
- private static AtomicBoolean isRefreshing = new AtomicBoolean(false);
+ private static final AtomicBoolean isRefreshing = new AtomicBoolean(false);
/**
* Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
* enqueuing Feeds for download from a previous call
*
- * @param context Might be used for accessing the database
- * @param feeds List of Feeds that should be refreshed.
+ * @param context Might be used for accessing the database
+ * @param feeds List of Feeds that should be refreshed.
*/
- public static void refreshAllFeeds(final Context context,
- final List<Feed> feeds) {
- if (isRefreshing.compareAndSet(false, true)) {
- new Thread() {
- public void run() {
- if (feeds != null) {
- refreshFeeds(context, feeds);
- } else {
- refreshFeeds(context, DBReader.getFeedList());
- }
- isRefreshing.set(false);
+ public static void refreshAllFeeds(final Context context, final List<Feed> feeds) {
+ refreshAllFeeds(context, feeds, null);
+ }
- SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
+ /**
+ * Refreshes a given list of Feeds in a separate Thread. This method might ignore subsequent calls if it is still
+ * enqueuing Feeds for download from a previous call
+ *
+ * @param context Might be used for accessing the database
+ * @param feeds List of Feeds that should be refreshed.
+ * @param callback Called after everything was added enqueued for download. Might be null.
+ */
+ public static void refreshAllFeeds(final Context context, final List<Feed> feeds, @Nullable Runnable callback) {
+ if (!isRefreshing.compareAndSet(false, true)) {
+ Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked");
+ return;
+ }
- if (FlattrUtils.hasToken()) {
- Log.d(TAG, "Flattring all pending things.");
- new FlattrClickWorker(context).executeAsync(); // flattr pending things
+ new Thread(() -> {
+ if (feeds != null) {
+ refreshFeeds(context, feeds);
+ } else {
+ refreshFeeds(context, DBReader.getFeedList());
+ }
+ isRefreshing.set(false);
- Log.d(TAG, "Fetching flattr status.");
- new FlattrStatusFetcher(context).start();
+ SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
+ prefs.edit().putLong(PREF_LAST_REFRESH, System.currentTimeMillis()).apply();
- }
- if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
- GpodnetSyncService.sendSyncIntent(context);
- }
- Log.d(TAG, "refreshAllFeeds autodownload");
- autodownloadUndownloadedItems(context);
- }
- }.start();
- } else {
- Log.d(TAG, "Ignoring request to refresh all feeds: Refresh lock is locked");
- }
+ if (FlattrUtils.hasToken()) {
+ Log.d(TAG, "Flattring all pending things.");
+ new FlattrClickWorker(context).executeAsync(); // flattr pending things
+
+ Log.d(TAG, "Fetching flattr status.");
+ new FlattrStatusFetcher(context).start();
+
+ }
+ if (ClientConfig.gpodnetCallbacks.gpodnetEnabled()) {
+ GpodnetSyncService.sendSyncIntent(context);
+ }
+ // Note: automatic download of episodes will be done but not here.
+ // Instead it is done after all feeds have been refreshed (asynchronously),
+ // in DownloadService.onDestroy()
+ // See Issue #2577 for the details of the rationale
+
+ if (callback != null) {
+ callback.run();
+ }
+ }).start();
+ }
+
+ public static long getLastRefreshAllFeedsTimeMillis(final Context context) {
+ SharedPreferences prefs = context.getSharedPreferences(DBTasks.PREF_NAME, MODE_PRIVATE);
+ return prefs.getLong(DBTasks.PREF_LAST_REFRESH, 0);
}
/**
@@ -224,27 +239,6 @@ public final class DBTasks {
}
/**
- * Downloads all pages of the given feed.
- *
- * @param context Used for requesting the download.
- * @param feed The Feed object.
- */
- public static void refreshCompleteFeed(final Context context, final Feed feed) {
- try {
- refreshFeed(context, feed, true, false);
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- DBWriter.addDownloadStatus(
- new DownloadStatus(feed, feed
- .getHumanReadableIdentifier(),
- DownloadError.ERROR_REQUEST_ERROR, false, e
- .getMessage()
- )
- );
- }
- }
-
- /**
* Downloads all pages of the given feed even if feed has not been modified since last refresh
*
* @param context Used for requesting the download.
@@ -293,7 +287,7 @@ public final class DBTasks {
* @param context Used for requesting the download.
* @param feed The Feed object.
*/
- public static void refreshFeed(Context context, Feed feed)
+ private static void refreshFeed(Context context, Feed feed)
throws DownloadRequestException {
Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")");
refreshFeed(context, feed, false, false);
@@ -325,31 +319,6 @@ public final class DBTasks {
DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force);
}
- /*
- * Checks if the app should refresh all feeds, i.e. if the last auto refresh failed.
- *
- * The feeds are only refreshed if an update interval or time of day is set and the last
- * (successful) refresh was before the last interval or more than a day ago, respectively.
- */
- public static void checkShouldRefreshFeeds(Context context) {
- long interval = 0;
- if(UserPreferences.getUpdateInterval() > 0) {
- interval = UserPreferences.getUpdateInterval();
- } else if(UserPreferences.getUpdateTimeOfDay().length > 0){
- interval = TimeUnit.DAYS.toMillis(1);
- }
- if(interval == 0) { // auto refresh is disabled
- return;
- }
- SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- long lastRefresh = prefs.getLong(PREF_LAST_REFRESH, 0);
- Log.d(TAG, "last refresh: " + Converter.getDurationStringLocalized(context,
- System.currentTimeMillis() - lastRefresh) + " ago");
- if(lastRefresh <= System.currentTimeMillis() - interval) {
- DBTasks.refreshAllFeeds(context, null);
- }
- }
-
/**
* Notifies the database about a missing FeedMedia file. This method will correct the FeedMedia object's values in the
* DB and send a FeedUpdateBroadcast.
@@ -365,27 +334,6 @@ public final class DBTasks {
}
/**
- * Request the download of all objects in the queue. from a separate Thread.
- *
- * @param context Used for requesting the download an accessing the database.
- */
- public static void downloadAllItemsInQueue(final Context context) {
- new Thread() {
- public void run() {
- List<FeedItem> queue = DBReader.getQueue();
- if (!queue.isEmpty()) {
- try {
- downloadFeedItems(context,
- queue.toArray(new FeedItem[queue.size()]));
- } catch (DownloadRequestException e) {
- e.printStackTrace();
- }
- }
- }
- }.start();
- }
-
- /**
* Requests the download of a list of FeedItem objects.
*
* @param context Used for requesting the download and accessing the DB.
@@ -804,10 +752,8 @@ public final class DBTasks {
*/
abstract static class QueryTask<T> implements Callable<T> {
private T result;
- private Context context;
public QueryTask(Context context) {
- this.context = context;
}
@Override
@@ -821,7 +767,7 @@ public final class DBTasks {
public abstract void execute(PodDBAdapter adapter);
- protected void setResult(T result) {
+ void setResult(T result) {
this.result = result;
}
}
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
new file mode 100644
index 000000000..0beb765e7
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
@@ -0,0 +1,293 @@
+package de.danoeh.antennapod.core.storage;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.media.MediaMetadataRetriever;
+import android.util.Log;
+
+import de.danoeh.antennapod.core.feed.FeedItem;
+
+class DBUpgrader {
+ /**
+ * Upgrades the given database to a new schema version
+ */
+ static void upgrade(final SQLiteDatabase db, final int oldVersion, final int newVersion) {
+ if (oldVersion <= 1) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
+ + PodDBAdapter.KEY_TYPE + " TEXT");
+ }
+ if (oldVersion <= 2) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
+ + " ADD COLUMN " + PodDBAdapter.KEY_LINK + " TEXT");
+ }
+ if (oldVersion <= 3) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + PodDBAdapter.KEY_ITEM_IDENTIFIER + " TEXT");
+ }
+ if (oldVersion <= 4) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
+ + PodDBAdapter.KEY_FEED_IDENTIFIER + " TEXT");
+ }
+ if (oldVersion <= 5) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
+ + " ADD COLUMN " + PodDBAdapter.KEY_REASON_DETAILED + " TEXT");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
+ + " ADD COLUMN " + PodDBAdapter.KEY_DOWNLOADSTATUS_TITLE + " TEXT");
+ }
+ if (oldVersion <= 6) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
+ + " ADD COLUMN " + PodDBAdapter.KEY_CHAPTER_TYPE + " INTEGER");
+ }
+ if (oldVersion <= 7) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE
+ + " INTEGER");
+ }
+ if (oldVersion <= 8) {
+ final int KEY_ID_POSITION = 0;
+ final int KEY_MEDIA_POSITION = 1;
+
+ // Add feeditem column to feedmedia table
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + PodDBAdapter.KEY_FEEDITEM
+ + " INTEGER");
+ Cursor feeditemCursor = db.query(PodDBAdapter.TABLE_NAME_FEED_ITEMS,
+ new String[]{PodDBAdapter.KEY_ID, PodDBAdapter.KEY_MEDIA}, "? > 0",
+ new String[]{PodDBAdapter.KEY_MEDIA}, null, null, null);
+ if (feeditemCursor.moveToFirst()) {
+ db.beginTransaction();
+ ContentValues contentValues = new ContentValues();
+ do {
+ long mediaId = feeditemCursor.getLong(KEY_MEDIA_POSITION);
+ contentValues.put(PodDBAdapter.KEY_FEEDITEM, feeditemCursor.getLong(KEY_ID_POSITION));
+ db.update(PodDBAdapter.TABLE_NAME_FEED_MEDIA, contentValues, PodDBAdapter.KEY_ID + "=?", new String[]{String.valueOf(mediaId)});
+ contentValues.clear();
+ } while (feeditemCursor.moveToNext());
+ db.setTransactionSuccessful();
+ db.endTransaction();
+ }
+ feeditemCursor.close();
+ }
+ if (oldVersion <= 9) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD
+ + " INTEGER DEFAULT 1");
+ }
+ if (oldVersion <= 10) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS
+ + " INTEGER");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + PodDBAdapter.KEY_FLATTR_STATUS
+ + " INTEGER");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + PodDBAdapter.KEY_PLAYED_DURATION
+ + " INTEGER");
+ }
+ if (oldVersion <= 11) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_USERNAME
+ + " TEXT");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_PASSWORD
+ + " TEXT");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE
+ + " INTEGER");
+ }
+ if (oldVersion <= 12) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_IS_PAGED + " INTEGER DEFAULT 0");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_NEXT_PAGE_LINK + " TEXT");
+ }
+ if (oldVersion <= 13) {
+ // remove duplicate rows in "Chapters" table that were created because of a bug.
+ db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " +
+ "(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)",
+ PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
+ PodDBAdapter.KEY_ID,
+ PodDBAdapter.KEY_ID,
+ PodDBAdapter.KEY_ID,
+ PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
+ PodDBAdapter.KEY_TITLE,
+ PodDBAdapter.KEY_START,
+ PodDBAdapter.KEY_FEEDITEM,
+ PodDBAdapter.KEY_LINK,
+ PodDBAdapter.KEY_CHAPTER_TYPE));
+ }
+ if (oldVersion <= 14) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " INTEGER");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " SET " + PodDBAdapter.KEY_AUTO_DOWNLOAD + " = "
+ + "(SELECT " + PodDBAdapter.KEY_AUTO_DOWNLOAD
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_ID
+ + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_FEED + ")");
+
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_HIDE + " TEXT");
+
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0");
+
+ // create indexes
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_FEED);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDMEDIA_FEEDITEM);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_QUEUE_FEEDITEM);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
+ }
+ if (oldVersion <= 15) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE + " INTEGER DEFAULT -1");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " SET " + PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE + "=0"
+ + " WHERE " + PodDBAdapter.KEY_DOWNLOADED + "=0");
+ Cursor c = db.rawQuery("SELECT " + PodDBAdapter.KEY_FILE_URL
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " WHERE " + PodDBAdapter.KEY_DOWNLOADED + "=1 "
+ + " AND " + PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE + "=-1", null);
+ if (c.moveToFirst()) {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ do {
+ String fileUrl = c.getString(0);
+ try {
+ mmr.setDataSource(fileUrl);
+ byte[] image = mmr.getEmbeddedPicture();
+ if (image != null) {
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " SET " + PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE + "=1"
+ + " WHERE " + PodDBAdapter.KEY_FILE_URL + "='" + fileUrl + "'");
+ } else {
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " SET " + PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE + "=0"
+ + " WHERE " + PodDBAdapter.KEY_FILE_URL + "='" + fileUrl + "'");
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } while (c.moveToNext());
+ }
+ c.close();
+ }
+ if (oldVersion <= 16) {
+ String selectNew = "SELECT " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_ID
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " INNER JOIN " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + " ON "
+ + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_ID + "="
+ + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_FEEDITEM
+ + " LEFT OUTER JOIN " + PodDBAdapter.TABLE_NAME_QUEUE + " ON "
+ + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_ID + "="
+ + PodDBAdapter.TABLE_NAME_QUEUE + "." + PodDBAdapter.KEY_FEEDITEM
+ + " WHERE "
+ + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_READ + " = 0 AND " // unplayed
+ + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_DOWNLOADED + " = 0 AND " // undownloaded
+ + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + PodDBAdapter.KEY_POSITION + " = 0 AND " // not partially played
+ + PodDBAdapter.TABLE_NAME_QUEUE + "." + PodDBAdapter.KEY_ID + " IS NULL"; // not in queue
+ String sql = "UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " SET " + PodDBAdapter.KEY_READ + "=" + FeedItem.NEW
+ + " WHERE " + PodDBAdapter.KEY_ID + " IN (" + selectNew + ")";
+ Log.d("Migration", "SQL: " + sql);
+ db.execSQL(sql);
+ }
+ if (oldVersion <= 17) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0");
+ }
+ if (oldVersion < 1030005) {
+ db.execSQL("UPDATE FeedItems SET auto_download=0 WHERE " +
+ "(read=1 OR id IN (SELECT feeditem FROM FeedMedia WHERE position>0 OR downloaded=1)) " +
+ "AND id NOT IN (SELECT feeditem FROM Queue)");
+ }
+ if (oldVersion < 1040001) {
+ db.execSQL(PodDBAdapter.CREATE_TABLE_FAVORITES);
+ }
+ if (oldVersion < 1040002) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
+ + " ADD COLUMN " + PodDBAdapter.KEY_LAST_PLAYED_TIME + " INTEGER DEFAULT 0");
+ }
+ if (oldVersion < 1040013) {
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_PUBDATE);
+ db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_READ);
+ }
+ if (oldVersion < 1050003) {
+ // Migrates feed list filter data
+
+ db.beginTransaction();
+
+ // Change to intermediate values to avoid overwriting in the following find/replace
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'unplayed', 'noplay')");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'not_queued', 'noqueue')");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'not_downloaded', 'nodl')");
+
+ // Replace played, queued, and downloaded with their opposites
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'played', 'unplayed')");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'queued', 'not_queued')");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'downloaded', 'not_downloaded')");
+
+ // Now replace intermediates for unplayed, not queued, etc. with their opposites
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'noplay', 'played')");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'noqueue', 'queued')");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'nodl', 'downloaded')");
+
+ // Paused doesn't have an opposite, so unplayed is the next best option
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + "\n" +
+ "SET " + PodDBAdapter.KEY_HIDE + " = replace(" + PodDBAdapter.KEY_HIDE + ", 'paused', 'unplayed')");
+
+ db.setTransactionSuccessful();
+ db.endTransaction();
+
+ // and now get ready for autodownload filters
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_INCLUDE_FILTER + " TEXT DEFAULT ''");
+
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_EXCLUDE_FILTER + " TEXT DEFAULT ''");
+
+ // and now auto refresh
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_KEEP_UPDATED + " INTEGER DEFAULT 1");
+ }
+ if (oldVersion < 1050004) {
+ // prevent old timestamps to be misinterpreted as ETags
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " SET " + PodDBAdapter.KEY_LASTUPDATE + "=NULL");
+ }
+ if (oldVersion < 1060200) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_CUSTOM_TITLE + " TEXT");
+ }
+ if (oldVersion < 1060596) {
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE_URL + " TEXT");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " ADD COLUMN " + PodDBAdapter.KEY_IMAGE_URL + " TEXT");
+
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + " SET " + PodDBAdapter.KEY_IMAGE_URL + " = ("
+ + " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES
+ + " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID
+ + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + PodDBAdapter.KEY_IMAGE + ")");
+
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS + " SET " + PodDBAdapter.KEY_IMAGE_URL + " = ("
+ + " SELECT " + PodDBAdapter.KEY_DOWNLOAD_URL
+ + " FROM " + PodDBAdapter.TABLE_NAME_FEED_IMAGES
+ + " WHERE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES + "." + PodDBAdapter.KEY_ID
+ + " = " + PodDBAdapter.TABLE_NAME_FEEDS + "." + PodDBAdapter.KEY_IMAGE + ")");
+
+ db.execSQL("DROP TABLE " + PodDBAdapter.TABLE_NAME_FEED_IMAGES);
+ }
+ }
+
+}
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 49ec07004..bbe6145ea 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
@@ -7,8 +7,6 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.event.MessageEvent;
import org.shredzone.flattr4j.model.Flattr;
import java.io.File;
@@ -25,14 +23,15 @@ import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.asynctask.FlattrClickWorker;
import de.danoeh.antennapod.core.event.FavoritesEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
+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.feed.FeedEvent;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
@@ -42,7 +41,9 @@ 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.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.Permutor;
import de.danoeh.antennapod.core.util.flattr.FlattrStatus;
import de.danoeh.antennapod.core.util.flattr.FlattrThing;
import de.danoeh.antennapod.core.util.flattr.SimpleFlattrThing;
@@ -115,11 +116,8 @@ public class DBWriter {
true);
editor.commit();
}
- if (PlaybackPreferences
- .getCurrentlyPlayingFeedMediaId() == media
- .getId()) {
- context.sendBroadcast(new Intent(
- PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ if (PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == media.getId()) {
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
}
}
// Gpodder: queue delete action for synchronization
@@ -156,8 +154,7 @@ public class DBWriter {
if (PlaybackPreferences.getCurrentlyPlayingMedia() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA
&& PlaybackPreferences.getLastPlayedFeedId() == feed
.getId()) {
- context.sendBroadcast(new Intent(
- PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE);
SharedPreferences.Editor editor = prefs.edit();
editor.putLong(
PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
@@ -165,17 +162,6 @@ public class DBWriter {
editor.commit();
}
- // delete image file
- if (feed.getImage() != null) {
- if (feed.getImage().isDownloaded()
- && feed.getImage().getFile_url() != null) {
- File imageFile = new File(feed.getImage()
- .getFile_url());
- imageFile.delete();
- } else if (requester.isDownloadingFile(feed.getImage())) {
- requester.cancelDownload(context, feed.getImage());
- }
- }
// delete stored media files and mark them as read
List<FeedItem> queue = DBReader.getQueue();
List<FeedItem> removed = new ArrayList<>();
@@ -187,6 +173,9 @@ public class DBWriter {
if(queue.remove(item)) {
removed.add(item);
}
+ if (item.getState() == FeedItem.State.PLAYING && PlaybackService.isRunning) {
+ context.stopService(new Intent(context, PlaybackService.class));
+ }
if (item.getMedia() != null
&& item.getMedia().isDownloaded()) {
File mediaFile = new File(item.getMedia()
@@ -196,16 +185,6 @@ public class DBWriter {
&& requester.isDownloadingFile(item.getMedia())) {
requester.cancelDownload(context, item.getMedia());
}
-
- if (item.hasItemImage()) {
- FeedImage image = item.getImage();
- if (image.isDownloaded() && image.getFile_url() != null) {
- File imgFile = new File(image.getFile_url());
- imgFile.delete();
- } else if (requester.isDownloadingFile(image)) {
- requester.cancelDownload(context, item.getImage());
- }
- }
}
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -382,8 +361,8 @@ public class DBWriter {
// add item to either front ot back of queue
boolean addToFront = UserPreferences.enqueueAtFront();
if (addToFront) {
- queue.add(0 + i, item);
- events.add(QueueEvent.added(item, 0 + i));
+ queue.add(i, item);
+ events.add(QueueEvent.added(item, i));
} else {
queue.add(item);
events.add(QueueEvent.added(item, queue.size() - 1));
@@ -478,22 +457,6 @@ public class DBWriter {
});
}
- public static Future<?> addFavoriteItemById(final long itemId) {
- return dbExec.submit(() -> {
- final FeedItem item = DBReader.getFeedItem(itemId);
- if (item == null) {
- Log.d(TAG, "Can't find item for itemId " + itemId);
- return;
- }
- final PodDBAdapter adapter = PodDBAdapter.getInstance().open();
- adapter.addFavoriteItem(item);
- adapter.close();
- item.addTag(FeedItem.TAG_FAVORITE);
- EventBus.getDefault().post(FavoritesEvent.added(item));
- EventBus.getDefault().post(FeedItemEvent.updated(item));
- });
- }
-
public static Future<?> removeFavoriteItem(final FeedItem item) {
return dbExec.submit(() -> {
final PodDBAdapter adapter = PodDBAdapter.getInstance().open();
@@ -782,21 +745,6 @@ public class DBWriter {
}
/**
- * Saves a FeedImage object in the database. This method will save all attributes of the FeedImage object. The
- * contents of FeedComponent-attributes (e.g. the FeedImages's 'feed'-attribute) will not be saved.
- *
- * @param image The FeedImage object.
- */
- public static Future<?> setFeedImage(final FeedImage image) {
- return dbExec.submit(() -> {
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.setImage(image);
- adapter.close();
- });
- }
-
- /**
* Updates download URL of a feed
*/
public static Future<?> updateFeedDownloadURL(final String original, final String updated) {
@@ -838,9 +786,9 @@ public class DBWriter {
*
* @param startFlattrClickWorker true if FlattrClickWorker should be started after the FlattrStatus has been saved
*/
- public static Future<?> setFeedItemFlattrStatus(final Context context,
- final FeedItem item,
- final boolean startFlattrClickWorker) {
+ private static Future<?> setFeedItemFlattrStatus(final Context context,
+ final FeedItem item,
+ final boolean startFlattrClickWorker) {
return dbExec.submit(() -> {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
@@ -992,6 +940,32 @@ public class DBWriter {
}
/**
+ * Similar to sortQueue, but allows more complex reordering by providing whole-queue context.
+ * @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) {
+ return dbExec.submit(() -> {
+ final PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ final List<FeedItem> queue = DBReader.getQueue(adapter);
+
+ if (queue != null) {
+ permutor.reorder(queue);
+ adapter.setQueue(queue);
+ if (broadcastUpdate) {
+ EventBus.getDefault().post(QueueEvent.sorted(queue));
+ }
+ } else {
+ Log.e(TAG, "reorderQueue: Could not load queue");
+ }
+ adapter.close();
+ });
+ }
+
+ /**
* Sets the 'auto_download'-attribute of specific FeedItem.
*
* @param feedItem FeedItem.
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 7051d7f4d..892a4675a 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
@@ -4,6 +4,7 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.URLUtil;
@@ -22,6 +23,7 @@ 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.util.FileNameGenerator;
+import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.URLChecker;
@@ -33,8 +35,8 @@ public class DownloadRequester {
private static final String TAG = "DownloadRequester";
public static final String IMAGE_DOWNLOADPATH = "images/";
- public static final String FEED_DOWNLOADPATH = "cache/";
- public static final String MEDIA_DOWNLOADPATH = "media/";
+ private static final String FEED_DOWNLOADPATH = "cache/";
+ private static final String MEDIA_DOWNLOADPATH = "media/";
/**
* Denotes the page of the feed that is contained in the DownloadRequest sent by the DownloadRequester.
@@ -48,7 +50,7 @@ public class DownloadRequester {
private static DownloadRequester downloader;
- private Map<String, DownloadRequest> downloads;
+ private final Map<String, DownloadRequest> downloads;
private DownloadRequester() {
downloads = new ConcurrentHashMap<>();
@@ -81,7 +83,7 @@ public class DownloadRequester {
Intent launchIntent = new Intent(context, DownloadService.class);
launchIntent.putExtra(DownloadService.EXTRA_REQUEST, request);
- context.startService(launchIntent);
+ ContextCompat.startForegroundService(context, launchIntent);
return true;
}
@@ -89,7 +91,9 @@ public class DownloadRequester {
private void download(Context context, 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;
+ 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");
@@ -174,8 +178,8 @@ 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(context),
- getFeedfileName(feed)), true, username, password, lastModified, true, args);
+ download(context, feed, null, new File(getFeedfilePath(), getFeedfileName(feed)),
+ true, username, password, lastModified, true, args);
}
}
@@ -201,8 +205,7 @@ public class DownloadRequester {
if (feedmedia.getFile_url() != null) {
dest = new File(feedmedia.getFile_url());
} else {
- dest = new File(getMediafilePath(context, feedmedia),
- getMediafilename(feedmedia));
+ dest = new File(getMediafilePath(feedmedia), getMediafilename(feedmedia));
}
download(context, feedmedia, feed,
dest, false, username, password, null, false, null);
@@ -240,6 +243,7 @@ public class DownloadRequester {
Log.d(TAG, "Cancelling download with url " + downloadUrl);
Intent cancelIntent = new Intent(DownloadService.ACTION_CANCEL_DOWNLOAD);
cancelIntent.putExtra(DownloadService.EXTRA_DOWNLOAD_URL, downloadUrl);
+ cancelIntent.setPackage(context.getPackageName());
context.sendBroadcast(cancelIntent);
}
@@ -248,8 +252,7 @@ public class DownloadRequester {
*/
public synchronized void cancelAllDownloads(Context context) {
Log.d(TAG, "Cancelling all running downloads");
- context.sendBroadcast(new Intent(
- DownloadService.ACTION_CANCEL_ALL_DOWNLOADS));
+ IntentUtils.sendLocalBroadcast(context, DownloadService.ACTION_CANCEL_ALL_DOWNLOADS);
}
/**
@@ -303,13 +306,11 @@ public class DownloadRequester {
return downloads.size();
}
- public synchronized String getFeedfilePath(Context context)
- throws DownloadRequestException {
- return getExternalFilesDirOrThrowException(context, FEED_DOWNLOADPATH)
- .toString() + "/";
+ private synchronized String getFeedfilePath() throws DownloadRequestException {
+ return getExternalFilesDirOrThrowException(FEED_DOWNLOADPATH).toString() + "/";
}
- public synchronized String getFeedfileName(Feed feed) {
+ private synchronized String getFeedfileName(Feed feed) {
String filename = feed.getDownload_url();
if (feed.getTitle() != null && !feed.getTitle().isEmpty()) {
filename = feed.getTitle();
@@ -317,10 +318,8 @@ public class DownloadRequester {
return "feed-" + FileNameGenerator.generateFileName(filename);
}
- public synchronized String getMediafilePath(Context context, FeedMedia media)
- throws DownloadRequestException {
+ private synchronized String getMediafilePath(FeedMedia media) throws DownloadRequestException {
File externalStorage = getExternalFilesDirOrThrowException(
- context,
MEDIA_DOWNLOADPATH
+ FileNameGenerator.generateFileName(media.getItem()
.getFeed().getTitle()) + "/"
@@ -328,8 +327,7 @@ public class DownloadRequester {
return externalStorage.toString();
}
- private File getExternalFilesDirOrThrowException(Context context,
- String type) throws DownloadRequestException {
+ private File getExternalFilesDirOrThrowException(String type) throws DownloadRequestException {
File result = UserPreferences.getDataFolder(type);
if (result == null) {
throw new DownloadRequestException(
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
index 97cbdca33..aae5b352e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/EpisodeCleanupAlgorithm.java
@@ -15,7 +15,7 @@ public abstract class EpisodeCleanupAlgorithm {
* or getPerformCleanupParameter.
* @return The number of episodes that were deleted.
*/
- public abstract int performCleanup(Context context, int numToRemove);
+ protected abstract int performCleanup(Context context, int numToRemove);
public int performCleanup(Context context) {
return performCleanup(context, getDefaultCleanupParameter());
@@ -26,7 +26,7 @@ public abstract class EpisodeCleanupAlgorithm {
* space to free to satisfy the episode cache conditions. If the conditions are already satisfied, this
* method should not have any effects.
*/
- public abstract int getDefaultCleanupParameter();
+ protected abstract int getDefaultCleanupParameter();
/**
* Cleans up just enough episodes to make room for the requested number
@@ -48,7 +48,7 @@ public abstract class EpisodeCleanupAlgorithm {
* @param amountOfRoomNeeded the number of episodes we want to download
* @return the number of episodes to delete in order to make room
*/
- protected int getNumEpisodesToCleanup(final int amountOfRoomNeeded) {
+ int getNumEpisodesToCleanup(final int amountOfRoomNeeded) {
if (amountOfRoomNeeded >= 0
&& UserPreferences.getEpisodeCacheSize() != UserPreferences
.getEpisodeCacheSizeUnlimited()) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemStatistics.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemStatistics.java
index 09949b87e..d84279f6e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemStatistics.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedItemStatistics.java
@@ -8,11 +8,11 @@ import java.util.Date;
* Contains information about a feed's items.
*/
public class FeedItemStatistics {
- private long feedID;
- private int numberOfItems;
- private int numberOfNewItems;
- private int numberOfInProgressItems;
- private Date lastUpdate;
+ private final long feedID;
+ private final int numberOfItems;
+ private final int numberOfNewItems;
+ private final int numberOfInProgressItems;
+ private final Date lastUpdate;
private static final Date UNKNOWN_DATE = new Date(0);
@@ -26,7 +26,7 @@ public class FeedItemStatistics {
* @param lastUpdate pubDate of the latest episode. A lastUpdate value of 0 will be interpreted as DATE_UNKOWN if
* numberOfItems is 0.
*/
- public FeedItemStatistics(long feedID, int numberOfItems, int numberOfNewItems, int numberOfInProgressItems, Date lastUpdate) {
+ private FeedItemStatistics(long feedID, int numberOfItems, int numberOfNewItems, int numberOfInProgressItems, Date lastUpdate) {
this.feedID = feedID;
this.numberOfItems = numberOfItems;
this.numberOfNewItems = numberOfNewItems;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
index aa5706ad0..f91e2ad08 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
@@ -19,6 +19,8 @@ import de.danoeh.antennapod.core.util.comparator.SearchResultValueComparator;
* Performs search on Feeds and FeedItems
*/
public class FeedSearcher {
+ private FeedSearcher(){}
+
private static final String TAG = "FeedSearcher";
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 dc8692866..4566df2fc 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
@@ -3,31 +3,33 @@ package de.danoeh.antennapod.core.storage;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.DatabaseErrorHandler;
import android.database.DatabaseUtils;
+import android.database.DefaultDatabaseErrorHandler;
import android.database.MergeCursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.media.MediaMetadataRetriever;
-import android.os.Build;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.text.TextUtils;
import android.util.Log;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
-import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedComponent;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
@@ -45,7 +47,7 @@ import de.greenrobot.event.EventBus;
public class PodDBAdapter {
private static final String TAG = "PodDBAdapter";
- private static final String DATABASE_NAME = "Antennapod.db";
+ public static final String DATABASE_NAME = "Antennapod.db";
/**
* Maximum number of arguments for IN-operator.
@@ -73,6 +75,7 @@ public class PodDBAdapter {
public static final String KEY_SIZE = "filesize";
public static final String KEY_MIME_TYPE = "mime_type";
public static final String KEY_IMAGE = "image";
+ public static final String KEY_IMAGE_URL = "image_url";
public static final String KEY_FEED = "feed";
public static final String KEY_MEDIA = "media";
public static final String KEY_DOWNLOADED = "downloaded";
@@ -113,26 +116,26 @@ public class PodDBAdapter {
public static final String KEY_EXCLUDE_FILTER = "exclude_filter";
// Table names
- private static final String TABLE_NAME_FEEDS = "Feeds";
- private static final String TABLE_NAME_FEED_ITEMS = "FeedItems";
- private static final String TABLE_NAME_FEED_IMAGES = "FeedImages";
- private static final String TABLE_NAME_FEED_MEDIA = "FeedMedia";
- private static final String TABLE_NAME_DOWNLOAD_LOG = "DownloadLog";
- private static final String TABLE_NAME_QUEUE = "Queue";
- private static final String TABLE_NAME_SIMPLECHAPTERS = "SimpleChapters";
- private static final String TABLE_NAME_FAVORITES = "Favorites";
+ static final String TABLE_NAME_FEEDS = "Feeds";
+ static final String TABLE_NAME_FEED_ITEMS = "FeedItems";
+ static final String TABLE_NAME_FEED_IMAGES = "FeedImages";
+ static final String TABLE_NAME_FEED_MEDIA = "FeedMedia";
+ static final String TABLE_NAME_DOWNLOAD_LOG = "DownloadLog";
+ static final String TABLE_NAME_QUEUE = "Queue";
+ static final String TABLE_NAME_SIMPLECHAPTERS = "SimpleChapters";
+ static final String TABLE_NAME_FAVORITES = "Favorites";
// SQL Statements for creating new tables
private static final String TABLE_PRIMARY_KEY = KEY_ID
+ " INTEGER PRIMARY KEY AUTOINCREMENT ,";
- public static final String CREATE_TABLE_FEEDS = "CREATE TABLE "
+ private static final String CREATE_TABLE_FEEDS = "CREATE TABLE "
+ TABLE_NAME_FEEDS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_CUSTOM_TITLE + " TEXT," + KEY_FILE_URL + " TEXT," + KEY_DOWNLOAD_URL + " TEXT,"
+ KEY_DOWNLOADED + " INTEGER," + KEY_LINK + " TEXT,"
+ KEY_DESCRIPTION + " TEXT," + KEY_PAYMENT_LINK + " TEXT,"
+ KEY_LASTUPDATE + " TEXT," + KEY_LANGUAGE + " TEXT," + KEY_AUTHOR
- + " TEXT," + KEY_IMAGE + " INTEGER," + KEY_TYPE + " TEXT,"
+ + " TEXT," + KEY_IMAGE_URL + " TEXT," + KEY_TYPE + " TEXT,"
+ KEY_FEED_IDENTIFIER + " TEXT," + KEY_AUTO_DOWNLOAD + " INTEGER DEFAULT 1,"
+ KEY_FLATTR_STATUS + " INTEGER,"
+ KEY_USERNAME + " TEXT,"
@@ -146,7 +149,7 @@ public class PodDBAdapter {
+ KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0,"
+ KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0)";
- public static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
+ TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_CONTENT_ENCODED + " TEXT," + KEY_PUBDATE
+ " INTEGER," + KEY_READ + " INTEGER," + KEY_LINK + " TEXT,"
@@ -154,15 +157,10 @@ public class PodDBAdapter {
+ KEY_MEDIA + " INTEGER," + KEY_FEED + " INTEGER,"
+ KEY_HAS_CHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER + " TEXT,"
+ KEY_FLATTR_STATUS + " INTEGER,"
- + KEY_IMAGE + " INTEGER,"
+ + KEY_IMAGE_URL + " TEXT,"
+ KEY_AUTO_DOWNLOAD + " INTEGER)";
- public static final String CREATE_TABLE_FEED_IMAGES = "CREATE TABLE "
- + TABLE_NAME_FEED_IMAGES + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
- + " TEXT," + KEY_FILE_URL + " TEXT," + KEY_DOWNLOAD_URL + " TEXT,"
- + KEY_DOWNLOADED + " INTEGER)";
-
- public static final String CREATE_TABLE_FEED_MEDIA = "CREATE TABLE "
+ private static final String CREATE_TABLE_FEED_MEDIA = "CREATE TABLE "
+ TABLE_NAME_FEED_MEDIA + " (" + TABLE_PRIMARY_KEY + KEY_DURATION
+ " INTEGER," + KEY_FILE_URL + " TEXT," + KEY_DOWNLOAD_URL
+ " TEXT," + KEY_DOWNLOADED + " INTEGER," + KEY_POSITION
@@ -173,53 +171,48 @@ public class PodDBAdapter {
+ KEY_HAS_EMBEDDED_PICTURE + " INTEGER,"
+ KEY_LAST_PLAYED_TIME + " INTEGER)";
- public static final String CREATE_TABLE_DOWNLOAD_LOG = "CREATE TABLE "
+ private static final String CREATE_TABLE_DOWNLOAD_LOG = "CREATE TABLE "
+ TABLE_NAME_DOWNLOAD_LOG + " (" + TABLE_PRIMARY_KEY + KEY_FEEDFILE
+ " INTEGER," + KEY_FEEDFILETYPE + " INTEGER," + KEY_REASON
+ " INTEGER," + KEY_SUCCESSFUL + " INTEGER," + KEY_COMPLETION_DATE
+ " INTEGER," + KEY_REASON_DETAILED + " TEXT,"
+ KEY_DOWNLOADSTATUS_TITLE + " TEXT)";
- public static final String CREATE_TABLE_QUEUE = "CREATE TABLE "
+ private static final String CREATE_TABLE_QUEUE = "CREATE TABLE "
+ TABLE_NAME_QUEUE + "(" + KEY_ID + " INTEGER PRIMARY KEY,"
+ KEY_FEEDITEM + " INTEGER," + KEY_FEED + " INTEGER)";
- public static final String CREATE_TABLE_SIMPLECHAPTERS = "CREATE TABLE "
+ private static final String CREATE_TABLE_SIMPLECHAPTERS = "CREATE TABLE "
+ TABLE_NAME_SIMPLECHAPTERS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
+ " TEXT," + KEY_START + " INTEGER," + KEY_FEEDITEM + " INTEGER,"
+ KEY_LINK + " TEXT," + KEY_CHAPTER_TYPE + " INTEGER)";
// SQL Statements for creating indexes
- public static final String CREATE_INDEX_FEEDITEMS_FEED = "CREATE INDEX "
+ static final String CREATE_INDEX_FEEDITEMS_FEED = "CREATE INDEX "
+ TABLE_NAME_FEED_ITEMS + "_" + KEY_FEED + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ KEY_FEED + ")";
- public static final String CREATE_INDEX_FEEDITEMS_IMAGE = "CREATE INDEX "
- + TABLE_NAME_FEED_ITEMS + "_" + KEY_IMAGE + " ON " + TABLE_NAME_FEED_ITEMS + " ("
- + KEY_IMAGE + ")";
-
- public static final String CREATE_INDEX_FEEDITEMS_PUBDATE = "CREATE INDEX IF NOT EXISTS "
+ static final String CREATE_INDEX_FEEDITEMS_PUBDATE = "CREATE INDEX IF NOT EXISTS "
+ TABLE_NAME_FEED_ITEMS + "_" + KEY_PUBDATE + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ KEY_PUBDATE + ")";
- public static final String CREATE_INDEX_FEEDITEMS_READ = "CREATE INDEX IF NOT EXISTS "
+ static final String CREATE_INDEX_FEEDITEMS_READ = "CREATE INDEX IF NOT EXISTS "
+ TABLE_NAME_FEED_ITEMS + "_" + KEY_READ + " ON " + TABLE_NAME_FEED_ITEMS + " ("
+ KEY_READ + ")";
-
- public static final String CREATE_INDEX_QUEUE_FEEDITEM = "CREATE INDEX "
+ static final String CREATE_INDEX_QUEUE_FEEDITEM = "CREATE INDEX "
+ TABLE_NAME_QUEUE + "_" + KEY_FEEDITEM + " ON " + TABLE_NAME_QUEUE + " ("
+ KEY_FEEDITEM + ")";
- public static final String CREATE_INDEX_FEEDMEDIA_FEEDITEM = "CREATE INDEX "
+ static final String CREATE_INDEX_FEEDMEDIA_FEEDITEM = "CREATE INDEX "
+ TABLE_NAME_FEED_MEDIA + "_" + KEY_FEEDITEM + " ON " + TABLE_NAME_FEED_MEDIA + " ("
+ KEY_FEEDITEM + ")";
- public static final String CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM = "CREATE INDEX "
+ static final String CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM = "CREATE INDEX "
+ TABLE_NAME_SIMPLECHAPTERS + "_" + KEY_FEEDITEM + " ON " + TABLE_NAME_SIMPLECHAPTERS + " ("
+ KEY_FEEDITEM + ")";
- public static final String CREATE_TABLE_FAVORITES = "CREATE TABLE "
+ static final String CREATE_TABLE_FAVORITES = "CREATE TABLE "
+ TABLE_NAME_FAVORITES + "(" + KEY_ID + " INTEGER PRIMARY KEY,"
+ KEY_FEEDITEM + " INTEGER," + KEY_FEED + " INTEGER)";
@@ -239,7 +232,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_LASTUPDATE,
TABLE_NAME_FEEDS + "." + KEY_LANGUAGE,
TABLE_NAME_FEEDS + "." + KEY_AUTHOR,
- TABLE_NAME_FEEDS + "." + KEY_IMAGE,
+ TABLE_NAME_FEEDS + "." + KEY_IMAGE_URL,
TABLE_NAME_FEEDS + "." + KEY_TYPE,
TABLE_NAME_FEEDS + "." + KEY_FEED_IDENTIFIER,
TABLE_NAME_FEEDS + "." + KEY_AUTO_DOWNLOAD,
@@ -272,7 +265,7 @@ public class PodDBAdapter {
TABLE_NAME_FEED_ITEMS + "." + KEY_HAS_CHAPTERS,
TABLE_NAME_FEED_ITEMS + "." + KEY_ITEM_IDENTIFIER,
TABLE_NAME_FEED_ITEMS + "." + KEY_FLATTR_STATUS,
- TABLE_NAME_FEED_ITEMS + "." + KEY_IMAGE,
+ TABLE_NAME_FEED_ITEMS + "." + KEY_IMAGE_URL,
TABLE_NAME_FEED_ITEMS + "." + KEY_AUTO_DOWNLOAD
};
@@ -282,7 +275,6 @@ public class PodDBAdapter {
private static final String[] ALL_TABLES = {
TABLE_NAME_FEEDS,
TABLE_NAME_FEED_ITEMS,
- TABLE_NAME_FEED_IMAGES,
TABLE_NAME_FEED_MEDIA,
TABLE_NAME_DOWNLOAD_LOG,
TABLE_NAME_QUEUE,
@@ -307,73 +299,46 @@ public class PodDBAdapter {
KEY_CONTENT_ENCODED, KEY_FEED};
private static Context context;
- private static PodDBHelper dbHelper;
private static volatile SQLiteDatabase db;
- private static Lock dbLock = new ReentrantLock();
- private static AtomicInteger counter = new AtomicInteger(0);
public static void init(Context context) {
PodDBAdapter.context = context.getApplicationContext();
}
- private static class PodDBHelperholder {
- public static final PodDBHelper dbHelper = new PodDBHelper(PodDBAdapter.context, DATABASE_NAME, null);
+ // Bill Pugh Singleton Implementation
+ private static class SingletonHolder {
+ private static final PodDBHelper dbHelper = new PodDBHelper(PodDBAdapter.context, DATABASE_NAME, null);
+ private static final PodDBAdapter dbAdapter = new PodDBAdapter();
}
public static PodDBAdapter getInstance() {
- dbHelper = PodDBHelperholder.dbHelper;
- return new PodDBAdapter();
+ return SingletonHolder.dbAdapter;
}
private PodDBAdapter() {
}
- public PodDBAdapter open() {
- int adapters = counter.incrementAndGet();
- Log.v(TAG, "Opening DB #" + adapters);
-
- if ((db == null) || (!db.isOpen()) || (db.isReadOnly())) {
- try {
- dbLock.lock();
- if ((db == null) || (!db.isOpen()) || (db.isReadOnly())) {
- db = openDb();
- }
- } finally {
- dbLock.unlock();
- }
+ public synchronized PodDBAdapter open() {
+ if (db == null || !db.isOpen() || db.isReadOnly()) {
+ db = openDb();
}
return this;
}
private SQLiteDatabase openDb() {
- SQLiteDatabase newDb = null;
+ SQLiteDatabase newDb;
try {
- newDb = dbHelper.getWritableDatabase();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- newDb.enableWriteAheadLogging();
- }
+ newDb = SingletonHolder.dbHelper.getWritableDatabase();
} catch (SQLException ex) {
Log.e(TAG, Log.getStackTraceString(ex));
- newDb = dbHelper.getReadableDatabase();
+ newDb = SingletonHolder.dbHelper.getReadableDatabase();
}
return newDb;
}
- public void close() {
- int adapters = counter.decrementAndGet();
- Log.v(TAG, "Closing DB #" + adapters);
-
- if (adapters == 0) {
- Log.v(TAG, "Closing DB, really");
- try {
- dbLock.lock();
- db.close();
- db = null;
- } finally {
- dbLock.unlock();
- }
- }
+ public synchronized void close() {
+ // do nothing
}
public static boolean deleteDatabase() {
@@ -394,7 +359,7 @@ public class PodDBAdapter {
*
* @return the id of the entry
*/
- public long setFeed(Feed feed) {
+ private long setFeed(Feed feed) {
ContentValues values = new ContentValues();
values.put(KEY_TITLE, feed.getFeedTitle());
values.put(KEY_LINK, feed.getLink());
@@ -402,12 +367,7 @@ public class PodDBAdapter {
values.put(KEY_PAYMENT_LINK, feed.getPaymentLink());
values.put(KEY_AUTHOR, feed.getAuthor());
values.put(KEY_LANGUAGE, feed.getLanguage());
- if (feed.getImage() != null) {
- if (feed.getImage().getId() == 0) {
- setImage(feed.getImage());
- }
- values.put(KEY_IMAGE, feed.getImage().getId());
- }
+ values.put(KEY_IMAGE_URL, feed.getImageUrl());
values.put(KEY_FILE_URL, feed.getFile_url());
values.put(KEY_DOWNLOAD_URL, feed.getDownload_url());
@@ -464,58 +424,7 @@ public class PodDBAdapter {
}
/**
- * Inserts or updates an image entry
- *
- * @return the id of the entry
- */
- public long setImage(FeedImage image) {
- boolean startedTransaction = false;
-
- try {
- if (!db.inTransaction()) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
- startedTransaction = true;
- }
-
- ContentValues values = new ContentValues();
- values.put(KEY_TITLE, image.getTitle());
- values.put(KEY_DOWNLOAD_URL, image.getDownload_url());
- values.put(KEY_DOWNLOADED, image.isDownloaded());
- values.put(KEY_FILE_URL, image.getFile_url());
- if (image.getId() == 0) {
- image.setId(db.insert(TABLE_NAME_FEED_IMAGES, null, values));
- } else {
- db.update(TABLE_NAME_FEED_IMAGES, values, KEY_ID + "=?",
- new String[]{String.valueOf(image.getId())});
- }
-
- final FeedComponent owner = image.getOwner();
- if (owner != null && owner.getId() != 0) {
- values.clear();
- values.put(KEY_IMAGE, image.getId());
- if (owner instanceof Feed) {
- db.update(TABLE_NAME_FEEDS, values, KEY_ID + "=?", new String[]{String.valueOf(image.getOwner().getId())});
- }
- }
- if (startedTransaction) {
- db.setTransactionSuccessful();
- }
- } catch (SQLException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- } finally {
- if (startedTransaction) {
- db.endTransaction();
- }
- }
- return image.getId();
- }
-
- /**
- * Inserts or updates an image entry
+ * Inserts or updates a media entry
*
* @return the id of the entry
*/
@@ -580,11 +489,7 @@ public class PodDBAdapter {
*/
public void setCompleteFeed(Feed... feeds) {
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
+ db.beginTransactionNonExclusive();
for (Feed feed : feeds) {
setFeed(feed);
if (feed.getItems() != null) {
@@ -630,31 +535,6 @@ public class PodDBAdapter {
}
/**
- * Counts feeds and feed items in the flattr queue
- */
- public int getFlattrQueueSize() {
- int res = 0;
- Cursor c = db.rawQuery(String.format("SELECT count(*) FROM %s WHERE %s=%s",
- TABLE_NAME_FEEDS, KEY_FLATTR_STATUS, String.valueOf(FlattrStatus.STATUS_QUEUE)), null);
- if (c.moveToFirst()) {
- res = c.getInt(0);
- c.close();
- } else {
- Log.e(TAG, "Unable to determine size of flattr queue: Could not count number of feeds");
- }
- c = db.rawQuery(String.format("SELECT count(*) FROM %s WHERE %s=%s",
- TABLE_NAME_FEED_ITEMS, KEY_FLATTR_STATUS, String.valueOf(FlattrStatus.STATUS_QUEUE)), null);
- if (c.moveToFirst()) {
- res += c.getInt(0);
- c.close();
- } else {
- Log.e(TAG, "Unable to determine size of flattr queue: Could not count number of feed items");
- }
-
- return res;
- }
-
- /**
* Updates the download URL of a Feed.
*/
public void setFeedDownloadUrl(String original, String updated) {
@@ -665,11 +545,7 @@ public class PodDBAdapter {
public void setFeedItemlist(List<FeedItem> items) {
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
+ db.beginTransactionNonExclusive();
for (FeedItem item : items) {
setFeedItem(item, true);
}
@@ -684,11 +560,7 @@ public class PodDBAdapter {
public long setSingleFeedItem(FeedItem item) {
long result = 0;
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
+ db.beginTransactionNonExclusive();
result = setFeedItem(item, true);
db.setTransactionSuccessful();
} catch (SQLException e) {
@@ -789,12 +661,7 @@ public class PodDBAdapter {
values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier());
values.put(KEY_FLATTR_STATUS, item.getFlattrStatus().toLong());
values.put(KEY_AUTO_DOWNLOAD, item.getAutoDownload());
- if (item.hasItemImage()) {
- if (item.getImage().getId() == 0) {
- setImage(item.getImage());
- }
- values.put(KEY_IMAGE, item.getImage().getId());
- }
+ values.put(KEY_IMAGE_URL, item.getImageUrl());
if (item.getId() == 0) {
item.setId(db.insert(TABLE_NAME_FEED_ITEMS, null, values));
@@ -814,11 +681,7 @@ public class PodDBAdapter {
public void setFeedItemRead(int played, long itemId, long mediaId,
boolean resetMediaPosition) {
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
+ db.beginTransactionNonExclusive();
ContentValues values = new ContentValues();
values.put(KEY_READ, played);
@@ -846,11 +709,7 @@ public class PodDBAdapter {
*/
public void setFeedItemRead(int read, long... itemIds) {
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
+ db.beginTransactionNonExclusive();
ContentValues values = new ContentValues();
for (long id : itemIds) {
values.clear();
@@ -865,7 +724,7 @@ public class PodDBAdapter {
}
}
- public void setChapters(FeedItem item) {
+ private void setChapters(FeedItem item) {
ContentValues values = new ContentValues();
for (Chapter chapter : item.getChapters()) {
values.put(KEY_TITLE, chapter.getTitle());
@@ -933,11 +792,7 @@ public class PodDBAdapter {
public void setFavorites(List<FeedItem> favorites) {
ContentValues values = new ContentValues();
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
+ db.beginTransactionNonExclusive();
db.delete(TABLE_NAME_FAVORITES, null, null);
for (int i = 0; i < favorites.size(); i++) {
FeedItem item = favorites.get(i);
@@ -977,7 +832,7 @@ public class PodDBAdapter {
db.execSQL(deleteClause);
}
- public boolean isItemInFavorites(FeedItem item) {
+ private boolean isItemInFavorites(FeedItem item) {
String query = String.format("SELECT %s from %s WHERE %s=%d",
KEY_ID, TABLE_NAME_FAVORITES, KEY_FEEDITEM, item.getId());
Cursor c = db.rawQuery(query, null);
@@ -986,25 +841,10 @@ public class PodDBAdapter {
return count > 0;
}
- public long getDownloadLogSize() {
- final String query = String.format("SELECT COUNT(%s) FROM %s", KEY_ID, TABLE_NAME_DOWNLOAD_LOG);
- Cursor result = db.rawQuery(query, null);
- long count = 0;
- if (result.moveToFirst()) {
- count = result.getLong(0);
- }
- result.close();
- return count;
- }
-
public void setQueue(List<FeedItem> queue) {
ContentValues values = new ContentValues();
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
+ db.beginTransactionNonExclusive();
db.delete(TABLE_NAME_QUEUE, null, null);
for (int i = 0; i < queue.size(); i++) {
FeedItem item = queue.get(i);
@@ -1025,7 +865,7 @@ public class PodDBAdapter {
db.delete(TABLE_NAME_QUEUE, null, null);
}
- public void removeFeedMedia(FeedMedia media) {
+ private void removeFeedMedia(FeedMedia media) {
// delete download log entries for feed media
db.delete(TABLE_NAME_DOWNLOAD_LOG, KEY_FEEDFILE + "=? AND " + KEY_FEEDFILETYPE + "=?",
new String[]{String.valueOf(media.getId()), String.valueOf(FeedMedia.FEEDFILETYPE_FEEDMEDIA)});
@@ -1034,29 +874,21 @@ public class PodDBAdapter {
new String[]{String.valueOf(media.getId())});
}
- public void removeChaptersOfItem(FeedItem item) {
+ private void removeChaptersOfItem(FeedItem item) {
db.delete(TABLE_NAME_SIMPLECHAPTERS, KEY_FEEDITEM + "=?",
new String[]{String.valueOf(item.getId())});
}
- public void removeFeedImage(FeedImage image) {
- db.delete(TABLE_NAME_FEED_IMAGES, KEY_ID + "=?",
- new String[]{String.valueOf(image.getId())});
- }
-
/**
* Remove a FeedItem and its FeedMedia entry.
*/
- public void removeFeedItem(FeedItem item) {
+ private void removeFeedItem(FeedItem item) {
if (item.getMedia() != null) {
removeFeedMedia(item.getMedia());
}
if (item.hasChapters() || item.getChapters() != null) {
removeChaptersOfItem(item);
}
- if (item.hasItemImage()) {
- removeFeedImage(item.getImage());
- }
db.delete(TABLE_NAME_FEED_ITEMS, KEY_ID + "=?",
new String[]{String.valueOf(item.getId())});
}
@@ -1066,14 +898,7 @@ public class PodDBAdapter {
*/
public void removeFeed(Feed feed) {
try {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- db.beginTransactionNonExclusive();
- } else {
- db.beginTransaction();
- }
- if (feed.getImage() != null) {
- removeFeedImage(feed.getImage());
- }
+ db.beginTransactionNonExclusive();
if (feed.getItems() != null) {
for (FeedItem item : feed.getItems()) {
removeFeedItem(item);
@@ -1127,7 +952,7 @@ public class PodDBAdapter {
return getAllItemsOfFeedCursor(feed.getId());
}
- public final Cursor getAllItemsOfFeedCursor(final long feedId) {
+ private Cursor getAllItemsOfFeedCursor(final long feedId) {
return db.query(TABLE_NAME_FEED_ITEMS, FEEDITEM_SEL_FI_SMALL, KEY_FEED
+ "=?", new String[]{String.valueOf(feedId)}, null, null,
null);
@@ -1144,18 +969,6 @@ public class PodDBAdapter {
}
/**
- * Returns a cursor for a DB query in the FeedMedia table for a given ID.
- *
- * @param item The item you want to get the FeedMedia from
- * @return The cursor of the query
- */
- public final Cursor getFeedMediaOfItemCursor(final FeedItem item) {
- return db.query(TABLE_NAME_FEED_MEDIA, null, KEY_ID + "=?",
- new String[]{String.valueOf(item.getMedia().getId())}, null,
- null, null);
- }
-
- /**
* Returns a cursor for a DB query in the FeedImages table for given IDs.
*
* @param imageIds IDs of the images
@@ -1370,11 +1183,7 @@ public class PodDBAdapter {
if (size == 1) {
return "(?)";
}
- StringBuilder builder =
- new StringBuilder("(")
- .append(TextUtils.join(",", Collections.nCopies(size, "?")))
- .append(")");
- return builder.toString();
+ return "(" + TextUtils.join(",", Collections.nCopies(size, "?")) + ")";
}
public final Cursor getFeedCursor(final long id) {
@@ -1417,17 +1226,12 @@ public class PodDBAdapter {
public Cursor getImageAuthenticationCursor(final String imageUrl) {
String downloadUrl = DatabaseUtils.sqlEscapeString(imageUrl);
final String query = ""
- + "SELECT " + KEY_USERNAME + "," + KEY_PASSWORD + " FROM " + TABLE_NAME_FEED_IMAGES
+ + "SELECT " + KEY_USERNAME + "," + KEY_PASSWORD + " FROM " + TABLE_NAME_FEED_ITEMS
+ " INNER JOIN " + TABLE_NAME_FEEDS
- + " ON " + TABLE_NAME_FEED_IMAGES + "." + KEY_ID + "=" + TABLE_NAME_FEEDS + "." + KEY_IMAGE
- + " WHERE " + TABLE_NAME_FEED_IMAGES + "." + KEY_DOWNLOAD_URL + "=" + downloadUrl
- + " UNION SELECT " + KEY_USERNAME + "," + KEY_PASSWORD
- + " FROM " + TABLE_NAME_FEED_IMAGES
- + " INNER JOIN " + TABLE_NAME_FEED_ITEMS
- + " ON " + TABLE_NAME_FEED_IMAGES + "." + KEY_ID + "=" + TABLE_NAME_FEED_ITEMS + "." + KEY_IMAGE
- + " INNER JOIN " + TABLE_NAME_FEEDS
- + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + TABLE_NAME_FEEDS + "." + KEY_ID
- + " WHERE " + TABLE_NAME_FEED_IMAGES + "." + KEY_DOWNLOAD_URL + "=" + downloadUrl;
+ + " ON " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + " = " + TABLE_NAME_FEEDS + "." + KEY_ID
+ + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_IMAGE_URL + "=" + downloadUrl
+ + " UNION SELECT " + KEY_USERNAME + "," + KEY_PASSWORD + " FROM " + TABLE_NAME_FEEDS
+ + " WHERE " + TABLE_NAME_FEEDS + "." + KEY_IMAGE_URL + "=" + downloadUrl;
return db.rawQuery(query, null);
}
@@ -1700,13 +1504,35 @@ public class PodDBAdapter {
}
/**
+ * Called when a database corruption happens
+ */
+ public static class PodDbErrorHandler implements DatabaseErrorHandler {
+ @Override
+ public void onCorruption(SQLiteDatabase db) {
+ Log.e(TAG, "Database corrupted: " + db.getPath());
+
+ File dbPath = new File(db.getPath());
+ File backupFolder = PodDBAdapter.context.getExternalFilesDir(null);
+ File backupFile = new File(backupFolder, "CorruptedDatabaseBackup.db");
+ try {
+ FileUtils.copyFile(dbPath, backupFile);
+ Log.d(TAG, "Dumped database to " + backupFile.getPath());
+ } catch (IOException e) {
+ Log.d(TAG, Log.getStackTraceString(e));
+ }
+
+ new DefaultDatabaseErrorHandler().onCorruption(db); // This deletes the database
+ }
+ }
+
+ /**
* Helper class for opening the Antennapod database.
*/
private static class PodDBHelper extends SQLiteOpenHelper {
- private static final int VERSION = 1060200;
+ private static final int VERSION = 1060596;
- private Context context;
+ private final Context context;
/**
* Constructor.
@@ -1717,7 +1543,7 @@ public class PodDBAdapter {
*/
public PodDBHelper(final Context context, final String name,
final CursorFactory factory) {
- super(context, name, factory, VERSION);
+ super(context, name, factory, VERSION, new PodDbErrorHandler());
this.context = context;
}
@@ -1725,7 +1551,6 @@ public class PodDBAdapter {
public void onCreate(final SQLiteDatabase db) {
db.execSQL(CREATE_TABLE_FEEDS);
db.execSQL(CREATE_TABLE_FEED_ITEMS);
- db.execSQL(CREATE_TABLE_FEED_IMAGES);
db.execSQL(CREATE_TABLE_FEED_MEDIA);
db.execSQL(CREATE_TABLE_DOWNLOAD_LOG);
db.execSQL(CREATE_TABLE_QUEUE);
@@ -1733,7 +1558,6 @@ public class PodDBAdapter {
db.execSQL(CREATE_TABLE_FAVORITES);
db.execSQL(CREATE_INDEX_FEEDITEMS_FEED);
- db.execSQL(CREATE_INDEX_FEEDITEMS_IMAGE);
db.execSQL(CREATE_INDEX_FEEDITEMS_PUBDATE);
db.execSQL(CREATE_INDEX_FEEDITEMS_READ);
db.execSQL(CREATE_INDEX_FEEDMEDIA_FEEDITEM);
@@ -1748,263 +1572,7 @@ public class PodDBAdapter {
EventBus.getDefault().post(ProgressEvent.start(context.getString(R.string.progress_upgrading_database)));
Log.w("DBAdapter", "Upgrading from version " + oldVersion + " to "
+ newVersion + ".");
- if (oldVersion <= 1) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
- + KEY_TYPE + " TEXT");
- }
- if (oldVersion <= 2) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
- + " ADD COLUMN " + KEY_LINK + " TEXT");
- }
- if (oldVersion <= 3) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + KEY_ITEM_IDENTIFIER + " TEXT");
- }
- if (oldVersion <= 4) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS + " ADD COLUMN "
- + KEY_FEED_IDENTIFIER + " TEXT");
- }
- if (oldVersion <= 5) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
- + " ADD COLUMN " + KEY_REASON_DETAILED + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_DOWNLOAD_LOG
- + " ADD COLUMN " + KEY_DOWNLOADSTATUS_TITLE + " TEXT");
- }
- if (oldVersion <= 6) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS
- + " ADD COLUMN " + KEY_CHAPTER_TYPE + " INTEGER");
- }
- if (oldVersion <= 7) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + KEY_PLAYBACK_COMPLETION_DATE
- + " INTEGER");
- }
- if (oldVersion <= 8) {
- final int KEY_ID_POSITION = 0;
- final int KEY_MEDIA_POSITION = 1;
-
- // Add feeditem column to feedmedia table
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + KEY_FEEDITEM
- + " INTEGER");
- Cursor feeditemCursor = db.query(PodDBAdapter.TABLE_NAME_FEED_ITEMS,
- new String[]{KEY_ID, KEY_MEDIA}, "? > 0",
- new String[]{KEY_MEDIA}, null, null, null);
- if (feeditemCursor.moveToFirst()) {
- db.beginTransaction();
- ContentValues contentValues = new ContentValues();
- do {
- long mediaId = feeditemCursor.getLong(KEY_MEDIA_POSITION);
- contentValues.put(KEY_FEEDITEM, feeditemCursor.getLong(KEY_ID_POSITION));
- db.update(PodDBAdapter.TABLE_NAME_FEED_MEDIA, contentValues, KEY_ID + "=?", new String[]{String.valueOf(mediaId)});
- contentValues.clear();
- } while (feeditemCursor.moveToNext());
- db.setTransactionSuccessful();
- db.endTransaction();
- }
- feeditemCursor.close();
- }
- if (oldVersion <= 9) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_AUTO_DOWNLOAD
- + " INTEGER DEFAULT 1");
- }
- if (oldVersion <= 10) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_FLATTR_STATUS
- + " INTEGER");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + KEY_FLATTR_STATUS
- + " INTEGER");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + KEY_PLAYED_DURATION
- + " INTEGER");
- }
- if (oldVersion <= 11) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_USERNAME
- + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_PASSWORD
- + " TEXT");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + KEY_IMAGE
- + " INTEGER");
- }
- if (oldVersion <= 12) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_IS_PAGED + " INTEGER DEFAULT 0");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_NEXT_PAGE_LINK + " TEXT");
- }
- if (oldVersion <= 13) {
- // remove duplicate rows in "Chapters" table that were created because of a bug.
- db.execSQL(String.format("DELETE FROM %s WHERE %s NOT IN " +
- "(SELECT MIN(%s) as %s FROM %s GROUP BY %s,%s,%s,%s,%s)",
- PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
- KEY_ID,
- KEY_ID,
- KEY_ID,
- PodDBAdapter.TABLE_NAME_SIMPLECHAPTERS,
- KEY_TITLE,
- KEY_START,
- KEY_FEEDITEM,
- KEY_LINK,
- KEY_CHAPTER_TYPE));
- }
- if (oldVersion <= 14) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " ADD COLUMN " + KEY_AUTO_DOWNLOAD + " INTEGER");
- db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " SET " + KEY_AUTO_DOWNLOAD + " = "
- + "(SELECT " + KEY_AUTO_DOWNLOAD
- + " FROM " + PodDBAdapter.TABLE_NAME_FEEDS
- + " WHERE " + PodDBAdapter.TABLE_NAME_FEEDS + "." + KEY_ID
- + " = " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + ")");
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_HIDE + " TEXT");
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + KEY_LAST_UPDATE_FAILED + " INTEGER DEFAULT 0");
-
- // create indexes
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_FEED);
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_IMAGE);
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDMEDIA_FEEDITEM);
- db.execSQL(PodDBAdapter.CREATE_INDEX_QUEUE_FEEDITEM);
- db.execSQL(PodDBAdapter.CREATE_INDEX_SIMPLECHAPTERS_FEEDITEM);
- }
- if (oldVersion <= 15) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + KEY_HAS_EMBEDDED_PICTURE + " INTEGER DEFAULT -1");
- db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=0"
- + " WHERE " + KEY_DOWNLOADED + "=0");
- Cursor c = db.rawQuery("SELECT " + KEY_FILE_URL
- + " FROM " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " WHERE " + KEY_DOWNLOADED + "=1 "
- + " AND " + KEY_HAS_EMBEDDED_PICTURE + "=-1", null);
- if (c.moveToFirst()) {
- MediaMetadataRetriever mmr = new MediaMetadataRetriever();
- do {
- String fileUrl = c.getString(0);
- try {
- mmr.setDataSource(fileUrl);
- byte[] image = mmr.getEmbeddedPicture();
- if (image != null) {
- db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=1"
- + " WHERE " + KEY_FILE_URL + "='" + fileUrl + "'");
- } else {
- db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " SET " + KEY_HAS_EMBEDDED_PICTURE + "=0"
- + " WHERE " + KEY_FILE_URL + "='" + fileUrl + "'");
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- } while (c.moveToNext());
- }
- c.close();
- }
- if (oldVersion <= 16) {
- String selectNew = "SELECT " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID
- + " FROM " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " INNER JOIN " + PodDBAdapter.TABLE_NAME_FEED_MEDIA + " ON "
- + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_FEEDITEM
- + " LEFT OUTER JOIN " + PodDBAdapter.TABLE_NAME_QUEUE + " ON "
- + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_ID + "="
- + PodDBAdapter.TABLE_NAME_QUEUE + "." + KEY_FEEDITEM
- + " WHERE "
- + PodDBAdapter.TABLE_NAME_FEED_ITEMS + "." + KEY_READ + " = 0 AND " // unplayed
- + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_DOWNLOADED + " = 0 AND " // undownloaded
- + PodDBAdapter.TABLE_NAME_FEED_MEDIA + "." + KEY_POSITION + " = 0 AND " // not partially played
- + PodDBAdapter.TABLE_NAME_QUEUE + "." + KEY_ID + " IS NULL"; // not in queue
- String sql = "UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
- + " SET " + KEY_READ + "=" + FeedItem.NEW
- + " WHERE " + KEY_ID + " IN (" + selectNew + ")";
- Log.d("Migration", "SQL: " + sql);
- db.execSQL(sql);
- }
- if (oldVersion <= 17) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0");
- }
- if (oldVersion < 1030005) {
- db.execSQL("UPDATE FeedItems SET auto_download=0 WHERE " +
- "(read=1 OR id IN (SELECT feeditem FROM FeedMedia WHERE position>0 OR downloaded=1)) " +
- "AND id NOT IN (SELECT feeditem FROM Queue)");
- }
- if (oldVersion < 1040001) {
- db.execSQL(CREATE_TABLE_FAVORITES);
- }
- if (oldVersion < 1040002) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEED_MEDIA
- + " ADD COLUMN " + PodDBAdapter.KEY_LAST_PLAYED_TIME + " INTEGER DEFAULT 0");
- }
- if (oldVersion < 1040013) {
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_PUBDATE);
- db.execSQL(PodDBAdapter.CREATE_INDEX_FEEDITEMS_READ);
- }
- if (oldVersion < 1050003) {
- // Migrates feed list filter data
-
- db.beginTransaction();
-
- // Change to intermediate values to avoid overwriting in the following find/replace
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'unplayed', 'noplay')");
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'not_queued', 'noqueue')");
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'not_downloaded', 'nodl')");
-
- // Replace played, queued, and downloaded with their opposites
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'played', 'unplayed')");
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'queued', 'not_queued')");
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'downloaded', 'not_downloaded')");
-
- // Now replace intermediates for unplayed, not queued, etc. with their opposites
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'noplay', 'played')");
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'noqueue', 'queued')");
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'nodl', 'downloaded')");
-
- // Paused doesn't have an opposite, so unplayed is the next best option
- db.execSQL("UPDATE " + TABLE_NAME_FEEDS + "\n" +
- "SET " + KEY_HIDE + " = replace(" + KEY_HIDE + ", 'paused', 'unplayed')");
-
- db.setTransactionSuccessful();
- db.endTransaction();
-
- // and now get ready for autodownload filters
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_INCLUDE_FILTER + " TEXT DEFAULT ''");
-
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_EXCLUDE_FILTER + " TEXT DEFAULT ''");
-
- // and now auto refresh
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_KEEP_UPDATED + " INTEGER DEFAULT 1");
- }
- if (oldVersion < 1050004) {
- // prevent old timestamps to be misinterpreted as ETags
- db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " SET " + PodDBAdapter.KEY_LASTUPDATE + "=NULL");
- }
- if (oldVersion < 1060200) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
- + " ADD COLUMN " + PodDBAdapter.KEY_CUSTOM_TITLE + " TEXT");
- }
-
+ DBUpgrader.upgrade(db, oldVersion, newVersion);
EventBus.getDefault().post(ProgressEvent.end());
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java
index 9efc5888f..8f2ce5465 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java
@@ -1,17 +1,19 @@
package de.danoeh.antennapod.core.syndication.handler;
-import de.danoeh.antennapod.core.feed.Feed;
import org.apache.commons.io.input.XmlStreamReader;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.IOException;
import java.io.Reader;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import de.danoeh.antennapod.core.feed.Feed;
+
public class FeedHandler {
public FeedHandlerResult parseFeed(Feed feed) throws SAXException, IOException,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
index f67721a6e..77300d864 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
@@ -9,8 +9,8 @@ import de.danoeh.antennapod.core.feed.Feed;
*/
public class FeedHandlerResult {
- public Feed feed;
- public Map<String, String> alternateFeedUrls;
+ public final Feed feed;
+ public final Map<String, String> alternateFeedUrls;
public FeedHandlerResult(Feed feed, Map<String, String> alternateFeedUrls) {
this.feed = feed;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java
index 66513a12e..1cd05aa26 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java
@@ -20,29 +20,29 @@ public class HandlerState {
/**
* Feed that the Handler is currently processing.
*/
- protected Feed feed;
+ Feed feed;
/**
* Contains links to related feeds, e.g. feeds with enclosures in other formats. The key of the map is the
* URL of the feed, the value is the title
*/
- protected Map<String, String> alternateUrls;
- protected ArrayList<FeedItem> items;
- protected FeedItem currentItem;
- protected Stack<SyndElement> tagstack;
+ final Map<String, String> alternateUrls;
+ private final ArrayList<FeedItem> items;
+ private FeedItem currentItem;
+ final Stack<SyndElement> tagstack;
/**
* Namespaces that have been defined so far.
*/
- protected Map<String, Namespace> namespaces;
- protected Stack<Namespace> defaultNamespaces;
+ final Map<String, Namespace> namespaces;
+ final Stack<Namespace> defaultNamespaces;
/**
* Buffer for saving characters.
*/
- protected StringBuffer contentBuf;
+ protected StringBuilder contentBuf;
/**
* Temporarily saved objects.
*/
- protected Map<String, Object> tempObjects;
+ private final Map<String, Object> tempObjects;
public HandlerState(Feed feed) {
this.feed = feed;
@@ -97,7 +97,7 @@ public class HandlerState {
return third;
}
- public StringBuffer getContentBuf() {
+ public StringBuilder getContentBuf() {
return contentBuf;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
index ae91c0743..ab66b912b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
@@ -18,10 +18,10 @@ import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
import de.danoeh.antennapod.core.syndication.namespace.atom.NSAtom;
/** Superclass for all SAX Handlers which process Syndication formats */
-public class SyndHandler extends DefaultHandler {
+class SyndHandler extends DefaultHandler {
private static final String TAG = "SyndHandler";
private static final String DEFAULT_PREFIX = "";
- protected HandlerState state;
+ final HandlerState state;
public SyndHandler(Feed feed, TypeGetter.Type type) {
state = new HandlerState(feed);
@@ -33,7 +33,7 @@ public class SyndHandler extends DefaultHandler {
@Override
public void startElement(String uri, String localName, String qName,
Attributes attributes) throws SAXException {
- state.contentBuf = new StringBuffer();
+ state.contentBuf = new StringBuilder();
Namespace handler = getHandlingNamespace(uri, qName);
if (handler != null) {
SyndElement element = handler.handleElementStart(localName, state,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java
index ee0a71f30..b4c77e58d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java
@@ -54,18 +54,19 @@ public class TypeGetter {
return Type.ATOM;
case RSS_ROOT:
String strVersion = xpp.getAttributeValue(null, "version");
- if (strVersion != null) {
- if (strVersion.equals("2.0")) {
- feed.setType(Feed.TYPE_RSS2);
- Log.d(TAG, "Recognized type RSS 2.0");
- return Type.RSS20;
- } else if (strVersion.equals("0.91")
- || strVersion.equals("0.92")) {
- Log.d(TAG, "Recognized type RSS 0.91/0.92");
- return Type.RSS091;
- }
+ if (strVersion == null) {
+ feed.setType(Feed.TYPE_RSS2);
+ Log.d(TAG, "Assuming type RSS 2.0");
+ return Type.RSS20;
+ } else if (strVersion.equals("2.0")) {
+ feed.setType(Feed.TYPE_RSS2);
+ Log.d(TAG, "Recognized type RSS 2.0");
+ return Type.RSS20;
+ } else if (strVersion.equals("0.91") || strVersion.equals("0.92")) {
+ Log.d(TAG, "Recognized type RSS 0.91/0.92");
+ return Type.RSS091;
}
- throw new UnsupportedFeedtypeException(Type.INVALID);
+ throw new UnsupportedFeedtypeException("Unsupported rss version");
default:
Log.d(TAG, "Type is invalid");
throw new UnsupportedFeedtypeException(Type.INVALID, tag);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java
index 3da9251d9..fd7d0a4e1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/UnsupportedFeedtypeException.java
@@ -4,8 +4,9 @@ import de.danoeh.antennapod.core.syndication.handler.TypeGetter.Type;
public class UnsupportedFeedtypeException extends Exception {
private static final long serialVersionUID = 9105878964928170669L;
- private TypeGetter.Type type;
- private String rootElement;
+ private final TypeGetter.Type type;
+ private String rootElement;
+ private String message = null;
public UnsupportedFeedtypeException(Type type) {
super();
@@ -17,6 +18,11 @@ public class UnsupportedFeedtypeException extends Exception {
this.rootElement = rootElement;
}
+ public UnsupportedFeedtypeException(String message) {
+ this.message = message;
+ type = Type.INVALID;
+ }
+
public TypeGetter.Type getType() {
return type;
}
@@ -27,7 +33,9 @@ public class UnsupportedFeedtypeException extends Exception {
@Override
public String getMessage() {
- if (type == TypeGetter.Type.INVALID) {
+ if (message != null) {
+ return message;
+ } else if (type == TypeGetter.Type.INVALID) {
return "Invalid type";
} else {
return "Type " + type + " not supported";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java
index 7d60566b2..b3b8a40ce 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java
@@ -7,7 +7,6 @@ import org.xml.sax.Attributes;
import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
public class NSITunes extends Namespace {
@@ -16,34 +15,27 @@ public class NSITunes extends Namespace {
public static final String NSURI = "http://www.itunes.com/dtds/podcast-1.0.dtd";
private static final String IMAGE = "image";
- private static final String IMAGE_TITLE = "image";
private static final String IMAGE_HREF = "href";
private static final String AUTHOR = "author";
public static final String DURATION = "duration";
- public static final String SUBTITLE = "subtitle";
- public static final String SUMMARY = "summary";
+ private static final String SUBTITLE = "subtitle";
+ private static final String SUMMARY = "summary";
@Override
public SyndElement handleElementStart(String localName, HandlerState state,
Attributes attributes) {
if (IMAGE.equals(localName)) {
- FeedImage image = new FeedImage();
- image.setTitle(IMAGE_TITLE);
- image.setDownload_url(attributes.getValue(IMAGE_HREF));
+ String url = attributes.getValue(IMAGE_HREF);
if (state.getCurrentItem() != null) {
- // this is an items image
- image.setTitle(state.getCurrentItem().getTitle() + IMAGE_TITLE);
- image.setOwner(state.getCurrentItem());
- state.getCurrentItem().setImage(image);
+ state.getCurrentItem().setImageUrl(url);
} else {
// this is the feed image
// prefer to all other images
- if (!TextUtils.isEmpty(image.getDownload_url())) {
- image.setOwner(state.getFeed());
- state.getFeed().setImage(image);
+ if (!TextUtils.isEmpty(url)) {
+ state.getFeed().setImageUrl(url);
}
}
}
@@ -55,6 +47,9 @@ public class NSITunes extends Namespace {
if(state.getContentBuf() == null) {
return;
}
+ SyndElement secondElement = state.getSecondTag();
+ String second = secondElement.getName();
+
if (AUTHOR.equals(localName)) {
if (state.getFeed() != null) {
String author = state.getContentBuf().toString();
@@ -103,10 +98,9 @@ public class NSITunes extends Namespace {
}
if (state.getCurrentItem() != null &&
(TextUtils.isEmpty(state.getCurrentItem().getDescription()) ||
- state.getCurrentItem().getDescription().length() * 1.25 < summary.length())
- ) {
+ state.getCurrentItem().getDescription().length() * 1.25 < summary.length())) {
state.getCurrentItem().setDescription(summary);
- } else if (state.getFeed() != null) {
+ } else if (NSRSS20.CHANNEL.equals(second) && state.getFeed() != null) {
state.getFeed().setDescription(summary);
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java
index f2cfc2e57..638383223 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java
@@ -7,7 +7,6 @@ import org.xml.sax.Attributes;
import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import de.danoeh.antennapod.core.syndication.namespace.atom.AtomText;
@@ -94,25 +93,16 @@ public class NSMedia extends Namespace {
}
state.getCurrentItem().setMedia(media);
} else if (state.getCurrentItem() != null && url != null && validTypeImage) {
- FeedImage image = new FeedImage();
- image.setDownload_url(url);
- image.setOwner(state.getCurrentItem());
-
- state.getCurrentItem().setImage(image);
+ state.getCurrentItem().setImageUrl(url);
}
} else if (IMAGE.equals(localName)) {
String url = attributes.getValue(IMAGE_URL);
if (url != null) {
- FeedImage image = new FeedImage();
- image.setDownload_url(url);
-
if (state.getCurrentItem() != null) {
- image.setOwner(state.getCurrentItem());
- state.getCurrentItem().setImage(image);
+ state.getCurrentItem().setImageUrl(url);
} else {
- if (state.getFeed().getImage() == null) {
- image.setOwner(state.getFeed());
- state.getFeed().setImage(image);
+ if (state.getFeed().getImageUrl() == null) {
+ state.getFeed().setImageUrl(url);
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
index 3d752df76..e391af1b2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
@@ -5,8 +5,6 @@ import android.util.Log;
import org.xml.sax.Attributes;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
@@ -23,9 +21,6 @@ public class NSRSS20 extends Namespace {
private static final String TAG = "NSRSS20";
- private static final String NSTAG = "rss";
- private static final String NSURI = "";
-
public static final String CHANNEL = "channel";
public static final String ITEM = "item";
private static final String GUID = "guid";
@@ -77,17 +72,6 @@ public class NSRSS20 extends Namespace {
state.getCurrentItem().setMedia(media);
}
- } else if (IMAGE.equals(localName)) {
- if (state.getTagstack().size() >= 1) {
- String parent = state.getTagstack().peek().getName();
- if (CHANNEL.equals(parent)) {
- Feed feed = state.getFeed();
- if(feed != null && feed.getImage() == null) {
- feed.setImage(new FeedImage());
- feed.getImage().setOwner(state.getFeed());
- }
- }
- }
}
return new SyndElement(localName, this);
}
@@ -134,11 +118,6 @@ public class NSRSS20 extends Namespace {
state.getCurrentItem().setTitle(title);
} else if (CHANNEL.equals(second) && state.getFeed() != null) {
state.getFeed().setTitle(title);
- } else if (IMAGE.equals(second) && CHANNEL.equals(third)) {
- if(state.getFeed() != null && state.getFeed().getImage() != null &&
- state.getFeed().getImage().getTitle() == null) {
- state.getFeed().getImage().setTitle(title);
- }
}
} else if (LINK.equals(top)) {
if (CHANNEL.equals(second) && state.getFeed() != null) {
@@ -150,9 +129,8 @@ public class NSRSS20 extends Namespace {
state.getCurrentItem().setPubDate(DateUtils.parse(content));
} else if (URL.equals(top) && IMAGE.equals(second) && CHANNEL.equals(third)) {
// prefer itunes:image
- if(state.getFeed() != null && state.getFeed().getImage() != null &&
- state.getFeed().getImage().getDownload_url() == null) {
- state.getFeed().getImage().setDownload_url(content);
+ if (state.getFeed() != null) {
+ state.getFeed().setImageUrl(content);
}
} else if (DESCR.equals(localName)) {
if (CHANNEL.equals(second) && state.getFeed() != null) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
index 703817a35..45266569a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
@@ -17,11 +17,11 @@ public class NSSimpleChapters extends Namespace {
public static final String NSTAG = "psc|sc";
public static final String NSURI = "http://podlove.org/simple-chapters";
- public static final String CHAPTERS = "chapters";
- public static final String CHAPTER = "chapter";
- public static final String START = "start";
- public static final String TITLE = "title";
- public static final String HREF = "href";
+ private static final String CHAPTERS = "chapters";
+ private static final String CHAPTER = "chapter";
+ private static final String START = "start";
+ private static final String TITLE = "title";
+ private static final String HREF = "href";
@Override
public SyndElement handleElementStart(String localName, HandlerState state,
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java
index cf118d202..e5fbdb9bb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/Namespace.java
@@ -1,8 +1,9 @@
package de.danoeh.antennapod.core.syndication.namespace;
-import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import org.xml.sax.Attributes;
+import de.danoeh.antennapod.core.syndication.handler.HandlerState;
+
public abstract class Namespace {
public static final String NSTAG = null;
@@ -14,7 +15,6 @@ public abstract class Namespace {
public abstract SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes);
/** Called by a Feedhandler when in endElement and it detects a namespace element
- * @return true if namespace handled the element, false if it ignored it
* */
public abstract void handleElementEnd(String localName, HandlerState state);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java
index 8adcd2086..ba1b8ba5c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/SyndElement.java
@@ -2,8 +2,8 @@ package de.danoeh.antennapod.core.syndication.namespace;
/** Defines a XML Element that is pushed on the tagstack */
public class SyndElement {
- protected String name;
- protected Namespace namespace;
+ private final String name;
+ private final Namespace namespace;
public SyndElement(String name, Namespace namespace) {
this.name = name;
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 43fe0edb7..b512dce3f 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,16 +1,17 @@
package de.danoeh.antennapod.core.syndication.namespace.atom;
+import org.apache.commons.text.StringEscapeUtils;
+
import de.danoeh.antennapod.core.syndication.namespace.Namespace;
import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
-import org.apache.commons.lang3.StringEscapeUtils;
/** Represents Atom Element which contains text (content, title, summary). */
public class AtomText extends SyndElement {
public static final String TYPE_TEXT = "text";
- public static final String TYPE_HTML = "html";
- public static final String TYPE_XHTML = "xhtml";
+ private static final String TYPE_HTML = "html";
+ private static final String TYPE_XHTML = "xhtml";
- private String type;
+ private final String type;
private String content;
public AtomText(String name, Namespace namespace, String 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 cfb20d578..83957456a 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
@@ -5,7 +5,6 @@ import android.util.Log;
import org.xml.sax.Attributes;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
@@ -48,8 +47,6 @@ public class NSAtom extends Namespace {
private static final String LINK_REL_ARCHIVES = "archives";
private static final String LINK_REL_ENCLOSURE = "enclosure";
private static final String LINK_REL_PAYMENT = "payment";
- private static final String LINK_REL_RELATED = "related";
- private static final String LINK_REL_SELF = "self";
private static final String LINK_REL_NEXT = "next";
// type-values
private static final String LINK_TYPE_ATOM = "application/atom+xml";
@@ -64,8 +61,8 @@ public class NSAtom extends Namespace {
private static final String isText = TITLE + "|" + CONTENT + "|"
+ SUBTITLE + "|" + SUMMARY;
- public static final String isFeed = FEED + "|" + NSRSS20.CHANNEL;
- public static final String isFeedItem = ENTRY + "|" + NSRSS20.ITEM;
+ private static final String isFeed = FEED + "|" + NSRSS20.CHANNEL;
+ private static final String isFeedItem = ENTRY + "|" + NSRSS20.ITEM;
@Override
public SyndElement handleElementStart(String localName, HandlerState state,
@@ -210,10 +207,10 @@ public class NSAtom extends Namespace {
state.getCurrentItem().setPubDate(DateUtils.parse(content));
} else if (PUBLISHED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null) {
state.getCurrentItem().setPubDate(DateUtils.parse(content));
- } else if (IMAGE_LOGO.equals(top) && state.getFeed() != null && state.getFeed().getImage() == null) {
- state.getFeed().setImage(new FeedImage(state.getFeed(), content, null));
+ } else if (IMAGE_LOGO.equals(top) && state.getFeed() != null && state.getFeed().getImageUrl() == null) {
+ state.getFeed().setImageUrl(content);
} else if (IMAGE_ICON.equals(top) && state.getFeed() != null) {
- state.getFeed().setImage(new FeedImage(state.getFeed(), content, null));
+ state.getFeed().setImageUrl(content);
} else if (AUTHOR_NAME.equals(top) && AUTHOR.equals(second) &&
state.getFeed() != null && state.getCurrentItem() == null) {
String currentName = state.getFeed().getAuthor();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
index 5169f7e76..e6d4ed6b7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.util;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
import android.util.Log;
import org.apache.commons.io.IOUtils;
@@ -14,7 +16,6 @@ import java.net.URL;
import java.util.Collections;
import java.util.List;
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.util.comparator.ChapterStartTimeComparator;
import de.danoeh.antennapod.core.util.id3reader.ChapterReader;
@@ -27,55 +28,74 @@ import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderExc
* Utility class for getting chapter data from media files.
*/
public class ChapterUtils {
+
private static final String TAG = "ChapterUtils";
private ChapterUtils() {
}
+ @Nullable
+ public static Chapter getCurrentChapter(Playable media) {
+ if (media.getChapters() == null) {
+ return null;
+ }
+ List<Chapter> chapters = media.getChapters();
+ if (chapters == null) {
+ return null;
+ }
+ Chapter current = chapters.get(0);
+ for (Chapter sc : chapters) {
+ if (sc.getStart() > media.getPosition()) {
+ break;
+ } else {
+ current = sc;
+ }
+ }
+ return current;
+ }
+
+ public static void loadChaptersFromStreamUrl(Playable media) {
+ ChapterUtils.readID3ChaptersFromPlayableStreamUrl(media);
+ if (media.getChapters() == null) {
+ ChapterUtils.readOggChaptersFromPlayableStreamUrl(media);
+ }
+ }
+
+ public static void loadChaptersFromFileUrl(Playable media) {
+ if (!media.localFileAvailable()) {
+ Log.e(TAG, "Could not load chapters from file url: local file not available");
+ return;
+ }
+ ChapterUtils.readID3ChaptersFromPlayableFileUrl(media);
+ if (media.getChapters() == null) {
+ ChapterUtils.readOggChaptersFromPlayableFileUrl(media);
+ }
+ }
+
/**
* Uses the download URL of a media object of a feeditem to read its ID3
* chapters.
*/
- public static void readID3ChaptersFromPlayableStreamUrl(Playable p) {
- if (p != null && p.getStreamUrl() != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
- InputStream in = null;
- try {
- URL url = new URL(p.getStreamUrl());
- ChapterReader reader = new ChapterReader();
-
- in = url.openStream();
- reader.readInputStream(in);
- List<Chapter> chapters = reader.getChapters();
+ private static void readID3ChaptersFromPlayableStreamUrl(Playable p) {
+ if (p == null || p.getStreamUrl() == null) {
+ Log.e(TAG, "Unable to read ID3 chapters: media or download URL was null");
+ return;
+ }
+ Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
+ InputStream in = null;
+ try {
+ URL url = new URL(p.getStreamUrl());
- if (chapters != null) {
- Collections
- .sort(chapters, new ChapterStartTimeComparator());
- processChapters(chapters, p);
- if (chaptersValid(chapters)) {
- p.setChapters(chapters);
- Log.i(TAG, "Chapters loaded");
- } else {
- Log.e(TAG, "Chapter data was invalid");
- }
- } else {
- Log.i(TAG, "ChapterReader could not find any ID3 chapters");
- }
- } catch (IOException | ID3ReaderException e) {
- e.printStackTrace();
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
+ in = url.openStream();
+ List<Chapter> chapters = readChaptersFrom(in);
+ if(!chapters.isEmpty()) {
+ p.setChapters(chapters);
}
- } else {
- Log.e(TAG,
- "Unable to read ID3 chapters: media or download URL was null");
+ Log.i(TAG, "Chapters loaded");
+ } catch (IOException | ID3ReaderException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ } finally {
+ IOUtils.closeQuietly(in);
}
}
@@ -83,107 +103,104 @@ public class ChapterUtils {
* Uses the file URL of a media object of a feeditem to read its ID3
* chapters.
*/
- public static void readID3ChaptersFromPlayableFileUrl(Playable p) {
- if (p != null && p.localFileAvailable() && p.getLocalMediaUrl() != null) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
- File source = new File(p.getLocalMediaUrl());
- if (source.exists()) {
- ChapterReader reader = new ChapterReader();
- InputStream in = null;
+ private static void readID3ChaptersFromPlayableFileUrl(Playable p) {
+ if (p == null || !p.localFileAvailable() || p.getLocalMediaUrl() == null) {
+ return;
+ }
+ Log.d(TAG, "Reading id3 chapters from item " + p.getEpisodeTitle());
+ File source = new File(p.getLocalMediaUrl());
+ if (!source.exists()) {
+ Log.e(TAG, "Unable to read id3 chapters: Source doesn't exist");
+ return;
+ }
- try {
- in = new BufferedInputStream(new FileInputStream(source));
- reader.readInputStream(in);
- List<Chapter> chapters = reader.getChapters();
+ InputStream in = null;
+ try {
+ in = new BufferedInputStream(new FileInputStream(source));
+ List<Chapter> chapters = readChaptersFrom(in);
+ if (!chapters.isEmpty()) {
+ p.setChapters(chapters);
+ }
+ Log.i(TAG, "Chapters loaded");
+ } catch (IOException | ID3ReaderException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
- if (chapters != null) {
- Collections.sort(chapters,
- new ChapterStartTimeComparator());
- processChapters(chapters, p);
- if (chaptersValid(chapters)) {
- p.setChapters(chapters);
- Log.i(TAG, "Chapters loaded");
- } else {
- Log.e(TAG, "Chapter data was invalid");
- }
- } else {
- Log.i(TAG,
- "ChapterReader could not find any ID3 chapters");
- }
- } catch (IOException | ID3ReaderException e) {
- e.printStackTrace();
- } finally {
- if (in != null) {
- try {
- in.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- } else {
- Log.e(TAG, "Unable to read id3 chapters: Source doesn't exist");
+ @NonNull
+ private static List<Chapter> readChaptersFrom(InputStream in) throws IOException, ID3ReaderException {
+ ChapterReader reader = new ChapterReader();
+ reader.readInputStream(in);
+ List<Chapter> chapters = reader.getChapters();
+
+ if (chapters == null) {
+ Log.i(TAG, "ChapterReader could not find any ID3 chapters");
+ return Collections.emptyList();
+ }
+ Collections.sort(chapters, new ChapterStartTimeComparator());
+ enumerateEmptyChapterTitles(chapters);
+ if (!chaptersValid(chapters)) {
+ Log.e(TAG, "Chapter data was invalid");
+ return Collections.emptyList();
+ }
+ return chapters;
+ }
+
+ private static void readOggChaptersFromPlayableStreamUrl(Playable media) {
+ if (media == null || !media.streamAvailable()) {
+ return;
+ }
+ InputStream input = null;
+ try {
+ URL url = new URL(media.getStreamUrl());
+ input = url.openStream();
+ if (input != null) {
+ readOggChaptersFromInputStream(media, input);
}
+ } catch (IOException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ } finally {
+ IOUtils.closeQuietly(input);
}
}
- public static void readOggChaptersFromPlayableStreamUrl(Playable media) {
- if (media != null && media.streamAvailable()) {
+ private static void readOggChaptersFromPlayableFileUrl(Playable media) {
+ if (media == null || media.getLocalMediaUrl() == null) {
+ return;
+ }
+ File source = new File(media.getLocalMediaUrl());
+ if (source.exists()) {
InputStream input = null;
try {
- URL url = new URL(media.getStreamUrl());
- input = url.openStream();
- if (input != null) {
- readOggChaptersFromInputStream(media, input);
- }
- } catch (IOException e) {
- e.printStackTrace();
+ input = new BufferedInputStream(new FileInputStream(source));
+ readOggChaptersFromInputStream(media, input);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
} finally {
IOUtils.closeQuietly(input);
}
}
}
- public static void readOggChaptersFromPlayableFileUrl(Playable media) {
- if (media != null && media.getLocalMediaUrl() != null) {
- File source = new File(media.getLocalMediaUrl());
- if (source.exists()) {
- InputStream input = null;
- try {
- input = new BufferedInputStream(new FileInputStream(source));
- readOggChaptersFromInputStream(media, input);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- } finally {
- IOUtils.closeQuietly(input);
- }
- }
- }
- }
-
- private static void readOggChaptersFromInputStream(Playable p,
- InputStream input) {
- if (BuildConfig.DEBUG)
- Log.d(TAG,
- "Trying to read chapters from item with title "
- + p.getEpisodeTitle());
+ private static void readOggChaptersFromInputStream(Playable p, InputStream input) {
+ Log.d(TAG, "Trying to read chapters from item with title " + p.getEpisodeTitle());
try {
VorbisCommentChapterReader reader = new VorbisCommentChapterReader();
reader.readInputStream(input);
List<Chapter> chapters = reader.getChapters();
- if (chapters != null) {
- Collections.sort(chapters, new ChapterStartTimeComparator());
- processChapters(chapters, p);
- if (chaptersValid(chapters)) {
- p.setChapters(chapters);
- Log.i(TAG, "Chapters loaded");
- } else {
- Log.e(TAG, "Chapter data was invalid");
- }
+ if (chapters == null) {
+ Log.i(TAG, "ChapterReader could not find any Ogg vorbis chapters");
+ return;
+ }
+ Collections.sort(chapters, new ChapterStartTimeComparator());
+ enumerateEmptyChapterTitles(chapters);
+ if (chaptersValid(chapters)) {
+ p.setChapters(chapters);
+ Log.i(TAG, "Chapters loaded");
} else {
- Log.i(TAG,
- "ChapterReader could not find any Ogg vorbis chapters");
+ Log.e(TAG, "Chapter data was invalid");
}
} catch (VorbisCommentReaderException e) {
e.printStackTrace();
@@ -193,7 +210,7 @@ public class ChapterUtils {
/**
* Makes sure that chapter does a title and an item attribute.
*/
- private static void processChapters(List<Chapter> chapters, Playable p) {
+ private static void enumerateEmptyChapterTitles(List<Chapter> chapters) {
for (int i = 0; i < chapters.size(); i++) {
Chapter c = chapters.get(i);
if (c.getTitle() == null) {
@@ -207,9 +224,6 @@ public class ChapterUtils {
return false;
}
for (Chapter c : chapters) {
- if (c.getTitle() == null) {
- return false;
- }
if (c.getStart() < 0) {
return false;
}
@@ -217,49 +231,4 @@ public class ChapterUtils {
return true;
}
- /**
- * Calls getCurrentChapter with current position.
- */
- public static Chapter getCurrentChapter(Playable media) {
- if (media.getChapters() != null) {
- List<Chapter> chapters = media.getChapters();
- Chapter current = null;
- if (chapters != null) {
- current = chapters.get(0);
- for (Chapter sc : chapters) {
- if (sc.getStart() > media.getPosition()) {
- break;
- } else {
- current = sc;
- }
- }
- }
- return current;
- } else {
- return null;
- }
- }
-
- public static void loadChaptersFromStreamUrl(Playable media) {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Starting chapterLoader thread");
- ChapterUtils.readID3ChaptersFromPlayableStreamUrl(media);
- if (media.getChapters() == null) {
- ChapterUtils.readOggChaptersFromPlayableStreamUrl(media);
- }
-
- if (BuildConfig.DEBUG)
- Log.d(TAG, "ChapterLoaderThread has finished");
- }
-
- public static void loadChaptersFromFileUrl(Playable media) {
- if (media.localFileAvailable()) {
- ChapterUtils.readID3ChaptersFromPlayableFileUrl(media);
- if (media.getChapters() == null) {
- ChapterUtils.readOggChaptersFromPlayableFileUrl(media);
- }
- } else {
- Log.e(TAG, "Could not load chapters from file url: local file not available");
- }
- }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Consumer.java b/core/src/main/java/de/danoeh/antennapod/core/util/Consumer.java
new file mode 100644
index 000000000..13a87af0d
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Consumer.java
@@ -0,0 +1,5 @@
+package de.danoeh.antennapod.core.util;
+
+public interface Consumer<T> {
+ void accept(T t);
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
index 70a180913..3cb226add 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java
@@ -28,7 +28,6 @@ public final class Converter {
/** Determines the length of the number for best readability.*/
private static final int NUM_LENGTH = 1024;
- private static final int DAYS_MIL = 86400000;
private static final int HOURS_MIL = 3600000;
private static final int MINUTES_MIL = 60000;
private static final int SECONDS_MIL = 1000;
@@ -71,8 +70,8 @@ public final class Converter {
int m = rest / MINUTES_MIL;
rest -= m * MINUTES_MIL;
int s = rest / SECONDS_MIL;
-
- return String.format("%02d:%02d:%02d", h, m, s);
+
+ return String.format(Locale.getDefault(), "%02d:%02d:%02d", h, m, s);
}
/** Converts milliseconds to a string containing hours and minutes */
@@ -81,7 +80,7 @@ public final class Converter {
int rest = duration - h * HOURS_MIL;
int m = rest / MINUTES_MIL;
- return String.format("%02d:%02d", h, m);
+ return String.format(Locale.getDefault(), "%02d:%02d", h, m);
}
/** Converts long duration string (HH:MM:SS) to milliseconds. */
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
index f63f0983f..5141e3a78 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
@@ -17,7 +17,9 @@ import java.util.TimeZone;
*/
public class DateUtils {
- private static final String TAG = "DateUtils";
+ private DateUtils(){}
+
+ private static final String TAG = "DateUtils";
private static final TimeZone defaultTimezone = TimeZone.getTimeZone("GMT");
@@ -31,6 +33,9 @@ public class DateUtils {
date = date.replaceAll("CEST$", "+02:00");
date = date.replaceAll("CET$", "+01:00");
+ // some generators use "Sept" for September
+ date = date.replaceAll("\\bSept\\b", "Sep");
+
// if datetime is more precise than seconds, make sure the value is in ms
if (date.contains(".")) {
int start = date.indexOf('.');
@@ -82,7 +87,8 @@ public class DateUtils {
"yyyy-MM-dd'T'HH:mm:ss'Z'",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ",
"yyyy-MM-ddZ",
- "yyyy-MM-dd"
+ "yyyy-MM-dd",
+ "EEE d MMM yyyy HH:mm:ss 'GMT'Z (z)"
};
SimpleDateFormat parser = new SimpleDateFormat("", Locale.US);
@@ -131,7 +137,7 @@ public class DateUtils {
if (parts.length >= 2) {
result += Integer.parseInt(parts[idx]) * 60000L;
idx++;
- result += (Float.parseFloat(parts[idx])) * 1000L;
+ result += (long) (Float.parseFloat(parts[idx]) * 1000L);
}
return result;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java b/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java
index 7779158e5..0c9989b43 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DownloadError.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.core.util;
import android.content.Context;
+
import de.danoeh.antennapod.core.R;
/** Utility class for Download Errors. */
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java b/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java
index f432424f8..69dc38895 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DuckType.java
@@ -92,7 +92,7 @@ public class DuckType {
* false otherwise.
*/
@SuppressWarnings("rawtypes")
- public boolean quacksLikeA(Class iface) {
+ private boolean quacksLikeA(Class iface) {
for (Method method : iface.getMethods()) {
if (findMethodBySignature(method) == null) {
return false;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/EpisodeFilter.java b/core/src/main/java/de/danoeh/antennapod/core/util/EpisodeFilter.java
deleted file mode 100644
index 89edd7dbe..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/EpisodeFilter.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import de.danoeh.antennapod.core.feed.FeedItem;
-
-public class EpisodeFilter {
-
- private EpisodeFilter() {
-
- }
-
- /** Return a copy of the itemlist without items which have no media. */
- public static ArrayList<FeedItem> getEpisodeList(List<FeedItem> items) {
- ArrayList<FeedItem> episodes = new ArrayList<>(items);
- for (FeedItem item : items) {
- if (item.getMedia() == null) {
- episodes.remove(item);
- }
- }
- return episodes;
- }
-
- public static int countItemsWithEpisodes(List<FeedItem> items) {
- int count = 0;
- for (FeedItem item : items) {
- if (item.getMedia() != null) {
- count++;
- }
- }
- return count;
- }
-
- public static FeedItem accessEpisodeByIndex(List<FeedItem> items,
- int position) {
- int count = 0;
- for (FeedItem item : items) {
-
- if (item.getMedia() != null) {
- if (count == position) {
- return item;
- } else {
- count++;
- }
- }
- }
- return null;
- }
-}
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 892e5ff38..826c06822 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
@@ -5,6 +5,7 @@ import java.util.List;
import de.danoeh.antennapod.core.feed.FeedItem;
public class FeedItemUtil {
+ private FeedItemUtil(){}
public static int indexOfItemWithDownloadUrl(List<FeedItem> items, String downloadUrl) {
if(items == null) {
@@ -75,4 +76,18 @@ public class FeedItemUtil {
return false;
}
+ /**
+ * 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.
+ */
+ public static String getLinkWithFallback(FeedItem item) {
+ if (item == null) {
+ return null;
+ } else if (item.getLink() != null) {
+ return item.getLink();
+ } else if (item.getFeed() != null) {
+ return item.getFeed().getLink();
+ }
+ return null;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java
new file mode 100644
index 000000000..afaf13390
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedUpdateUtils.java
@@ -0,0 +1,31 @@
+package de.danoeh.antennapod.core.util;
+
+import android.content.Context;
+import android.util.Log;
+
+import org.awaitility.core.ConditionTimeoutException;
+
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.core.storage.DBTasks;
+
+import static org.awaitility.Awaitility.with;
+
+public class FeedUpdateUtils {
+ private static final String TAG = "FeedUpdateUtils";
+
+ private FeedUpdateUtils() {}
+
+ public static void startAutoUpdate(Context context, Runnable callback) {
+ try {
+ with().pollInterval(1, TimeUnit.SECONDS)
+ .await()
+ .atMost(10, TimeUnit.SECONDS)
+ .until(() -> NetworkUtils.networkAvailable() && NetworkUtils.isDownloadAllowed());
+ DBTasks.refreshAllFeeds(context, null, callback);
+ } catch (ConditionTimeoutException ignore) {
+ Log.d(TAG, "Blocking automatic update: no wifi available / no mobile updates allowed");
+ }
+ }
+
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java
index bf14cd23e..29d095cd2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedtitleComparator.java
@@ -1,11 +1,11 @@
package de.danoeh.antennapod.core.util;
-import de.danoeh.antennapod.core.feed.Feed;
-
import java.util.Comparator;
+import de.danoeh.antennapod.core.feed.Feed;
+
/** Compares the title of two feeds for sorting. */
-public class FeedtitleComparator implements Comparator<Feed> {
+class FeedtitleComparator implements Comparator<Feed> {
@Override
public int compare(Feed lhs, Feed rhs) {
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 8da176c82..e99461806 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
@@ -1,10 +1,10 @@
package de.danoeh.antennapod.core.util;
+import android.text.TextUtils;
+
import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.text.RandomStringGenerator;
-import java.util.Arrays;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
/** Generates valid filenames for a given string. */
public class FileNameGenerator {
@@ -33,7 +33,15 @@ public class FileNameGenerator {
buf.append(c);
}
}
- return buf.toString().trim();
+ String filename = buf.toString().trim();
+ if(TextUtils.isEmpty(filename)) {
+ return new RandomStringGenerator.Builder()
+ .withinRange('0', 'z')
+ .filteredBy(Character::isLetterOrDigit)
+ .build()
+ .generate(8);
+ }
+ return filename;
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Function.java b/core/src/main/java/de/danoeh/antennapod/core/util/Function.java
new file mode 100644
index 000000000..c4f4ad68a
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Function.java
@@ -0,0 +1,7 @@
+package de.danoeh.antennapod.core.util;
+
+import io.reactivex.annotations.NonNull;
+
+public interface Function<T, R> {
+ R apply(@NonNull T t);
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java
index f48b9169b..1da5417c1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntList.java
@@ -8,7 +8,7 @@ import java.util.Arrays;
public final class IntList {
private int[] values;
- protected int size;
+ private int size;
/**
* Constructs an empty instance with a default initial capacity.
@@ -22,7 +22,7 @@ public final class IntList {
*
* @param initialCapacity {@code >= 0;} initial capacity of the list
*/
- public IntList(int initialCapacity) {
+ private IntList(int initialCapacity) {
if(initialCapacity < 0) {
throw new IllegalArgumentException("initial capacity must be 0 or higher");
}
@@ -200,7 +200,7 @@ public final class IntList {
* @param value value to find
* @return index of value or -1
*/
- public int indexOf(int value) {
+ private int indexOf(int value) {
for (int i = 0; i < size; i++) {
if (values[i] == value) {
return i;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
index 1571b71c2..e81ab47ed 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/IntentUtils.java
@@ -8,6 +8,7 @@ import android.content.pm.ResolveInfo;
import java.util.List;
public class IntentUtils {
+ private IntentUtils(){}
/*
* Checks if there is at least one exported activity that can be performed for the intent
@@ -23,4 +24,8 @@ public class IntentUtils {
return false;
}
+ public static void sendLocalBroadcast(Context context, String action) {
+ context.sendBroadcast(new Intent(action).setPackage(context.getPackageName()));
+ }
+
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java
index 287ec4d0c..90e0b0981 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/LangUtils.java
@@ -5,9 +5,12 @@ import android.support.v4.util.ArrayMap;
import java.nio.charset.Charset;
public class LangUtils {
+
+ private LangUtils(){}
+
public static final Charset UTF_8 = Charset.forName("UTF-8");
- private static ArrayMap<String, String> languages;
+ private static final ArrayMap<String, String> languages;
static {
languages = new ArrayMap<>();
languages.put("af", "Afrikaans");
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java b/core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java
index c5ac44bf5..78ed002ac 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/LongIntMap.java
@@ -88,7 +88,7 @@ public class LongIntMap {
/**
* Removes the mapping at the given index.
*/
- public void removeAt(int index) {
+ private void removeAt(int index) {
System.arraycopy(keys, index + 1, keys, index, size - (index + 1));
System.arraycopy(values, index + 1, values, index, size - (index + 1));
size--;
@@ -130,7 +130,7 @@ public class LongIntMap {
* smallest key and <code>keyAt(size()-1)</code> will return the largest
* key.</p>
*/
- public long keyAt(int index) {
+ private long keyAt(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException("n >= size()");
} else if(index < 0) {
@@ -150,7 +150,7 @@ public class LongIntMap {
* smallest key and <code>valueAt(size()-1)</code> will return the value
* associated with the largest key.</p>
*/
- public int valueAt(int index) {
+ private int valueAt(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException("n >= size()");
} else if(index < 0) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
index c73d6f6e3..b34ba196d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
@@ -8,6 +8,7 @@ import android.net.wifi.WifiManager;
import android.support.v4.net.ConnectivityManagerCompat;
import android.text.TextUtils;
import android.util.Log;
+
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
@@ -17,15 +18,16 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.storage.DBWriter;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
-import rx.Observable;
-import rx.Subscriber;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
public class NetworkUtils {
+ private NetworkUtils(){}
private static final String TAG = NetworkUtils.class.getSimpleName();
@@ -53,7 +55,7 @@ public class NetworkUtils {
Log.d(TAG, "Auto-dl filter is disabled");
return true;
} else {
- WifiManager wm = (WifiManager) context
+ WifiManager wm = (WifiManager) context.getApplicationContext()
.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wm.getConnectionInfo();
List<String> selectedNetworks = Arrays
@@ -92,7 +94,7 @@ public class NetworkUtils {
return UserPreferences.isAllowMobileUpdate() || !NetworkUtils.isNetworkMetered();
}
- public static boolean isNetworkMetered() {
+ private static boolean isNetworkMetered() {
ConnectivityManager connManager = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
return ConnectivityManagerCompat.isActiveNetworkMetered(connManager);
@@ -102,7 +104,7 @@ public class NetworkUtils {
* Returns the SSID of the wifi connection, or <code>null</code> if there is no wifi.
*/
public static String getWifiSsid() {
- WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
if (wifiInfo != null) {
return wifiInfo.getSSID();
@@ -110,66 +112,59 @@ public class NetworkUtils {
return null;
}
- public static Observable<Long> getFeedMediaSizeObservable(FeedMedia media) {
- return Observable.create(new Observable.OnSubscribe<Long>() {
- @Override
- public void call(Subscriber<? super Long> subscriber) {
- if (!NetworkUtils.isDownloadAllowed()) {
- subscriber.onNext(0L);
- subscriber.onCompleted();
+ public static Single<Long> getFeedMediaSizeObservable(FeedMedia media) {
+ return Single.create((SingleOnSubscribe<Long>) emitter -> {
+ if (!NetworkUtils.isDownloadAllowed()) {
+ emitter.onSuccess(0L);
+ return;
+ }
+ long size = Integer.MIN_VALUE;
+ if (media.isDownloaded()) {
+ File mediaFile = new File(media.getLocalMediaUrl());
+ if (mediaFile.exists()) {
+ size = mediaFile.length();
+ }
+ } else if (!media.checkedOnSizeButUnknown()) {
+ // only query the network if we haven't already checked
+
+ String url = media.getDownload_url();
+ if(TextUtils.isEmpty(url)) {
+ emitter.onSuccess(0L);
return;
}
- long size = Integer.MIN_VALUE;
- if (media.isDownloaded()) {
- File mediaFile = new File(media.getLocalMediaUrl());
- if (mediaFile.exists()) {
- size = mediaFile.length();
- }
- } else if (!media.checkedOnSizeButUnknown()) {
- // only query the network if we haven't already checked
-
- String url = media.getDownload_url();
- if(TextUtils.isEmpty(url)) {
- subscriber.onNext(0L);
- subscriber.onCompleted();
- return;
- }
- OkHttpClient client = AntennapodHttpClient.getHttpClient();
- Request.Builder httpReq = new Request.Builder()
- .url(url)
- .header("Accept-Encoding", "identity")
- .head();
- try {
- Response response = client.newCall(httpReq.build()).execute();
- if (response.isSuccessful()) {
- String contentLength = response.header("Content-Length");
- try {
- size = Integer.parseInt(contentLength);
- } catch (NumberFormatException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- }
+ OkHttpClient client = AntennapodHttpClient.getHttpClient();
+ Request.Builder httpReq = new Request.Builder()
+ .url(url)
+ .header("Accept-Encoding", "identity")
+ .head();
+ try {
+ Response response = client.newCall(httpReq.build()).execute();
+ if (response.isSuccessful()) {
+ String contentLength = response.header("Content-Length");
+ try {
+ size = Integer.parseInt(contentLength);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
}
- } catch (IOException e) {
- subscriber.onNext(0L);
- subscriber.onCompleted();
- Log.e(TAG, Log.getStackTraceString(e));
- return; // better luck next time
}
+ } catch (IOException e) {
+ emitter.onSuccess(0L);
+ Log.e(TAG, Log.getStackTraceString(e));
+ return; // better luck next time
}
- Log.d(TAG, "new size: " + size);
- if (size <= 0) {
- // they didn't tell us the size, but we don't want to keep querying on it
- media.setCheckedOnSizeButUnknown();
- } else {
- media.setSize(size);
- }
- subscriber.onNext(size);
- subscriber.onCompleted();
- DBWriter.setFeedMedia(media);
}
+ Log.d(TAG, "new size: " + size);
+ if (size <= 0) {
+ // they didn't tell us the size, but we don't want to keep querying on it
+ media.setCheckedOnSizeButUnknown();
+ } else {
+ media.setSize(size);
+ }
+ emitter.onSuccess(size);
+ DBWriter.setFeedMedia(media);
})
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Permutor.java b/core/src/main/java/de/danoeh/antennapod/core/util/Permutor.java
new file mode 100644
index 000000000..7d6e20ab1
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Permutor.java
@@ -0,0 +1,17 @@
+package de.danoeh.antennapod.core.util;
+
+import java.util.List;
+
+/**
+ * Interface for passing around list permutor method. This is used for cases where a simple comparator
+ * won't work (e.g. Random, Smart Shuffle, etc).
+ *
+ * @param <E> the type of elements in the list
+ */
+public interface Permutor<E> {
+ /**
+ * Reorders the specified list.
+ * @param queue A (modifiable) list of elements to be reordered
+ */
+ void reorder(List<E> queue);
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java b/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java
index 7377b202d..9408be348 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/QueueAccess.java
@@ -58,19 +58,4 @@ public abstract class QueueAccess {
};
}
- public static QueueAccess NotInQueueAccess() {
- return new QueueAccess() {
- @Override
- public boolean contains(long id) {
- return false;
- }
-
- @Override
- public boolean remove(long id) {
- return false;
- }
- };
-
- }
-
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java
index c3b4c0e15..5c827dfe9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/QueueSorter.java
@@ -2,7 +2,12 @@ package de.danoeh.antennapod.core.util;
import android.content.Context;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Comparator;
+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;
@@ -20,11 +25,15 @@ public class QueueSorter {
DURATION_ASC,
DURATION_DESC,
FEED_TITLE_ASC,
- FEED_TITLE_DESC
+ FEED_TITLE_DESC,
+ RANDOM,
+ SMART_SHUFFLE_ASC,
+ SMART_SHUFFLE_DESC
}
public static void sort(final Context context, final Rule rule, final boolean broadcastUpdate) {
Comparator<FeedItem> comparator = null;
+ Permutor<FeedItem> permutor = null;
switch (rule) {
case EPISODE_TITLE_ASC:
@@ -68,11 +77,109 @@ public class QueueSorter {
case FEED_TITLE_DESC:
comparator = (f1, f2) -> f2.getFeed().getTitle().compareTo(f1.getFeed().getTitle());
break;
+ case RANDOM:
+ permutor = Collections::shuffle;
+ break;
+ case SMART_SHUFFLE_ASC:
+ permutor = (queue) -> smartShuffle(queue, true);
+ break;
+ case SMART_SHUFFLE_DESC:
+ permutor = (queue) -> smartShuffle(queue, false);
+ break;
default:
}
if (comparator != null) {
DBWriter.sortQueue(comparator, broadcastUpdate);
+ } else if (permutor != null) {
+ DBWriter.reorderQueue(permutor, broadcastUpdate);
+ }
+ }
+
+ /**
+ * Implements a reordering by pubdate that avoids consecutive episodes from the same feed in
+ * the queue.
+ *
+ * A listener might want to hear episodes from any given feed in pubdate order, but would
+ * prefer a more balanced ordering that avoids having to listen to clusters of consecutive
+ * episodes from the same feed. This is what "Smart Shuffle" tries to accomplish.
+ *
+ * The Smart Shuffle algorithm involves choosing episodes (in round-robin fashion) from a
+ * collection of individual, pubdate-sorted lists that each contain only items from a specific
+ * feed.
+ *
+ * Of course, clusters of consecutive episodes <i>at the end of the queue</i> may be
+ * unavoidable. This seems unlikely to be an issue for most users who presumably maintain
+ * large queues with new episodes continuously being added.
+ *
+ * For example, given a queue containing three episodes each from three different feeds
+ * (A, B, and C), a simple pubdate sort might result in a queue that looks like the following:
+ *
+ * B1, B2, B3, A1, A2, C1, C2, C3, A3
+ *
+ * (note that feed B episodes were all published before the first feed A episode, so a simple
+ * pubdate sort will often result in significant clustering of episodes from a single feed)
+ *
+ * Using Smart Shuffle, the resulting queue would look like the following:
+ *
+ * A1, B1, C1, A2, B2, C2, A3, B3, C3
+ *
+ * (note that episodes above <i>aren't strictly ordered in terms of pubdate</i>, but episodes
+ * of each feed <b>do</b> appear in pubdate order)
+ *
+ * @param queue A (modifiable) list of FeedItem elements to be reordered.
+ * @param ascending {@code true} to use ascending pubdate in the reordering;
+ * {@code false} for descending.
+ */
+ private static void smartShuffle(List<FeedItem> queue, boolean ascending) {
+
+ // Divide FeedItems into lists by feed
+
+ Map<Long, List<FeedItem>> map = new HashMap<>();
+
+ while (!queue.isEmpty()) {
+ FeedItem item = queue.remove(0);
+ Long id = item.getFeedId();
+ if (!map.containsKey(id)) {
+ map.put(id, new ArrayList<>());
+ }
+ map.get(id).add(item);
+ }
+
+ // Sort each individual list by PubDate (ascending/descending)
+
+ Comparator<FeedItem> itemComparator = ascending
+ ? (f1, f2) -> f1.getPubDate().compareTo(f2.getPubDate())
+ : (f1, f2) -> f2.getPubDate().compareTo(f1.getPubDate());
+
+ for (Long id : map.keySet()) {
+ Collections.sort(map.get(id), itemComparator);
+ }
+
+ // Create a list of the individual FeedItems lists, and sort it by feed title (ascending).
+ // Doing this ensures that the feed order we use is predictable/deterministic.
+
+ List<List<FeedItem>> feeds = new ArrayList<>(map.values());
+ Collections.sort(feeds,
+ // (we use a desc sort here, since we're iterating back-to-front below)
+ (f1, f2) -> f2.get(0).getFeed().getTitle().compareTo(f1.get(0).getFeed().getTitle()));
+
+ // Cycle through the (sorted) feed lists in a round-robin fashion, removing the first item
+ // and adding it back into to the original queue
+
+ while (!feeds.isEmpty()) {
+ // Iterate across the (sorted) list of feeds, removing the first item in each, and
+ // appending it to the queue. Note that we're iterating back-to-front here, since we
+ // will be deleting feed lists as they become empty.
+ for (int i = feeds.size() - 1; i >= 0; --i) {
+ List<FeedItem> items = feeds.get(i);
+ queue.add(items.remove(0));
+ // Removed the last item in this particular feed? Then remove this feed from the
+ // list of feeds.
+ if (items.isEmpty()) {
+ feeds.remove(i);
+ }
+ }
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java
index ee306a401..d1818aeef 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/RewindAfterPauseUtils.java
@@ -9,6 +9,7 @@ import java.util.concurrent.TimeUnit;
* Media file should be "rewinded" x seconds after user resumes the playback.
*/
public class RewindAfterPauseUtils {
+ private RewindAfterPauseUtils(){}
public static final long ELAPSED_TIME_FOR_SHORT_REWIND = TimeUnit.MINUTES.toMillis(1);
public static final long ELAPSED_TIME_FOR_MEDIUM_REWIND = TimeUnit.HOURS.toMillis(1);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
index 001bd6a2c..5ae00460e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.core.util;
import android.content.Context;
import android.content.Intent;
-
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
@@ -10,14 +9,14 @@ import android.os.Build;
import android.support.v4.content.FileProvider;
import android.util.Log;
+import java.io.File;
+import java.util.List;
+
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import java.io.File;
-import java.util.List;
-
/** Utility methods for sharing data */
public class ShareUtils {
private static final String TAG = "ShareUtils";
@@ -51,11 +50,15 @@ public class ShareUtils {
return item.getFeed().getTitle() + ": " + item.getTitle();
}
+ public static boolean hasLinkToShare(FeedItem item) {
+ return FeedItemUtil.getLinkWithFallback(item) != null;
+ }
+
public static void shareFeedItemLink(Context context, FeedItem item, boolean withPosition) {
- String text = getItemShareText(item) + " " + item.getLink();
+ String text = getItemShareText(item) + " " + FeedItemUtil.getLinkWithFallback(item);
if(withPosition) {
int pos = item.getMedia().getPosition();
- text = item.getLink() + " [" + Converter.getDurationStringLong(pos) + "]";
+ text += " [" + Converter.getDurationStringLong(pos) + "]";
}
shareLink(context, text);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/StorageUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/StorageUtils.java
index 1ef81bf64..3a6bf5755 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/StorageUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/StorageUtils.java
@@ -14,6 +14,8 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
* Utility functions for handling storage errors
*/
public class StorageUtils {
+ private StorageUtils(){}
+
private static final String TAG = "StorageUtils";
public static boolean storageAvailable() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java
index f67367643..14f091249 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ThemeUtils.java
@@ -1,17 +1,25 @@
package de.danoeh.antennapod.core.util;
+import android.content.Context;
+import android.support.annotation.AttrRes;
+import android.support.annotation.ColorInt;
import android.util.Log;
+import android.util.TypedValue;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
public class ThemeUtils {
+ private ThemeUtils(){}
+
private static final String TAG = "ThemeUtils";
public static int getSelectionBackgroundColor() {
int theme = UserPreferences.getTheme();
if (theme == R.style.Theme_AntennaPod_Dark) {
return R.color.selection_background_color_dark;
+ } else if (theme == R.style.Theme_AntennaPod_TrueBlack){
+ return R.color.selection_background_color_trueblack;
} else if (theme == R.style.Theme_AntennaPod_Light) {
return R.color.selection_background_color_light;
} else {
@@ -20,4 +28,10 @@ public class ThemeUtils {
return R.color.selection_background_color_light;
}
}
+
+ public static @ColorInt int getColorFromAttr(Context context, @AttrRes int attr) {
+ TypedValue typedValue = new TypedValue();
+ context.getTheme().resolveAttribute(attr, typedValue, true);
+ return typedValue.data;
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java
index 5274ffc9e..8bd23c2ed 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.util.comparator;
-import de.danoeh.antennapod.core.feed.Chapter;
-
import java.util.Comparator;
+import de.danoeh.antennapod.core.feed.Chapter;
+
public class ChapterStartTimeComparator implements Comparator<Chapter> {
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java
index ebdbfe2a5..868f3b835 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/DownloadStatusComparator.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.util.comparator;
-import de.danoeh.antennapod.core.service.download.DownloadStatus;
-
import java.util.Comparator;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+
/** Compares the completion date of two Downloadstatus objects. */
public class DownloadStatusComparator implements Comparator<DownloadStatus> {
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 a1f3ec699..a96eda3c5 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
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.util.comparator;
-import de.danoeh.antennapod.core.feed.FeedItem;
-
import java.util.Comparator;
+import de.danoeh.antennapod.core.feed.FeedItem;
+
/** Compares the pubDate of two FeedItems for sorting*/
public class FeedItemPubdateComparator implements Comparator<FeedItem> {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java
index 84d244660..d65eb3e0b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.util.comparator;
-import de.danoeh.antennapod.core.feed.FeedItem;
-
import java.util.Comparator;
+import de.danoeh.antennapod.core.feed.FeedItem;
+
public class PlaybackCompletionDateComparator implements Comparator<FeedItem> {
public int compare(FeedItem lhs, FeedItem rhs) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java
index d23901a45..56a684475 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/SearchResultValueComparator.java
@@ -1,10 +1,10 @@
package de.danoeh.antennapod.core.util.comparator;
+import java.util.Comparator;
+
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.SearchResult;
-import java.util.Comparator;
-
public class SearchResultValueComparator implements Comparator<SearchResult> {
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
new file mode 100644
index 000000000..1629f4aaf
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/download/AutoUpdateManager.java
@@ -0,0 +1,185 @@
+package de.danoeh.antennapod.core.util.download;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.app.job.JobInfo;
+import android.app.job.JobScheduler;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.SystemClock;
+import android.support.annotation.RequiresApi;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.receiver.FeedUpdateReceiver;
+import de.danoeh.antennapod.core.service.FeedUpdateJobService;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.FeedUpdateUtils;
+
+public class AutoUpdateManager {
+ private static final int JOB_ID_FEED_UPDATE = 42;
+ private static final String TAG = "AutoUpdateManager";
+
+ private AutoUpdateManager() {
+
+ }
+
+ /**
+ * Sets the interval in which the feeds are refreshed automatically
+ */
+ public static void restartUpdateIntervalAlarm(Context context, long triggerAtMillis, long intervalMillis) {
+ Log.d(TAG, "Restarting update alarm.");
+
+ if (Build.VERSION.SDK_INT >= 24) {
+ restartJobServiceInterval(context, intervalMillis);
+ } else {
+ restartAlarmManagerInterval(context, triggerAtMillis, intervalMillis);
+ }
+ }
+
+ /**
+ * Sets time of day the feeds are refreshed automatically
+ */
+ public static void restartUpdateTimeOfDayAlarm(Context context, int hoursOfDay, int minute) {
+ Log.d(TAG, "Restarting update alarm.");
+
+ Calendar now = Calendar.getInstance();
+ Calendar alarm = (Calendar)now.clone();
+ alarm.set(Calendar.HOUR_OF_DAY, hoursOfDay);
+ alarm.set(Calendar.MINUTE, minute);
+ if (alarm.before(now) || alarm.equals(now)) {
+ alarm.add(Calendar.DATE, 1);
+ }
+
+ if (Build.VERSION.SDK_INT >= 24) {
+ long triggerAtMillis = alarm.getTimeInMillis() - now.getTimeInMillis();
+ restartJobServiceTriggerAt(context, triggerAtMillis);
+ } else {
+ restartAlarmManagerTimeOfDay(context, alarm);
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
+ private static JobInfo.Builder getFeedUpdateJobBuilder(Context context) {
+ ComponentName serviceComponent = new ComponentName(context, FeedUpdateJobService.class);
+ JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_FEED_UPDATE, serviceComponent);
+ builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
+ builder.setPersisted(true);
+ return builder;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private static void restartJobServiceInterval(Context context, long intervalMillis) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ if (jobScheduler == null) {
+ Log.d(TAG, "JobScheduler was null.");
+ return;
+ }
+
+ JobInfo oldJob = jobScheduler.getPendingJob(JOB_ID_FEED_UPDATE);
+ if (oldJob != null && oldJob.getIntervalMillis() == intervalMillis) {
+ Log.d(TAG, "JobScheduler was already set at interval " + intervalMillis + ", ignoring.");
+ return;
+ }
+
+ JobInfo.Builder builder = getFeedUpdateJobBuilder(context);
+ builder.setPeriodic(intervalMillis);
+ jobScheduler.cancel(JOB_ID_FEED_UPDATE);
+
+ if (intervalMillis <= 0) {
+ Log.d(TAG, "Automatic update was deactivated");
+ return;
+ }
+
+ jobScheduler.schedule(builder.build());
+ Log.d(TAG, "JobScheduler was set at interval " + intervalMillis);
+ }
+
+ private static void restartAlarmManagerInterval(Context context, long triggerAtMillis, long intervalMillis) {
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+ if (alarmManager == null) {
+ Log.d(TAG, "AlarmManager was null");
+ return;
+ }
+
+ PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0,
+ new Intent(context, FeedUpdateReceiver.class), 0);
+ alarmManager.cancel(updateIntent);
+
+ if (intervalMillis <= 0) {
+ Log.d(TAG, "Automatic update was deactivated");
+ return;
+ }
+
+ alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + triggerAtMillis,
+ updateIntent);
+ Log.d(TAG, "Changed alarm to new interval " + TimeUnit.MILLISECONDS.toHours(intervalMillis) + " h");
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.N)
+ private static void restartJobServiceTriggerAt(Context context, long triggerAtMillis) {
+ JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
+ if (jobScheduler == null) {
+ Log.d(TAG, "JobScheduler was null.");
+ return;
+ }
+
+ JobInfo.Builder builder = getFeedUpdateJobBuilder(context);
+ builder.setMinimumLatency(triggerAtMillis);
+ jobScheduler.cancel(JOB_ID_FEED_UPDATE);
+ jobScheduler.schedule(builder.build());
+ Log.d(TAG, "JobScheduler was set for " + triggerAtMillis);
+ }
+
+ private static void restartAlarmManagerTimeOfDay(Context context, Calendar alarm) {
+ AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
+
+ if (alarmManager == null) {
+ Log.d(TAG, "AlarmManager was null");
+ return;
+ }
+
+ PendingIntent updateIntent = PendingIntent.getBroadcast(context, 0,
+ new Intent(context, FeedUpdateReceiver.class), 0);
+ alarmManager.cancel(updateIntent);
+
+ Log.d(TAG, "Alarm set for: " + alarm.toString() + " : " + alarm.getTimeInMillis());
+ alarmManager.set(AlarmManager.RTC_WAKEUP,
+ alarm.getTimeInMillis(),
+ updateIntent);
+ Log.d(TAG, "Changed alarm to new time of day " + alarm.get(Calendar.HOUR_OF_DAY) + ":" + alarm.get(Calendar.MINUTE));
+ }
+
+ /*
+ * Checks if the app should refresh all feeds, i.e. if the last auto refresh failed.
+ *
+ * The feeds are only refreshed if an update interval or time of day is set and the last
+ * (successful) refresh was before the last interval or more than a day ago, respectively.
+ *
+ */
+ public static void checkShouldRefreshFeeds(Context context) {
+ long interval = 0;
+ if(UserPreferences.getUpdateInterval() > 0) {
+ interval = UserPreferences.getUpdateInterval();
+ } else if(UserPreferences.getUpdateTimeOfDay().length > 0){
+ interval = TimeUnit.DAYS.toMillis(1);
+ }
+ if(interval == 0) { // auto refresh is disabled
+ return;
+ }
+ long lastRefresh = DBTasks.getLastRefreshAllFeedsTimeMillis(context);
+ Log.d(TAG, "last refresh: " + Converter.getDurationStringLocalized(context,
+ System.currentTimeMillis() - lastRefresh) + " ago");
+ if(lastRefresh <= System.currentTimeMillis() - interval) {
+ FeedUpdateUtils.startAutoUpdate(context, null);
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/exception/MediaFileNotFoundException.java b/core/src/main/java/de/danoeh/antennapod/core/util/exception/MediaFileNotFoundException.java
index 287fe1100..3000e2fa4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/exception/MediaFileNotFoundException.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/exception/MediaFileNotFoundException.java
@@ -5,18 +5,13 @@ import de.danoeh.antennapod.core.feed.FeedMedia;
public class MediaFileNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
- private FeedMedia media;
+ private final FeedMedia media;
public MediaFileNotFoundException(String msg, FeedMedia media) {
super(msg);
this.media = media;
}
- public MediaFileNotFoundException(FeedMedia media) {
- super();
- this.media = media;
- }
-
public FeedMedia getMedia() {
return media;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrServiceCreator.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrServiceCreator.java
index c6150b0fd..d4d5843d2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrServiceCreator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrServiceCreator.java
@@ -10,7 +10,9 @@ import de.danoeh.antennapod.core.BuildConfig;
/** Ensures that only one instance of the FlattrService class exists at a time */
-public class FlattrServiceCreator {
+class FlattrServiceCreator {
+ private FlattrServiceCreator(){}
+
public static final String TAG = "FlattrServiceCreator";
private static volatile FlattrService flattrService;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrStatus.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrStatus.java
index d82171d1a..40a9fc7d5 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrStatus.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrStatus.java
@@ -3,9 +3,9 @@ package de.danoeh.antennapod.core.util.flattr;
import java.util.Calendar;
public class FlattrStatus {
- public static final int STATUS_UNFLATTERED = 0;
+ private static final int STATUS_UNFLATTERED = 0;
public static final int STATUS_QUEUE = 1;
- public static final int STATUS_FLATTRED = 2;
+ private static final int STATUS_FLATTRED = 2;
private int status = STATUS_UNFLATTERED;
private Calendar lastFlattred;
@@ -38,7 +38,7 @@ public class FlattrStatus {
status = STATUS_QUEUE;
}
- public void fromLong(long status) {
+ private void fromLong(long status) {
if (status == STATUS_UNFLATTERED || status == STATUS_QUEUE)
this.status = (int) status;
else {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrThing.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrThing.java
index 515028ab6..d5bb88771 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrThing.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrThing.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.core.util.flattr;
public interface FlattrThing {
- public String getTitle();
- public String getPaymentLink();
- public FlattrStatus getFlattrStatus();
+ String getTitle();
+ String getPaymentLink();
+ FlattrStatus getFlattrStatus();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
index 6ddfb0366..919fc82f2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/FlattrUtils.java
@@ -1,8 +1,6 @@
package de.danoeh.antennapod.core.util.flattr;
import android.content.Context;
-import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
@@ -36,6 +34,8 @@ import de.danoeh.antennapod.core.storage.DBWriter;
*/
public class FlattrUtils {
+ private FlattrUtils(){}
+
private static final String TAG = "FlattrUtils";
private static final String HOST_NAME = "de.danoeh.antennapod";
@@ -99,7 +99,7 @@ public class FlattrUtils {
cachedToken = token;
}
- public static void deleteToken() {
+ private static void deleteToken() {
Log.d(TAG, "Deleting flattr token");
storeToken(null);
}
@@ -116,7 +116,7 @@ public class FlattrUtils {
public static List<Flattr> retrieveFlattredThings()
throws FlattrException {
- ArrayList<Flattr> myFlattrs = new ArrayList<Flattr>();
+ ArrayList<Flattr> myFlattrs = new ArrayList<>();
if (hasToken()) {
FlattrService fs = FlattrServiceCreator.getService(retrieveToken());
@@ -172,17 +172,11 @@ public class FlattrUtils {
// ------------------------------------------------ DIALOGS
- public static void showRevokeDialog(final Context context) {
+ private static void showRevokeDialog(final Context context) {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.access_revoked_title);
builder.setMessage(R.string.access_revoked_info);
- builder.setNeutralButton(android.R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
+ builder.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.cancel());
builder.create().show();
}
@@ -197,27 +191,15 @@ public class FlattrUtils {
builder.setTitle(R.string.no_flattr_token_title);
builder.setMessage(R.string.no_flattr_token_msg);
builder.setPositiveButton(R.string.authenticate_now_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- context.startActivity(
- ClientConfig.flattrCallbacks.getFlattrAuthenticationActivityIntent(context));
- }
-
- }
+ (dialog, which) -> context.startActivity(
+ ClientConfig.flattrCallbacks.getFlattrAuthenticationActivityIntent(context))
);
builder.setNegativeButton(R.string.visit_website_label,
- new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- Uri uri = Uri.parse(url);
- context.startActivity(new Intent(Intent.ACTION_VIEW,
- uri));
- }
-
+ (dialog, which) -> {
+ Uri uri = Uri.parse(url);
+ context.startActivity(new Intent(Intent.ACTION_VIEW,
+ uri));
}
);
builder.create().show();
@@ -231,13 +213,7 @@ public class FlattrUtils {
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setTitle(R.string.error_label);
builder.setMessage(msg);
- builder.setNeutralButton(android.R.string.ok, new OnClickListener() {
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- dialog.cancel();
- }
- });
+ builder.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.cancel());
builder.create().show();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/SimpleFlattrThing.java b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/SimpleFlattrThing.java
index 2c178496e..43cd5f170 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/flattr/SimpleFlattrThing.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/flattr/SimpleFlattrThing.java
@@ -24,7 +24,7 @@ public class SimpleFlattrThing implements FlattrThing {
return this.status;
}
- private String title;
- private String url;
- private FlattrStatus status;
+ private final String title;
+ private final String url;
+ private final FlattrStatus status;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java
index 386f46724..6e5c3e84b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/MoreContentListFooterUtil.java
@@ -27,8 +27,8 @@ public class MoreContentListFooterUtil {
}
public void setLoadingState(boolean newState) {
- final ImageView imageView = (ImageView) root.findViewById(R.id.imgExpand);
- final ProgressBar progressBar = (ProgressBar) root.findViewById(R.id.progBar);
+ final ImageView imageView = root.findViewById(R.id.imgExpand);
+ final ProgressBar progressBar = root.findViewById(R.id.progBar);
if (newState) {
imageView.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java
new file mode 100644
index 000000000..52a43aab2
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/NotificationUtils.java
@@ -0,0 +1,65 @@
+package de.danoeh.antennapod.core.util.gui;
+
+
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.Context;
+import android.os.Build;
+import android.support.annotation.RequiresApi;
+
+import de.danoeh.antennapod.core.R;
+
+public class NotificationUtils {
+ public static final String CHANNEL_ID_USER_ACTION = "user_action";
+ public static final String CHANNEL_ID_DOWNLOADING = "downloading";
+ public static final String CHANNEL_ID_PLAYING = "playing";
+ public static final String CHANNEL_ID_ERROR = "error";
+
+ public static void createChannels(Context context) {
+ if (android.os.Build.VERSION.SDK_INT < 26) {
+ return;
+ }
+ NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (mNotificationManager != null) {
+ mNotificationManager.createNotificationChannel(createChannelUserAction(context));
+ mNotificationManager.createNotificationChannel(createChannelDownloading(context));
+ mNotificationManager.createNotificationChannel(createChannelPlaying(context));
+ mNotificationManager.createNotificationChannel(createChannelError(context));
+ }
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private static NotificationChannel createChannelUserAction(Context c) {
+ NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_USER_ACTION,
+ c.getString(R.string.notification_channel_user_action), NotificationManager.IMPORTANCE_HIGH);
+ mChannel.setDescription(c.getString(R.string.notification_channel_user_action_description));
+ return mChannel;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private static NotificationChannel createChannelDownloading(Context c) {
+ NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_DOWNLOADING,
+ c.getString(R.string.notification_channel_downloading), NotificationManager.IMPORTANCE_LOW);
+ mChannel.setDescription(c.getString(R.string.notification_channel_downloading_description));
+ mChannel.setShowBadge(false);
+ return mChannel;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private static NotificationChannel createChannelPlaying(Context c) {
+ NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_PLAYING,
+ c.getString(R.string.notification_channel_playing), NotificationManager.IMPORTANCE_LOW);
+ mChannel.setDescription(c.getString(R.string.notification_channel_playing_description));
+ mChannel.setShowBadge(false);
+ return mChannel;
+ }
+
+ @RequiresApi(api = Build.VERSION_CODES.O)
+ private static NotificationChannel createChannelError(Context c) {
+ NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID_ERROR,
+ c.getString(R.string.notification_channel_error), NotificationManager.IMPORTANCE_HIGH);
+ mChannel.setDescription(c.getString(R.string.notification_channel_error_description));
+ return mChannel;
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/gui/PictureInPictureUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/gui/PictureInPictureUtil.java
new file mode 100644
index 000000000..f763653a1
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/gui/PictureInPictureUtil.java
@@ -0,0 +1,27 @@
+package de.danoeh.antennapod.core.util.gui;
+
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.os.Build;
+
+public class PictureInPictureUtil {
+ private PictureInPictureUtil() {
+ }
+
+ public static boolean supportsPictureInPicture(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ PackageManager packageManager = activity.getPackageManager();
+ return packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE);
+ } else {
+ return false;
+ }
+ }
+
+ public static boolean isInPictureInPictureMode(Activity activity) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && supportsPictureInPicture(activity)) {
+ return activity.isInPictureInPictureMode();
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
index 1807421b0..f681b8062 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
@@ -8,7 +8,6 @@ import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.List;
-import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.ID3Chapter;
import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
@@ -27,24 +26,24 @@ public class ChapterReader extends ID3Reader {
@Override
public int onStartTagHeader(TagHeader header) {
chapters = new ArrayList<>();
- System.out.println(header.toString());
+ Log.d(TAG, "header: " + header);
return ID3Reader.ACTION_DONT_SKIP;
}
@Override
public int onStartFrameHeader(FrameHeader header, InputStream input)
throws IOException, ID3ReaderException {
- System.out.println(header.toString());
+ Log.d(TAG, "header: " + header);
switch (header.getId()) {
case FRAME_ID_CHAPTER:
if (currentChapter != null) {
if (!hasId3Chapter(currentChapter)) {
chapters.add(currentChapter);
- if (BuildConfig.DEBUG) Log.d(TAG, "Found chapter: " + currentChapter);
+ Log.d(TAG, "Found chapter: " + currentChapter);
currentChapter = null;
}
}
- StringBuffer elementId = new StringBuffer();
+ StringBuilder elementId = new StringBuilder();
readISOString(elementId, input, Integer.MAX_VALUE);
char[] startTimeSource = readBytes(input, 4);
long startTime = ((int) startTimeSource[0] << 24)
@@ -55,11 +54,11 @@ public class ChapterReader extends ID3Reader {
return ID3Reader.ACTION_DONT_SKIP;
case FRAME_ID_TITLE:
if (currentChapter != null && currentChapter.getTitle() == null) {
- StringBuffer title = new StringBuffer();
+ StringBuilder title = new StringBuilder();
readString(title, input, header.getSize());
currentChapter
.setTitle(title.toString());
- if (BuildConfig.DEBUG) Log.d(TAG, "Found title: " + currentChapter.getTitle());
+ Log.d(TAG, "Found title: " + currentChapter.getTitle());
return ID3Reader.ACTION_DONT_SKIP;
}
@@ -68,13 +67,13 @@ public class ChapterReader extends ID3Reader {
if (currentChapter != null) {
// skip description
int descriptionLength = readString(null, input, header.getSize());
- StringBuffer link = new StringBuffer();
+ StringBuilder link = new StringBuilder();
readISOString(link, input, header.getSize() - descriptionLength);
String decodedLink = URLDecoder.decode(link.toString(), "UTF-8");
currentChapter.setLink(decodedLink);
- if (BuildConfig.DEBUG) Log.d(TAG, "Found link: " + currentChapter.getLink());
+ Log.d(TAG, "Found link: " + currentChapter.getLink());
return ID3Reader.ACTION_DONT_SKIP;
}
break;
@@ -102,17 +101,17 @@ public class ChapterReader extends ID3Reader {
chapters.add(currentChapter);
}
}
- System.out.println("Reached end of tag");
+ Log.d(TAG, "Reached end of tag");
if (chapters != null) {
for (Chapter c : chapters) {
- System.out.println(c.toString());
+ Log.d(TAG, "chapter: " + c);
}
}
}
@Override
public void onNoTagHeaderFound() {
- System.out.println("No tag header found");
+ Log.d(TAG, "No tag header found");
super.onNoTagHeaderFound();
}
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 a238c11e9..7290b9d98 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
@@ -1,7 +1,5 @@
package de.danoeh.antennapod.core.util.id3reader;
-import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
-import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
import org.apache.commons.io.IOUtils;
import java.io.IOException;
@@ -9,6 +7,9 @@ import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
+import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
+import de.danoeh.antennapod.core.util.id3reader.model.TagHeader;
+
/**
* Reads the ID3 Tag of a given file. In order to use this class, you should
* create a subclass of it and overwrite the onStart* - or onEnd* - methods.
@@ -18,10 +19,10 @@ public class ID3Reader {
private static final int ID3_LENGTH = 3;
private static final int FRAME_ID_LENGTH = 4;
- protected static final int ACTION_SKIP = 1;
- protected static final int ACTION_DONT_SKIP = 2;
+ private static final int ACTION_SKIP = 1;
+ static final int ACTION_DONT_SKIP = 2;
- protected int readerPosition;
+ private int readerPosition;
private static final byte ENCODING_UTF16_WITH_BOM = 1;
private static final byte ENCODING_UTF16_WITHOUT_BOM = 2;
@@ -29,7 +30,7 @@ public class ID3Reader {
private TagHeader tagHeader;
- public ID3Reader() {
+ ID3Reader() {
}
public final void readInputStream(InputStream input) throws IOException,
@@ -91,7 +92,7 @@ public class ID3Reader {
* Read a certain number of bytes from the given input stream. This method
* changes the readerPosition-attribute.
*/
- protected char[] readBytes(InputStream input, int number)
+ char[] readBytes(InputStream input, int number)
throws IOException, ID3ReaderException {
char[] header = new char[number];
for (int i = 0; i < number; i++) {
@@ -110,7 +111,7 @@ public class ID3Reader {
* Skip a certain number of bytes on the given input stream. This method
* changes the readerPosition-attribute.
*/
- protected void skipBytes(InputStream input, int number) throws IOException {
+ void skipBytes(InputStream input, int number) throws IOException {
if (number <= 0) {
number = 1;
}
@@ -169,7 +170,7 @@ public class ID3Reader {
return out;
}
- protected int readString(StringBuffer buffer, InputStream input, int max) throws IOException,
+ protected int readString(StringBuilder buffer, InputStream input, int max) throws IOException,
ID3ReaderException {
if (max > 0) {
char[] encoding = readBytes(input, 1);
@@ -190,9 +191,8 @@ public class ID3Reader {
}
}
- protected int readISOString(StringBuffer buffer, InputStream input, int max)
+ 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) {
@@ -203,7 +203,7 @@ public class ID3Reader {
return bytesRead;
}
- private int readUnicodeString(StringBuffer strBuffer, InputStream input, int max, Charset charset)
+ private int readUnicodeString(StringBuilder strBuffer, InputStream input, int max, Charset charset)
throws IOException, ID3ReaderException {
byte[] buffer = new byte[max];
int c, cZero = -1;
@@ -230,20 +230,20 @@ public class ID3Reader {
return i;
}
- public int onStartTagHeader(TagHeader header) {
+ int onStartTagHeader(TagHeader header) {
return ACTION_SKIP;
}
- public int onStartFrameHeader(FrameHeader header, InputStream input)
+ int onStartFrameHeader(FrameHeader header, InputStream input)
throws IOException, ID3ReaderException {
return ACTION_SKIP;
}
- public void onEndTag() {
+ void onEndTag() {
}
- public void onNoTagHeaderFound() {
+ void onNoTagHeaderFound() {
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java
index 89eab1398..2f3f378ab 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/FrameHeader.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.util.id3reader.model;
public class FrameHeader extends Header {
- protected char flags;
+ private final char flags;
public FrameHeader(String id, int size, char flags) {
super(id, size);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java
index 346e2893f..29185748f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/Header.java
@@ -2,10 +2,10 @@ package de.danoeh.antennapod.core.util.id3reader.model;
public abstract class Header {
- protected String id;
- protected int size;
+ final String id;
+ final int size;
- public Header(String id, int size) {
+ Header(String id, int size) {
super();
this.id = id;
this.size = size;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java
index 0a6b8357f..b652a139c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/model/TagHeader.java
@@ -2,8 +2,8 @@ package de.danoeh.antennapod.core.util.id3reader.model;
public class TagHeader extends Header {
- protected char version;
- protected byte flags;
+ private final char version;
+ private final byte flags;
public TagHeader(String id, int size, char version, byte flags) {
super(id, size);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java
index 846733882..16d05dbb9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/AudioPlayer.java
@@ -21,18 +21,12 @@ public class AudioPlayer extends MediaPlayer implements IPlayer {
private final SharedPreferences.OnSharedPreferenceChangeListener sonicListener =
(sharedPreferences, key) -> {
- if (key.equals(UserPreferences.PREF_SONIC)) {
+ if (key.equals(UserPreferences.PREF_MEDIA_PLAYER)) {
checkMpi();
}
};
@Override
- public void setScreenOnWhilePlaying(boolean screenOn) {
- Log.e(TAG, "Setting screen on while playing not supported in Audio Player");
- throw new UnsupportedOperationException("Setting screen on while playing not supported in Audio Player");
- }
-
- @Override
public void setDisplay(SurfaceHolder sh) {
if (sh != null) {
Log.e(TAG, "Setting display not supported in Audio Player");
@@ -40,11 +34,6 @@ public class AudioPlayer extends MediaPlayer implements IPlayer {
}
}
- @Override
- public void setVideoScalingMode(int mode) {
- throw new UnsupportedOperationException("Setting scaling mode is not supported in Audio Player");
- }
-
@Override
protected boolean useSonic() {
return UserPreferences.useSonic();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
index c4acdb65e..645bae5f3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
@@ -23,7 +23,7 @@ public class ExternalMedia implements Playable {
public static final String PREF_MEDIA_TYPE = "ExternalMedia.PrefMediaType";
public static final String PREF_LAST_PLAYED_TIME = "ExternalMedia.PrefLastPlayedTime";
- private String source;
+ private final String source;
private String episodeTitle;
private String feedTitle;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
index d67153a4e..a372f4241 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
@@ -6,13 +6,11 @@ import android.view.SurfaceHolder;
import java.io.IOException;
public interface IPlayer {
- boolean canSetPitch();
boolean canSetSpeed();
boolean canDownmix();
- float getCurrentPitchStepsAdjustment();
int getCurrentPosition();
@@ -20,20 +18,12 @@ public interface IPlayer {
int getDuration();
- float getMaxSpeedMultiplier();
-
- float getMinSpeedMultiplier();
-
- boolean isLooping();
-
boolean isPlaying();
void pause();
void prepare() throws IllegalStateException, IOException;
- void prepareAsync();
-
void release();
void reset();
@@ -42,21 +32,11 @@ public interface IPlayer {
void setAudioStreamType(int streamtype);
- void setScreenOnWhilePlaying(boolean screenOn);
-
void setDataSource(String path) throws IllegalStateException, IOException,
- IllegalArgumentException, SecurityException;
+ IllegalArgumentException, SecurityException;
void setDisplay(SurfaceHolder sh);
- void setEnableSpeedAdjustment(boolean enableSpeedAdjustment);
-
- void setLooping(boolean looping);
-
- void setPitchStepsAdjustment(float pitchSteps);
-
- void setPlaybackPitch(float f);
-
void setPlaybackSpeed(float f);
void setDownmix(boolean enable);
@@ -67,7 +47,5 @@ public interface IPlayer {
void stop();
- public void setVideoScalingMode(int mode);
-
- public void setWakeMode(Context context, int mode);
+ void setWakeMode(Context context, int mode);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/MediaPlayerError.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/MediaPlayerError.java
index 5ba7f11d6..b04c02075 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/MediaPlayerError.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/MediaPlayerError.java
@@ -2,10 +2,12 @@ package de.danoeh.antennapod.core.util.playback;
import android.content.Context;
import android.media.MediaPlayer;
+
import de.danoeh.antennapod.core.R;
/** Utility class for MediaPlayer errors. */
public class MediaPlayerError {
+ private MediaPlayerError(){}
/** Get a human-readable string for a specific error code. */
public static String getErrorString(Context context, int code) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
index 279c56338..da9b96430 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
@@ -3,6 +3,8 @@ package de.danoeh.antennapod.core.util.playback;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Parcelable;
+import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
import android.util.Log;
import java.util.List;
@@ -11,6 +13,7 @@ import de.danoeh.antennapod.core.asynctask.ImageResource;
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.storage.DBReader;
import de.danoeh.antennapod.core.util.ShownotesProvider;
@@ -173,12 +176,31 @@ public interface Playable extends Parcelable,
* Provides utility methods for Playable objects.
*/
class PlayableUtils {
+ private PlayableUtils(){}
+
private static final String TAG = "PlayableUtils";
/**
* Restores a playable object from a sharedPreferences file. This method might load data from the database,
* depending on the type of playable that was restored.
*
+ * @return The restored Playable object
+ */
+ @Nullable
+ public static Playable createInstanceFromPreferences(Context context) {
+ long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMedia();
+ if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());
+ return PlayableUtils.createInstanceFromPreferences(context,
+ (int) currentlyPlayingMedia, prefs);
+ }
+ return null;
+ }
+
+ /**
+ * Restores a playable object from a sharedPreferences file. This method might load data from the database,
+ * depending on the type of playable that was restored.
+ *
* @param type An integer that represents the type of the Playable object
* that is restored.
* @param pref The SharedPreferences file from which the Playable object
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 9d3854f41..847c5223b 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
@@ -7,13 +7,13 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.media.MediaPlayer;
import android.os.Build;
import android.os.IBinder;
-import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+import android.support.v4.content.ContextCompat;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -22,10 +22,8 @@ import android.widget.ImageButton;
import android.widget.SeekBar;
import android.widget.TextView;
-import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
-import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
@@ -40,10 +38,12 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.playback.Playable.PlayableUtils;
-import rx.Observable;
-import rx.Subscription;
-import rx.android.schedulers.AndroidSchedulers;
-import rx.schedulers.Schedulers;
+import io.reactivex.Maybe;
+import io.reactivex.MaybeOnSubscribe;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
/**
* Communicates with the playback service. GUI classes should use this class to
@@ -59,7 +59,7 @@ public abstract class PlaybackController {
private PlaybackService playbackService;
private Playable media;
- private PlayerStatus status;
+ private PlayerStatus status = PlayerStatus.STOPPED;
private final ScheduledThreadPoolExecutor schedExecutor;
private static final int SCHED_EX_POOLSIZE = 1;
@@ -69,8 +69,10 @@ public abstract class PlaybackController {
private boolean mediaInfoLoaded = false;
private boolean released = false;
+ private boolean initialized = false;
- private Subscription serviceBinder;
+ private Disposable serviceBinder;
+ private Disposable mediaLoader;
/**
* True if controller should reinit playback service if 'pause' button is
@@ -87,21 +89,27 @@ public abstract class PlaybackController {
Thread t = new Thread(r);
t.setPriority(Thread.MIN_PRIORITY);
return t;
- }, new RejectedExecutionHandler() {
- @Override
- public void rejectedExecution(Runnable r,
- ThreadPoolExecutor executor) {
- Log.w(TAG, "Rejected execution of runnable in schedExecutor");
- }
- }
+ }, (r, executor) -> Log.w(TAG, "Rejected execution of runnable in schedExecutor")
);
}
/**
- * Creates a new connection to the playbackService. Should be called in the
- * activity's onResume() method.
+ * Creates a new connection to the playbackService.
*/
public void init() {
+ if (PlaybackService.isRunning) {
+ initServiceRunning();
+ } else {
+ initServiceNotRunning();
+ }
+ }
+
+ private synchronized void initServiceRunning() {
+ if (initialized) {
+ return;
+ }
+ initialized = true;
+
activity.registerReceiver(statusUpdate, new IntentFilter(
PlaybackService.ACTION_PLAYER_STATUS_CHANGED));
@@ -139,7 +147,7 @@ public abstract class PlaybackController {
}
if(serviceBinder != null) {
- serviceBinder.unsubscribe();
+ serviceBinder.dispose();
}
try {
activity.unbindService(mConnection);
@@ -173,18 +181,18 @@ public abstract class PlaybackController {
*/
private void bindToService() {
Log.d(TAG, "Trying to connect to service");
- if(serviceBinder != null) {
- serviceBinder.unsubscribe();
+ if (serviceBinder != null) {
+ serviceBinder.dispose();
}
serviceBinder = Observable.fromCallable(this::getPlayLastPlayedMediaIntent)
- .subscribeOn(Schedulers.newThread())
+ .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(intent -> {
boolean bound = false;
if (!PlaybackService.started) {
if (intent != null) {
Log.d(TAG, "Calling start service");
- activity.startService(intent);
+ ContextCompat.startForegroundService(activity, intent);
bound = activity.bindService(intent, mConnection, 0);
} else {
status = PlayerStatus.STOPPED;
@@ -204,31 +212,24 @@ public abstract class PlaybackController {
* Returns an intent that starts the PlaybackService and plays the last
* played media or null if no last played media could be found.
*/
- private Intent getPlayLastPlayedMediaIntent() {
+ @Nullable private Intent getPlayLastPlayedMediaIntent() {
Log.d(TAG, "Trying to restore last played media");
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
- activity.getApplicationContext());
- long currentlyPlayingMedia = PlaybackPreferences.getCurrentlyPlayingMedia();
- if (currentlyPlayingMedia != PlaybackPreferences.NO_MEDIA_PLAYING) {
- Playable media = PlayableUtils.createInstanceFromPreferences(activity,
- (int) currentlyPlayingMedia, prefs);
- if (media != null) {
- Intent serviceIntent = new Intent(activity, PlaybackService.class);
- serviceIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
- serviceIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, false);
- serviceIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, true);
- boolean fileExists = media.localFileAvailable();
- boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
- if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
- DBTasks.notifyMissingFeedMediaFile(activity, (FeedMedia) media);
- }
- serviceIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM,
- lastIsStream || !fileExists);
- return serviceIntent;
- }
+ Playable media = PlayableUtils.createInstanceFromPreferences(activity);
+ if (media == null) {
+ Log.d(TAG, "No last played media found");
+ return null;
}
- Log.d(TAG, "No last played media found");
- return null;
+
+ boolean fileExists = media.localFileAvailable();
+ boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
+ if (!fileExists && !lastIsStream && media instanceof FeedMedia) {
+ DBTasks.notifyMissingFeedMediaFile(activity, (FeedMedia) media);
+ }
+
+ return new PlaybackServiceStarter(activity, media)
+ .startWhenPrepared(false)
+ .shouldStream(lastIsStream || !fileExists)
+ .getIntent();
}
@@ -517,7 +518,7 @@ public abstract class PlaybackController {
"PlaybackService has no media object. Trying to restore last played media.");
Intent serviceIntent = getPlayLastPlayedMediaIntent();
if (serviceIntent != null) {
- activity.startService(serviceIntent);
+ ContextCompat.startForegroundService(activity, serviceIntent);
}
}
*/
@@ -582,6 +583,10 @@ public abstract class PlaybackController {
public void playPause() {
if (playbackService == null) {
+ new PlaybackServiceStarter(activity, media)
+ .startWhenPrepared(true)
+ .streamIfLastWasStream()
+ .start();
Log.w(TAG, "Play/Pause button was pressed, but playbackservice was null!");
return;
}
@@ -615,6 +620,8 @@ public abstract class PlaybackController {
public int getPosition() {
if (playbackService != null) {
return playbackService.getCurrentPosition();
+ } else if (media != null) {
+ return media.getPosition();
} else {
return PlaybackService.INVALID_TIME;
}
@@ -623,12 +630,17 @@ public abstract class PlaybackController {
public int getDuration() {
if (playbackService != null) {
return playbackService.getDuration();
+ } else if (media != null) {
+ return media.getDuration();
} else {
return PlaybackService.INVALID_TIME;
}
}
public Playable getMedia() {
+ if (media == null) {
+ media = PlayableUtils.createInstanceFromPreferences(activity);
+ }
return media;
}
@@ -686,7 +698,7 @@ public abstract class PlaybackController {
return org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(activity.getApplicationContext())
|| UserPreferences.useSonic()
|| Build.VERSION.SDK_INT >= 23
- || playbackService != null && playbackService.canSetSpeed();
+ || (playbackService != null && playbackService.canSetSpeed());
}
public void setPlaybackSpeed(float speed) {
@@ -720,8 +732,13 @@ public abstract class PlaybackController {
}
public boolean isPlayingVideoLocally() {
- return playbackService != null && PlaybackService.getCurrentMediaType() == MediaType.VIDEO
- && !PlaybackService.isCasting();
+ if (PlaybackService.isCasting()) {
+ return false;
+ } else if (playbackService != null) {
+ return PlaybackService.getCurrentMediaType() == MediaType.VIDEO;
+ } else {
+ return getMedia() != null && getMedia().getMediaType() == MediaType.VIDEO;
+ }
}
public Pair<Integer, Integer> getVideoSize() {
@@ -761,12 +778,36 @@ public abstract class PlaybackController {
}
}
+ private void initServiceNotRunning() {
+ mediaLoader = Maybe.create((MaybeOnSubscribe<Playable>) emitter -> {
+ Playable media = getMedia();
+ if (media != null) {
+ emitter.onSuccess(media);
+ } else {
+ emitter.onComplete();
+ }
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(media -> {
+ if (media.getMediaType() == MediaType.AUDIO) {
+ TypedArray res = activity.obtainStyledAttributes(new int[]{
+ de.danoeh.antennapod.core.R.attr.av_play_big});
+ getPlayButton().setImageResource(
+ res.getResourceId(0, de.danoeh.antennapod.core.R.drawable.ic_play_arrow_grey600_36dp));
+ res.recycle();
+ } else {
+ getPlayButton().setImageResource(R.drawable.ic_av_play_circle_outline_80dp);
+ }
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
/**
* Refreshes the current position of the media file that is playing.
*/
public class MediaPositionObserver implements Runnable {
- public static final int WAITING_INTERVALL = 1000;
+ static final int WAITING_INTERVALL = 1000;
@Override
public void run() {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
new file mode 100644
index 000000000..f7d2ee409
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
@@ -0,0 +1,78 @@
+package de.danoeh.antennapod.core.util.playback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.media.MediaPlayer;
+import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
+
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+
+public class PlaybackServiceStarter {
+ private final Context context;
+ private final Playable media;
+ private boolean startWhenPrepared = false;
+ private boolean shouldStream = false;
+ private boolean callEvenIfRunning = false;
+ private boolean prepareImmediately = true;
+
+ public PlaybackServiceStarter(Context context, Playable media) {
+ this.context = context;
+ this.media = media;
+ }
+
+ /**
+ * Default value: false
+ */
+ public PlaybackServiceStarter shouldStream(boolean shouldStream) {
+ this.shouldStream = shouldStream;
+ return this;
+ }
+
+ public PlaybackServiceStarter streamIfLastWasStream() {
+ boolean lastIsStream = PlaybackPreferences.getCurrentEpisodeIsStream();
+ return shouldStream(lastIsStream);
+ }
+
+ /**
+ * Default value: false
+ */
+ public PlaybackServiceStarter startWhenPrepared(boolean startWhenPrepared) {
+ this.startWhenPrepared = startWhenPrepared;
+ return this;
+ }
+
+ /**
+ * Default value: false
+ */
+ public PlaybackServiceStarter callEvenIfRunning(boolean callEvenIfRunning) {
+ this.callEvenIfRunning = callEvenIfRunning;
+ return this;
+ }
+
+ /**
+ * Default value: true
+ */
+ public PlaybackServiceStarter prepareImmediately(boolean prepareImmediately) {
+ this.prepareImmediately = prepareImmediately;
+ return this;
+ }
+
+ public Intent getIntent() {
+ Intent launchIntent = new Intent(context, PlaybackService.class);
+ launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media);
+ launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, startWhenPrepared);
+ launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, shouldStream);
+ launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, prepareImmediately);
+
+ return launchIntent;
+ }
+
+ public void start() {
+ if (PlaybackService.isRunning && !callEvenIfRunning) {
+ return;
+ }
+ ContextCompat.startForegroundService(context, getIntent());
+ }
+}
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 efdf46a97..34cfe6d05 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
@@ -14,6 +14,7 @@ import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
+import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -42,22 +43,22 @@ public class Timeline {
private final int pageMargin;
public Timeline(Context context, ShownotesProvider shownotesProvider) {
- if (shownotesProvider == null) throw new IllegalArgumentException("shownotesProvider = null");
+ if (shownotesProvider == null) {
+ throw new IllegalArgumentException("shownotesProvider = null");
+ }
this.shownotesProvider = shownotesProvider;
noShownotesLabel = context.getString(R.string.no_shownotes_label);
- TypedArray res = context.getTheme().obtainStyledAttributes(
- new int[]{ android.R.attr.textColorPrimary});
+ TypedArray res = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.textColorPrimary});
@ColorInt int col = res.getColor(0, 0);
colorPrimaryString = "rgba(" + Color.red(col) + "," + Color.green(col) + "," +
- Color.blue(col) + "," + (Color.alpha(col)/256.0) + ")";
+ Color.blue(col) + "," + (Color.alpha(col) / 255.0) + ")";
res.recycle();
- res = context.getTheme().obtainStyledAttributes(
- new int[]{android.R.attr.textColorSecondary});
+ res = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.textColorSecondary});
col = res.getColor(0, 0);
colorSecondaryString = "rgba(" + Color.red(col) + "," + Color.green(col) + "," +
- Color.blue(col) + "," + (Color.alpha(col)/256.0) + ")";
+ Color.blue(col) + "," + (Color.alpha(col) / 255.0) + ")";
res.recycle();
pageMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
@@ -93,9 +94,9 @@ public class Timeline {
return null;
}
- if(TextUtils.isEmpty(shownotes)) {
+ if (TextUtils.isEmpty(shownotes)) {
Log.d(TAG, "shownotesProvider contained no shownotes. Returning 'no shownotes' message");
- shownotes ="<html>" +
+ shownotes = "<html>" +
"<head>" +
"<style type='text/css'>" +
"html, body { margin: 0; padding: 0; width: 100%; height: 100%; } " +
@@ -113,15 +114,15 @@ public class Timeline {
}
// replace ASCII line breaks with HTML ones if shownotes don't contain HTML line breaks already
- if(!LINE_BREAK_REGEX.matcher(shownotes).find() && !shownotes.contains("<p>")) {
+ if (!LINE_BREAK_REGEX.matcher(shownotes).find() && !shownotes.contains("<p>")) {
shownotes = shownotes.replace("\n", "<br />");
}
Document document = Jsoup.parse(shownotes);
// apply style
- String styleStr = String.format(WEBVIEW_STYLE, colorPrimaryString, "100%", pageMargin,
- pageMargin, pageMargin, pageMargin);
+ String styleStr = String.format(Locale.getDefault(), WEBVIEW_STYLE, colorPrimaryString, "100%",
+ pageMargin, pageMargin, pageMargin, pageMargin);
document.head().appendElement("style").attr("type", "text/css").text(styleStr);
// apply timecode links
@@ -139,7 +140,7 @@ public class Timeline {
String rep;
if (playable == null || playable.getDuration() > time) {
- rep = String.format(TIMECODE_LINK, time, group);
+ rep = String.format(Locale.getDefault(), TIMECODE_LINK, time, group);
} else {
rep = group;
}
@@ -150,7 +151,7 @@ public class Timeline {
element.html(buffer.toString());
}
}
-
+
return document.toString();
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
index 368379509..1d04fb878 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
@@ -7,11 +7,6 @@ public class VideoPlayer extends MediaPlayer implements IPlayer {
private static final String TAG = "VideoPlayer";
@Override
- public boolean canSetPitch() {
- return false;
- }
-
- @Override
public boolean canSetSpeed() {
return false;
}
@@ -22,44 +17,11 @@ public class VideoPlayer extends MediaPlayer implements IPlayer {
}
@Override
- public float getCurrentPitchStepsAdjustment() {
- return 1;
- }
-
- @Override
public float getCurrentSpeedMultiplier() {
return 1;
}
@Override
- public float getMaxSpeedMultiplier() {
- return 1;
- }
-
- @Override
- public float getMinSpeedMultiplier() {
- return 1;
- }
-
- @Override
- public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) throws UnsupportedOperationException {
- Log.e(TAG, "Setting enable speed adjustment unsupported in video player");
- throw new UnsupportedOperationException("Setting enable speed adjustment unsupported in video player");
- }
-
- @Override
- public void setPitchStepsAdjustment(float pitchSteps) {
- Log.e(TAG, "Setting pitch steps adjustment unsupported in video player");
- throw new UnsupportedOperationException("Setting pitch steps adjustment unsupported in video player");
- }
-
- @Override
- public void setPlaybackPitch(float f) {
- Log.e(TAG, "Setting playback pitch unsupported in video player");
- throw new UnsupportedOperationException("Setting playback pitch unsupported in video player");
- }
-
- @Override
public void setPlaybackSpeed(float f) {
Log.e(TAG, "Setting playback speed unsupported in video player");
throw new UnsupportedOperationException("Setting playback speed unsupported in video player");
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java
index 13cb9f002..c5ad9cfd6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/FeedDiscoverer.java
@@ -41,7 +41,7 @@ public class FeedDiscoverer {
* @return A map which contains the feed URLs as keys and titles as values (the feed URL is also used as a title if
* a title cannot be found).
*/
- public Map<String, String> findLinks(String in, String baseUrl) throws IOException {
+ public Map<String, String> findLinks(String in, String baseUrl) {
return findLinks(Jsoup.parse(in), baseUrl);
}
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 bd40f398d..cc6a8ec90 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
@@ -40,9 +40,9 @@ public class HtmlToPlainText {
}
// the formatting rules, implemented in a breadth-first DOM traverse
- private class FormattingVisitor implements NodeVisitor {
+ private static class FormattingVisitor implements NodeVisitor {
- private StringBuilder accum = new StringBuilder(); // holds the accumulated text
+ private final StringBuilder accum = new StringBuilder(); // holds the accumulated text
// hit when the node is first seen
public void head(Node node, int depth) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java
index 4799d3881..cdf171299 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/OggInputStream.java
@@ -6,8 +6,8 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
-public class OggInputStream extends InputStream {
- private InputStream input;
+class OggInputStream extends InputStream {
+ private final InputStream input;
/** True if OggInputStream is currently inside an Ogg page. */
private boolean isInPage;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
index 6243da5bc..569ff3438 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
@@ -1,13 +1,14 @@
package de.danoeh.antennapod.core.util.vorbiscommentreader;
import android.util.Log;
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
import java.util.ArrayList;
import java.util.List;
+import de.danoeh.antennapod.core.BuildConfig;
+import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
+
public class VorbisCommentChapterReader extends VorbisCommentReader {
private static final String TAG = "VorbisCommentChptrReadr";
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java
index 5f9dd0faf..ff7508390 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentHeader.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.core.util.vorbiscommentreader;
-public class VorbisCommentHeader {
- private String vendorString;
- private long userCommentLength;
+class VorbisCommentHeader {
+ private final String vendorString;
+ private final long userCommentLength;
public VorbisCommentHeader(String vendorString, long userCommentLength) {
super();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
index 49ea18721..55498afcb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentReader.java
@@ -19,29 +19,29 @@ public abstract class VorbisCommentReader {
private static final int PACKET_TYPE_COMMENT = 3;
/** Called when Reader finds identification header. */
- public abstract void onVorbisCommentFound();
+ protected abstract void onVorbisCommentFound();
- public abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
+ protected abstract void onVorbisCommentHeaderFound(VorbisCommentHeader header);
/**
* Is called every time the Reader finds a content vector. The handler
* should return true if it wants to handle the content vector.
*/
- public abstract boolean onContentVectorKey(String content);
+ protected abstract boolean onContentVectorKey(String content);
/**
* Is called if onContentVectorKey returned true for the key.
*
* @throws VorbisCommentReaderException
*/
- public abstract void onContentVectorValue(String key, String value)
+ protected abstract void onContentVectorValue(String key, String value)
throws VorbisCommentReaderException;
- public abstract void onNoVorbisCommentFound();
+ protected abstract void onNoVorbisCommentFound();
- public abstract void onEndOfComment();
+ protected abstract void onEndOfComment();
- public abstract void onError(VorbisCommentReaderException exception);
+ protected abstract void onError(VorbisCommentReaderException exception);
public void readInputStream(InputStream input)
throws VorbisCommentReaderException {
diff --git a/core/src/main/res/drawable-hdpi-v11/ic_stat_antenna_default.png b/core/src/main/res/drawable-hdpi-v11/ic_stat_antenna_default.png
deleted file mode 100644
index af99f4b3b..000000000
--- a/core/src/main/res/drawable-hdpi-v11/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi-v11/ic_stat_authentication.png b/core/src/main/res/drawable-hdpi-v11/ic_stat_authentication.png
deleted file mode 100755
index 398dffa4b..000000000
--- a/core/src/main/res/drawable-hdpi-v11/ic_stat_authentication.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.png
new file mode 100755
index 000000000..67924a5a2
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_baseline_question_answer_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_bug_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_bug_grey600_24dp.png
new file mode 100644
index 000000000..daadfb35f
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_bug_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_bug_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_bug_white_24dp.png
new file mode 100644
index 000000000..549f67bf4
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_bug_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cellphone_text_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_cellphone_text_grey600_24dp.png
new file mode 100644
index 000000000..71ccc26ea
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_cellphone_text_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_cellphone_text_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_cellphone_text_white_24dp.png
new file mode 100644
index 000000000..70e52c60b
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_cellphone_text_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
new file mode 100644
index 000000000..212948b70
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
new file mode 100644
index 000000000..cce69655d
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.png
new file mode 100644
index 000000000..3668c9a00
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.png
new file mode 100644
index 000000000..a1a2c5b68
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_format_list_bulleted_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.png
new file mode 100644
index 000000000..da5398d15
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_forum_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_launcher.png b/core/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 8bd22b54a..000000000
--- a/core/src/main/res/drawable-hdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_poll_box_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_poll_box_grey600_24dp.png
new file mode 100644
index 000000000..2ee172a51
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_poll_box_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_poll_box_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_poll_box_white_24dp.png
new file mode 100644
index 000000000..3fe2256c7
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_poll_box_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_sd_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_sd_grey600_24dp.png
new file mode 100644
index 000000000..2c7c210d3
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_sd_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_sd_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_sd_white_24dp.png
new file mode 100644
index 000000000..77fd16301
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_sd_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-hdpi/ic_stat_antenna_default.png
index fb15f7ee1..af99f4b3b 100644
--- a/core/src/main/res/drawable-hdpi/ic_stat_antenna_default.png
+++ b/core/src/main/res/drawable-hdpi/ic_stat_antenna_default.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_stat_authentication.png b/core/src/main/res/drawable-hdpi/ic_stat_authentication.png
index f40820ef7..398dffa4b 100755..100644
--- a/core/src/main/res/drawable-hdpi/ic_stat_authentication.png
+++ b/core/src/main/res/drawable-hdpi/ic_stat_authentication.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_swap_vertical_grey600_24dp.png b/core/src/main/res/drawable-hdpi/ic_swap_vertical_grey600_24dp.png
new file mode 100644
index 000000000..cd3508d72
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_swap_vertical_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-hdpi/ic_swap_vertical_white_24dp.png b/core/src/main/res/drawable-hdpi/ic_swap_vertical_white_24dp.png
new file mode 100644
index 000000000..b26af6aac
--- /dev/null
+++ b/core/src/main/res/drawable-hdpi/ic_swap_vertical_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-ldpi-v11/ic_stat_antenna_default.png b/core/src/main/res/drawable-ldpi-v11/ic_stat_antenna_default.png
deleted file mode 100644
index ddf545c0b..000000000
--- a/core/src/main/res/drawable-ldpi-v11/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-ldpi/ic_launcher.png b/core/src/main/res/drawable-ldpi/ic_launcher.png
deleted file mode 100644
index 494468020..000000000
--- a/core/src/main/res/drawable-ldpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-ldpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-ldpi/ic_stat_antenna_default.png
index 501dfa848..ddf545c0b 100644
--- a/core/src/main/res/drawable-ldpi/ic_stat_antenna_default.png
+++ b/core/src/main/res/drawable-ldpi/ic_stat_antenna_default.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi-v11/ic_stat_antenna_default.png b/core/src/main/res/drawable-mdpi-v11/ic_stat_antenna_default.png
deleted file mode 100644
index 41fd20655..000000000
--- a/core/src/main/res/drawable-mdpi-v11/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi-v11/ic_stat_authentication.png b/core/src/main/res/drawable-mdpi-v11/ic_stat_authentication.png
deleted file mode 100755
index 550b56b33..000000000
--- a/core/src/main/res/drawable-mdpi-v11/ic_stat_authentication.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.png
new file mode 100755
index 000000000..e87df752e
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_baseline_question_answer_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_bug_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_bug_grey600_24dp.png
new file mode 100644
index 000000000..4b372a4e3
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_bug_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_bug_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_bug_white_24dp.png
new file mode 100644
index 000000000..9d7603552
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_bug_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cellphone_text_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_cellphone_text_grey600_24dp.png
new file mode 100644
index 000000000..c26da2ce2
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_cellphone_text_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_cellphone_text_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_cellphone_text_white_24dp.png
new file mode 100644
index 000000000..8569a642d
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_cellphone_text_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
new file mode 100644
index 000000000..498ddf8eb
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
new file mode 100644
index 000000000..66a6d7681
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.png
new file mode 100644
index 000000000..726eae499
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.png
new file mode 100644
index 000000000..0cc401dff
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_format_list_bulleted_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.png
new file mode 100644
index 000000000..d3bcfe7b6
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_forum_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_launcher.png b/core/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 219e8c5ab..000000000
--- a/core/src/main/res/drawable-mdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_poll_box_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_poll_box_grey600_24dp.png
new file mode 100644
index 000000000..6fefa8b8c
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_poll_box_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_poll_box_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_poll_box_white_24dp.png
new file mode 100644
index 000000000..cf45cde6d
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_poll_box_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_sd_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_sd_grey600_24dp.png
new file mode 100644
index 000000000..81c5f77d9
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_sd_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_sd_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_sd_white_24dp.png
new file mode 100644
index 000000000..a012f237c
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_sd_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-mdpi/ic_stat_antenna_default.png
index 152239888..41fd20655 100644
--- a/core/src/main/res/drawable-mdpi/ic_stat_antenna_default.png
+++ b/core/src/main/res/drawable-mdpi/ic_stat_antenna_default.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_stat_authentication.png b/core/src/main/res/drawable-mdpi/ic_stat_authentication.png
index 7fab11a83..550b56b33 100755..100644
--- a/core/src/main/res/drawable-mdpi/ic_stat_authentication.png
+++ b/core/src/main/res/drawable-mdpi/ic_stat_authentication.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_swap_vertical_grey600_24dp.png b/core/src/main/res/drawable-mdpi/ic_swap_vertical_grey600_24dp.png
new file mode 100644
index 000000000..c2aeb3b5d
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_swap_vertical_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-mdpi/ic_swap_vertical_white_24dp.png b/core/src/main/res/drawable-mdpi/ic_swap_vertical_white_24dp.png
new file mode 100644
index 000000000..ea4faabdf
--- /dev/null
+++ b/core/src/main/res/drawable-mdpi/ic_swap_vertical_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi-v11/ic_stat_antenna_default.png b/core/src/main/res/drawable-xhdpi-v11/ic_stat_antenna_default.png
deleted file mode 100644
index 30431ed6a..000000000
--- a/core/src/main/res/drawable-xhdpi-v11/ic_stat_antenna_default.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi-v11/ic_stat_authentication.png b/core/src/main/res/drawable-xhdpi-v11/ic_stat_authentication.png
deleted file mode 100755
index e83cbc333..000000000
--- a/core/src/main/res/drawable-xhdpi-v11/ic_stat_authentication.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.png
new file mode 100755
index 000000000..731f89c83
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_baseline_question_answer_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_bug_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_bug_grey600_24dp.png
new file mode 100644
index 000000000..49e5deaa9
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_bug_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_bug_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_bug_white_24dp.png
new file mode 100644
index 000000000..7416bde03
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_bug_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cellphone_text_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_cellphone_text_grey600_24dp.png
new file mode 100644
index 000000000..fc8219fa9
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_cellphone_text_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_cellphone_text_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_cellphone_text_white_24dp.png
new file mode 100644
index 000000000..9468fa9a9
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_cellphone_text_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
new file mode 100644
index 000000000..df64c26fe
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
new file mode 100644
index 000000000..9e49cbe0a
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.png
new file mode 100644
index 000000000..322adb6e0
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.png
new file mode 100644
index 000000000..c25860017
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_format_list_bulleted_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.png
new file mode 100644
index 000000000..ac6876140
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_forum_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_launcher.png b/core/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index 2dbfd8874..000000000
--- a/core/src/main/res/drawable-xhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_poll_box_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_poll_box_grey600_24dp.png
new file mode 100644
index 000000000..e09b052b9
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_poll_box_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_poll_box_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_poll_box_white_24dp.png
new file mode 100644
index 000000000..9ed41f906
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_poll_box_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_sd_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_sd_grey600_24dp.png
new file mode 100644
index 000000000..c2bc3fa9f
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_sd_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_sd_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_sd_white_24dp.png
new file mode 100644
index 000000000..76e620405
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_sd_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.png b/core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.png
index 3d8a046a8..30431ed6a 100644
--- a/core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.png
+++ b/core/src/main/res/drawable-xhdpi/ic_stat_antenna_default.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_stat_authentication.png b/core/src/main/res/drawable-xhdpi/ic_stat_authentication.png
index 200b60c96..e83cbc333 100755..100644
--- a/core/src/main/res/drawable-xhdpi/ic_stat_authentication.png
+++ b/core/src/main/res/drawable-xhdpi/ic_stat_authentication.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_swap_vertical_grey600_24dp.png b/core/src/main/res/drawable-xhdpi/ic_swap_vertical_grey600_24dp.png
new file mode 100644
index 000000000..c6139c821
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_swap_vertical_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xhdpi/ic_swap_vertical_white_24dp.png b/core/src/main/res/drawable-xhdpi/ic_swap_vertical_white_24dp.png
new file mode 100644
index 000000000..c4bee7069
--- /dev/null
+++ b/core/src/main/res/drawable-xhdpi/ic_swap_vertical_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.png
new file mode 100755
index 000000000..255b82707
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_baseline_question_answer_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_bug_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_bug_grey600_24dp.png
new file mode 100644
index 000000000..7bbf31c6b
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_bug_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_bug_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_bug_white_24dp.png
new file mode 100644
index 000000000..fe2c2bee3
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_bug_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cellphone_text_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_cellphone_text_grey600_24dp.png
new file mode 100644
index 000000000..82a453f05
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_cellphone_text_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_cellphone_text_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_cellphone_text_white_24dp.png
new file mode 100644
index 000000000..d1f990a65
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_cellphone_text_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
new file mode 100644
index 000000000..951244d68
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
new file mode 100644
index 000000000..3eaedd318
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.png
new file mode 100644
index 000000000..87f8073ea
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.png
new file mode 100644
index 000000000..da3433c61
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_format_list_bulleted_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.png
new file mode 100644
index 000000000..7a3204693
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_forum_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_launcher.png b/core/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 41b261b4f..000000000
--- a/core/src/main/res/drawable-xxhdpi/ic_launcher.png
+++ /dev/null
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_poll_box_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_poll_box_grey600_24dp.png
new file mode 100644
index 000000000..3d24d9670
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_poll_box_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_poll_box_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_poll_box_white_24dp.png
new file mode 100644
index 000000000..3d9f54b6c
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_poll_box_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_sd_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_sd_grey600_24dp.png
new file mode 100644
index 000000000..7e8fa947e
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_sd_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_sd_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_sd_white_24dp.png
new file mode 100644
index 000000000..66c74428e
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_sd_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_swap_vertical_grey600_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_swap_vertical_grey600_24dp.png
new file mode 100644
index 000000000..2d71e15fc
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_swap_vertical_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxhdpi/ic_swap_vertical_white_24dp.png b/core/src/main/res/drawable-xxhdpi/ic_swap_vertical_white_24dp.png
new file mode 100644
index 000000000..24df5b942
--- /dev/null
+++ b/core/src/main/res/drawable-xxhdpi/ic_swap_vertical_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.png b/core/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.png
new file mode 100755
index 000000000..0d697e0f9
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_baseline_question_answer_white_24db.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_bug_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_bug_grey600_24dp.png
new file mode 100644
index 000000000..b612b2aa9
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_bug_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_bug_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_bug_white_24dp.png
new file mode 100644
index 000000000..32a3f5511
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_bug_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_grey600_24dp.png
new file mode 100644
index 000000000..829b15396
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_white_24dp.png
new file mode 100644
index 000000000..baa783a92
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_cellphone_text_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
new file mode 100644
index 000000000..6c4c2b94c
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
new file mode 100644
index 000000000..fd5114044
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_checkbox_multiple_marked_outline_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.png
new file mode 100644
index 000000000..c56590fe0
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.png
new file mode 100644
index 000000000..5deea3286
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_format_list_bulleted_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.png
new file mode 100644
index 000000000..0ae33696b
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_forum_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_poll_box_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_poll_box_grey600_24dp.png
new file mode 100644
index 000000000..c97eba579
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_poll_box_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_poll_box_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_poll_box_white_24dp.png
new file mode 100644
index 000000000..54e169d54
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_poll_box_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_sd_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_sd_grey600_24dp.png
new file mode 100644
index 000000000..744b2c8f4
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_sd_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_sd_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_sd_white_24dp.png
new file mode 100644
index 000000000..1bee910ed
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_sd_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_grey600_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_grey600_24dp.png
new file mode 100644
index 000000000..631ef6d0a
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_grey600_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_white_24dp.png b/core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_white_24dp.png
new file mode 100644
index 000000000..5bf24ac32
--- /dev/null
+++ b/core/src/main/res/drawable-xxxhdpi/ic_swap_vertical_white_24dp.png
Binary files differ
diff --git a/core/src/main/res/drawable/bg_splash.xml b/core/src/main/res/drawable/bg_splash.xml
index dd66e3083..32241ec22 100644
--- a/core/src/main/res/drawable/bg_splash.xml
+++ b/core/src/main/res/drawable/bg_splash.xml
@@ -2,12 +2,12 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
- android:drawable="@color/overlay_dark"/>
+ android:drawable="@color/ic_launcher_background"/>
<item>
<bitmap
android:gravity="center"
- android:src="@drawable/ic_launcher"/>
+ android:src="@mipmap/ic_launcher_foreground"/>
</item>
</layer-list> \ No newline at end of file
diff --git a/core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml b/core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml
new file mode 100644
index 000000000..5f58e8421
--- /dev/null
+++ b/core/src/main/res/drawable/overlay_drawable_dark_trueblack.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+
+ <item>
+ <shape android:shape="rectangle" >
+ <solid android:color="#45B3E1" />
+ </shape>
+ </item>
+ <item android:top="1dp">
+ <shape android:shape="rectangle" >
+ <solid android:color="@color/black" />
+ </shape>
+ </item>
+
+</layer-list> \ No newline at end of file
diff --git a/core/src/main/res/drawable/progress_bar_horizontal_trueblack.xml b/core/src/main/res/drawable/progress_bar_horizontal_trueblack.xml
new file mode 100644
index 000000000..604bb2655
--- /dev/null
+++ b/core/src/main/res/drawable/progress_bar_horizontal_trueblack.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:id="@android:id/background">
+ <shape>
+ <solid android:color="#000000"/>
+ </shape>
+ </item>
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape>
+ <solid android:color="@color/holo_blue_dark"/>
+ </shape>
+ </clip>
+ </item>
+</layer-list>
diff --git a/app/src/main/res/layout/player_widget.xml b/core/src/main/res/layout/player_widget.xml
index d9f442f96..4c98895a0 100644
--- a/app/src/main/res/layout/player_widget.xml
+++ b/core/src/main/res/layout/player_widget.xml
@@ -45,7 +45,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:textColor="?android:attr/textColorSecondary" />
+ android:textColor="@color/white" />
</LinearLayout>
</RelativeLayout>
diff --git a/core/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/core/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/core/src/main/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="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/core/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
new file mode 100644
index 000000000..036d09bc5
--- /dev/null
+++ b/core/src/main/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="@color/ic_launcher_background"/>
+ <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
+</adaptive-icon> \ No newline at end of file
diff --git a/core/src/main/res/mipmap-hdpi/ic_launcher.png b/core/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..12e9b3395
--- /dev/null
+++ b/core/src/main/res/mipmap-hdpi/ic_launcher.png
Binary files differ
diff --git a/core/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/core/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..d687f94bb
--- /dev/null
+++ b/core/src/main/res/mipmap-hdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/main/res/mipmap-hdpi/ic_launcher_round.png b/core/src/main/res/mipmap-hdpi/ic_launcher_round.png
new file mode 100644
index 000000000..12e9b3395
--- /dev/null
+++ b/core/src/main/res/mipmap-hdpi/ic_launcher_round.png
Binary files differ
diff --git a/core/src/main/res/mipmap-mdpi/ic_launcher.png b/core/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..1da13d374
--- /dev/null
+++ b/core/src/main/res/mipmap-mdpi/ic_launcher.png
Binary files differ
diff --git a/core/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/core/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..2e7a4b74d
--- /dev/null
+++ b/core/src/main/res/mipmap-mdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/main/res/mipmap-mdpi/ic_launcher_round.png b/core/src/main/res/mipmap-mdpi/ic_launcher_round.png
new file mode 100644
index 000000000..1da13d374
--- /dev/null
+++ b/core/src/main/res/mipmap-mdpi/ic_launcher_round.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xhdpi/ic_launcher.png b/core/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..32b022ada
--- /dev/null
+++ b/core/src/main/res/mipmap-xhdpi/ic_launcher.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/core/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..5f90a1d11
--- /dev/null
+++ b/core/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/core/src/main/res/mipmap-xhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..32b022ada
--- /dev/null
+++ b/core/src/main/res/mipmap-xhdpi/ic_launcher_round.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xxhdpi/ic_launcher.png b/core/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..321600e15
--- /dev/null
+++ b/core/src/main/res/mipmap-xxhdpi/ic_launcher.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/core/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..d72ffedbb
--- /dev/null
+++ b/core/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/core/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..321600e15
--- /dev/null
+++ b/core/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/core/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..ff2870dca
--- /dev/null
+++ b/core/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/core/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
new file mode 100644
index 000000000..0af16fadf
--- /dev/null
+++ b/core/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png
Binary files differ
diff --git a/core/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/core/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
new file mode 100644
index 000000000..ff2870dca
--- /dev/null
+++ b/core/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
Binary files differ
diff --git a/core/src/main/res/values-ar/strings.xml b/core/src/main/res/values-ar/strings.xml
index 7517e8dae..5c29b5119 100644
--- a/core/src/main/res/values-ar/strings.xml
+++ b/core/src/main/res/values-ar/strings.xml
@@ -1,16 +1,34 @@
<?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">تحديث التسجيل</string>
+ <string name="feeds_label">مغذيات</string>
+ <string name="statistics_label">إحصائيات</string>
+ <string name="add_feed_label">إضافة بودكاست</string>
+ <string name="episodes_label">حلقات</string>
<string name="all_episodes_short_label">الكل</string>
+ <string name="new_episodes_label">جديد</string>
<string name="favorite_episodes_label">المفضلات</string>
<string name="new_label">جديد</string>
- <string name="settings_label">اعدادات</string>
+ <string name="settings_label">إعدادات</string>
<string name="downloads_label">تنزيل</string>
<string name="downloads_running_label">جارى التشغيل</string>
<string name="downloads_completed_label">اكتمل</string>
<string name="downloads_log_label">سجل</string>
+ <string name="subscriptions_label">تسجيلات</string>
+ <string name="subscriptions_list_label">لائحة التسجيلات</string>
<string name="cancel_download_label">الغاء التنزيل</string>
+ <string name="playback_history_label">أرشيف التشغيل</string>
+ <string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_summary">تزامن مع أجهزة أخرى</string>
+ <string name="gpodnet_auth_label">تسجيل الدخول لموقع gpodder</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_mode">نمط الإحصائيات</string>
<!--Main activity-->
<string name="drawer_open">قائمة الفتح</string>
<string name="drawer_close">قائمة الاغلاف</string>
@@ -26,11 +44,13 @@
<string name="copied_url_msg">تم نسخ الرابط للحافظة</string>
<string name="go_to_position_label">اذهب لهذا الموقع</string>
<!--Playback history-->
+ <string name="clear_history_label">مسح الأرشيف</string>
<!--Other-->
<string name="confirm_label">تأكيد</string>
<string name="cancel_label">الغاء</string>
<string name="yes">نعم</string>
<string name="no">لا</string>
+ <string name="reset">إعادة التعيين</string>
<string name="author_label">المؤلف</string>
<string name="language_label">لغة</string>
<string name="url_label">عنوان الموقع</string>
@@ -60,7 +80,6 @@
<string name="show_info_label">اظهار المعلومات</string>
<string name="share_label">مشاركة</string>
<string name="share_link_label">مشاركة الرابط</string>
- <string name="episode_actions">تطبيق الاجراء</string>
<string name="hide_unplayed_episodes_label">لم يتم تشغيله</string>
<string name="hide_paused_episodes_label">ايقاف مؤقت</string>
<string name="hide_downloaded_episodes_label">تم التنزيل</string>
@@ -105,23 +124,73 @@
<!--Auto-Flattr dialog-->
<!--Search-->
<!--OPML import and export-->
+ <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>
+ <string name="export_error_label">حدث خطأ أثناء التصدير</string>
+ <string name="export_success_title">تم التصدير بنجاح</string>
<!--Sleep timer-->
+ <string name="enter_time_here_label">أدخل التوقيت</string>
+ <string name="timer_vibration_label">تشغيل الهزاز</string>
+ <string name="time_seconds">ثواني</string>
+ <string name="time_minutes">دقائق</string>
+ <string name="time_hours">ساعات</string>
<!--gpodder.net-->
+ <string name="gpodnet_taglist_header">الفئات</string>
+ <string name="gpodnet_toplist_header">أقوى البودكاستات</string>
+ <string name="gpodnet_suggestions_header">إقتراحات</string>
+ <string name="gpodnetauth_login_title">تسجيل الدخول</string>
+ <string name="gpodnetauth_login_butLabel">تسجيل الدخول</string>
+ <string name="username_label">إسم المستخدم</string>
+ <string name="password_label">كلمة المرور</string>
+ <string name="gpodnetauth_device_title">اختيار الأجهزة:</string>
+ <string name="gpodnetauth_device_butCreateNewDevice">أدخل جهازا جديدا</string>
+ <string name="gpodnetauth_device_chooseExistingDevice">اختر جهازا من القائمة: </string>
+ <string name="gpodnetauth_device_butChoose">إختر</string>
+ <string name="gpodnetauth_finish_title">لقد تم تسجيل الدخول بنجاح!</string>
+ <string name="gpodnetauth_finish_butgomainscreen">إذهب إلى الصفحة الرئيسية</string>
+ <string name="gpodnetsync_auth_error_descr">خطأ في إسم المستخدم أو كلمة المرور</string>
+ <string name="gpodnetsync_pref_report_successful">تم بنجاح</string>
<!--Directory chooser-->
+ <string name="selected_folder_label">إختيار المستند:</string>
+ <string name="create_folder_label">أنشأ مستندا جديدا</string>
+ <string name="choose_data_directory">إختيار مستند البيانات</string>
<!--Online feed view-->
+ <string name="downloading_label">تحميل ...</string>
<!--Content descriptions for image buttons-->
+ <string name="media_type_audio_label">صوت</string>
+ <string name="media_type_video_label">فيديو</string>
+ <string name="load_next_page_label">تحميل الصفحة التالية</string>
<!--Feed information screen-->
+ <string name="authentication_label">تسجيل الدخول</string>
<!--Progress information-->
<!--AntennaPodSP-->
<!--Episodes apply actions-->
<string name="all_label">الكل</string>
+ <string name="selected_all_label">إختيار كل الحلقات</string>
<string name="unplayed_label">لم يتم تشغيله</string>
<string name="downloaded_label">تم التنزيل</string>
<string name="not_downloaded_label">لم يتم التنزيل</string>
<!--Sort-->
<!--Rating dialog-->
<!--Audio controls-->
+ <string name="volume">مستوى الصوت</string>
<!--proxy settings-->
+ <string name="proxy_type_label">النوع</string>
+ <!--Database import/export-->
+ <string name="label_import">استيراد</string>
+ <string name="label_export">تصدير</string>
+ <string name="export_ok">تم التصدير بنجاح</string>
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
+ <string name="notification_channel_downloading">تحميل</string>
+ <string name="notification_channel_playing">يشغل حاليا</string>
+ <string name="notification_channel_error">الأخطاء</string>
</resources>
diff --git a/core/src/main/res/values-az/strings.xml b/core/src/main/res/values-az/strings.xml
index bde3d8393..92bfb7f44 100644
--- a/core/src/main/res/values-az/strings.xml
+++ b/core/src/main/res/values-az/strings.xml
@@ -165,7 +165,6 @@
<string name="deselect_all_label">Seçimi ləğv et</string>
<string name="opml_export_label">OPML ixraçı</string>
<string name="export_error_label">İxracın xətası</string>
- <string name="opml_export_success_sum">OPML fayl:\u0020 yazılıb</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Yuxu taymerini qoy</string>
<string name="disable_sleeptimer_label">Yuxu taymerini keçir</string>
@@ -197,6 +196,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-b+ast/strings.xml b/core/src/main/res/values-b+ast/strings.xml
index 87ca9ed3e..421896086 100644
--- a/core/src/main/res/values-b+ast/strings.xml
+++ b/core/src/main/res/values-b+ast/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Favoritos</string>
<string name="new_label">Nuevo</string>
<string name="settings_label">Axustes</string>
- <string name="add_new_feed_label">Amestar podcast</string>
<string name="downloads_label">Descargues</string>
<string name="downloads_running_label">N\'execución</string>
<string name="downloads_completed_label">Completóse</string>
@@ -110,7 +109,6 @@
<string name="feed_remover_msg">Desaniciando fees</string>
<string name="load_complete_feed">Completóse\'l refrescu\'l feed</string>
<string name="hide_episodes_title">Anubrir episodios</string>
- <string name="episode_actions">Aplicar aiciones</string>
<string name="hide_unplayed_episodes_label">Ensin reproducir</string>
<string name="hide_paused_episodes_label">Posóse</string>
<string name="hide_played_episodes_label">Reprodúxose</string>
@@ -244,7 +242,6 @@
<!--Preferences-->
<string name="storage_pref">Almacenamientu</string>
<string name="project_pref">Proyeutu</string>
- <string name="services_label">Servicios</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Llimpieza d\'episodios</string>
<string name="pref_pauseOnDisconnect_sum">Posa la reproducción al desconeutase los auriculares o Bluetooth</string>
@@ -376,6 +373,8 @@
<string name="port_label">Puertu</string>
<string name="optional_hint">(Opcional)</string>
<string name="proxy_checking">Comprobando...</string>
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-bg/strings.xml b/core/src/main/res/values-bg/strings.xml
new file mode 100644
index 000000000..19dc2a971
--- /dev/null
+++ b/core/src/main/res/values-bg/strings.xml
@@ -0,0 +1,256 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!--Activitiy and fragment titles-->
+ <string name="feeds_label">Емисии</string>
+ <string name="statistics_label">Статистика</string>
+ <string name="add_feed_label">Добавяне на подкаст</string>
+ <string name="episodes_label">Епизоди</string>
+ <string name="all_episodes_short_label">Всички</string>
+ <string name="favorite_episodes_label">Любими</string>
+ <string name="new_label">Нови</string>
+ <string name="settings_label">Настройки</string>
+ <string name="downloads_label">Изтеглени</string>
+ <string name="downloads_running_label">Текущи</string>
+ <string name="downloads_completed_label">Завършени</string>
+ <string name="downloads_log_label">Дневник</string>
+ <string name="subscriptions_label">Абонаменти</string>
+ <string name="subscriptions_list_label">Списък с абонаменти</string>
+ <string name="cancel_download_label">Отказ\nИзтегляне</string>
+ <string name="playback_history_label">История</string>
+ <string name="gpodnet_main_label">gpodder.net</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>
+ <!--Statistics fragment-->
+ <!--Main activity-->
+ <string name="drawer_feed_counter_new_unplayed">Брой нови и непускани епизоди</string>
+ <string name="drawer_feed_counter_new">Брой нови епизоди</string>
+ <string name="drawer_feed_counter_unplayed">Брой непускани епизоди</string>
+ <string name="drawer_feed_counter_downloaded">Брой изтеглени епизоди</string>
+ <string name="drawer_feed_counter_none">Никакви</string>
+ <!--Webview actions-->
+ <string name="open_in_browser_label">Отвори в браузъра</string>
+ <string name="copy_url_label">Копирай URL адрес</string>
+ <string name="share_url_label">Сподели URL адрес</string>
+ <string name="copied_url_msg">Адресът е копиран</string>
+ <!--Playback history-->
+ <string name="clear_history_label">Изтриване на историята</string>
+ <!--Other-->
+ <string name="confirm_label">Потвърди</string>
+ <string name="cancel_label">Отказ</string>
+ <string name="yes">Да</string>
+ <string name="no">Не</string>
+ <string name="reset">Нулиране</string>
+ <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>
+ <string name="refresh_label">Обнови</string>
+ <string name="external_storage_error_msg">Няма налична външна памет. Уверете се, че външната памет е монтирана, за да може приложението да работи правилно.</string>
+ <string name="chapter_duration">Времетраене: %1$s</string>
+ <string name="description_label">Описание</string>
+ <string name="most_recent_prefix">Най-нов епизод:\u0020</string>
+ <string name="size_prefix">Размер:\u0020</string>
+ <string name="loading_label">Зареждане…</string>
+ <string name="feed_auto_download_always">Винаги</string>
+ <string name="feed_auto_download_never">Никога</string>
+ <string name="episode_cleanup_never">Никога</string>
+ <!--'Add Feed' Activity labels-->
+ <string name="feedurl_label">URL адрес на емисията</string>
+ <string name="etxtFeedurlHint">www.primer.com/emisiq</string>
+ <string name="txtvfeedurl_label">Добавяне на подкаст по URL адрес</string>
+ <string name="podcastdirectories_label">Намиране на подкаст в директория</string>
+ <string name="podcastdirectories_descr">Можете да търсите нови подкасти в iTunes или fyyd, или да прегледате gpodder.net според име, категория и популярност.</string>
+ <!--Actions on feeds-->
+ <string name="mark_all_read_label">Маркирай всички като слушани</string>
+ <string name="mark_all_read_msg">Всички епизоди са маркирани като слушани</string>
+ <string name="mark_all_read_confirmation_msg">Моля, потвърдете, че искате да маркирате всички епизоди като слушани.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Моля, потвърдете, че искате да маркирате всички епизоди в тази емисия като слушани.</string>
+ <string name="mark_all_seen_label">Маркирай всички като прегледани</string>
+ <string name="mark_all_seen_msg">Всички епизоди са маркирани като прегледани</string>
+ <string name="mark_all_seen_confirmation_msg">Моля, потвърдете, че искате да маркирате всички епизоди като прегледани.</string>
+ <string name="show_info_label">Покажи информация</string>
+ <string name="rename_feed_label">Преименуване на подкаст</string>
+ <string name="remove_feed_label">Премахване на подкаст</string>
+ <string name="share_label">Споделяне...</string>
+ <string name="share_link_label">Сподели връзка</string>
+ <string name="share_file_label">Сподели файл</string>
+ <string name="share_link_with_position_label">Сподели връзка с позиция</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_remover_msg">Премахване на емисията</string>
+ <string name="hide_downloaded_episodes_label">Изтеглени</string>
+ <string name="hide_not_downloaded_episodes_label">Неизтеглени</string>
+ <!--actions on feeditems-->
+ <string name="stream_label">Стрийм</string>
+ <string name="marked_as_seen_label">Маркиран като прегледан</string>
+ <string name="mark_read_label">Маркирай като слушан</string>
+ <string name="marked_as_read_label">Маркиран като слушан</string>
+ <string name="mark_unread_label">Маркирай като неслушан</string>
+ <string name="add_to_queue_label">Добави в опашката</string>
+ <string name="added_to_queue_label">Добавен в опашката</string>
+ <string name="remove_from_queue_label">Премахни от опашката</string>
+ <!--Download messages and labels-->
+ <string name="download_type_feed">Емисия</string>
+ <string name="authentication_notification_title">Необходимо удостоверяване</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Разреши временно</string>
+ <!--Mediaplayer messages-->
+ <string name="player_error_msg">Грешка!</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="clear_queue_label">Изчистване на опашката</string>
+ <string name="clear_queue_confirmation_msg">Моля, потвърдете, че искате да изчистите ВСИЧКИ епизоди в опашката</string>
+ <!--Flattr-->
+ <!--Flattr-->
+ <!--Variable Speed-->
+ <!--Empty list labels-->
+ <!--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="flattr_label">Flattr</string>
+ <string name="pref_episode_cleanup_title">Почистване на епизодите</string>
+ <string name="pref_episode_cleanup_summary">Епизодите, които не са в опашката и не са в любими, отговарят на условията за премахване, ако автоматичното изтегляне се нуждае от място за нови епизоди</string>
+ <string name="pref_pauseOnDisconnect_sum">Пауза на възпроизвеждането, когато слушалките или Bluetooth прекъснат връзка</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">Възстановане на възпроизвеждането, когато слушалките се свържат отново</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Възстановане на възпроизвеждането, когато Bluetooth се свърже отново</string>
+ <string name="pref_followQueue_sum">Преминаване към следващия епизод в опашката след завършване на възпроизвеждането</string>
+ <string name="pref_auto_delete_sum">Изтриване на епизода, когато възпроизвеждането завърши</string>
+ <string name="pref_auto_delete_title">Автоматично изтриване</string>
+ <string name="pref_smart_mark_as_played_sum">Маркиране на епизодите като слушани, дори когато остават определени секунди от времето за възпроизвеждане</string>
+ <string name="playback_pref">Възпроизвеждане</string>
+ <string name="network_pref">Мрежа</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Актуализиране през интервал или час на деня</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Задайте интервал или конкретно време от деня, за да опресните автоматично емисиите</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Можете да зададете <i>интервал</i> като \"на всеки 2 часа\", да зададете конкретно <i>време от деня</i> като \"7:00 ч.\", или да изберете <i>деактивиране</i> на всички автоматични актуализации.\n\n<small>Моля, обърнете внимание: Времето за актуализиране е неточно. Може да срещнете кратко забавяне.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Деактивиране</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Задай интервал</string>
+ <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_mobileUpdate_title">Мобилни актуализации</string>
+ <string name="pref_mobileUpdate_sum">Разрешаване на актуализации чрез мобилната връзка за данни</string>
+ <string name="flattr_settings_label">Flattr настройки</string>
+ <string name="pref_flattr_auth_title">Flattr вход</string>
+ <string name="user_interface_label">Потребителски интерфейс</string>
+ <string name="pref_set_theme_title">Избор на тема</string>
+ <string name="pref_nav_drawer_feed_order_title">Задайте ред на абонаментите</string>
+ <string name="pref_nav_drawer_feed_order_sum">Променете реда на абонаментите си</string>
+ <string name="pref_nav_drawer_feed_counter_title">Задайте брояча на абонаментите</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Променете информацията, показвана от брояча на абонаментите</string>
+ <string name="pref_set_theme_sum">Променете външния вид на AntennaPod.</string>
+ <string name="pref_automatic_download_title">Автоматично изтегляне</string>
+ <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>
+ <string name="pref_episode_cache_title">Кеш на епизодите</string>
+ <string name="pref_theme_title_light">Светла</string>
+ <string name="pref_theme_title_dark">Тъмна</string>
+ <string name="pref_episode_cache_unlimited">Неограничен</string>
+ <string name="pref_update_interval_hours_plural">часа</string>
+ <string name="pref_update_interval_hours_singular">час</string>
+ <string name="pref_update_interval_hours_manual">Ръчно</string>
+ <string name="pref_gpodnet_authenticate_title">Вход</string>
+ <string name="pref_gpodnet_authenticate_sum">Влезте с профила си в gpodder.net, за да синхронизирате абонаментите си.</string>
+ <string name="pref_gpodnet_logout_title">Изход</string>
+ <string name="pref_gpodnet_logout_toast">Успешен изход</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_current_value">Текуща стойност: %1$s</string>
+ <string name="pref_proxy_title">Прокси</string>
+ <string name="pref_faq">Често задавани въпроси</string>
+ <string name="pref_known_issues">Известни проблеми</string>
+ <!--Auto-Flattr dialog-->
+ <!--Search-->
+ <string name="search_status_no_results">Няма намерени резултати</string>
+ <string name="search_label">Търсене</string>
+ <string name="no_results_for_query">Няма намерени резултати за \"%1$s\"</string>
+ <!--OPML import and export-->
+ <string name="opml_import_option">Опция %1$d</string>
+ <string name="opml_import_label">OPML импортиране</string>
+ <string name="opml_directory_error">ГРЕШКА!</string>
+ <string name="opml_import_error_no_file">Не е избран файл!</string>
+ <string name="select_all_label">Избери всички</string>
+ <string name="opml_export_label">OPML експортиране</string>
+ <string name="html_export_label">HTML експортиране</string>
+ <string name="exporting_label">Експортиране...</string>
+ <string name="export_error_label">Грешка при експортиране</string>
+ <string name="opml_import_ask_read_permission">Необходим е достъп до външната памет за прочитане на OPML файла</string>
+ <!--Sleep timer-->
+ <string name="timer_vibration_label">Вибрация</string>
+ <string name="time_seconds">секунди</string>
+ <string name="time_minutes">минути</string>
+ <string name="time_hours">часа</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">1 секунда</item>
+ <item quantity="other">%d секунди</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">1 минута</item>
+ <item quantity="other">%d минути</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">1 час</item>
+ <item quantity="other">%d часа</item>
+ </plurals>
+ <!--gpodder.net-->
+ <string name="gpodnet_taglist_header">КАТЕГОРИИ</string>
+ <string name="gpodnet_toplist_header">ТОП ПОДКАСТИ</string>
+ <string name="gpodnet_suggestions_header">ПРЕДЛОЖЕНИЯ</string>
+ <string name="gpodnet_search_hint">Търсене в gpodder.net</string>
+ <string name="gpodnetauth_login_title">Вход</string>
+ <string name="gpodnetauth_login_butLabel">Вход</string>
+ <string name="username_label">Потребителско име</string>
+ <string name="password_label">Парола</string>
+ <!--Directory chooser-->
+ <string name="choose_data_directory">Изберете папка с данни</string>
+ <string name="choose_data_directory_permission_rationale">Необходим е достъп до външната памет за промяна на папката с данни</string>
+ <!--Online feed view-->
+ <!--Content descriptions for image buttons-->
+ <!--Feed information screen-->
+ <!--Progress information-->
+ <!--AntennaPodSP-->
+ <string name="filter">Филтър</string>
+ <string name="search_fyyd_label">Търсене във fyyd</string>
+ <!--Episodes apply actions-->
+ <!--Sort-->
+ <!--Rating dialog-->
+ <!--Audio controls-->
+ <!--proxy settings-->
+ <string name="proxy_type_label">Тип</string>
+ <string name="host_label">Хост</string>
+ <string name="port_label">Порт</string>
+ <string name="proxy_test_label">Тест</string>
+ <string name="proxy_test_successful">Тестът е успешен</string>
+ <string name="proxy_test_failed">Тестът е неуспешен</string>
+ <string name="proxy_port_invalid_error">Портът не е валиден</string>
+ <!--Database import/export-->
+ <!--Casting-->
+ <!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
+</resources>
diff --git a/core/src/main/res/values-ca-rES/strings.xml b/core/src/main/res/values-ca-rES/strings.xml
index 4b0bf218c..d9f216268 100644
--- a/core/src/main/res/values-ca-rES/strings.xml
+++ b/core/src/main/res/values-ca-rES/strings.xml
@@ -8,7 +8,6 @@
<string name="favorite_episodes_label">Preferits</string>
<string name="new_label">Nous</string>
<string name="settings_label">Ajustaments</string>
- <string name="add_new_feed_label">Afegir Podcast</string>
<string name="downloads_label">Descàrregues</string>
<string name="downloads_running_label">Actius</string>
<string name="downloads_completed_label">Finalitzats</string>
@@ -56,6 +55,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-ca/strings.xml b/core/src/main/res/values-ca/strings.xml
index 349dde72c..dfd89ca0b 100644
--- a/core/src/main/res/values-ca/strings.xml
+++ b/core/src/main/res/values-ca/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Favorits</string>
<string name="new_label">Nous</string>
<string name="settings_label">Configuració</string>
- <string name="add_new_feed_label">Afegeix podcast</string>
<string name="downloads_label">Baixades</string>
<string name="downloads_running_label">En execució</string>
<string name="downloads_completed_label">Completat</string>
@@ -39,8 +38,8 @@
<string name="drawer_feed_order_last_update">Ordre per data de publicació</string>
<string name="drawer_feed_order_most_played">Ordre per número d\'episodis reproduïts</string>
<string name="drawer_feed_counter_new_unplayed">Número d\'episodis nous i per reproduir</string>
- <string name="drawer_feed_counter_new">Número de episodis nous</string>
- <string name="drawer_feed_counter_unplayed">Número de episodis per reproduir</string>
+ <string name="drawer_feed_counter_new">Número d\'episodis nous</string>
+ <string name="drawer_feed_counter_unplayed">Número d\'episodis per reproduir</string>
<string name="drawer_feed_counter_downloaded">Número d\'episodis descarregats</string>
<string name="drawer_feed_counter_none">Cap</string>
<!--Webview actions-->
@@ -115,6 +114,7 @@
<string name="remove_feed_label">Esborra podcast</string>
<string name="share_label">Compartir...</string>
<string name="share_link_label">Comparteix l\'enllaç</string>
+ <string name="share_file_label">Comparteix un fitxer</string>
<string name="share_link_with_position_label">Comparteix enllaç amb posició</string>
<string name="share_feed_url_label">Comparteix adreça del canal</string>
<string name="share_item_url_label">Comparteix adreça del fitxer del canal</string>
@@ -123,7 +123,6 @@
<string name="feed_remover_msg">S\'està esborrant el canal</string>
<string name="load_complete_feed">S\'ha actualitzat el canal</string>
<string name="hide_episodes_title">Amaga Episodis</string>
- <string name="episode_actions">Aplica accions</string>
<string name="hide_unplayed_episodes_label">Per reproduir</string>
<string name="hide_paused_episodes_label">Pausat</string>
<string name="hide_played_episodes_label">Reproduit</string>
@@ -143,6 +142,7 @@
<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="remove_episode_lable">Esborra episodi</string>
<string name="marked_as_seen_label">Marcat com a vist</string>
<string name="mark_read_label">Marca com a llegit</string>
@@ -280,7 +280,6 @@
<string name="other_pref">Altres</string>
<string name="about_pref">Quant a</string>
<string name="queue_label">Cua</string>
- <string name="services_label">Serveis</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Neteja l\'episodi</string>
<string name="pref_episode_cleanup_summary">Els episodis que no es troben a la cua i no són favorits haurien de ser candidats a ser esborrats si l\'Auto Descàrrega necessita espai per a nous episodis</string>
@@ -298,6 +297,8 @@
<string name="pref_smart_mark_as_played_title">Marca intel·ligent com a reproduït</string>
<string name="pref_skip_keeps_episodes_sum">Mantenir episodis quan són passats de llarg</string>
<string name="pref_skip_keeps_episodes_title">Mantenir els episodis passats de llarg</string>
+ <string name="pref_favorite_keeps_episodes_sum">Conserva els episodis marcats com a favorits.</string>
+ <string name="pref_favorite_keeps_episodes_title">Conserva els episodis favorits.</string>
<string name="playback_pref">Reproducció</string>
<string name="network_pref">Xarxa</string>
<string name="pref_autoUpdateIntervallOrTime_title">Actualitza interval o horari del dia</string>
@@ -341,6 +342,8 @@
<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>
@@ -397,8 +400,6 @@
<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_sonic_title">Reproductor Sonic Media Player</string>
- <string name="pref_sonic_message">Fes servir el reproductor Sonic Media Player integrat en comptes del reproductor natiu d\'Android i Prestissimo</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>
@@ -408,7 +409,7 @@
<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>
<string name="pref_cast_message_free_flavor">Chromecast requereix de llibreries propietàries de terceres parts que estan inhabilitades en aquesta versió d\'AntennaPod</string>
- <string name="pref_enqueue_downloaded_title">Posa a la cua els descarregats</string>
+ <string name="pref_enqueue_downloaded_title">Afegeix les baixades a la cua</string>
<string name="pref_enqueue_downloaded_summary">Afegeix els episodis descarregats a la cua</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Activa la compartició automàtica per Flattr</string>
@@ -446,8 +447,6 @@
<string name="html_export_label">Exporta HTML</string>
<string name="exporting_label">Exportant...</string>
<string name="export_error_label">Error d\'exportació</string>
- <string name="opml_export_success_title">S\'ha exportat l\'OPML correctament.</string>
- <string name="opml_export_success_sum">El fitxer OPML s\'ha escrit a:\u0020</string>
<string name="opml_import_ask_read_permission">Per llegir arxius OPML és necessari accés a la memòria externa</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Defineix un temporitzador</string>
@@ -614,6 +613,13 @@
<string name="proxy_host_empty_error">El host no pot estar buit</string>
<string name="proxy_host_invalid_error">El host no és una adreça IP o domini vàlid</string>
<string name="proxy_port_invalid_error">El port no és vàlid</string>
+ <!--Database import/export-->
+ <string name="import_export">Importa/exporta la base de dades</string>
+ <string name="import_export_warning">Podeu fer servir aquesta funcionalitat experimental per a transferir les vostres subscripcions i episodis reproduïts a un altre dispositiu.\n\nNomés podreu importar la base de dades fent servir la mateixa versió de l\'AntennaPod. En cas contrari, aquesta funcionalitat es comportarà de forma imprevisible.\n\nDesprés d\'importar-los, els episodis es poden mostrar com a baixats tot i no estar disponibles. Premeu el botó per a reproduir-los per a que l\'AntennaPod ho detecti.</string>
+ <string name="label_import">Importa</string>
+ <string name="label_export">Exporta</string>
+ <string name="import_select_file">Tria un fitxer per a importar</string>
+ <string name="import_ok">S\'ha importat amb èxit.\n\nPremeu D\'acord per a reiniciar l\'AntennaPod.</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Reproduir a...</string>
<string name="cast_disconnect_label">Desconnectar la sessió de difusió </string>
@@ -630,4 +636,5 @@
<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>
<string name="cast_failed_media_error_skipping">Error en la reproducció. Saltant...</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-cs-rCZ/strings.xml b/core/src/main/res/values-cs-rCZ/strings.xml
index 9eb2ccb7f..1896381a1 100644
--- a/core/src/main/res/values-cs-rCZ/strings.xml
+++ b/core/src/main/res/values-cs-rCZ/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Oblíbené</string>
<string name="new_label">Nový</string>
<string name="settings_label">Nastavení</string>
- <string name="add_new_feed_label">Přidat podcast</string>
<string name="downloads_label">Stahování</string>
<string name="downloads_running_label">Právě běží</string>
<string name="downloads_completed_label">Dokončeno</string>
@@ -89,6 +88,7 @@
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 den po dokončení</item>
<item quantity="few">%d dny po dokončení</item>
+ <item quantity="many">%d dnů po dokončení</item>
<item quantity="other">%d dnů po dokončení</item>
</plurals>
<!--'Add Feed' Activity labels-->
@@ -118,7 +118,6 @@
<string name="feed_remover_msg">Odstranit kanál</string>
<string name="load_complete_feed">Obnovit kompletní kanál</string>
<string name="hide_episodes_title">Skrýt epizody</string>
- <string name="episode_actions">Provést akce</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>
@@ -186,6 +185,7 @@
<plurals name="downloads_left">
<item quantity="one">%d čekající na stažení</item>
<item quantity="few">%d čekající na stažení</item>
+ <item quantity="many">%d čekajících na stažení</item>
<item quantity="other">%d čekajících na stažení</item>
</plurals>
<string name="downloads_processing">Probíhá stahování</string>
@@ -276,7 +276,6 @@
<string name="other_pref">Ostatní</string>
<string name="about_pref">O aplikaci</string>
<string name="queue_label">Fronta</string>
- <string name="services_label">Služby</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Vyčistit epizody</string>
<string name="pref_episode_cleanup_summary">Epizody, které nejsou ve frontě a nejsou označeny za oblíbené by mělo být možné smazat, pokud bude funkce automatického stahování potřebovat místo pro nové epizody</string>
@@ -387,7 +386,6 @@
<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_sonic_message">Použít připojený sonic media player jako náhradu za výchozí přehrávač médií pro Android a Prestissimo</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>
@@ -431,8 +429,6 @@
<string name="html_export_label">HTML export</string>
<string name="exporting_label">Export</string>
<string name="export_error_label">Chyba exportu</string>
- <string name="opml_export_success_title">OPML export byl úspěšný.</string>
- <string name="opml_export_success_sum">OPML soubor byl zapsán do:\u0020</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>
@@ -450,16 +446,19 @@
<plurals name="time_seconds_quantified">
<item quantity="one">1 sekunda</item>
<item quantity="few">%d sekundy</item>
+ <item quantity="many">%d sekund</item>
<item quantity="other">%d sekund</item>
</plurals>
<plurals name="time_minutes_quantified">
<item quantity="one">1 minuta</item>
<item quantity="few">%d minuty</item>
+ <item quantity="many">%d minut</item>
<item quantity="other">%d minut</item>
</plurals>
<plurals name="time_hours_quantified">
<item quantity="one">1 hodina</item>
<item quantity="few">%d hodiny</item>
+ <item quantity="many">%d hodin</item>
<item quantity="other">%d hodin</item>
</plurals>
<string name="auto_enable_label">Automaticky zapnout</string>
@@ -602,6 +601,7 @@
<string name="proxy_host_empty_error">Host nesmí být prázdný</string>
<string name="proxy_host_invalid_error">Host není platná IP nebo doména</string>
<string name="proxy_port_invalid_error">Neplatný port</string>
+ <!--Database import/export-->
<!--Casting-->
<string name="cast_media_route_menu_title">Přehrát na...</string>
<string name="cast_disconnect_label">Odpojit sezení vysílání</string>
@@ -618,4 +618,5 @@
<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-->
</resources>
diff --git a/core/src/main/res/values-da/strings.xml b/core/src/main/res/values-da/strings.xml
index f1eb85f7a..9035054f1 100644
--- a/core/src/main/res/values-da/strings.xml
+++ b/core/src/main/res/values-da/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Foretrukne</string>
<string name="new_label">Ny(e)</string>
<string name="settings_label">Indstillinger</string>
- <string name="add_new_feed_label">Tilføj podcast</string>
<string name="downloads_label">Overførsler</string>
<string name="downloads_running_label">I gang</string>
<string name="downloads_completed_label">Færdige</string>
@@ -22,7 +21,7 @@
<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">Grænsen for størrelsen på udsendelsesmellemlageret er nået. Du kan øge størrelsen på mellemlageret i Indstillinger.</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>
<!--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>
@@ -115,6 +114,7 @@
<string name="remove_feed_label">Fjern podcast</string>
<string name="share_label">Del…</string>
<string name="share_link_label">Del link</string>
+ <string name="share_file_label">Del fil</string>
<string name="share_link_with_position_label">Del link med position</string>
<string name="share_feed_url_label">Del webadresse for feedet</string>
<string name="share_item_url_label">Del webadresse for udsendelsen</string>
@@ -123,7 +123,6 @@
<string name="feed_remover_msg">Fjerner feed</string>
<string name="load_complete_feed">Opdater hele feedet</string>
<string name="hide_episodes_title">Skjul udsendelser</string>
- <string name="episode_actions">Anvend handlinger</string>
<string name="hide_unplayed_episodes_label">Uafspillede</string>
<string name="hide_paused_episodes_label">Sat på pause</string>
<string name="hide_played_episodes_label">Afspillede</string>
@@ -143,6 +142,7 @@
<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="remove_episode_lable">Fjern udsendelse</string>
<string name="marked_as_seen_label">Markeret som set</string>
<string name="mark_read_label">Marker som læst</string>
@@ -280,7 +280,6 @@
<string name="other_pref">Andre</string>
<string name="about_pref">Om</string>
<string name="queue_label">Kø</string>
- <string name="services_label">Tjenester</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Oprydning i udsendelser</string>
<string name="pref_episode_cleanup_summary">Tillad at udsendelser, som ikke er i køen og som ikke er markeret som foretrukne, kan fjernes, hvis Automatisk overførsel har brug for plads til nye udsendelser</string>
@@ -298,6 +297,8 @@
<string name="pref_smart_mark_as_played_title">Smart markering af afspillet</string>
<string name="pref_skip_keeps_episodes_sum">Behold udsendelser når de bliver sprunget over</string>
<string name="pref_skip_keeps_episodes_title">Behold oversprungne udsendelser</string>
+ <string name="pref_favorite_keeps_episodes_sum">Behold udsendelser, som er markeret som foretrukne</string>
+ <string name="pref_favorite_keeps_episodes_title">Behold foretrukne udsendelser</string>
<string name="playback_pref">Afspilning</string>
<string name="network_pref">Netværk</string>
<string name="pref_autoUpdateIntervallOrTime_title">Opdateringsinterval eller -klokkeslæt</string>
@@ -341,6 +342,8 @@
<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>
@@ -397,8 +400,6 @@
<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_sonic_title">Sonic-medieafspiller</string>
- <string name="pref_sonic_message">Brug indbygget Sonic-medieafspiller i stedet for Androids indbyggede medieafspiller og Prestissimo</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>
@@ -446,8 +447,6 @@
<string name="html_export_label">HTML-eksport</string>
<string name="exporting_label">Eksporterer…</string>
<string name="export_error_label">Eksportfejl</string>
- <string name="opml_export_success_title">OPML-eksport lykkedes.</string>
- <string name="opml_export_success_sum">.opml-filen blev skrevet til:\u0020</string>
<string name="opml_import_ask_read_permission">Adgang til eksternt lager er påkrævet for at læse OPML-filen</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Indstil søvntimer</string>
@@ -615,6 +614,7 @@
<string name="proxy_host_empty_error">Vært kan ikke være tom</string>
<string name="proxy_host_invalid_error">Vært er ikke en gyldig IP-adresse eller et gyldigt domæne</string>
<string name="proxy_port_invalid_error">Port ikke gyldig</string>
+ <!--Database import/export-->
<!--Casting-->
<string name="cast_media_route_menu_title">Afspil på …</string>
<string name="cast_disconnect_label">Afbryd cast-sessionen</string>
@@ -631,4 +631,5 @@
<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>
<string name="cast_failed_media_error_skipping">Fejl ved afspilning af medie. Springer over…</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-de/strings.xml b/core/src/main/res/values-de/strings.xml
index c7da72961..b8f4ffbab 100644
--- a/core/src/main/res/values-de/strings.xml
+++ b/core/src/main/res/values-de/strings.xml
@@ -1,15 +1,16 @@
<?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">Abonnements aktualisieren</string>
<string name="feeds_label">Feeds</string>
<string name="statistics_label">Statistiken</string>
<string name="add_feed_label">Podcast hinzufügen</string>
<string name="episodes_label">Episoden</string>
<string name="all_episodes_short_label">Alle</string>
+ <string name="new_episodes_label">Neu</string>
<string name="favorite_episodes_label">Favoriten</string>
<string name="new_label">Neu</string>
<string name="settings_label">Einstellungen</string>
- <string name="add_new_feed_label">Podcast hinzufügen</string>
<string name="downloads_label">Downloads</string>
<string name="downloads_running_label">Aktiv</string>
<string name="downloads_completed_label">Beendet</string>
@@ -19,10 +20,12 @@
<string name="cancel_download_label">Download abbrechen</string>
<string name="playback_history_label">Zuletzt gespielt</string>
<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>
@@ -57,15 +60,16 @@
<string name="yes">Ja</string>
<string name="no">Nein</string>
<string name="reset">Reset</string>
- <string name="author_label">Autor</string>
+ <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>
+ <string name="needs_storage_permission">Für diese Funktion wird die Speicher-Berechtigung benötigt</string>
<string name="refresh_label">Aktualisieren</string>
- <string name="external_storage_error_msg">Der externe Speicher ist nicht verfügbar. Bitte stelle sicher, dass das externe Speichermedium eingelegt ist, damit die Anwendung funktioniert.</string>
+ <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>
@@ -111,19 +115,22 @@
<string name="mark_all_seen_msg">Alle Episoden als gesehen markiert</string>
<string name="mark_all_seen_confirmation_msg">Bitte bestätige, dass alle Episoden als gesehen markiert werden sollen.</string>
<string name="show_info_label">Informationen anzeigen</string>
- <string name="rename_feed_label">Podcast umbenennen</string>
+ <string name="show_feed_settings_label">Zeige Feed-Einstellungen</string>
+ <string name="feed_info_label">Feed-Informationen</string>
+ <string name="feed_settings_label">Feed-Einstellungen</string>
+ <string name="rename_feed_label">Feed umbenennen</string>
<string name="remove_feed_label">Podcast entfernen</string>
<string name="share_label">Teilen…</string>
- <string name="share_link_label">Teile Link</string>
+ <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_feed_url_label">Teile URL des Podcasts</string>
- <string name="share_item_url_label">Teile URL der Episode</string>
- <string name="share_item_url_with_position_label">Teile URL der Episode mit Zeitmarke</string>
- <string name="feed_delete_confirmation_msg">Bitte bestätige, dass du den Feed \"%1$s\" und ALLE heruntergeladenen Episoden löschen möchtest.</string>
- <string name="feed_remover_msg">Entferne Feed</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="load_complete_feed">Kompletten Feed aktualisieren</string>
<string name="hide_episodes_title">Episoden verbergen</string>
- <string name="episode_actions">Aktionen anwenden</string>
+ <string name="batch_edit">Stapelbearbeitung</string>
<string name="hide_unplayed_episodes_label">Ungespielt</string>
<string name="hide_paused_episodes_label">Pausiert</string>
<string name="hide_played_episodes_label">Gespielt</string>
@@ -132,6 +139,7 @@
<string name="hide_downloaded_episodes_label">Heruntergeladen</string>
<string name="hide_not_downloaded_episodes_label">Nicht heruntergeladen</string>
<string name="hide_has_media_label">Hat Medien</string>
+ <string name="hide_is_favorite_label">Favorit</string>
<string name="filtered_label">Gefiltert</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Aktualisierung fehlgeschlagen</string>
<string name="open_podcast">Podcast öffnen</string>
@@ -143,7 +151,9 @@
<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="remove_episode_lable">Episode entfernen</string>
+ <string name="mark_as_seen_label">Als gelesen markieren</string>
<string name="marked_as_seen_label">Als gesehen markiert</string>
<string name="mark_read_label">Als gespielt markieren</string>
<string name="marked_as_read_label">Als gespielt markiert</string>
@@ -167,6 +177,8 @@
<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>
@@ -217,6 +229,7 @@
<string name="playback_error_unknown">Unbekannter Fehler</string>
<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-->
@@ -233,7 +246,9 @@
<string name="date">Datum</string>
<string name="duration">Dauer</string>
<string name="episode_title">Episodentitel</string>
- <string name="feed_title">Podcastname</string>
+ <string name="feed_title">Feedname</string>
+ <string name="random">Zufällig</string>
+ <string name="smart_shuffle">Schlaues Mischen</string>
<string name="ascending">Aufsteigend</string>
<string name="descending">Absteigend</string>
<string name="clear_queue_confirmation_msg">Bitte bestätige, dass ALLE Episoden aus der Abspielliste entfernt werden sollen</string>
@@ -280,8 +295,17 @@
<string name="other_pref">Anderes</string>
<string name="about_pref">Über</string>
<string name="queue_label">Abspielliste</string>
- <string name="services_label">Dienste</string>
+ <string name="integrations_label">Einbindungen</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Micropayment-Dienst</string>
+ <string name="automation">Automatisierung</string>
+ <string name="download_pref_details">Details</string>
+ <string name="import_export_pref">Import/Export</string>
+ <string name="appearance">Erscheinungsbild</string>
+ <string name="external_elements">Externe Elemente</string>
+ <string name="interruptions">Unterbrechungen</string>
+ <string name="buttons">Buttons zur Steuerung der Wiedergabe</string>
+ <string name="media_player">Medienabspieler</string>
<string name="pref_episode_cleanup_title">Automatisches Löschen</string>
<string name="pref_episode_cleanup_summary">Episoden, die weder in der Abspielliste noch Favoriten sind, können gelöscht werden, wenn beim automatischen Herunterladen Speicherplatz für neue Episoden gebraucht wird</string>
<string name="pref_pauseOnDisconnect_sum">Wiedergabe pausieren, wenn Kopfhörer ausgesteckt oder Bluetooth getrennt wird</string>
@@ -295,9 +319,11 @@
<string name="pref_auto_delete_sum">Episode löschen, wenn die Wiedergabe endet</string>
<string name="pref_auto_delete_title">Automatisches Löschen</string>
<string name="pref_smart_mark_as_played_sum">Episoden werden bereits als gespielt markiert, wenn weniger als eine bestimmte Anzahl Sekunden Restspielzeit übrig sind</string>
- <string name="pref_smart_mark_as_played_title">Schlau als gespielt markieren</string>
+ <string name="pref_smart_mark_as_played_title">Schlaues \"als gespielt markieren\"</string>
<string name="pref_skip_keeps_episodes_sum">Behalte Episoden beim Überspringen</string>
<string name="pref_skip_keeps_episodes_title">Behalte übersprungene Episoden</string>
+ <string name="pref_favorite_keeps_episodes_sum">Lösche Episoden nicht, wenn sie als Favorit markiert wurden.</string>
+ <string name="pref_favorite_keeps_episodes_title">Favorisierte Episoden nicht löschen</string>
<string name="playback_pref">Wiedergabe</string>
<string name="network_pref">Netzwerk</string>
<string name="pref_autoUpdateIntervallOrTime_title">Aktualisierungsintervall oder -tageszeit</string>
@@ -335,12 +361,13 @@
<string name="pref_nav_drawer_feed_order_title">Reihenfolge der Abonnements einstellen</string>
<string name="pref_nav_drawer_feed_order_sum">Ändere die Reihenfolge deiner Abonnements</string>
<string name="pref_nav_drawer_feed_counter_title">Abonnement-Zähler einstellen</string>
- <string name="pref_nav_drawer_feed_counter_sum">Ändere, welche Information der Abonnement-Zähler anzeigt</string>
<string name="pref_set_theme_sum">Ändere das Aussehen von AntennaPod.</string>
<string name="pref_automatic_download_title">Automatisches Herunterladen</string>
<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="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>
@@ -397,8 +424,7 @@
<string name="crash_report_sum">Sende den aktuellen Absturzbericht per E-Mail</string>
<string name="send_email">E-Mail senden</string>
<string name="experimental_pref">Experimentell</string>
- <string name="pref_sonic_title">Sonic Media Player</string>
- <string name="pref_sonic_message">Benutze den integrierten Sonic Mediaplayer als Ersatz für Androids eigenen Mediaplayer und Prestissimo</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>
@@ -408,8 +434,13 @@
<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>
<string name="pref_cast_message_free_flavor">Chromecast benötigt proprietäre Bibliotheken von Drittanbietern, die in dieser Version von AntennaPod deaktiviert sind</string>
- <string name="pref_enqueue_downloaded_title">Downloads zur Abspielliste hinzufügen</string>
+ <string name="pref_enqueue_downloaded_title">Downloads einreihen</string>
<string name="pref_enqueue_downloaded_summary">Füge heruntergeladene Episoden zur Abspielliste hinzu</string>
+ <string name="media_player_builtin">Androids eingebauter Abspieler</string>
+ <string name="pref_videoBehavior_title">Beim Beenden des Videos</string>
+ <string name="pref_videoBehavior_sum">Verhalten beim Verlassen der Video-Wiedergabe</string>
+ <string name="stop_playback">Wiedergabe anhalten</string>
+ <string name="continue_playback">Audiowiedergabe fortsetzen</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Automatisches Flattrn aktivieren</string>
<string name="auto_flattr_after_percent">Flattr eine Episode, sobald %d Prozent gespielt worden sind</string>
@@ -419,8 +450,6 @@
<string name="search_hint">Suche nach Episoden</string>
<string name="found_in_shownotes_label">In Shownotizen gefunden</string>
<string name="found_in_chapters_label">In Kapiteln gefunden</string>
- <string name="found_in_authors_label">In Autoren gefunden</string>
- <string name="found_in_feeds_label">In Feeds gefunden</string>
<string name="search_status_no_results">Keine Ergebnisse gefunden</string>
<string name="search_label">Suchen</string>
<string name="found_in_title_label">In Titel gefunden</string>
@@ -446,8 +475,8 @@
<string name="html_export_label">HTML Export</string>
<string name="exporting_label">Exportiere…</string>
<string name="export_error_label">Exportfehler</string>
- <string name="opml_export_success_title">OPML Export erfolgreich</string>
- <string name="opml_export_success_sum">Die OPML Datei wurde unter dem folgenden Pfad gespeichert:\u0020</string>
+ <string name="export_success_title">Export erfolgreich</string>
+ <string name="export_success_sum">Die exportierte Datei wurde geschrieben nach:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Zugriff auf externen Speicher wird benötigt, um die OPML Datei zu lesen</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Timer einstellen</string>
@@ -614,6 +643,14 @@
<string name="proxy_host_empty_error">Host darf nicht leer sein</string>
<string name="proxy_host_invalid_error">Host ist keine gültige IP oder Domain</string>
<string name="proxy_port_invalid_error">Port ungültig</string>
+ <!--Database import/export-->
+ <string name="import_export">Datenbankimport/-export</string>
+ <string name="import_export_warning">Diese experimentelle Funktion kann dazu benutzt werden, deine Abonnements und abgespielten Episoden auf ein anderes Gerät zu übertragen.\n\nExportierte Datenbanken können nur mit der gleichen AntennaPod-Version wieder importiert werden, andernfalls kann dies zu unerwartetem Verhalten führen.\n\nNach dem Importieren können Episoden als heruntergeladen angezeigt werden, obwohl sie dies nicht sind. Einfach den Abspielknopf der Episoden drücken, damit AntennaPod das erkennt.</string>
+ <string name="label_import">Import</string>
+ <string name="label_export">Export</string>
+ <string name="import_select_file">Zu importierende Datei auswählen</string>
+ <string name="export_ok">Export erfolgreich.</string>
+ <string name="import_ok">Import erfolgreich.\n\nBitte OK drücken, um AntennaPod neuzustarten</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Abspielen auf...</string>
<string name="cast_disconnect_label">Chromecast-Sitzung trennen</string>
@@ -630,4 +667,13 @@
<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>
<string name="cast_failed_media_error_skipping">Fehler bei Wiedergabe. Überspringe...</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Handlung notwendig</string>
+ <string name="notification_channel_user_action_description">Wird gezeigt, wenn deine Handlung notwendig ist, zum Beispiel wenn du ein Passwort eingeben musst.</string>
+ <string name="notification_channel_downloading">Lädt herunter</string>
+ <string name="notification_channel_downloading_description">Wird gezeigt beim Herunterladen.</string>
+ <string name="notification_channel_playing">Jetzt spielt</string>
+ <string name="notification_channel_playing_description">Erlaubt es, die Wiedergabe zu steuern. Dies ist die Hauptbenachrichtigung, die du siehst, während ein Podcast abgespielt wird.</string>
+ <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>
</resources>
diff --git a/core/src/main/res/values-el/strings.xml b/core/src/main/res/values-el/strings.xml
index 9768efa59..3438c9acc 100644
--- a/core/src/main/res/values-el/strings.xml
+++ b/core/src/main/res/values-el/strings.xml
@@ -1,17 +1,18 @@
<?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">Ανανέωση Συνδρομών</string>
<string name="feeds_label">Ροές</string>
<string name="statistics_label">Στατιστικά</string>
<string name="add_feed_label">Προσθήκη Podcast</string>
<string name="episodes_label">Επεισόδια</string>
<string name="all_episodes_short_label">Όλα</string>
+ <string name="new_episodes_label">Νέα</string>
<string name="favorite_episodes_label">Αγαπημένα</string>
<string name="new_label">Νέα</string>
<string name="settings_label">Ρυθμίσεις</string>
- <string name="add_new_feed_label">Προσθήκη Podcast</string>
<string name="downloads_label">Λήψεις</string>
- <string name="downloads_running_label">Εκτέλεση</string>
+ <string name="downloads_running_label">Εκτέλείται</string>
<string name="downloads_completed_label">Ολοκληρώθηκε</string>
<string name="downloads_log_label">Είσοδος</string>
<string name="subscriptions_label">Συνδρομές</string>
@@ -19,43 +20,59 @@
<string name="cancel_download_label">Ακύρωση\nΛήψης</string>
<string name="playback_history_label">Ιστορικό Αναπαραγωγής</string>
<string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_summary">Συγχρονισμός με άλλες συσκευές</string>
<string name="gpodnet_auth_label">gpodder.net Σύνδεση</string>
+ <string name="synchronizing">Εκτελείται συγχρονισμός</string>
<!--Statistics fragment-->
+ <string name="total_time_listened_to_podcasts">Συνολικός χρόνος εκτελεσμένων podcasts:</string>
<!--Main activity-->
<string name="drawer_open">Άνοιγμα μενού</string>
<string name="drawer_close">Κλείσιμο μενού</string>
<string name="drawer_feed_order_alphabetical">Ταξινόμηση αλφαβητικά</string>
<string name="drawer_feed_order_last_update">Ταξινόμηση κατά ημερομηνία δημοσίευσης</string>
+ <string name="drawer_feed_order_most_played">Ταξινόμηση κατά αριθμό εκτελεσμένων επεισοδίων</string>
+ <string name="drawer_feed_counter_new_unplayed">Αριθμός νέων επεισοδίων και αριθμός μη εκτελεσμένων επεισοδίων</string>
<string name="drawer_feed_counter_new">Αριθμός νέων επεισοδίων</string>
+ <string name="drawer_feed_counter_unplayed">Αριθμός μη εκτελεσμένων επεισοδίων</string>
+ <string name="drawer_feed_counter_downloaded">Αριθμός ληφθέντων επεισοδίων</string>
+ <string name="drawer_feed_counter_none">Κενό</string>
<!--Webview actions-->
<string name="open_in_browser_label">Άνοιγμα στον Περιηγητή</string>
<string name="copy_url_label">Αντιγραφή διεύθυνσης URL</string>
- <string name="share_url_label">Μοιρασμα URL</string>
+ <string name="share_url_label">Μοίρασμα URL</string>
<string name="copied_url_msg">Αντιγραφή URL στο Πρόχειρο</string>
+ <string name="go_to_position_label">Μετάβαση σε αυτή τη θέση</string>
<!--Playback history-->
<string name="clear_history_label">Εκκαθάριση Ιστορικού</string>
<!--Other-->
<string name="confirm_label">Επιβεβαίωση</string>
<string name="cancel_label">Ακύρωση</string>
- <string name="author_label">Δημιουργος</string>
- <string name="language_label">Γλωσσα</string>
- <string name="podcast_settings_label">Ρυθμισεις</string>
- <string name="cover_label">Εικονα</string>
- <string name="error_label">Σφαλμα</string>
+ <string name="yes">Ναι</string>
+ <string name="no">Όχι</string>
+ <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>
<string name="refresh_label">Ανανέωση</string>
<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="episodes_suffix">\u0020επεισοδια</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="save_username_password_label">Αποθήκευση του όνοματος χρήστη και του κωδικόυ πρόσβασης</string>
+ <string name="save_username_password_label">Αποθήκευση του ονόματος χρήστη και του κωδικού πρόσβασης</string>
<string name="close_label">Κλείσιμο</string>
- <string name="retry_label">Επανάληψη</string>
+ <string name="retry_label">Επαναπροσπάθεια</string>
<string name="auto_download_label">Συμπερίληψη στην αυτόματη λήψη</string>
+ <string name="auto_download_apply_to_items_title">Εφαρμογή σε προηγούμενα επεισόδια</string>
+ <string name="auto_delete_label">Αυτόματη διαγραφή επεισοδίου</string>
<string name="parallel_downloads_suffix">\u0020παράλληλες λήψεις</string>
<string name="feed_auto_download_always">Πάντα</string>
<string name="feed_auto_download_never">Ποτέ</string>
@@ -64,9 +81,13 @@
<string name="feedurl_label">URL της Ροής</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Προσθήκη Podcast με τη διεύθυνση URL</string>
+ <string name="podcastdirectories_label">Εύρεση Podcast στο Φάκελο</string>
<string name="browse_gpoddernet_label">Περιήγηση στο gpodder.net</string>
<!--Actions on feeds-->
<string name="show_info_label">Εμφάνιση πληροφοριών</string>
+ <string name="feed_info_label">Πληροφορίες Ροής</string>
+ <string name="feed_settings_label">Ρυθμίσεις Ροής</string>
+ <string name="rename_feed_label">Μετονομασία podcast</string>
<string name="remove_feed_label">Κατάργηση Podcast</string>
<string name="hide_episodes_title">Απόκρυψη Επεισοδίων</string>
<string name="hide_downloaded_episodes_label">Ειλημμένα</string>
@@ -181,7 +202,6 @@
<string name="other_pref">Άλλα</string>
<string name="about_pref">Σχετικά με</string>
<string name="queue_label">Σειρά αναμονής</string>
- <string name="services_label">Υπηρεσίες</string>
<string name="flattr_label">Flattr</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Συνέχιση της αναπαραγωγής, όταν τα ακουστικά επανασυνδέονται</string>
<string name="pref_followQueue_sum">Μετάβαση στο επόμενο στοιχείο σειράς αναμονής όταν η αναπαραγωγή ολοκληρωθεί</string>
@@ -256,8 +276,6 @@
<string name="choose_file_from_external_application">Χρησιμοποιήστε εξωτερική εφαρμογή</string>
<string name="opml_export_label">OPML εξαγωγή</string>
<string name="export_error_label">Σφάλμα κατά την εξαγωγή</string>
- <string name="opml_export_success_title">Η Εξαγωγή OPML είναι επιτυχής</string>
- <string name="opml_export_success_sum">Το αρχείο .opml συντάχθηκε για:\u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Ρύθμιση του χρονοδιακόπτη ύπνου</string>
<string name="disable_sleeptimer_label">Απενεργοποίηση χρονοδιακόπτη ύπνου</string>
@@ -339,6 +357,10 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
+ <string name="notification_channel_downloading">Λήψη</string>
+ <string name="notification_channel_error">Σφάλματα</string>
</resources>
diff --git a/core/src/main/res/values-es-rES/strings.xml b/core/src/main/res/values-es-rES/strings.xml
index f183c8028..024989498 100644
--- a/core/src/main/res/values-es-rES/strings.xml
+++ b/core/src/main/res/values-es-rES/strings.xml
@@ -8,7 +8,6 @@
<string name="favorite_episodes_label">Favoritos</string>
<string name="new_label">Nuevos</string>
<string name="settings_label">Ajustes</string>
- <string name="add_new_feed_label">Añadir podcast</string>
<string name="downloads_label">Descargas</string>
<string name="cancel_download_label">Cancelar descarga</string>
<string name="playback_history_label">Historial de reproducción</string>
@@ -194,7 +193,6 @@
<string name="deselect_all_label">Deseleccionar todo</string>
<string name="opml_export_label">Exportar a OPML</string>
<string name="export_error_label">Error en la exportación</string>
- <string name="opml_export_success_sum">El archivo OPML se ha escrito en:\u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Establecer un temporizador</string>
<string name="disable_sleeptimer_label">Desactivar el temporizador</string>
@@ -227,6 +225,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-es/strings.xml b/core/src/main/res/values-es/strings.xml
index eb2343f28..7a421de59 100644
--- a/core/src/main/res/values-es/strings.xml
+++ b/core/src/main/res/values-es/strings.xml
@@ -1,15 +1,16 @@
<?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">Actualizar suscripciones</string>
<string name="feeds_label">Canales</string>
<string name="statistics_label">Estadísticas</string>
<string name="add_feed_label">Añadir podcast</string>
<string name="episodes_label">Episodios</string>
<string name="all_episodes_short_label">Todos</string>
+ <string name="new_episodes_label">Nuevo</string>
<string name="favorite_episodes_label">Favoritos</string>
<string name="new_label">Nuevos</string>
<string name="settings_label">Ajustes</string>
- <string name="add_new_feed_label">Añadir podcast</string>
<string name="downloads_label">Descargas</string>
<string name="downloads_running_label">En curso</string>
<string name="downloads_completed_label">Completadas</string>
@@ -19,15 +20,19 @@
<string name="cancel_download_label">Cancelar\ndescarga</string>
<string name="playback_history_label">Historial de reproducciones</string>
<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">Caché de episodios completa</string>
<string name="episode_cache_full_message">Se ha alcanzado el límite de caché de episodios. Puedes aumentar el tamaño de la caché en las Opciones.</string>
+ <string name="synchronizing">Sincronizando...</string>
<!--Statistics fragment-->
<string name="total_time_listened_to_podcasts">Tiempo total reproducido:</string>
<string name="statistics_details_dialog">Empezados %1$d episodios de %2$d.\n\nReproducidos %3$s de %4$s.</string>
<string name="statistics_mode">Modo de estadísticas</string>
+ <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">Resumir todos los podcasts marcados como reproducidos</string>
+ <string name="statistics_speed_not_counted">Aviso: La velocidad de reproducción nunca se tiene en cuenta.</string>
<!--Main activity-->
<string name="drawer_open">Abrir menú</string>
<string name="drawer_close">Cerrar menú</string>
@@ -109,18 +114,23 @@
<string name="mark_all_seen_msg">Marcar todos los episodios como vistos</string>
<string name="mark_all_seen_confirmation_msg">Por favor confirma que quieres marcar todos los episodios como vistos.</string>
<string name="show_info_label">Información del programa</string>
+ <string name="show_feed_settings_label">Mostrar configuraciones del feed</string>
+ <string name="feed_info_label">Informaciones del feed</string>
+ <string name="feed_settings_label">Configuraciones del feed</string>
<string name="rename_feed_label">Renombrar Podcast</string>
<string name="remove_feed_label">Eliminar podcast</string>
<string name="share_label">Compartir…</string>
<string name="share_link_label">Compartir el enlace de la web</string>
+ <string name="share_file_label">Compartir archivo</string>
<string name="share_link_with_position_label">Compartir enlace con posición</string>
<string name="share_feed_url_label">Compartir URL del canal</string>
<string name="share_item_url_label">Compartir URL del archivo del episodio</string>
<string name="share_item_url_with_position_label">Compartir URL del episodio con posición</string>
+ <string name="feed_delete_confirmation_msg">Por favor, confirma que quieres borrar el feed \"%1$s\" y TODOS los episodios descargados de dicho feed.</string>
<string name="feed_remover_msg">Quitando el canal</string>
<string name="load_complete_feed">Actualizar el canal completo</string>
<string name="hide_episodes_title">Ocultar episodios</string>
- <string name="episode_actions">Aplicar acciones</string>
+ <string name="batch_edit">Edición por lotes</string>
<string name="hide_unplayed_episodes_label">No escuchados</string>
<string name="hide_paused_episodes_label">Pausados</string>
<string name="hide_played_episodes_label">Escuchados</string>
@@ -140,6 +150,7 @@
<string name="stream_label">Transmitir</string>
<string name="remove_label">Quitar</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="remove_episode_lable">Quitar episodio</string>
<string name="marked_as_seen_label">Marcar como visto</string>
<string name="mark_read_label">Marcar como escuchado</string>
@@ -164,6 +175,8 @@
<string name="download_failed">fallido</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>
@@ -214,6 +227,7 @@
<string name="playback_error_unknown">Error desconocido</string>
<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 podcast</string>
<string name="unknown_media_key">AntennaPod - Tecla multimedia desconocida: %1$d</string>
<!--Queue operations-->
@@ -231,6 +245,8 @@
<string name="duration">Duración</string>
<string name="episode_title">Título del episodio</string>
<string name="feed_title">Título del feed</string>
+ <string name="random">Aleatorio</string>
+ <string name="smart_shuffle">Aleatorio inteligente</string>
<string name="ascending">Ascendente</string>
<string name="descending">Descendente</string>
<string name="clear_queue_confirmation_msg">Confirme que quiere borrar TODOS los episodios de la cola</string>
@@ -277,13 +293,23 @@
<string name="other_pref">Otros</string>
<string name="about_pref">Acerca de</string>
<string name="queue_label">Cola</string>
- <string name="services_label">Servicios</string>
+ <string name="integrations_label">Integraciones</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Servicio de micropagos</string>
+ <string name="automation">Automatización</string>
+ <string name="download_pref_details">Detalles</string>
+ <string name="import_export_pref">Importar/Exportar</string>
+ <string name="appearance">Apariencia</string>
+ <string name="external_elements">Elementos externos</string>
+ <string name="interruptions">Interrupciones</string>
+ <string name="buttons">Botones</string>
+ <string name="media_player">Reproductor multimedia</string>
<string name="pref_episode_cleanup_title">Limpieza de episodios</string>
<string name="pref_episode_cleanup_summary">Los episodios que no estén en la cola ni en Favoritos pueden eliminarse si Descarga automática necesita espacio para episodios nuevos</string>
<string name="pref_pauseOnDisconnect_sum">Pausar la reproducción al desconectar los auriculares o el bluetooth</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Reanudar reproducción cuando se reconecten los auriculares</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Reanudar reproducción cuando se reconecte el bluetooth</string>
+ <string name="pref_hardwareForwardButtonSkips_title">Saltar episodio con botón avance</string>
<string name="pref_hardwareForwardButtonSkips_sum">Al pulsar el botón físico de avanzar se saltará al siguiente episodio en lugar de sólo avanzar</string>
<string name="pref_hardwarePreviousButtonRestarts_title">Botón anterior reinicia</string>
<string name="pref_hardwarePreviousButtonRestarts_sum">Al pulsar el botón físico de retroceder se comenzará el episodio de nuevo, en lugar de rebobinar</string>
@@ -291,8 +317,11 @@
<string name="pref_auto_delete_sum">Borrar episodio cuando finalice la reproducción</string>
<string name="pref_auto_delete_title">Eliminar automáticamente</string>
<string name="pref_smart_mark_as_played_sum">Marcar episodios como escuchados incluso si todavía quedan unos segundos por escuchar</string>
+ <string name="pref_smart_mark_as_played_title">Marcar como terminado inteligente</string>
<string name="pref_skip_keeps_episodes_sum">Conservar episodios al saltarlos</string>
<string name="pref_skip_keeps_episodes_title">Conservar episodios saltados</string>
+ <string name="pref_favorite_keeps_episodes_sum">Conservar los episodios cuando se marcan como favoritos</string>
+ <string name="pref_favorite_keeps_episodes_title">Conservar los episodios favoritos</string>
<string name="playback_pref">Reproducción</string>
<string name="network_pref">Red</string>
<string name="pref_autoUpdateIntervallOrTime_title">Intervalo de actualización u hora del día</string>
@@ -336,12 +365,15 @@
<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 para las redes WiFi marcadas.</string>
+ <string name="pref_autodl_allow_on_mobile_title">Descargar bajo conexión móvil</string>
+ <string name="pref_autodl_allow_on_mobile_sum">Permite descarga automática sobre la red de Internet del móvil.</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 está cargando</string>
<string name="pref_parallel_downloads_title">Descargas paralelas</string>
<string name="pref_episode_cache_title">Caché de episodios</string>
<string name="pref_theme_title_light">Claro</string>
<string name="pref_theme_title_dark">Oscuro</string>
+ <string name="pref_theme_title_trueblack">Negro total</string>
<string name="pref_episode_cache_unlimited">Ilimitado</string>
<string name="pref_update_interval_hours_plural">horas</string>
<string name="pref_update_interval_hours_singular">hora</string>
@@ -364,6 +396,10 @@
<string name="pref_gpodnet_notifications_sum">Este ajuste no afecta a 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 para la reproducción de audio a velocidad variable</string>
+ <string name="pref_fast_forward">Intervalo de avance</string>
+ <string name="pref_fast_forward_sum">Personaliza el número de segundos a avanzar cuando se pulsa el botón de avance rápido</string>
+ <string name="pref_rewind">Intervalo de retroceso</string>
+ <string name="pref_rewind_sum">Personaliza el número de segundos a retroceder cuando se pulsa el botón de retroceder</string>
<string name="pref_gpodnet_sethostname_title">Definir nombre de equipo</string>
<string name="pref_gpodnet_sethostname_use_default_host">Usar nombre de equipo por defecto</string>
<string name="pref_expandNotify_title">Expandir Notificación</string>
@@ -388,8 +424,7 @@
<string name="crash_report_sum">Enviar el último informe de fallo por e-mail</string>
<string name="send_email">Enviar e-mail</string>
<string name="experimental_pref">Experimental</string>
- <string name="pref_sonic_title">Sonic media player</string>
- <string name="pref_sonic_message">Usar el reproductor Sonic Media incorporado en lugar del reproductor multimedia de Android y Prestissimo</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>
@@ -399,8 +434,13 @@
<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>
<string name="pref_cast_message_free_flavor">Chromecast requiere librerías propietarias de terceros que están deshabilitadas en esta versión de AntennaPod</string>
- <string name="pref_enqueue_downloaded_title">Descargados en cola</string>
+ <string name="pref_enqueue_downloaded_title">Poner descargados en cola</string>
<string name="pref_enqueue_downloaded_summary">Agregar episodios descargados a la cola</string>
+ <string name="media_player_builtin">Reproductor Android integrado</string>
+ <string name="pref_videoBehavior_title">Funcionamiento del video</string>
+ <string name="pref_videoBehavior_sum">Funcionamiento al dejar la reproducción de video</string>
+ <string name="stop_playback">Parar reproducción</string>
+ <string name="continue_playback">Continuar reproducción</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Habilitar Flattr automático</string>
<string name="auto_flattr_after_percent">Hacer Flattr del episodio en cuanto se haya reproducido el %d por ciento</string>
@@ -411,6 +451,7 @@
<string name="found_in_shownotes_label">Encontrado en las notas del show</string>
<string name="found_in_chapters_label">Encontrado en los capítulos</string>
<string name="found_in_authors_label">Encontrado en los autores</string>
+ <string name="found_in_feeds_label">Encontrado en los feeds</string>
<string name="search_status_no_results">No se han encontrado resultados</string>
<string name="search_label">Buscar</string>
<string name="found_in_title_label">Encontrado en el título</string>
@@ -436,8 +477,8 @@
<string name="html_export_label">Exportar a HTML</string>
<string name="exporting_label">Exportando…</string>
<string name="export_error_label">Error en la exportación</string>
- <string name="opml_export_success_title">Exportación a OPML exitosa</string>
- <string name="opml_export_success_sum">El archivo OPML se ha escrito en:\u0020</string>
+ <string name="export_success_title">Exportación exitosa</string>
+ <string name="export_success_sum">El archivo exportado fué grabado en::\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Es necesario el acceso al almacenamiento externo para leer archivos OPML</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Establecer un temporizador</string>
@@ -604,6 +645,14 @@
<string name="proxy_host_empty_error">El host no puede estar en blanco</string>
<string name="proxy_host_invalid_error">El host no es una IP ni un host válido</string>
<string name="proxy_port_invalid_error">Puerto inválido</string>
+ <!--Database import/export-->
+ <string name="import_export">Importar/Exportar base de datos</string>
+ <string name="import_export_warning">Esta función experimental se puede usar para transferir tus suscripciones y episodios reproducidos a otro dispositivo.\n\nLas base de datos exportadas solo se pueden importar cuando se usa la misma versión de AntennaPod. En otro caso, esta función podría provocar comportamiento inesperado.\n\nDespués de importar, los episodios podrían mostrarse como descargados cuando no lo están. Reproduce estos episodios para que AntennaPod lo detecte.</string>
+ <string name="label_import">Importar</string>
+ <string name="label_export">Exportar</string>
+ <string name="import_select_file">Seleccionar firchero a importar</string>
+ <string name="export_ok">Exportación exitosa.</string>
+ <string name="import_ok">Importación exitosa.\n\nPor favor, pulse OK para reiniciar AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Reproducir en...</string>
<string name="cast_disconnect_label">Desconectar la sesión Cast</string>
@@ -620,4 +669,13 @@
<string name="cast_failed_seek">Fallo al cambiar de posición en el dispositivo Cast</string>
<string name="cast_failed_receiver_player_error">El reproductor ha encontrado un error grave</string>
<string name="cast_failed_media_error_skipping">Error reproduciendo medio. Saltando...</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Acción necesaria</string>
+ <string name="notification_channel_user_action_description">Se muestra si su acción es necesaria, por ejemplo, si necesita ingresar una contraseña.</string>
+ <string name="notification_channel_downloading">Descargando</string>
+ <string name="notification_channel_downloading_description">Se muestra mientras se está descargando.</string>
+ <string name="notification_channel_playing">Reproducción actual</string>
+ <string name="notification_channel_playing_description">Permite controlar la reproducción. Esta es la notificación principal que ves al reproducir un podcast.</string>
+ <string name="notification_channel_error">Errores</string>
+ <string name="notification_channel_error_description">Se muestra si algo salió mal, por ejemplo, si falla la descarga o la sincronización del gpodder.</string>
</resources>
diff --git a/core/src/main/res/values-et/strings.xml b/core/src/main/res/values-et/strings.xml
index 629fd4654..912e4376a 100644
--- a/core/src/main/res/values-et/strings.xml
+++ b/core/src/main/res/values-et/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Lemmikud</string>
<string name="new_label">Uus</string>
<string name="settings_label">Seaded</string>
- <string name="add_new_feed_label">Lisa taskuhääling</string>
<string name="downloads_label">Allalaadimised</string>
<string name="downloads_running_label">Käimas</string>
<string name="downloads_completed_label">Lõpetatud</string>
@@ -100,12 +99,12 @@
<string name="remove_feed_label">Eemalda taskuhääling</string>
<string name="share_label">Jaga...</string>
<string name="share_link_label">Jaga linki</string>
+ <string name="share_file_label">Jaga faili</string>
<string name="share_link_with_position_label">Jaga linki koos asukohaga</string>
<string name="share_feed_url_label">Jaga uudisvoo URL-i</string>
<string name="feed_remover_msg">Uudisvoo eemaldamine</string>
<string name="load_complete_feed">Värskenda kogu uudisvoogu</string>
<string name="hide_episodes_title">Peida saated</string>
- <string name="episode_actions">Rakenda tegevused</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>
@@ -148,6 +147,7 @@
<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_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>
@@ -234,7 +234,6 @@
<string name="other_pref">Muud</string>
<string name="about_pref">Info</string>
<string name="queue_label">Järjekord</string>
- <string name="services_label">Teenused</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Saadete kustutamien</string>
<string name="pref_followQueue_sum">Kui saade lõpeb, siis esita kohe järgmine järjekorras olev saade.</string>
@@ -242,6 +241,7 @@
<string name="pref_auto_delete_title">Automaatne kustutamine</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_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>
@@ -343,8 +343,6 @@
<string name="html_export_label">HTML eksport</string>
<string name="exporting_label">Eksportimine...</string>
<string name="export_error_label">Viga eksportimisel</string>
- <string name="opml_export_success_title">OPML eksport oli edukas.</string>
- <string name="opml_export_success_sum">.opml fail kirjutati kausta:\u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Määra unetaimer</string>
<string name="disable_sleeptimer_label">Keela unetaimer</string>
@@ -369,6 +367,9 @@
<item quantity="one">1 tund</item>
<item quantity="other">%d tundi</item>
</plurals>
+ <string name="auto_enable_label">Automaatne kustutamine</string>
+ <string name="sleep_timer_enabled_label">Unetaimer on sisse lülitatud</string>
+ <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>
@@ -386,6 +387,7 @@
<string name="gpodnetauth_device_chooseExistingDevice">Vali olemasolev seade:</string>
<string name="gpodnetauth_device_errorEmpty">Seadme ID ei tohi olla tühi</string>
<string name="gpodnetauth_device_errorAlreadyUsed">Seadme ID on juba kasutuses</string>
+ <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_butsyncnow">Alusta kohe sünkroonimist</string>
@@ -491,8 +493,17 @@
<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_port_invalid_error">Port pole korrektne</string>
+ <!--Database import/export-->
+ <string name="import_export">Andmebaasi importimine/eksportimine</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>
<!--Casting-->
<string name="cast_media_route_menu_title">Esita...</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>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-fa/strings.xml b/core/src/main/res/values-fa/strings.xml
index 28dfeb6e8..2e4f6d7aa 100644
--- a/core/src/main/res/values-fa/strings.xml
+++ b/core/src/main/res/values-fa/strings.xml
@@ -1,15 +1,180 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
+ <string name="feeds_label">خوراک</string>
+ <string name="statistics_label">آمار</string>
+ <string name="add_feed_label">اضافه کردن پادکست</string>
+ <string name="episodes_label">قسمت ها</string>
+ <string name="all_episodes_short_label">همه</string>
+ <string name="favorite_episodes_label">علاقه مندی ها</string>
+ <string name="new_label">جدید</string>
+ <string name="settings_label">تنظیمات</string>
+ <string name="downloads_label">دانلودها</string>
+ <string name="downloads_running_label">در حال اجرا</string>
+ <string name="downloads_completed_label">تکمیل شده</string>
+ <string name="downloads_log_label">log</string>
+ <string name="subscriptions_label">اشتراک ها</string>
+ <string name="subscriptions_list_label">لیست اشتراک</string>
+ <string name="cancel_download_label">بارگیری n\ لغو</string>
+ <string name="playback_history_label">تاریخچه پخش</string>
+ <string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_auth_label">gpodder.net Login</string>
+ <string name="free_space_label">%1$s free </string>
+ <string name="episode_cache_full_title">ظرفیت حافظه پنهان تکمیل شده است</string>
+ <string name="episode_cache_full_message">حد مجاز تکمیل شده است . شما می توانید اندازه حافظه پنهان را در تنظیمات افزایش دهید.</string>
<!--Statistics fragment-->
+ <string name="total_time_listened_to_podcasts">مجموع زمان پخش پادکست ها:</string>
+ <string name="statistics_mode">حالت آمار</string>
+ <string name="statistics_mode_normal">مدت زمان واقعی پخش را محاسبه کنید. دو بار پخش کردن دو بار شمارش می شود، در حالیکه علامت گذاری شده به عنوان پخش شده محاسبه نمی شود.</string>
+ <string name="statistics_mode_count_all">مجموع پادکست هایی که به عنوان پخش شده مشخص شده است را جمع کنید</string>
+ <string name="statistics_speed_not_counted">توجه: سرعت پخش هرگز به حساب نمی آید.</string>
<!--Main activity-->
+ <string name="drawer_open">بازکردن منو</string>
+ <string name="drawer_close">بستن منو</string>
+ <string name="drawer_preferences">تنظیمات بیشتر</string>
+ <string name="drawer_feed_order_unplayed_episodes">مرتب سازی بر اساس شمارنده</string>
+ <string name="drawer_feed_order_alphabetical">مرتب سازی بر اساس حروف الفبا</string>
+ <string name="drawer_feed_order_last_update">مرتب سازی بر اساس تاریخ انتشار</string>
+ <string name="drawer_feed_order_most_played">مرتب سازی بر اساس تعداد قسمت پخش شده</string>
+ <string name="drawer_feed_counter_new_unplayed">تعداد قسمت های جدید و پخش نشده</string>
+ <string name="drawer_feed_counter_new">تعداد قسمت های جدید</string>
+ <string name="drawer_feed_counter_unplayed">تعداد قسمت های پخش نشده</string>
+ <string name="drawer_feed_counter_downloaded">تعداد قسمت های دانلود شده</string>
+ <string name="drawer_feed_counter_none">هیچ یک</string>
<!--Webview actions-->
+ <string name="open_in_browser_label">باز کردن در مرور گر</string>
+ <string name="copy_url_label">کپی کردن URL </string>
+ <string name="share_url_label">اشتراک گذاری URL </string>
+ <string name="copied_url_msg">URL در کلیپ بورد کپی شد.</string>
+ <string name="go_to_position_label">برو به این موقعیت</string>
<!--Playback history-->
+ <string name="clear_history_label">پاک کردن تاریخچه</string>
<!--Other-->
+ <string name="confirm_label">تایید</string>
+ <string name="cancel_label">لغو</string>
+ <string name="yes">بله</string>
+ <string name="no">خیر</string>
+ <string name="reset">بازنشانی</string>
+ <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>
+ <string name="refresh_label">تازه کردن</string>
+ <string name="external_storage_error_msg">هیچ فضای ذخیره سازی خارجی موجود نیست لطفا مطمئن شوید که کارت حافظه به درستی نصب شده است تا برنامه بتواند به درستی کار کند.</string>
+ <string name="chapters_label">فصل ها</string>
+ <string name="chapter_duration">مدت زمان:%1$s</string>
+ <string name="shownotes_label">Shownotes</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>
+ <string name="auto_download_label">شامل در دریافت خودکار است</string>
+ <string name="auto_download_apply_to_items_title">اعمال به قسمت های قبلی</string>
+ <string name="auto_download_apply_to_items_message">تنظیمات جدید<i>دانلود خودکار</i> به طور اتوماتیک به قسمت های جدید اعمال خواهد شد. \ n آیا شما همچنین می خواهید آن را به قسمت هایی که قبلا منتشر شده اعمال کنید؟</string>
+ <string name="auto_delete_label">حذف خودکار قسمت ها </string>
+ <string name="parallel_downloads_suffix">\u0020دانلود همزمان</string>
+ <string name="feed_auto_download_global">پیش فرض جهانی</string>
+ <string name="feed_auto_download_always">همیشه</string>
+ <string name="feed_auto_download_never">Never</string>
+ <string name="send_label">ارسال...</string>
+ <string name="episode_cleanup_never">هرگز</string>
+ <string name="episode_cleanup_queue_removal">وقتی که در صف نیست</string>
+ <string name="episode_cleanup_after_listening">بعد از تمام شدن</string>
+ <plurals name="episode_cleanup_days_after_listening">
+ <item quantity="one">%dروز بعد از اتمام</item>
+ <item quantity="other">%dروز بعد از اتمام</item>
+ </plurals>
<!--'Add Feed' Activity labels-->
+ <string name="feedurl_label">Feed 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">Browse gpodder.net</string>
<!--Actions on feeds-->
+ <string name="mark_all_read_label">علامت گذاری همه به عنوان پخش شده</string>
+ <string name="mark_all_read_msg">همه قسمت ها به عنوان پخش شده علامت گذاری شد.</string>
+ <string name="mark_all_read_confirmation_msg">لطفا تأیید کنید که میخواهید تمام قسمتها را بعنوان پخش شده علامت بزنید.</string>
+ <string name="mark_all_read_feed_confirmation_msg">لطفا تأیید کنید که میخواهید تمام قسمتهای این خوراک را بعنوان پخش شده علامت بزنید.</string>
+ <string name="mark_all_seen_label">علامت گذاری همه به عنوان دیده شده</string>
+ <string name="mark_all_seen_msg">همه قسمت ها بعنوان دیده شده علامت گذاری شد.</string>
+ <string name="mark_all_seen_confirmation_msg">لطفا تأیید کنید که میخواهید تمام قسمتها را بعنوان دیده شده علامت بزنید.</string>
+ <string name="show_info_label">نمایش اطلاعات</string>
+ <string name="rename_feed_label"> تغییر نام پادکست</string>
+ <string name="remove_feed_label">حذف پادکست</string>
+ <string name="share_label">اشتراک گذاری...</string>
+ <string name="share_link_label">اشتراک گذاری لینک</string>
+ <string name="share_file_label">اشتراک گذاری فایل</string>
+ <string name="share_link_with_position_label">اشتراک گذاری لینک با موقعیت پخش</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="hide_unplayed_episodes_label">پخش نشده</string>
+ <string name="hide_paused_episodes_label">متوقف شد</string>
+ <string name="hide_played_episodes_label">پخش شد</string>
+ <string name="hide_queued_episodes_label">در صف</string>
+ <string name="hide_not_queued_episodes_label">خارج از صف</string>
+ <string name="hide_downloaded_episodes_label">دانلود شده</string>
+ <string name="hide_not_downloaded_episodes_label">دانلود نشده</string>
+ <string name="hide_has_media_label">دارای رسانه</string>
+ <string name="filtered_label">فیلتر شده</string>
+ <string name="open_podcast">باز کردن پادکست</string>
<!--actions on feeditems-->
+ <string name="download_label">دانلود</string>
+ <string name="play_label">پخش</string>
+ <string name="pause_label">مکث</string>
+ <string name="stop_label">توقف</string>
+ <string name="remove_label">حذف</string>
+ <string name="delete_label">حذف</string>
+ <string name="delete_failed">فایل حذف نشد.! راه اندازی مجدد دستگاه می تواند کمک کند.</string>
+ <string name="remove_episode_lable">حذف قسمت</string>
+ <string name="marked_as_seen_label">علامت گذاری به عنوان دیده شده</string>
+ <string name="mark_read_label">علامت گذاری به عنوان پخش شده</string>
+ <string name="marked_as_read_label">بعنوان پخش شده علامت گذاری شد</string>
+ <string name="mark_unread_label">علامت گذاری به عنوان پخش نشده</string>
+ <string name="add_to_queue_label">افزودن به صف</string>
+ <string name="added_to_queue_label">به صف اضافه شد</string>
+ <string name="remove_from_queue_label">حذف از صف</string>
+ <string name="add_to_favorite_label">اضافه کردن به علاقه مندی ها</string>
+ <string name="added_to_favorites">به موارد دلخواه اضافه شد.</string>
+ <string name="remove_from_favorite_label">از علاقه مندی ها حذف شود</string>
+ <string name="removed_from_favorites">از موارد دلخواه حذف شد.</string>
+ <string name="skip_episode_label">رد شدن از قسمت</string>
+ <string name="activate_auto_download">فعال کردن دانلود خودکار</string>
+ <string name="deactivate_auto_download">غیر فعال کردن دانلود خودکار</string>
+ <string name="reset_position">تنظیم مجدد موقعیت پخش</string>
+ <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_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 Data </string>
+ <string name="download_error_error_unknown">خطای ناشناخته</string>
+ <string name="download_error_unsupported_type"> عدم پشتیبانی از این نوع خوراک</string>
+ <string name="download_error_connection_error">خطای اتصال</string>
+ <string name="download_error_unknown_host">میزبان ناشناس</string>
+ <string name="download_error_unauthorized">خطای احراز هویت</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_type_feed">خوراک</string>
<!--Mediaplayer messages-->
<!--Queue operations-->
<!--Flattr-->
@@ -33,6 +198,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-fi/strings.xml b/core/src/main/res/values-fi/strings.xml
index 28dfeb6e8..2d9481b84 100644
--- a/core/src/main/res/values-fi/strings.xml
+++ b/core/src/main/res/values-fi/strings.xml
@@ -33,6 +33,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-fr/strings.xml b/core/src/main/res/values-fr/strings.xml
index 4b17fed06..1a3fc9c1b 100644
--- a/core/src/main/res/values-fr/strings.xml
+++ b/core/src/main/res/values-fr/strings.xml
@@ -1,28 +1,31 @@
<?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">Mettre à jour les abonnements</string>
<string name="feeds_label">Flux</string>
<string name="statistics_label">Statistiques</string>
<string name="add_feed_label">Ajouter un podcast</string>
<string name="episodes_label">Épisodes</string>
<string name="all_episodes_short_label">Tout</string>
+ <string name="new_episodes_label">Nouveaux</string>
<string name="favorite_episodes_label">Favoris</string>
<string name="new_label">Nouveau</string>
<string name="settings_label">Préférences</string>
- <string name="add_new_feed_label">Ajouter un podcast</string>
<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és</string>
+ <string name="downloads_log_label">Journal d\'activité</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>
- <string name="playback_history_label">Journal des lectures</string>
+ <string name="playback_history_label">Journal de lecture</string>
<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>
@@ -45,9 +48,9 @@
<string name="drawer_feed_counter_none">Aucun</string>
<!--Webview actions-->
<string name="open_in_browser_label">Ouvrir dans le navigateur</string>
- <string name="copy_url_label">Copier l\'URL</string>
- <string name="share_url_label">Partager l\'URL</string>
- <string name="copied_url_msg">URL copiée dans le presse-papier</string>
+ <string name="copy_url_label">Copier le lien</string>
+ <string name="share_url_label">Partager le lien</string>
+ <string name="copied_url_msg">Lien copié dans le presse-papier</string>
<string name="go_to_position_label">Aller à cette position</string>
<!--Playback history-->
<string name="clear_history_label">Effacer le journal</string>
@@ -59,7 +62,7 @@
<string name="reset">Réinitialiser</string>
<string name="author_label">Auteur</string>
<string name="language_label">Langue</string>
- <string name="url_label">URL</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>
@@ -84,7 +87,7 @@
<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="parallel_downloads_suffix">\u0020téléchargements parallèles</string>
- <string name="feed_auto_download_global">Global défaut</string>
+ <string name="feed_auto_download_global">Option par défaut</string>
<string name="feed_auto_download_always">Toujours</string>
<string name="feed_auto_download_never">Jamais</string>
<string name="send_label">Envoyer...</string>
@@ -96,9 +99,9 @@
<item quantity="other">%d jours après avoir été écouté</item>
</plurals>
<!--'Add Feed' Activity labels-->
- <string name="feedurl_label">URL du flux</string>
- <string name="etxtFeedurlHint">URL du flux</string>
- <string name="txtvfeedurl_label">Ajouter un podcast par son URL</string>
+ <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 le podcast dans la bibliothèque</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>
@@ -111,27 +114,31 @@
<string name="mark_all_seen_msg">Tous les épisodes ont été marqués vus</string>
<string name="mark_all_seen_confirmation_msg">Merci de confirmer que vous voulez marquer tous les épisodes comme vus.</string>
<string name="show_info_label">Voir les détails</string>
+ <string name="show_feed_settings_label">Paramètres de flux...</string>
+ <string name="feed_info_label">Infos du flux</string>
+ <string name="feed_settings_label">Paramètres du flux</string>
<string name="rename_feed_label">Renommer le podcast</string>
<string name="remove_feed_label">Supprimer le podcast</string>
<string name="share_label">Partager...</string>
- <string name="share_link_label">Partager un lien vers le site</string>
- <string name="share_link_with_position_label">Partager lien avec position</string>
- <string name="share_feed_url_label">Partager lien du flux</string>
- <string name="share_item_url_label">Partager le lien de l\'épisode</string>
- <string name="share_item_url_with_position_label">Partager le lien de l\'épisode avec la position</string>
+ <string name="share_link_label">Partager le lien du site</string>
+ <string name="share_file_label">Partager le fichier</string>
+ <string name="share_link_with_position_label">Partager le lien avec la position</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 flux \"%1$s\" et TOUS les épisodes que vous avez téléchargés.</string>
<string name="feed_remover_msg">Flux en cours de suppression</string>
<string name="load_complete_feed">Mettre à jour tout le flux</string>
<string name="hide_episodes_title">Cacher épisodes</string>
- <string name="episode_actions">Appliquer les actions</string>
- <string name="hide_unplayed_episodes_label">Non joués</string>
+ <string name="batch_edit">Edition groupée</string>
+ <string name="hide_unplayed_episodes_label">Non lus</string>
<string name="hide_paused_episodes_label">En pause</string>
- <string name="hide_played_episodes_label">Joués</string>
- <string name="hide_queued_episodes_label">Rajouté à la liste de lecture</string>
- <string name="hide_not_queued_episodes_label">Non rajouté à la liste de lecture</string>
+ <string name="hide_played_episodes_label">Lus</string>
+ <string name="hide_queued_episodes_label">Dans la liste de lecture</string>
+ <string name="hide_not_queued_episodes_label">Pas dans la liste de lecture</string>
<string name="hide_downloaded_episodes_label">Téléchargé</string>
<string name="hide_not_downloaded_episodes_label">Non téléchargé</string>
- <string name="hide_has_media_label">À des médias</string>
+ <string name="hide_has_media_label">Avec média</string>
<string name="filtered_label">Filtré</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} La dernière mise à jour a échoué</string>
<string name="open_podcast">Ouvrir Podcast</string>
@@ -143,6 +150,7 @@
<string name="stream_label">Lire en ligne</string>
<string name="remove_label">Supprimer</string>
<string name="delete_label">Effacer</string>
+ <string name="delete_failed">Suppression du fichier impossible. Redémarrer pourrait aider.</string>
<string name="remove_episode_lable">Supprimer cet épisode</string>
<string name="marked_as_seen_label">Marqué comme vu</string>
<string name="mark_read_label">Marquer comme lu</string>
@@ -167,6 +175,8 @@
<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>
@@ -184,7 +194,7 @@
<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>
<string name="download_report_content_title">Rapport des téléchargements</string>
- <string name="download_error_malformed_url">URL incorrecte</string>
+ <string name="download_error_malformed_url">Lien incorrecte</string>
<string name="download_error_io_error">Erreur d\'E/S</string>
<string name="download_error_request_error">Erreur de requête</string>
<string name="download_error_db_access">Problème d\'accès à la base de données</string>
@@ -203,8 +213,8 @@
<string name="authentication_notification_title">Authentification requise</string>
<string name="authentication_notification_msg">La ressource que vous avez demandé nécessite un nom d\'utilisateur et un mot de passe</string>
<string name="confirm_mobile_download_dialog_title">Confirmer le téléchargement mobile</string>
- <string name="confirm_mobile_download_dialog_message_not_in_queue">Le téléchargement sur la connexion mobile est désactivé dans les options.\n\nVous pouvez choisir d\'ajouter seulement l\'épisode à la liste de lecture ou vous pouvez autoriser temporairement le téléchargement.\n\n<small>Votre choix sera retenu pour les 10 prochaines minutes.</small></string>
- <string name="confirm_mobile_download_dialog_message">Le téléchargement sur la connexion mobile est désactivé dans les options.\n\nVoulez-vous autoriser temporairement le téléchargement?\n\n<small>Votre choix sera retenu pour les 10 prochaines minutes.</small></string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Le téléchargement avec la connexion mobile est désactivé dans les options.\n\nVous pouvez choisir d\'ajouter seulement l\'épisode à la liste de lecture ou vous pouvez autoriser temporairement le téléchargement.\n\n<small>Votre choix sera retenu pour les 10 prochaines minutes.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Le téléchargement avec la connexion mobile est désactivé dans les options.\n\nVoulez-vous autoriser temporairement le téléchargement?\n\n<small>Votre choix sera retenu pour les 10 prochaines minutes.</small></string>
<string name="confirm_mobile_download_dialog_only_add_to_queue">Rajouter à la liste de lecture</string>
<string name="confirm_mobile_download_dialog_enable_temporarily">Autoriser temporairement</string>
<!--Mediaplayer messages-->
@@ -217,6 +227,7 @@
<string name="playback_error_unknown">Erreur inconnue</string>
<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-->
@@ -234,6 +245,8 @@
<string name="duration">Durée</string>
<string name="episode_title">Titre de l\'épisode</string>
<string name="feed_title">Nom du flux</string>
+ <string name="random">Aléatoire</string>
+ <string name="smart_shuffle">Tri intelligent</string>
<string name="ascending">Ordre croissant</string>
<string name="descending">Ordre décroissant</string>
<string name="clear_queue_confirmation_msg">Veuillez confirmer que vous voulez bien supprimer TOUS les épisodes de la liste de lecture</string>
@@ -273,20 +286,29 @@
<string name="no_items_label">Cette liste est vide.</string>
<string name="no_feeds_label">Vous n\'êtes encore abonné à aucun flux.</string>
<string name="no_chapters_label">Cet épisode n\'a pas de chapitres.</string>
- <string name="no_shownotes_label">Aucun descriptif pour cet épisode.</string>
+ <string name="no_shownotes_label">Aucune notes pour cet épisode.</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</string>
- <string name="services_label">Services</string>
+ <string name="integrations_label">Intégrations</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Service de micropaiement</string>
+ <string name="automation">Automatisation</string>
+ <string name="download_pref_details">Détails</string>
+ <string name="import_export_pref">Importation / Exportation</string>
+ <string name="appearance">Apparence</string>
+ <string name="external_elements">Eléments externes</string>
+ <string name="interruptions">Interruptions</string>
+ <string name="buttons">Boutons</string>
+ <string name="media_player">Lecteur multimédia</string>
<string name="pref_episode_cleanup_title">Nettoyage des épisodes</string>
<string name="pref_episode_cleanup_summary">Les épisodes qui ne sont pas dans la liste de lecture et qui ne sont pas marqués comme favoris peuvent être supprimés si l\'espace est insuffisant pour le téléchargement automatique de nouveaux épisodes</string>
<string name="pref_pauseOnDisconnect_sum">Interrompre la lecture lorsque le casque ou le bluetooth sont déconnectés</string>
- <string name="pref_unpauseOnHeadsetReconnect_sum">Reprendre la lecture quand les écouteurs sont reconnectés</string>
- <string name="pref_unpauseOnBluetoothReconnect_sum">Reprendre la lecture quand le Bluetooth se reconnecte</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">Reprendre la lecture quand les écouteurs sont connectés</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Reprendre la lecture quand le Bluetooth se connecte</string>
<string name="pref_hardwareForwardButtonSkips_title">Le bouton \"saut avant\" saute l\'épisode</string>
<string name="pref_hardwareForwardButtonSkips_sum">Passer à l\'épisode suivant au lieu de faire un saut avant quand un bouton physique \"saut avant\" est pressé</string>
<string name="pref_hardwarePreviousButtonRestarts_title">Le bouton \"saut arrière\" redémarre l\'épisode</string>
@@ -294,15 +316,17 @@
<string name="pref_followQueue_sum">Après la fin d\'un épisode, passer au suivant</string>
<string name="pref_auto_delete_sum">Supprimer l\'épisode quand la lecture est finie</string>
<string name="pref_auto_delete_title">Suppression automatique</string>
- <string name="pref_smart_mark_as_played_sum">Les épisodes seront marqués comme lus même s\'il reste quelques secondes à jouer</string>
+ <string name="pref_smart_mark_as_played_sum">Les épisodes seront marqués comme lus même s\'il reste quelques secondes à écouter</string>
<string name="pref_smart_mark_as_played_title">Marquer comme lu intelligemment</string>
<string name="pref_skip_keeps_episodes_sum">Garder les épisodes quand ils sont passés</string>
<string name="pref_skip_keeps_episodes_title">Garder les épisodes passés</string>
+ <string name="pref_favorite_keeps_episodes_sum">Garder les épisodes marqués comme favoris</string>
+ <string name="pref_favorite_keeps_episodes_title">Garder les épisodes favoris</string>
<string name="playback_pref">Lecture</string>
<string name="network_pref">Réseau</string>
<string name="pref_autoUpdateIntervallOrTime_title">Mettre à jour l’intervalle ou l\'heure</string>
<string name="pref_autoUpdateIntervallOrTime_sum">Indiquer un intervalle ou une heure spécifique de mise à jour des flux</string>
- <string name="pref_autoUpdateIntervallOrTime_message">Vous pouvez mettre en place un <i>intervalle</i> comme \"toutes les 2 heures\", une <i>heure précise</i> comme \"7:00\" ou désactiver les mises à jours automatique.\n\n<small>Note: Il est possible qu\'il y ait un délai car l\'heure de mise à jour peut être inexacte.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Vous pouvez mettre en place un <i>intervalle</i> comme \"toutes les 2 heures\", une <i>heure précise</i> comme \"7:00\" ou désactiver les mises à jours automatique.\n\n<small>Note: les heures de mise à jour ne sont pas précises. Vous pouvez avoir un petit délai.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Désactiver</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Définir intervalle</string>
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Régler l\'heure</string>
@@ -311,11 +335,11 @@
<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 du casque</string>
- <string name="pref_unpauseOnHeadsetReconnect_title">Reconnexion du casque</string>
- <string name="pref_unpauseOnBluetoothReconnect_title">Reconnexion Bluetooth</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_mobileUpdate_title">Mises à jour mobile</string>
- <string name="pref_mobileUpdate_sum">Autoriser les mises à jour à travers la connexion de données mobile</string>
+ <string name="pref_mobileUpdate_sum">Autoriser les mises à jour avec la connexion mobile</string>
<string name="refreshing_label">Mise à jour en cours</string>
<string name="flattr_settings_label">Paramètres Flattr</string>
<string name="pref_flattr_auth_title">Connexion à Flattr</string>
@@ -341,12 +365,15 @@
<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="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">Épisodes stockés localement</string>
<string name="pref_theme_title_light">Clair</string>
<string name="pref_theme_title_dark">Sombre</string>
+ <string name="pref_theme_title_trueblack">Noir / True Black</string>
<string name="pref_episode_cache_unlimited">Illimité</string>
<string name="pref_update_interval_hours_plural">heures</string>
<string name="pref_update_interval_hours_singular">heure</string>
@@ -397,8 +424,7 @@
<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="experimental_pref">Expérimental</string>
- <string name="pref_sonic_title">Lecteur multimédia Sonic</string>
- <string name="pref_sonic_message">Utiliser le lecteur multimédia interne Sonic au lieu du lecteur natif d\'Android ou Prestissimo</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>
@@ -410,9 +436,14 @@
<string name="pref_cast_message_free_flavor">Chromecast nécessite des bibliothèques tierces qui sont désactivées dans cette version d\'AntennaPod</string>
<string name="pref_enqueue_downloaded_title">Ajouter à la liste après téléchargement</string>
<string name="pref_enqueue_downloaded_summary">Mettre les épisodes dans la la liste de lecture après téléchargement</string>
+ <string name="media_player_builtin">Lecteur natif d\'Android</string>
+ <string name="pref_videoBehavior_title">Sorti du lecteur pendant une vidéo</string>
+ <string name="pref_videoBehavior_sum">Définir ce qu\'il se passe si une vidéo est quittée pendant sa lecture</string>
+ <string name="stop_playback">Arrêter la lecture</string>
+ <string name="continue_playback">Continuer la lecture</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Activer le paiement flattr automatique</string>
- <string name="auto_flattr_after_percent">Lancer un paiement flattr pour un épisode dès que %d de l\'épisode a été joué</string>
+ <string name="auto_flattr_after_percent">Lancer un paiement flattr quand %d pourcent de l\'épisode a été lu</string>
<string name="auto_flattr_ater_beginning">Lancer le paiement flattr d\'un épisode dès que la lecture commence</string>
<string name="auto_flattr_ater_end">Lancer le paiement flattr d\'un épisode à la fin de la lecture</string>
<!--Search-->
@@ -432,7 +463,7 @@
<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">Importation OPML</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>
@@ -442,12 +473,12 @@
<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">Exportation OPML</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>
<string name="export_error_label">Erreur d\'exportation</string>
- <string name="opml_export_success_title">Exportation OPML réussie.</string>
- <string name="opml_export_success_sum">Le fichier .opml a été écrit ici :\u0020</string>
+ <string name="export_success_title">Export réussi</string>
+ <string name="export_success_sum">Le fichier a été exporté dans :\n\n%1$s</string>
<string name="opml_import_ask_read_permission">L\'accès au stockage externe est requis pour lire le fichier OPML</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Activation du minuteur d\'arrêt</string>
@@ -566,15 +597,15 @@
<string name="selected_all_label">Tous les épisodes ont été sélectionné</string>
<string name="none_label">Aucun</string>
<string name="deselected_all_label">Tous les épisodes ont été désélectionné</string>
- <string name="played_label">Joués</string>
- <string name="selected_played_label">Episodes joués sélectionnés</string>
- <string name="unplayed_label">Non joués</string>
- <string name="selected_unplayed_label">Episodes non joués sélectionnés</string>
+ <string name="played_label">Lus</string>
+ <string name="selected_played_label">Episodes lus sélectionnés</string>
+ <string name="unplayed_label">Non lus</string>
+ <string name="selected_unplayed_label">Episodes non lus sélectionnés</string>
<string name="downloaded_label">Téléchargés</string>
<string name="selected_downloaded_label">Episodes téléchargés sélectionnés</string>
<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 liste de lecture</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="not_queued_label">En dehors de la liste de lecture</string>
<string name="selected_not_queued_label">Episodes absents de la liste de lecture sélectionnés</string>
@@ -614,6 +645,14 @@
<string name="proxy_host_empty_error">Hôte ne peut pas être vide</string>
<string name="proxy_host_invalid_error">L\'hôte n\'est pas une adresse IP ou un domaine valide</string>
<string name="proxy_port_invalid_error">Port non valide</string>
+ <!--Database import/export-->
+ <string name="import_export">Import / Export de la base de données</string>
+ <string name="import_export_warning">Cette fonction expérimentale peut-être utilisée pour transférer vos abonnements et épisodes lus sur un autre appareil.\n\nLes bases de données exportées peuvent uniquement être importées sur la même version d\'AntennaPod. Dans le cas contraire, des dysfonctionnements peuvent apparaître.\n\nAprès import, il est possible que des épisodes apparaissent téléchargés alors qu\'ils ne le sont pas. Appuyer sur le bouton de lecture pour qu\'AntennaPod le détecte.</string>
+ <string name="label_import">Importer</string>
+ <string name="label_export">Exporter</string>
+ <string name="import_select_file">Sélectionner le fichier à importer</string>
+ <string name="export_ok">Export réussi.</string>
+ <string name="import_ok">Import réussi.\n\nAppuyer sur OK pour redémarrer AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Lire sur...</string>
<string name="cast_disconnect_label">Déconnecter la session cast</string>
@@ -630,4 +669,13 @@
<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>
<string name="cast_failed_media_error_skipping">Erreur de lecture du média. Passage au suivant...</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Action requise</string>
+ <string name="notification_channel_user_action_description">S\'affiche si une action est requise. Par exemple, un mot de passe à saisir.</string>
+ <string name="notification_channel_downloading">Téléchargement en cours</string>
+ <string name="notification_channel_downloading_description">S\'affiche lorsqu\'un téléchargement est en cours.</string>
+ <string name="notification_channel_playing">Lecture en cours</string>
+ <string name="notification_channel_playing_description">Permet de contrôler la lecture. C\'est la notification principale pendant la lecture d\'un podcast.</string>
+ <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>
</resources>
diff --git a/core/src/main/res/values-gl-rES/strings.xml b/core/src/main/res/values-gl-rES/strings.xml
index 995687fa2..555356f0f 100644
--- a/core/src/main/res/values-gl-rES/strings.xml
+++ b/core/src/main/res/values-gl-rES/strings.xml
@@ -1,15 +1,16 @@
<?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">Actualizar suscrició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="episodes_label">Episodios</string>
<string name="all_episodes_short_label">Todo</string>
+ <string name="new_episodes_label">Novo</string>
<string name="favorite_episodes_label">Favoritos</string>
<string name="new_label">Novo</string>
<string name="settings_label">Axustes</string>
- <string name="add_new_feed_label">Engadir Podcast</string>
<string name="downloads_label">Descargas</string>
<string name="downloads_running_label">Descargando</string>
<string name="downloads_completed_label">Completado</string>
@@ -19,10 +20,12 @@
<string name="cancel_download_label">Cancelar\nDescarga</string>
<string name="playback_history_label">Historial de reprodución</string>
<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 gratis</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>
@@ -111,10 +114,14 @@
<string name="mark_all_seen_msg">Marcáronse todos os episodios como vistos</string>
<string name="mark_all_seen_confirmation_msg">Por favor confirme que quere marcar todos os episodios como vistos.</string>
<string name="show_info_label">Mostrar información</string>
+ <string name="show_feed_settings_label">Mostrar axustes da fonte</string>
+ <string name="feed_info_label">Info da fonte</string>
+ <string name="feed_settings_label">Axustes da fonte</string>
<string name="rename_feed_label">Mudar nome do podcast</string>
<string name="remove_feed_label">Quitar podcast</string>
<string name="share_label">Compartir...</string>
<string name="share_link_label">Compartir ligazón</string>
+ <string name="share_file_label">Compartir ficheiro</string>
<string name="share_link_with_position_label">Compartir ligazón con posición</string>
<string name="share_feed_url_label">Compartir URL da fonte</string>
<string name="share_item_url_label">Compartir a URL do ficheiro do episodio</string>
@@ -123,7 +130,7 @@
<string name="feed_remover_msg">Eliminando a fonte</string>
<string name="load_complete_feed">Actualizar completamente a fonte</string>
<string name="hide_episodes_title">Ocultar episodios</string>
- <string name="episode_actions">Aplicar accións</string>
+ <string name="batch_edit">Edición por lote</string>
<string name="hide_unplayed_episodes_label">Non reproducido</string>
<string name="hide_paused_episodes_label">En pausa</string>
<string name="hide_played_episodes_label">Reproducido</string>
@@ -143,6 +150,7 @@
<string name="stream_label">Enviar</string>
<string name="remove_label">Eliminar</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="remove_episode_lable">Eliminar episodio</string>
<string name="marked_as_seen_label">Marcar como visto</string>
<string name="mark_read_label">Marcar como reproducido</string>
@@ -167,6 +175,8 @@
<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>
@@ -194,7 +204,7 @@
</plurals>
<string name="downloads_processing">Procesando as descargas</string>
<string name="download_notification_title">Descargando datos do podcast</string>
- <string name="download_report_content">%1$ddescargas exitosas, %2$d fallaron</string>
+ <string name="download_report_content">%1$d descargas con éxito, %2$d fallaron</string>
<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>
@@ -217,6 +227,7 @@
<string name="playback_error_unknown">Fallo descoñecido</string>
<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-->
@@ -234,6 +245,8 @@
<string name="duration">Duración</string>
<string name="episode_title">Título do episodio</string>
<string name="feed_title">Título da fonte</string>
+ <string name="random">Aleatorio</string>
+ <string name="smart_shuffle">Barallado intelixente</string>
<string name="ascending">Ascendente</string>
<string name="descending">Descendente</string>
<string name="clear_queue_confirmation_msg">Por favor confirme que quere limpar a cola e TODOS os episodios nela</string>
@@ -280,8 +293,17 @@
<string name="other_pref">Outro</string>
<string name="about_pref">Sobre</string>
<string name="queue_label">Cola</string>
- <string name="services_label">Servizos</string>
+ <string name="integrations_label">Integracións</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Servizo de micropagamentos</string>
+ <string name="automation">Automatizado</string>
+ <string name="download_pref_details">Detalles</string>
+ <string name="import_export_pref">Importar/Exportar</string>
+ <string name="appearance">Aspecto</string>
+ <string name="external_elements">Elementos externos</string>
+ <string name="interruptions">Interrupcións</string>
+ <string name="buttons">Botóns</string>
+ <string name="media_player">Reprodutor de medios</string>
<string name="pref_episode_cleanup_title">Limpeza de episodios</string>
<string name="pref_episode_cleanup_summary">Os episodios que non están na cola e tampouco son favoritos deberían poder ser candidatos a ser eliminados si a función Descarga Automática precisa espazo para novos episodios.</string>
<string name="pref_pauseOnDisconnect_sum">Deter a reprodución cando se desconectan os auriculares ou bluetooth</string>
@@ -298,6 +320,8 @@
<string name="pref_smart_mark_as_played_title">Marcar como Reproducido automático</string>
<string name="pref_skip_keeps_episodes_sum">Manter os episodios cando son saltados</string>
<string name="pref_skip_keeps_episodes_title">Manter episodios saltados</string>
+ <string name="pref_favorite_keeps_episodes_sum">Manter os episodios cando son marcados como Favoritos</string>
+ <string name="pref_favorite_keeps_episodes_title">Manter os episodios favoritos</string>
<string name="playback_pref">Reprodución</string>
<string name="network_pref">Rede</string>
<string name="pref_autoUpdateIntervallOrTime_title">Intervalo de actualización ou Hora do día</string>
@@ -341,12 +365,15 @@
<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="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_theme_title_light">Claro</string>
<string name="pref_theme_title_dark">Oscuro</string>
+ <string name="pref_theme_title_trueblack">Negro lexítimo</string>
<string name="pref_episode_cache_unlimited">Ilimitado</string>
<string name="pref_update_interval_hours_plural">horas</string>
<string name="pref_update_interval_hours_singular">hora</string>
@@ -397,8 +424,7 @@
<string name="crash_report_sum">Enviar por email o informe de fallo xeral no aplicativo</string>
<string name="send_email">Enviar email</string>
<string name="experimental_pref">En probas</string>
- <string name="pref_sonic_title">Sonic Media Player</string>
- <string name="pref_sonic_message">Utilizar o sonic media player incluído no lugar do reprodutor nativo de Android e Prestissimo</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>
@@ -408,8 +434,13 @@
<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>
<string name="pref_cast_message_free_flavor">Chromecast precisa software propietario de terceiras partes que están deshabilitadas en esta versión de AntennaPod</string>
- <string name="pref_enqueue_downloaded_title">Descargas engadidas a cola</string>
+ <string name="pref_enqueue_downloaded_title">Foron descargados os elementos da cola</string>
<string name="pref_enqueue_downloaded_summary">Engadir os episodios descargados a cola</string>
+ <string name="media_player_builtin">Reprodutor android nativo</string>
+ <string name="pref_videoBehavior_title">Comportamento de video</string>
+ <string name="pref_videoBehavior_sum">Comportamento cando saia do vídeo</string>
+ <string name="stop_playback">Para a reprodución</string>
+ <string name="continue_playback">Reprodución contínua</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Hablitar o flattring automático</string>
<string name="auto_flattr_after_percent">Flattr o episodio tan pronto como o %d por cento foi reproducido</string>
@@ -446,8 +477,8 @@
<string name="html_export_label">Exportar HTML</string>
<string name="exporting_label">Exportando...</string>
<string name="export_error_label">Fallo ao exportar</string>
- <string name="opml_export_success_title">Exportación OPML exitosa.</string>
- <string name="opml_export_success_sum">O ficheiro .opml foi gardado en:\u0020</string>
+ <string name="export_success_title">Exportado con éxito</string>
+ <string name="export_success_sum">Escribeuse o ficheiro exportado en:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Precísase acceso ao almacenamento externo para ler o ficheiro OPML</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Establecer apagado automático</string>
@@ -614,6 +645,14 @@
<string name="proxy_host_empty_error">Servidor non pode quedar baldeiro</string>
<string name="proxy_host_invalid_error">O servidor indicado non é un dominio ou IP válidos</string>
<string name="proxy_port_invalid_error">Porto non válido</string>
+ <!--Database import/export-->
+ <string name="import_export">Importar/Exportar base de datos</string>
+ <string name="import_export_warning">Esta función experimental utilízase para transferir as súas suscricións e episodios reproducidos en outro dispositivo.\n\nAs bases de datos exportadas só se poden importar si utiliza a misma versión de AntennaPod. De todos xeitos, esta función pode comportarse de xeito raro.\n\nDespois de importar, os episodios poderían ser mostrados como descargados sin telo sido. Simplemente pulse o botón de reprodución dos episodios para que AntennaPod detecte esto.</string>
+ <string name="label_import">Importar</string>
+ <string name="label_export">Exportar</string>
+ <string name="import_select_file">Escolla o ficheiro a importar</string>
+ <string name="export_ok">Exportado con éxito.</string>
+ <string name="import_ok">Importación correcta.\n\nPulse OK para reiniciar AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Reproducir en...</string>
<string name="cast_disconnect_label">Desconectar a sesión de emisión</string>
@@ -630,4 +669,13 @@
<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>
<string name="cast_failed_media_error_skipping">Fallo na reprodución de medios. Saltando...</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Acción requerida</string>
+ <string name="notification_channel_user_action_description">Mostrado si a súa acción é requerida, por exemplo si precisa introducir o contrasinal.</string>
+ <string name="notification_channel_downloading">Descargando</string>
+ <string name="notification_channel_downloading_description">Mostrado durante a descarga actual.</string>
+ <string name="notification_channel_playing">Soando agora</string>
+ <string name="notification_channel_playing_description">Permite controlar a reprodución. Esta é a notificación principal que verá mentras reproduce un podcast.</string>
+ <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>
</resources>
diff --git a/core/src/main/res/values-hi-rIN/strings.xml b/core/src/main/res/values-hi-rIN/strings.xml
index 9e30ff59d..dc99bc9e1 100644
--- a/core/src/main/res/values-hi-rIN/strings.xml
+++ b/core/src/main/res/values-hi-rIN/strings.xml
@@ -4,7 +4,6 @@
<string name="feeds_label">फिड्स</string>
<string name="new_label">नया</string>
<string name="settings_label">सेटिंग्स</string>
- <string name="add_new_feed_label">पॉडकास्ट जोड़ें</string>
<string name="downloads_label">डाउनलोड</string>
<string name="cancel_download_label">डाउनलोड रद्द करें</string>
<string name="playback_history_label">प्लेबैक इतिहास</string>
@@ -141,7 +140,6 @@
<string name="other_pref">अन्य</string>
<string name="about_pref">के बारे में</string>
<string name="queue_label">पंक्ति</string>
- <string name="services_label">सेवाएं</string>
<string name="flattr_label">Flattr</string>
<string name="pref_followQueue_sum">प्लेबैक के पूरा होने पर अगली पंक्ति आइटम के लिए जाएँ</string>
<string name="playback_pref">प्लेबैक</string>
@@ -200,7 +198,6 @@
<string name="deselect_all_label">सभी का चयन रद्द करें</string>
<string name="opml_export_label">OPML निर्यात</string>
<string name="export_error_label">निर्यात त्रुटि</string>
- <string name="opml_export_success_sum">.ompl फ़ाइल लिखा गया था:\u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">स्लीप टाइमर सेट</string>
<string name="disable_sleeptimer_label">स्लीप टाइमर अक्षम</string>
@@ -261,6 +258,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-hu/strings.xml b/core/src/main/res/values-hu/strings.xml
index 91eef6144..6d4018c25 100644
--- a/core/src/main/res/values-hu/strings.xml
+++ b/core/src/main/res/values-hu/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Kedvencek</string>
<string name="new_label">Új</string>
<string name="settings_label">Beállítások</string>
- <string name="add_new_feed_label">Podcast hozzáadása</string>
<string name="downloads_label">Letöltések</string>
<string name="downloads_running_label">Futó</string>
<string name="downloads_completed_label">Befejezett</string>
@@ -33,6 +32,7 @@
<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_counter_new">Új epizódok száma</string>
<string name="drawer_feed_counter_unplayed">Nem játszott epizódok száma</string>
@@ -62,6 +62,7 @@
<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="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>
@@ -83,7 +84,7 @@
<string name="feed_auto_download_never">Soha</string>
<string name="send_label">Küldés…</string>
<string name="episode_cleanup_never">Soha</string>
- <string name="episode_cleanup_queue_removal">Ha nem várakozik</string>
+ <string name="episode_cleanup_queue_removal">Ha nincs sorbaállítva</string>
<string name="episode_cleanup_after_listening">Befejezés után</string>
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 nappal a befejezés után</item>
@@ -109,19 +110,20 @@
<string name="remove_feed_label">Podcast eltávolítása</string>
<string name="share_label">Megosztás…</string>
<string name="share_link_label">Link megosztása</string>
+ <string name="share_file_label">Fájl megosztása</string>
<string name="share_link_with_position_label">Link megosztása pozícióval</string>
<string name="share_feed_url_label">Idővonal URL megosztása</string>
<string name="share_item_url_label">Epizód fájl URL megosztása</string>
<string name="share_item_url_with_position_label">Epizód fá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 csatornát, az összes letöltött epizóddal együtt.</string>
<string name="feed_remover_msg">Idővonal eltávolítása</string>
<string name="load_complete_feed">Teljes idővonal frissítése</string>
<string name="hide_episodes_title">Epizódok elrejtése</string>
- <string name="episode_actions">Műveletek alkalmazá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">Várakozó</string>
- <string name="hide_not_queued_episodes_label">Nem várakozó</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_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>
@@ -138,12 +140,12 @@
<string name="delete_label">Törlés</string>
<string name="remove_episode_lable">Epizód eltávolítása</string>
<string name="marked_as_seen_label">Megtekintettként megjelölve</string>
- <string name="mark_read_label">Lejátszottként megjelölés</string>
+ <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="mark_unread_label">Nem lejátszottként megjelölés</string>
- <string name="add_to_queue_label">Várakozási sorhoz adás</string>
- <string name="added_to_queue_label">Várakozási sorhoz adva</string>
- <string name="remove_from_queue_label">Várakozási sorból eltávolítás</string>
+ <string name="mark_unread_label">Jelölés nem játszottnak</string>
+ <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>
@@ -160,6 +162,8 @@
<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_insufficient_space">Túl kevés tárhely</string>
<string name="download_error_file_error">Fájl Hiba</string>
@@ -174,29 +178,212 @@
<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_report_content_title">Jelentés letöltése</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>
+ <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>
+ </plurals>
+ <string name="downloads_processing">Letöltések feldolgozása</string>
+ <string name="download_notification_title">Podcast adatok letöltése</string>
+ <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>
<!--Mediaplayer messages-->
+ <string name="player_error_msg">Hiba!</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="playback_error_unknown">Ismeretlen hiba</string>
+ <string name="player_buffering_msg">Pufferelés</string>
+ <string name="playbackservice_notification_title">Podcast lejátszása</string>
<!--Queue operations-->
+ <string name="lock_queue">Lejátszási sor lezárá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_unlocked">Lejátszási sor feloldva</string>
+ <string name="clear_queue_label">Lejátszási sor tisztítása</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="date">Dátum</string>
+ <string name="duration">Hossz</string>
+ <string name="episode_title">Epizód cím</string>
+ <string name="feed_title">Csatorna cím</string>
+ <string name="ascending">Növekvő</string>
+ <string name="descending">Csökkenő</string>
<!--Flattr-->
+ <string name="flattr_auth_label">Flattr bejelentkezés</string>
+ <string name="access_revoked_title">Hozzáférés megtagadva</string>
<!--Flattr-->
<!--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="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_items_label">Nincs elem a listában</string>
+ <string name="no_feeds_label">Egy csatornára sem iratkoztál még fel</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="flattr_label">Flattr</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="pref_auto_delete_title">Autómata törlés</string>
+ <string name="pref_smart_mark_as_played_title">Intelligens játszottnak jelölés</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_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_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_mobileUpdate_sum">Frissítések engedélyezése mobiladat-kapcsolaton keresztül</string>
+ <string name="flattr_settings_label">Flattr beállítások</string>
+ <string name="pref_flattr_auth_title">Flattr bejelentkezés</string>
+ <string name="pref_revokeAccess_title">Hozzáférés megvonása</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álaasztása</string>
+ <string name="pref_set_theme_sum">AntennaPod kinézetének megváltoztatása</string>
+ <string name="pref_automatic_download_title">Autómatikus letöltés</string>
+ <string name="pref_automatic_download_sum">Epizódok autómatikus 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_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_theme_title_light">Világos</string>
+ <string name="pref_theme_title_dark">Sötét</string>
+ <string name="pref_update_interval_hours_plural">óra</string>
+ <string name="pref_update_interval_hours_singular">óra</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_logout_title">Kijelentkezés</string>
+ <string name="pref_gpodnet_logout_toast">Kijelentkezés sikeres</string>
+ <string name="pref_gpodnet_sync_changes_title">Változások szinkronizálása most</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_expandNotify_title">Értesítés kibontása</string>
+ <string name="pref_expandNotify_sum">Mindig kibontja az értesítést a vezérlő gombok megjelenítéséhez.</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_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="experimental_pref">Kísérleti</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_cast_title">Chromecast támogatás</string>
<!--Auto-Flattr dialog-->
<!--Search-->
+ <string name="search_hint">Epizódok keresése</string>
+ <string name="search_label">Keresés</string>
<!--OPML import and export-->
+ <string name="start_import_label">Importálás indítása</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>
<!--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>
<!--gpodder.net-->
+ <string name="gpodnet_taglist_header">KATEGÓRIÁK</string>
+ <string name="gpodnet_suggestions_header">AJÁNLÁSOK</string>
+ <string name="gpodnetauth_login_title">Bejelentkezés</string>
+ <string name="gpodnetauth_login_butLabel">Bejelentkezés</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="gpodnetsync_pref_report_successful">Sikeres</string>
+ <string name="gpodnetsync_pref_report_failed">Sikertelen</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="set_to_default_folder">Alapértelmezett mappa kiválasztása</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>
<!--Content descriptions for image buttons-->
+ <string name="media_type_audio_label">Hang</string>
+ <string name="media_type_video_label">Video</string>
+ <string name="load_next_page_label">Következő oldal betöltése</string>
<!--Feed information screen-->
<!--Progress information-->
<!--AntennaPodSP-->
+ <string name="search_itunes_label">iTunes keresés</string>
+ <string name="search_fyyd_label">fyyd keresés</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>
<!--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>
<!--Rating dialog-->
+ <string name="rating_later_label">Kérdezz rákésőbb</string>
<!--Audio controls-->
+ <string name="audio_controls">Hang vezé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>
<!--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="proxy_test_label">Teszt</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>
+ <!--Database import/export-->
+ <string name="import_export">Adatbázis importálása/exportálása</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>
<!--Casting-->
<!--<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>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-id/strings.xml b/core/src/main/res/values-id/strings.xml
index 743812a58..62254518d 100644
--- a/core/src/main/res/values-id/strings.xml
+++ b/core/src/main/res/values-id/strings.xml
@@ -7,7 +7,6 @@
<string name="favorite_episodes_label">Favorit</string>
<string name="new_label">Baru</string>
<string name="settings_label">Pengaturan</string>
- <string name="add_new_feed_label">Tambah Podcast</string>
<string name="downloads_label">Unduhan</string>
<string name="subscriptions_label">Abonemen</string>
<string name="subscriptions_list_label">Daftar Abonemen</string>
@@ -149,6 +148,8 @@
<string name="left_short">Kiri</string>
<string name="right_short">Kanan</string>
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-is-rIS/strings.xml b/core/src/main/res/values-is-rIS/strings.xml
index 28dfeb6e8..2d9481b84 100644
--- a/core/src/main/res/values-is-rIS/strings.xml
+++ b/core/src/main/res/values-is-rIS/strings.xml
@@ -33,6 +33,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-it-rIT/strings.xml b/core/src/main/res/values-it-rIT/strings.xml
index 4d4e44bff..6aee5c314 100644
--- a/core/src/main/res/values-it-rIT/strings.xml
+++ b/core/src/main/res/values-it-rIT/strings.xml
@@ -1,44 +1,53 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
+ <string name="feed_update_receiver_name">Aggiorna Sottoscrizioni</string>
<string name="feeds_label">Feed</string>
<string name="statistics_label">Statistiche</string>
<string name="add_feed_label">Aggiungi un podcast</string>
<string name="episodes_label">Episodi</string>
<string name="all_episodes_short_label">Tutti</string>
+ <string name="new_episodes_label">Novità</string>
<string name="favorite_episodes_label">Preferiti</string>
<string name="new_label">Nuovo</string>
<string name="settings_label">Impostazioni</string>
- <string name="add_new_feed_label">Aggiungi podcast</string>
<string name="downloads_label">Download</string>
<string name="downloads_running_label">In esecuzione</string>
<string name="downloads_completed_label">Completati</string>
<string name="downloads_log_label">Registro</string>
- <string name="subscriptions_label">Iscrizioni</string>
- <string name="subscriptions_list_label">Lista Iscrizioni</string>
- <string name="cancel_download_label">Annulla download</string>
+ <string name="subscriptions_label">Sottoscrizioni</string>
+ <string name="subscriptions_list_label">Elenco sottoscrizioni</string>
+ <string name="cancel_download_label">Annulla\nil Download</string>
<string name="playback_history_label">Cronologia delle riproduzioni</string>
<string name="gpodnet_main_label">gpodder.net</string>
- <string name="gpodnet_auth_label">gpodder.net login</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">Il limite della cache degli episodi è stato raggiunto. Puoi incrementare la dimensione della cache nelle Impostazioni.</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 podcast:</string>
+ <string name="total_time_listened_to_podcasts">Tempo totale di riproduzione:</string>
<string name="statistics_details_dialog">%1$d di %2$d episodi iniziati.\n\nRiprodotti %3$s di %4$s.</string>
+ <string name="statistics_mode">Modalità di calcolo</string>
+ <string name="statistics_mode_normal">Calcola il tempo di riproduzione reale. Riprodurre un podcast due volte verrà contato due volte, segnarlo come riprodotto no.</string>
+ <string name="statistics_mode_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>
<!--Main activity-->
<string name="drawer_open">Apri il menù</string>
<string name="drawer_close">Chiudi il menù</string>
- <string name="drawer_preferences">Preferenze Cassetto</string>
+ <string name="drawer_preferences">Preferenze del Drawer</string>
+ <string name="drawer_feed_order_unplayed_episodes">Ordina per contatore</string>
<string name="drawer_feed_order_alphabetical">Ordina alfabeticamente</string>
<string name="drawer_feed_order_last_update">Ordina per data di pubblicazione</string>
+ <string name="drawer_feed_order_most_played">Ordina per numero di episodi riprodotti</string>
<string name="drawer_feed_counter_new_unplayed">Numero di episodi nuovi e non riprodotti</string>
<string name="drawer_feed_counter_new">Numero di episodi nuovi</string>
<string name="drawer_feed_counter_unplayed">Numero di episodi non riprodotti</string>
<string name="drawer_feed_counter_downloaded">Numero di episodi scaricati</string>
- <string name="drawer_feed_counter_none">Nessuno</string>
+ <string name="drawer_feed_counter_none">Nulla</string>
<!--Webview actions-->
- <string name="open_in_browser_label">Apri nel browser</string>
+ <string name="open_in_browser_label">Apri nel Browser</string>
<string name="copy_url_label">Copia URL</string>
<string name="share_url_label">Condividi URL</string>
<string name="copied_url_msg">URL copiato negli appunti</string>
@@ -50,6 +59,7 @@
<string name="cancel_label">Annulla</string>
<string name="yes">Sì</string>
<string name="no">No</string>
+ <string name="reset">Reimposta</string>
<string name="author_label">Autore</string>
<string name="language_label">Lingua</string>
<string name="url_label">URL</string>
@@ -60,9 +70,10 @@
<string name="refresh_label">Aggiorna</string>
<string name="external_storage_error_msg">Non risulta disponibile lo spazio di archiviazione esterno. Assicurati che lo spazio di archiviazione sia montato per permettere all\'applicazione di funzionare correttamente.</string>
<string name="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">Episodi più recenti:\u0020</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>
@@ -73,14 +84,16 @@
<string name="retry_label">Riprova</string>
<string name="auto_download_label">Includi nei download automatici</string>
<string name="auto_download_apply_to_items_title">Applica ai Precedenti Episodi</string>
- <string name="auto_download_apply_to_items_message">La nuova funzione <i>Auto Download</i> sarà applicata automaticamente ai nuovi episodi.\nVuoi attivarla anche per gli episodi pubblicati precedentemente?</string>
+ <string name="auto_download_apply_to_items_message">L\'opzione <i>Download Automatico</i> verrà applicata ai nuovi episodi.\nVuoi anche applicarla agli episodi precedenti?</string>
<string name="auto_delete_label">Elimina Episodi Automaticamente</string>
<string name="parallel_downloads_suffix">\u0020download paralleli</string>
+ <string name="feed_auto_download_global">Predefinita globale</string>
<string name="feed_auto_download_always">Sempre</string>
<string name="feed_auto_download_never">Mai</string>
<string name="send_label">Invia...</string>
<string name="episode_cleanup_never">Mai</string>
<string name="episode_cleanup_queue_removal">Quando non è in coda</string>
+ <string name="episode_cleanup_after_listening">Dopo il completamento</string>
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 giorno dopo il completamento</item>
<item quantity="other">%d giorni dopo il completamento</item>
@@ -90,26 +103,34 @@
<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>
<!--Actions on feeds-->
<string name="mark_all_read_label">Segna tutti come riprodotti</string>
<string name="mark_all_read_msg">Segnati tutti gli episodi come riprodotti</string>
- <string name="mark_all_read_confirmation_msg">Per favore conferma che vuoi segnare tutti gli episodi come riprodotti.</string>
- <string name="mark_all_read_feed_confirmation_msg">Per favore conferma che vuoi segnare tutti gli episodi di questo feed come riprodotti.</string>
+ <string name="mark_all_read_confirmation_msg">Conferma che desideri segnare tutti gli episodi come riprodotti.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Conferma che desideri segnare tutti gli episodi in questo feed come riprodotti.</string>
<string name="mark_all_seen_label">Segna tutti come visti</string>
+ <string name="mark_all_seen_msg">Segnati tutti gli episodi come visti</string>
+ <string name="mark_all_seen_confirmation_msg">Conferma che desideri segnare tutti gli episodi come visti.</string>
<string name="show_info_label">Informazioni</string>
- <string name="rename_feed_label">Rinomina Podcast</string>
+ <string name="show_feed_settings_label">Mostra le impostazioni del feed</string>
+ <string name="feed_info_label">Informazioni feed</string>
+ <string name="feed_settings_label">Impostazioni feed</string>
+ <string name="rename_feed_label">Rinomina podcast</string>
<string name="remove_feed_label">Rimuovi podcast</string>
<string name="share_label">Condividi...</string>
- <string name="share_link_label">Condividi il link al sito</string>
+ <string name="share_link_label">Condividi il link</string>
+ <string name="share_file_label">Condividi il file</string>
<string name="share_link_with_position_label">Condividi il Link con la Posizione</string>
- <string name="share_feed_url_label">Condividi URL del Feed</string>
- <string name="share_item_url_label">Condividi URL del File dell\'episodio</string>
- <string name="share_item_url_with_position_label">Condividi l\'URL del File dell\'epsiodio con la Posizione</string>
- <string name="feed_remover_msg">Rimozione feed</string>
+ <string name="share_feed_url_label">Condividi URL del feed</string>
+ <string name="share_item_url_label">Condividi l\'URL dell\'episodio</string>
+ <string name="share_item_url_with_position_label">Condividi l\'URL del file dell\'epsiodio con la posizione</string>
+ <string name="feed_delete_confirmation_msg">Conferma che desideri cancellare il feed \"%1$s\" e TUTTI i suoi episodi scaricati.</string>
+ <string name="feed_remover_msg">Rimozione del Feed in corso</string>
<string name="load_complete_feed">Ricarica il feed completo</string>
<string name="hide_episodes_title">Nascondi gli episodi</string>
- <string name="episode_actions">Applica azioni</string>
+ <string name="batch_edit">Modifica in gruppo</string>
<string name="hide_unplayed_episodes_label">Non riprodotti</string>
<string name="hide_paused_episodes_label">In pausa</string>
<string name="hide_played_episodes_label">Riprodotti</string>
@@ -117,6 +138,7 @@
<string name="hide_not_queued_episodes_label">Non in coda</string>
<string name="hide_downloaded_episodes_label">Scaricati</string>
<string name="hide_not_downloaded_episodes_label">Non scaricati</string>
+ <string name="hide_has_media_label">Pulizia dell\'episodio</string>
<string name="filtered_label">Filtrati</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Ultimo aggiornamento fallito</string>
<string name="open_podcast">Apri Podcast</string>
@@ -128,6 +150,7 @@
<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="remove_episode_lable">Rimuovi l\'episodio</string>
<string name="marked_as_seen_label">Segna come visto</string>
<string name="mark_read_label">Segna come riprodotto</string>
@@ -143,18 +166,20 @@
<string name="visit_website_label">Visita il sito</string>
<string name="support_label">Carica questo su Flattr</string>
<string name="skip_episode_label">Salta l\'episodio</string>
- <string name="activate_auto_download">Attiva il download automatico</string>
- <string name="deactivate_auto_download">Disattiva il download automatico</string>
- <string name="reset_position">Azzera la posizione della riproduzione</string>
- <string name="removed_item">Oggetto rimosso</string>
+ <string name="activate_auto_download">Attiva il Download Automatico</string>
+ <string name="deactivate_auto_download">Disattiva il Download Automatico</string>
+ <string name="reset_position">Azzera la Posizione di Riproduzione</string>
+ <string name="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 su file</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>
@@ -166,8 +191,9 @@
<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 Downoad</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>
@@ -179,7 +205,7 @@
<string name="downloads_processing">Elaborazione dei download in corso</string>
<string name="download_notification_title">Download podcast in corso</string>
<string name="download_report_content">%1$d download con successo, %2$d falliti</string>
- <string name="download_log_title_unknown">Titolo sconosciuto</string>
+ <string name="download_log_title_unknown">Titolo Sconosciuto</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">File multimediali</string>
<string name="download_type_image">Immagine</string>
@@ -187,8 +213,10 @@
<string name="authentication_notification_title">Autenticazione richiesta</string>
<string name="authentication_notification_msg">La risorsa che hai richiesto richiede un nome utente e una password</string>
<string name="confirm_mobile_download_dialog_title">Conferma il download su cellulare</string>
- <string name="confirm_mobile_download_dialog_only_add_to_queue">Aggiungi solo alla coda</string>
- <string name="confirm_mobile_download_dialog_enable_temporarily">Abilita temporaneamente</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">Il download tramite rete mobile è disattivato nelle impostazioni.\n\nÈ possibile scegliere di aggiungere semplicemente l\'episodio alla coda o consentire temporaneamente il download.\n\n<small>La scelta verrà ricordata per 10 minuti.</small></string>
+ <string name="confirm_mobile_download_dialog_message">Il download tramite rete mobile è disattivato nelle impostazioni.\n\nVuoi abilitare temporaneamente il download?\n\n<small>La scelta verrà ricordata per 10 minuti.</small></string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Aggiungi alla coda</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Consenti temporaneamente</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Errore!</string>
<string name="player_stopped_msg">Nessun media in riproduzione</string>
@@ -199,6 +227,7 @@
<string name="playback_error_unknown">Errore sconosciuto</string>
<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-->
@@ -206,7 +235,7 @@
<string name="unlock_queue">Sblocca la coda</string>
<string name="queue_locked">Coda bloccata</string>
<string name="queue_unlocked">Coda sbloccata</string>
- <string name="clear_queue_label">Svuota la coda</string>
+ <string name="clear_queue_label">Svuota la Coda</string>
<string name="undo">Undo</string>
<string name="removed_from_queue">Oggetto rimosso</string>
<string name="move_to_top_label">Sposta all\'inizio</string>
@@ -214,8 +243,10 @@
<string name="sort">Ordina</string>
<string name="date">Per data</string>
<string name="duration">Per durata</string>
- <string name="episode_title">Titolo episodio</string>
- <string name="feed_title">Titolo feed</string>
+ <string name="episode_title">Titolo dell\'episodio</string>
+ <string name="feed_title">Titolo del feed</string>
+ <string name="random">Casuale</string>
+ <string name="smart_shuffle">Casuale intelligente</string>
<string name="ascending">In ordine crescente</string>
<string name="descending">In ordine decrescente</string>
<string name="clear_queue_confirmation_msg">Per favore conferma che vuoi rimuovere dalla coda TUTTI gli episodi in essa presenti.</string>
@@ -248,38 +279,66 @@
<!--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_label">Non ci sono oggetti in questo elenco.</string>
<string name="no_feeds_label">Non sei ancora abbonato a nessun feed.</string>
<string name="no_chapters_label">Questo episodio non ha capitoli.</string>
+ <string name="no_shownotes_label">Questo episodio non ha note.</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="services_label">Servizi</string>
+ <string name="integrations_label">Integrazioni</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Servizio micropagamenti</string>
+ <string name="automation">Automazione</string>
+ <string name="download_pref_details">Dettagli</string>
+ <string name="import_export_pref">Importa/Esporta</string>
+ <string name="appearance">Aspetto</string>
+ <string name="external_elements">Elementi esterni</string>
+ <string name="interruptions">Interruzioni</string>
+ <string name="buttons">Pulsanti</string>
+ <string name="media_player">Media player</string>
+ <string name="pref_episode_cleanup_title">Pulizia episodi</string>
+ <string name="pref_episode_cleanup_summary">Gli episodi che non sono in coda e non sono tra i preferiti potrebbero essere rimossi se i Download Automatici richiedono maggiore spazio.</string>
+ <string name="pref_pauseOnDisconnect_sum">Sospendi la riproduzione quando le cuffie o il bluetooth vengono disconnessi</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Riprendi la riproduzione quando vengono riconnesse le cuffie</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Riprendi la riproduzione quando il Bluetooth si riconnette</string>
+ <string name="pref_hardwareForwardButtonSkips_title">Il tasto Avanti salta la traccia</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">Quando viene premuto un tasto Avanti fisico, viene saltata la traccia invece di andare avanti veloce</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">Il tasto Indietro riavvia la traccia</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">Quando viene premuto un tasto Indietro fisico, viene riavviata la traccia invece di tornare indietro</string>
<string name="pref_followQueue_sum">Passa al prossimo episodio in coda quanto si completa una riproduzione</string>
<string name="pref_auto_delete_sum">Elimina l\'episodio quando viene completata la riproduzione</string>
<string name="pref_auto_delete_title">Elimina automaticamente</string>
- <string name="pref_skip_keeps_episodes_title">Manteni gli Episodi Saltati</string>
+ <string name="pref_smart_mark_as_played_sum">Contrassegna gli episodi come riprodotti anche se rimangono alcuni secondi da riprodurre</string>
+ <string name="pref_smart_mark_as_played_title">Segna come Riprodotto dopo</string>
+ <string name="pref_skip_keeps_episodes_sum">Mantieni in coda gli episodi quando vengono saltati</string>
+ <string name="pref_skip_keeps_episodes_title">Manteni gli episodi saltati</string>
+ <string name="pref_favorite_keeps_episodes_sum">Mantieni gli episodi quando sono segnati come Preferiti</string>
+ <string name="pref_favorite_keeps_episodes_title">Mantieni episodi preferiti</string>
<string name="playback_pref">Riproduzione</string>
<string name="network_pref">Rete</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">Intervallo o orario di aggiornamento</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">Imposta un intervallo di tempo o un orario specifico in cui le sottoscrizioni vengono aggiornate automaticamente</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Puoi impostare un <i>intervallo</i> come \"ogni 2 ore\", impostare <i>un\'ora del giorno</i> specifica, come \"7:00\" oppure <i>disabilitare</i> gli aggiornamenti automatici del tutto.\n\n<small>Nota: I tempi di aggiornamento non sono perfetti. Potrai riscontrare dei brevi ritardi.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Disabilita</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Imposta Intervallo</string>
+ <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">Playback continuo</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">Riconnetti le cuffie</string>
+ <string name="pref_unpauseOnHeadsetReconnect_title">Riconnessione cuffie</string>
<string name="pref_unpauseOnBluetoothReconnect_title">Riconnessione Bluetooth</string>
- <string name="pref_mobileUpdate_title">Update su rete mobile</string>
+ <string name="pref_mobileUpdate_title">Aggiornamenti su Reti a Consumo</string>
<string name="pref_mobileUpdate_sum">Permetti gli aggiornamenti tramite connessione dati mobile</string>
<string name="refreshing_label">Aggiornamento</string>
<string name="flattr_settings_label">Impostazioni Flattr</string>
@@ -292,19 +351,29 @@
<string name="pref_auto_flattr_title">Flattr automatico</string>
<string name="pref_auto_flattr_sum">Configura l\'esecuzione automatica di Flattr</string>
<string name="user_interface_label">Interfaccia utente</string>
- <string name="pref_set_theme_title">Seleziona il tema</string>
- <string name="pref_nav_drawer_feed_order_title">Imposta l\'ordine delle Iscrizioni</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">Aggiunti o rimuovi gli elementi che appaiono nel menù laterale.</string>
+ <string name="pref_nav_drawer_feed_order_title">Imposta l\'ordine delle sottoscrizioni</string>
+ <string name="pref_nav_drawer_feed_order_sum">Modifica l\'ordine delle tue sottoscrizioni</string>
+ <string name="pref_nav_drawer_feed_counter_title">Contatore delle sottoscrizioni</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Scegli cosa rappresenta il numero nel menù laterale alla voce Sottoscrizioni</string>
<string name="pref_set_theme_sum">Cambia l\'aspetto di AntennaPod</string>
- <string name="pref_automatic_download_title">Download automatico</string>
+ <string name="pref_automatic_download_title">Download Automatico</string>
<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="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 paralleli</string>
- <string name="pref_episode_cache_title">Cache degli episodi</string>
- <string name="pref_theme_title_light">Light</string>
- <string name="pref_theme_title_dark">Dark</string>
+ <string name="pref_parallel_downloads_title">Download Contemporanei</string>
+ <string name="pref_episode_cache_title">Cache degli Episodi</string>
+ <string name="pref_theme_title_light">Chiaro</string>
+ <string name="pref_theme_title_dark">Scuro</string>
+ <string name="pref_theme_title_trueblack">Nero</string>
<string name="pref_episode_cache_unlimited">Illimitato</string>
<string name="pref_update_interval_hours_plural">ore</string>
<string name="pref_update_interval_hours_singular">ora</string>
@@ -315,51 +384,102 @@
<string name="pref_gpodnet_logout_toast">Logout effettuato</string>
<string name="pref_gpodnet_setlogin_information_title">Cambia le informazioni di login</string>
<string name="pref_gpodnet_setlogin_information_sum">Cambia le informazioni di login per il tuo account gpodder.net.</string>
+ <string name="pref_gpodnet_sync_changes_title">Esegui sincronizzazione delle modifiche</string>
+ <string name="pref_gpodnet_sync_changes_sum">Sincronizza le modifiche alle sottoscrizioni e allo stato degli episodi su gpodder.net</string>
+ <string name="pref_gpodnet_full_sync_title">Esegui sincronizzazione completa</string>
+ <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_fast_forward">Tempo di salto in avanti</string>
+ <string name="pref_fast_forward_sum">Personalizza il numero di secondi da saltare in avanti quando si preme il tasto Avanti Veloce</string>
+ <string name="pref_rewind">Tempo di salto indietro</string>
+ <string name="pref_rewind_sum">Personalizza il numero di secondi da saltare indietro quando si preme il tasto Riavvolgi</string>
<string name="pref_gpodnet_sethostname_title">Imposta l\'hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Usa l\'host di default</string>
<string name="pref_expandNotify_title">Espandi le notifiche</string>
<string name="pref_expandNotify_sum">Espandi sempre le notifiche per mostrare i pulsanti di riproduzione.</string>
<string name="pref_persistNotify_title">Controlli di riproduzione persistenti</string>
<string name="pref_persistNotify_sum">Mantieni le notifiche e i controlli del blocco dello schermo quando la riproduzione è in pausa.</string>
+ <string name="pref_compact_notification_buttons_title">Pulsanti su schermata di blocco</string>
+ <string name="pref_compact_notification_buttons_sum">Modifica i pulsanti di riproduzione sulla schemata di blocco. Play/Pausa è sempre presente.</string>
+ <string name="pref_compact_notification_buttons_dialog_title">Seleziona al massimo %1$d voci</string>
+ <string name="pref_compact_notification_buttons_dialog_error">Puoi selezionare al massimo %1$d voci.</string>
+ <string name="pref_lockscreen_background_title">Cambia sfondo della schermata di blocco</string>
+ <string name="pref_lockscreen_background_sum">Sostituisce l\'immagine della schermata di blocco con quella dell\'episodio in riproduzione. Mostrerà l\'immagine anche in app di terze parti.</string>
<string name="pref_showDownloadReport_title">Mostra il Rapporto del Download</string>
+ <string name="pref_showDownloadReport_sum">Se il download fallisce, genera un report che mostra i dettagli dell\'errore.</string>
<string name="pref_expand_notify_unsupport_toast">Le versioni di Android prima della 4.1 non supportano le notifiche estese.</string>
+ <string name="pref_queueAddToFront_sum">Aggiungi un nuovo episodio in testa alla coda.</string>
<string name="pref_queueAddToFront_title">Aggiungi in cima alla coda</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_title">Dimensione cache delle immagini</string>
+ <string name="pref_image_cache_size_sum">Dimensione cache per le 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="experimental_pref">Sperimentale</string>
- <string name="pref_current_value">Valore corrente: %1$s</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_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>
+ <string name="pref_cast_message_free_flavor">Chromecast richiede librerie proprietarie di terze parti che sono disabilitate in questa versione di AntennaPod</string>
+ <string name="pref_enqueue_downloaded_title">Aggiungi i download alla coda</string>
+ <string name="pref_enqueue_downloaded_summary">Aggiungi gli episodi scaricati alla coda di riproduzione</string>
+ <string name="media_player_builtin">Player Android integrato</string>
+ <string name="pref_videoBehavior_title">Comportamento del video</string>
+ <string name="pref_videoBehavior_sum">Comportamento quando si esce dalla riproduzione video</string>
+ <string name="stop_playback">Interrompi riproduzione</string>
+ <string name="continue_playback">Continua la riproduzione</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Abilita l\'esecuzione automatica di Flattr</string>
<string name="auto_flattr_after_percent">Carica l\'episodio su Flattr appena è stato riprodotto al %d percento</string>
<string name="auto_flattr_ater_beginning">Carica l\'episodio su Flattr appena comincia la riproduzione</string>
<string name="auto_flattr_ater_end">Carica l\'episodio su Flattr appena finisce la riproduzione</string>
<!--Search-->
+ <string name="search_hint">Cerca negli episodi</string>
+ <string name="found_in_shownotes_label">Trovato nelle note dell\'episodio</string>
<string name="found_in_chapters_label">Trovato nei capitoli</string>
+ <string name="found_in_authors_label">Trovato nell\'autore</string>
+ <string name="found_in_feeds_label">Trovato nel feed</string>
<string name="search_status_no_results">Nessun risultato trovato</string>
<string name="search_label">Ricerca</string>
<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">Importazione OPML</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">Esportazione su OPML</string>
+ <string name="html_export_label">Esporta in HTML</string>
+ <string name="exporting_label">Esportazione in corso...</string>
<string name="export_error_label">Errore di esportazione</string>
- <string name="opml_export_success_title">Esportazione OPML avvenuta con successo.</string>
- <string name="opml_export_success_sum">Il file .opml è stato scritto su:\u0020</string>
+ <string name="export_success_title">Esportazione eseguita</string>
+ <string name="export_success_sum">Il file esportato è stato salvato in:\n\n%1$s</string>
+ <string name="opml_import_ask_read_permission">E\' necessario accedere alla memoria esterna per leggere il file OPML</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Imposta timer</string>
<string name="disable_sleeptimer_label">Disabilita il timer di spegnimento</string>
@@ -367,6 +487,8 @@
<string name="sleep_timer_label">Timer di spegnimento</string>
<string name="time_left_label">Tempo residuo:\u0020</string>
<string name="time_dialog_invalid_input">Input non valido, il campo deve essere un numero intero.</string>
+ <string name="timer_about_to_expire_label"><b>Quando il timer sta per scadere:</b></string>
+ <string name="shake_to_reset_label">Scuoti per resettare il timer</string>
<string name="timer_vibration_label">Vibra</string>
<string name="time_seconds">secondi</string>
<string name="time_minutes">minuti</string>
@@ -383,6 +505,9 @@
<item quantity="one">1 ora</item>
<item quantity="other">%d ore</item>
</plurals>
+ <string name="auto_enable_label">Abilita automaticamente</string>
+ <string name="sleep_timer_enabled_label">Timer spegnimento abilitato</string>
+ <string name="sleep_timer_disabled_label">Timer spegnimento disabilitato</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">CATEGORIE</string>
<string name="gpodnet_toplist_header">TOP PODCAST</string>
@@ -402,6 +527,7 @@
<string name="gpodnetauth_device_chooseExistingDevice">Scegli un dispositivo esistente:</string>
<string name="gpodnetauth_device_errorEmpty">L\'ID del dispositivo non può essere vuoto</string>
<string name="gpodnetauth_device_errorAlreadyUsed">ID di dispositivo già in uso</string>
+ <string name="gpodnetauth_device_caption_errorEmpty">La didascalia non può essere vuota</string>
<string name="gpodnetauth_device_butChoose">Scegli</string>
<string name="gpodnetauth_finish_title">Login effettuato!</string>
<string name="gpodnetauth_finish_descr">Congraturazioni! Il tuo account gpodder.net è stato collegato con il tuo dispositivo. Da ora AntennaPod sincronizzerà automaticamente le sottoscrizioni sul tuo dispositivo con il tuo account gpodder.net.</string>
@@ -411,10 +537,14 @@
<string name="gpodnetsync_auth_error_descr">Nome utente o password errati</string>
<string name="gpodnetsync_error_title">gpodder.net errore di sincronizzazione</string>
<string name="gpodnetsync_error_descr">Rilevato un errore in fase di sincronizzazione:\u0020</string>
+ <string name="gpodnetsync_pref_report_successful">Eseguito</string>
+ <string name="gpodnetsync_pref_report_failed">Fallito</string>
<!--Directory chooser-->
<string name="selected_folder_label">Seleziona la cartella:</string>
<string name="create_folder_label">Crea una cartella</string>
<string name="choose_data_directory">Scegli la directory per i dati</string>
+ <string name="choose_data_directory_message">Scegli la base della tua cartella dati. AntennaPod creerà le sottocartelle appropriate.</string>
+ <string name="choose_data_directory_permission_rationale">E\' necessario accedere alla memoria esterna per cambiare la cartella dei dati</string>
<string name="create_folder_msg">Crea una nuova directory con nome \"%1$s\"?</string>
<string name="create_folder_success">Crea una nuova directory</string>
<string name="create_folder_error_no_write_access">Impossibile scrivere in questa directory</string>
@@ -428,10 +558,13 @@
<string name="set_to_default_folder">Scegli la cartella predefinita</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Sospendi la riproduzione invece di abbassare il volume quando un\'altra app emette un suono</string>
<string name="pref_pausePlaybackForFocusLoss_title">Pausa su interruzione</string>
+ <string name="pref_resumeAfterCall_sum">Riprendi la riproduzione al termine di una chiamata</string>
<string name="pref_resumeAfterCall_title">Riprendi dopo la chiamata</string>
+ <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>
<!--Content descriptions for image buttons-->
<string name="rewind_label">Riavvolgi</string>
<string name="fast_forward_label">Avanti veloce</string>
@@ -445,47 +578,104 @@
<!--Feed information screen-->
<string name="authentication_label">Autenticazione</string>
<string name="authentication_descr">Cambia il tuo nome utente e la tua password per questo podcast e i suoi episodi.</string>
+ <string name="auto_download_settings_label">Impostazioni download automatici</string>
+ <string name="episode_filters_label">Filtro degli episodi</string>
+ <string name="episode_filters_description">Elenco di termini per filtrare gli episodi da includere o escludere dai download automatici.</string>
<string name="episode_filters_include">Includi</string>
<string name="episode_filters_exclude">Escludi</string>
+ <string name="episode_filters_hint">Parole singole \n\"Parole multiple\"</string>
<string name="keep_updated">Mantieni Aggiornato</string>
<!--Progress information-->
+ <string name="progress_upgrading_database">Aggiornamento del database</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importazione di sottoscrizioni da applicazioni monouso in corso...</string>
<string name="search_itunes_label">Cerca su iTunes</string>
<string name="filter">Filtro</string>
+ <string name="search_fyyd_label">Cerca su fyyd</string>
<!--Episodes apply actions-->
- <string name="all_label">Tutti</string>
- <string name="selected_all_label">Tutti gli Episodi Selezionati</string>
- <string name="none_label">Nessuno</string>
- <string name="deselected_all_label">Tutti gli Episodi Deselezionati</string>
- <string name="played_label">Riprodotto</string>
- <string name="unplayed_label">Non riprodotto</string>
+ <string name="all_label">Tutto</string>
+ <string name="selected_all_label">Seleziona tutti gli Episodi</string>
+ <string name="none_label">Nulla</string>
+ <string name="deselected_all_label">De-seleziona tutti gli episodi</string>
+ <string name="played_label">Riprodotti</string>
+ <string name="selected_played_label">Selezionati gli episodi riprodotti</string>
+ <string name="unplayed_label">Non riprodotti</string>
+ <string name="selected_unplayed_label">Selezionati gli episodi non riprodotti</string>
<string name="downloaded_label">Scaricati</string>
+ <string name="selected_downloaded_label">Seleziona gli episodi scaricati</string>
<string name="not_downloaded_label">Non scaricati</string>
+ <string name="selected_not_downloaded_label">Seleziona gli episodi non scaricati</string>
<string name="queued_label">In coda</string>
+ <string name="selected_queued_label">Seleziona gli episodi in coda</string>
<string name="not_queued_label">Non in coda</string>
+ <string name="selected_not_queued_label">Seleziona gli episodi non in coda</string>
+ <string name="has_media">Ha media</string>
+ <string name="selected_has_media_label">Seleziona gli episodi con elementi multimediali</string>
<!--Sort-->
<string name="sort_title_a_z">Titolo (A \u2192 Z)</string>
<string name="sort_title_z_a">Titolo (Z \u2192 A)</string>
- <string name="sort_date_new_old">Data (New \u2192 Old)</string>
- <string name="sort_date_old_new">Data (Old \u2192 New)</string>
- <string name="sort_duration_short_long">Durata (Short \u2192 Long)</string>
- <string name="sort_duration_long_short">Durata (Long \u2192 Short)</string>
+ <string name="sort_date_new_old">Data (Nuovi \u2192 Vecchi)</string>
+ <string name="sort_date_old_new">Data (Vecchi \u2192 Nuovi)</string>
+ <string name="sort_duration_short_long">Durata (Corti \u2192 Lunghi)</string>
+ <string name="sort_duration_long_short">Durata (Lunghi \u2192 Corti)</string>
<!--Rating dialog-->
<string name="rating_title">Ti piace AntennaPod?</string>
+ <string name="rating_message">Ci farebbe molto piacere se potessi valutare AntennaPod.</string>
+ <string name="rating_never_label">Lasciatemi in pace</string>
<string name="rating_later_label">Ricordamelo più tardi</string>
+ <string name="rating_now_label">Certo, facciamolo!</string>
<!--Audio controls-->
+ <string name="audio_controls">Controlli audio</string>
+ <string name="playback_speed">Velocità di riproduzione</string>
<string name="volume">Volume</string>
- <string name="left_short">L</string>
- <string name="right_short">R</string>
+ <string name="left_short">Sx</string>
+ <string name="right_short">Dx</string>
<string name="audio_effects">Effetti Audio</string>
+ <string name="stereo_to_mono">Downmix: da Stereo a Mono</string>
+ <string name="sonic_only">solo Sonic</string>
<!--proxy settings-->
<string name="proxy_type_label">Tipo</string>
<string name="host_label">Host</string>
<string name="port_label">Porta</string>
<string name="optional_hint">(Opzionale)</string>
+ <string name="proxy_test_label">Test</string>
+ <string name="proxy_checking">Controllo in corso...</string>
+ <string name="proxy_test_successful">Test avvenuto con successo</string>
+ <string name="proxy_test_failed">Test fallito</string>
+ <string name="proxy_host_empty_error">L\'host non può essere vuoto</string>
+ <string name="proxy_host_invalid_error">L\'host non è un IP o un dominio valido</string>
<string name="proxy_port_invalid_error">Porta non valida</string>
+ <!--Database import/export-->
+ <string name="import_export">Importa/Esporta database</string>
+ <string name="import_export_warning">Questa funzione sperimentale può essere usata per trasferire le sottoscrizioni e gli episoti completati ad un altro dispositivo.\n\nIl database potrà essere importato solo se si usa la stessa versione di AntennaPod, altrimenti potrebbero generarsi comportamenti anomali.\n\nDopo l\'importazione, gli episodi potrebbero essere mostrati come scaricati anche se non lo sono. Basta premere Play per farli riconoscere ad AntennaPod.</string>
+ <string name="label_import">Importa</string>
+ <string name="label_export">Esporta</string>
+ <string name="import_select_file">Scegli file da importare</string>
+ <string name="export_ok">Esportazione eseguita</string>
+ <string name="import_ok">Importazione eseguita.\n\nPremi OK per riavviare AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Riproduci su...</string>
+ <string name="cast_disconnect_label">Disconnetti la sessione di cast</string>
+ <string name="cast_not_castable">Il media selezionato non è compatibile con il dispositivo di ricezione</string>
+ <string name="cast_failed_to_play">Avvio della riproduzione del media fallito</string>
+ <string name="cast_failed_to_stop">Arresto della riproduzione del media fallito</string>
+ <string name="cast_failed_to_pause">Pausa della riproduzione del media fallita</string>
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <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>
+ <string name="cast_failed_media_error_skipping">Errore nella riproduzione. Salto...</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Azione richesta</string>
+ <string name="notification_channel_user_action_description">Mostra se è richesto un tuo intervento, per sempio se è necessario inserire la password.</string>
+ <string name="notification_channel_downloading">Scaricando</string>
+ <string name="notification_channel_downloading_description">Mostra mentre è in corso il download</string>
+ <string name="notification_channel_playing">In riproduzione</string>
+ <string name="notification_channel_playing_description">Permette di controllare la riproduzione. Questa è la principale notifica visibile quando un prodcast è in riproduzione.</string>
+ <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>
</resources>
diff --git a/core/src/main/res/values-it/strings.xml b/core/src/main/res/values-it/strings.xml
index 2549a5bac..d6effd4ef 100644
--- a/core/src/main/res/values-it/strings.xml
+++ b/core/src/main/res/values-it/strings.xml
@@ -5,34 +5,48 @@
<string name="statistics_label">Statistiche</string>
<string name="add_feed_label">Aggiungi un podcast</string>
<string name="episodes_label">Episodi</string>
- <string name="all_episodes_short_label">Tutto</string>
+ <string name="all_episodes_short_label">Tutti</string>
<string name="favorite_episodes_label">Preferiti</string>
<string name="new_label">Nuovo</string>
<string name="settings_label">Impostazioni</string>
- <string name="add_new_feed_label">Aggiungi un podcast</string>
<string name="downloads_label">Download</string>
<string name="downloads_running_label">In corso</string>
<string name="downloads_completed_label">Completati</string>
<string name="downloads_log_label">Registro</string>
- <string name="subscriptions_label">Sottoscrizioni</string>
- <string name="subscriptions_list_label">Elenco delle sottoscrizioni</string>
+ <string name="subscriptions_label">Abbonamenti</string>
+ <string name="subscriptions_list_label">Elenco degli Abbonamenti</string>
<string name="cancel_download_label">Annulla\nil download</string>
<string name="playback_history_label">Cronologia delle riproduzioni</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_auth_label">Accesso a gpodder.net</string>
<string name="free_space_label">%1$s liberi</string>
+ <string name="episode_cache_full_title">Cache degli episodi piena</string>
+ <string name="episode_cache_full_message">Lo spazio di memoria della cache dell\'episodio è esaurito. Puoi aumentarlo nelle Impostazioni</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>
+ <string name="statistics_mode">Modalità di statistica</string>
+ <string name="statistics_mode_normal">Calcola l\'attuale tempo di riproduzione. Riprodurre un podcast due volte verrà contato due volte, mentre segnarlo come riprodotto no.</string>
+ <string name="statistics_mode_count_all">Somma tutti i podcast segnati come riprodotti</string>
+ <string name="statistics_speed_not_counted">Avviso: La velocità di riproduzione non viene considerata.</string>
<!--Main activity-->
<string name="drawer_open">Apri il menù</string>
<string name="drawer_close">Chiudi il menù</string>
+ <string name="drawer_preferences">Preferenze del Drawer</string>
+ <string name="drawer_feed_order_unplayed_episodes">Ordina per contatore</string>
<string name="drawer_feed_order_alphabetical">Ordina alfabeticamente</string>
- <string name="drawer_feed_order_last_update">Ordina secondo la data di pubblicazione</string>
+ <string name="drawer_feed_order_last_update">Ordina per data di pubblicazione</string>
+ <string name="drawer_feed_order_most_played">Ordina per numero di episodi riprodotti</string>
+ <string name="drawer_feed_counter_new_unplayed">Numero di episodi nuovi e non riprodotti</string>
<string name="drawer_feed_counter_new">Numero di episodi nuovi</string>
<string name="drawer_feed_counter_unplayed">Numero di episodi non riprodotti</string>
+ <string name="drawer_feed_counter_downloaded">Numero di episodi scaricati</string>
+ <string name="drawer_feed_counter_none">Nulla</string>
<!--Webview actions-->
- <string name="open_in_browser_label">Apri nel browser</string>
+ <string name="open_in_browser_label">Apri nel Browser</string>
<string name="copy_url_label">Copia l\'URL</string>
<string name="share_url_label">Condividi l\'URL</string>
+ <string name="copied_url_msg">URL copiato negli appunti</string>
<string name="go_to_position_label">Vai a questa posizione</string>
<!--Playback history-->
<string name="clear_history_label">Pulisci la cronologia</string>
@@ -41,6 +55,7 @@
<string name="cancel_label">Annulla</string>
<string name="yes">Sì</string>
<string name="no">No</string>
+ <string name="reset">Reimposta</string>
<string name="author_label">Autore</string>
<string name="language_label">Lingua</string>
<string name="url_label">URL</string>
@@ -49,24 +64,31 @@
<string name="error_label">Errore</string>
<string name="error_msg_prefix">È avvenuto un errore:</string>
<string name="refresh_label">Ricarica</string>
+ <string name="external_storage_error_msg">Non è stata trovata nessuna memoria esterna. Controlla che la memoria esterna sia montata in modo che l\'app possa rilevarla</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">Lunghezza:\u0020</string>
<string name="size_prefix">Dimensione:\u0020</string>
- <string name="processing_label">Processando</string>
- <string name="loading_label">Caricamento in corso...</string>
+ <string name="processing_label">Elaborazione in corso</string>
+ <string name="loading_label">Caricamento...</string>
<string name="save_username_password_label">Salva il nome utente e la password</string>
<string name="close_label">Chiudi</string>
<string name="retry_label">Riprova</string>
<string name="auto_download_label">Includi nei download automatici</string>
<string name="auto_download_apply_to_items_title">Applica agli episodi precedenti</string>
- <string name="parallel_downloads_suffix">\u0020download paralleli</string>
+ <string name="auto_download_apply_to_items_message">L\'opzione <i>Download Automatico</i> verrà applicata ai nuovi episodi.\nVuoi anche applicarla agli episodi precedenti?</string>
+ <string name="auto_delete_label">Elimina Episodi Automaticamente</string>
+ <string name="parallel_downloads_suffix">\u0020download contemporanei</string>
+ <string name="feed_auto_download_global">Impostazione Globale</string>
<string name="feed_auto_download_always">Sempre</string>
<string name="feed_auto_download_never">Mai</string>
<string name="send_label">Invia...</string>
<string name="episode_cleanup_never">Mai</string>
+ <string name="episode_cleanup_queue_removal">Quando non è in coda</string>
<string name="episode_cleanup_after_listening">Dopo il completamento</string>
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 giorno dopo il completamento</item>
@@ -76,22 +98,41 @@
<string name="feedurl_label">URL del feed</string>
<string name="etxtFeedurlHint">www.example.com/feed</string>
<string name="txtvfeedurl_label">Aggiungi un podcast inserendo un 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>
<!--Actions on feeds-->
<string name="mark_all_read_label">Segna tutti come riprodotti</string>
+ <string name="mark_all_read_msg">Segnati tutti gli episodi come riprodotti</string>
<string name="mark_all_read_confirmation_msg">Conferma che desideri segnare tutti gli episodi come riprodotti.</string>
<string name="mark_all_read_feed_confirmation_msg">Conferma che desideri segnare tutti gli episodi in questo feed come riprodotti.</string>
+ <string name="mark_all_seen_label">Segna tutti come visti</string>
+ <string name="mark_all_seen_msg">Segnati tutti gli episodi come visti</string>
+ <string name="mark_all_seen_confirmation_msg">Conferma che desideri segnare tutti gli episodi come visti.</string>
<string name="show_info_label">Mostra delle informazioni</string>
+ <string name="rename_feed_label">Rinomina Podcast</string>
+ <string name="remove_feed_label">Rimuovi Podcast</string>
+ <string name="share_label">Condividi...</string>
+ <string name="share_link_label">Condividi il link</string>
+ <string name="share_file_label">Condividi il file</string>
+ <string name="share_link_with_position_label">Condividi il Link con la Posizione</string>
+ <string name="share_feed_url_label">Condividi URL del Feed</string>
+ <string name="share_item_url_label">Condividi l\'URL dell\'episodio</string>
+ <string name="share_item_url_with_position_label">Condividi l\'URL del File dell\'epsiodio con la Posizione</string>
+ <string name="feed_delete_confirmation_msg">Conferma che desideri cancellare il feed \"%1$s\" e TUTTI i suoi episodi scaricati.</string>
+ <string name="feed_remover_msg">Rimozione del Feed in corso</string>
+ <string name="load_complete_feed">Aggiorna il feed completo</string>
<string name="hide_episodes_title">Nascondi gli episodi</string>
- <string name="episode_actions">Applica le azioni</string>
<string name="hide_unplayed_episodes_label">Non riprodotti</string>
<string name="hide_paused_episodes_label">In pausa</string>
- <string name="hide_played_episodes_label">Riprodotto</string>
+ <string name="hide_played_episodes_label">Riprodotti</string>
<string name="hide_queued_episodes_label">In coda</string>
<string name="hide_not_queued_episodes_label">Non in coda</string>
- <string name="hide_downloaded_episodes_label">Scaricato</string>
- <string name="hide_not_downloaded_episodes_label">Non scaricato</string>
- <string name="filtered_label">Filtrato</string>
+ <string name="hide_downloaded_episodes_label">Scaricati</string>
+ <string name="hide_not_downloaded_episodes_label">Non scaricati</string>
+ <string name="filtered_label">Filtrati</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} Ultimo aggiornamento fallito</string>
+ <string name="open_podcast">Apri il Podcast</string>
<!--actions on feeditems-->
<string name="download_label">Scarica</string>
<string name="play_label">Riproduci</string>
@@ -99,34 +140,71 @@
<string name="stop_label">Interrompi</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="remove_episode_lable">Rimuovi l\'episodio</string>
+ <string name="marked_as_seen_label">Segna come visto</string>
<string name="mark_read_label">Segna come riprodotto</string>
<string name="marked_as_read_label">Segnato come riprodotto</string>
<string name="mark_unread_label">Segna come non riprodotto</string>
<string name="add_to_queue_label">Aggiungi alla coda</string>
<string name="added_to_queue_label">Aggiunto alla Coda</string>
<string name="remove_from_queue_label">Rimuovi dalla coda</string>
+ <string name="add_to_favorite_label">Aggiungi ai preferiti</string>
+ <string name="added_to_favorites">Aggiunto ai preferiti</string>
+ <string name="remove_from_favorite_label">Rimuovi dai preferiti</string>
+ <string name="removed_from_favorites">Rimosso dai preferiti</string>
<string name="visit_website_label">Visita il sito web</string>
+ <string name="support_label">Carica su Flattr</string>
<string name="skip_episode_label">Salta l\'episodio</string>
+ <string name="activate_auto_download">Attiva il Download Automatico</string>
+ <string name="deactivate_auto_download">Disattiva il Download Automatico</string>
+ <string name="reset_position">Azzera la Posizione di Riproduzione</string>
+ <string name="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_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>
+ <string name="download_error_unsupported_type">Tipo di feed non supportato</string>
+ <string name="download_error_connection_error">Errore di Connessione</string>
+ <string name="download_error_unknown_host">Host Sconosciuto</string>
+ <string name="download_error_unauthorized">Errore di Autenticazione</string>
+ <string name="download_error_file_type_type">Errore Formato FIle</string>
+ <string name="download_error_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\nDisabilitato <i>Download Automatico</i> per questo elemento</string>
+ <string name="download_canceled_autodownload_enabled_msg">Download annullato\n<i>Download Automatico</i> disabilitato per questo elemento</string>
+ <string name="download_report_title">Download completato con errori</string>
+ <string name="download_report_content_title">Rapporto del Downoad</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>
+ <string name="download_error_db_access">Errore di accesso al database</string>
+ <plurals name="downloads_left">
+ <item quantity="one">%d download rimasto</item>
+ <item quantity="other">%d download rimanenti</item>
+ </plurals>
+ <string name="downloads_processing">Elaborazione dei download in corso</string>
<string name="download_notification_title">Download dei dati del podcast in corso</string>
<string name="download_report_content">%1$d download hanno avuto successo, %2$d hanno fallito</string>
+ <string name="download_log_title_unknown">Titolo Sconosciuto</string>
<string name="download_type_feed">Feed</string>
<string name="download_type_media">File multimediale</string>
<string name="download_type_image">Immagine</string>
<string name="download_request_error_dialog_message_prefix">Si è verificato un errore nello scaricare il file:\u0020</string>
<string name="authentication_notification_title">Autenticazione richiesta</string>
<string name="authentication_notification_msg">La risorsa che hai richiesto richiede un nome utente e una password</string>
+ <string name="confirm_mobile_download_dialog_title">Conferma il download su dati mobili</string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">Metti in Coda</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">Consenti temporaneamente</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">Errore!</string>
<string name="player_stopped_msg">Nessun elemento multimediale in riproduzione</string>
@@ -138,52 +216,115 @@
<string name="no_media_playing_label">Nessun elemento multimediale in riproduzione</string>
<string name="player_buffering_msg">Buffer in corso</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="clear_queue_label">Svuota la Coda</string>
<string name="undo">Annulla</string>
<string name="removed_from_queue">Oggetto rimosso</string>
<string name="move_to_top_label">Sposta in cima</string>
<string name="move_to_bottom_label">Sposta in fondo</string>
<string name="sort">Ordina</string>
- <string name="date">Data</string>
- <string name="duration">Durata</string>
- <string name="ascending">Ascendente</string>
- <string name="descending">Discendente</string>
+ <string name="date">Per data</string>
+ <string name="duration">Per durata</string>
+ <string name="episode_title">Titolo dell\'episodio</string>
+ <string name="feed_title">Titolo del feed</string>
+ <string name="ascending">Crescente</string>
+ <string name="descending">Decrescente</string>
+ <string name="clear_queue_confirmation_msg">Per favore conferma che vuoi rimuovere dalla coda TUTTI gli episodi in essa presenti.</string>
<!--Flattr-->
<string name="flattr_auth_label">Accesso a Flattr</string>
+ <string name="flattr_auth_explanation">Premi il tasto seguente per iniziare il processo di autenticazione. Sarai trasferito alla pagina di login di flattr sul tuo browser e ti sarà richiesto di garantire ad AntennaPod il permesso di effettuare microdonazioni. Dopo la tua autorizzazione, sarai riportato alla seguente schermata in modo automatico.</string>
<string name="authenticate_label">Autentica</string>
<string name="return_home_label">Ritorna alla pagina iniziale</string>
- <string name="no_flattr_token_notification_msg">Il tuo account flattr non sembra connesso ad AntennaPod. Clicca qui per accedere.</string>
+ <string name="flattr_auth_success">Autenticazione avvenuta con successo! Adesso puoi microdonare con flattr dall\'interno dell\'app.</string>
+ <string name="no_flattr_token_title">Nessun token flattr trovato</string>
+ <string name="no_flattr_token_notification_msg">Il tuo account Flattr non sembra essere collegato ad AntennaPod. Premi qui per accedere.</string>
+ <string name="no_flattr_token_msg">Il tuo account flattr non sembra essere collegato ad AntennaPod. Potresti collegare il tuo account ad AntennaPod per utilizzare flattr dall\'app oppure puoi visitare il sito per utilizzare flattr direttamente da lì.</string>
<string name="authenticate_now_label">Autentica</string>
<string name="action_forbidden_title">Azione proibita</string>
+ <string name="action_forbidden_msg">AntennaPod non ha il permesso di effettuare questa azione. La ragione potrebbe essere che il token di accesso di AntennaPod al tuo account è stato revocato. Puoi eseguire la re-autenticazione o altrimenti visitare il sito web.</string>
<string name="access_revoked_title">Accesso revocato</string>
+ <string name="access_revoked_info">Hai revocato l\'accesso di AntennaPod al tuo account. Al fine di completare il processo devi rimuovere l\'app dalla lista delle applicazioni autorizzare nelle impostazioni del tuo account sul sito di flattr.</string>
<!--Flattr-->
+ <string name="flattr_click_success">Caricata una cosa su Flattr!</string>
+ <string name="flattr_click_success_count">Caricate %d cose su Flattr!</string>
+ <string name="flattr_click_success_queue">Caricato su Flattr: %s.</string>
+ <string name="flattr_click_failure_count">Caricamento su Flattr fallito per %d cose!</string>
+ <string name="flattr_click_failure">Non caricato su Flattr: %s.</string>
+ <string name="flattr_click_enqueued">La cosa verrà caricata su Flattr più tardi</string>
+ <string name="flattring_thing">Caricamento su Flattr di %s in corso</string>
+ <string name="flattring_label">AntennaPod sta eseguendo Flattr</string>
+ <string name="flattrd_label">AntennaPod ha caricato su Flattr</string>
+ <string name="flattrd_failed_label">Caricamento su Flattr di AntennaPod fallito</string>
+ <string name="flattr_retrieving_status">Ricezione di cose caricate su Flattr in corso</string>
<!--Variable Speed-->
<string name="download_plugin_label">Scarica plugin</string>
<string name="no_playback_plugin_title">Plugin non installato</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_label">Non ci sono oggetti in questo elenco.</string>
- <string name="no_feeds_label">Non sei ancora iscritto a nessun feed.</string>
+ <string name="no_feeds_label">Non sei ancora abbonato a nessun feed.</string>
+ <string name="no_chapters_label">Questo episodio non ha capitoli.</string>
+ <string name="no_shownotes_label">Questo episodio non ha note.</string>
<!--Preferences-->
+ <string name="storage_pref">Memoria</string>
+ <string name="project_pref">Progetto</string>
<string name="other_pref">Altro</string>
<string name="about_pref">Riguardo a</string>
<string name="queue_label">Coda</string>
- <string name="services_label">Servizi</string>
<string name="flattr_label">Flattr</string>
+ <string name="pref_episode_cleanup_title">Pulizia dell\'episodio</string>
+ <string name="pref_episode_cleanup_summary">Gli episodi che non sono in coda e non sono preferiti dovrebbero essere idonei alla rimozione se Auto Download richiede spazio per nuovi episodi</string>
+ <string name="pref_pauseOnDisconnect_sum">Sospendi la riproduzione quando le cuffie o il bluetooth sono disconnessi</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Riprendi la riproduzione quando le cuffie vengono ricollegate</string>
- <string name="pref_followQueue_sum">Salta all\'elemento successivo della lista al termine della riproduzione</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">Reprendi la riproduzione quando le cuffie o il bluetooth sono disconnessi</string>
+ <string name="pref_followQueue_sum">Passa al prossimo episodio in coda al termine della riproduzione</string>
<string name="pref_auto_delete_sum">Elimina l\'episodio al termine della riproduzione</string>
- <string name="pref_auto_delete_title">Eliminazione Automatica</string>
+ <string name="pref_auto_delete_title">Elimina Automaticamente</string>
+ <string name="pref_smart_mark_as_played_sum">Contrassegna gli episodi come riprodotti anche se rimane meno di un certo numero di secondi di tempo di riproduzione</string>
+ <string name="pref_skip_keeps_episodes_title">Manteni gli Episodi Saltati</string>
<string name="playback_pref">Riproduzione</string>
<string name="network_pref">Rete</string>
- <string name="pref_mobileUpdate_sum">Permetti gli aggiornamenti su rete dati</string>
- <string name="refreshing_label">Ricaricamento</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Puoi impostare un <i>intervallo</i> come \"ogni 2 ore\", impostare <i>un\'ora del giorno</i> specifica, come \"7:00\" oppure <i>disabilitare</i> gli aggiornamenti automatici del tutto.\n\n<small>Nota: I tempi di aggiornamento non sono perfetti. Potrai riscontrare dei brevi ritardi.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">Disabilita</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">Imposta Intervallo</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">Riconnetti le Cuffie</string>
+ <string name="pref_unpauseOnBluetoothReconnect_title">Riconnessione Bluetooth</string>
+ <string name="pref_mobileUpdate_title">Aggiornamenti su Reti a Consumo</string>
+ <string name="pref_mobileUpdate_sum">Permetti gli aggiornamenti tramite connessione dati a consumo</string>
+ <string name="refreshing_label">Aggiornamento</string>
+ <string name="flattr_settings_label">Impostazioni Flattr</string>
<string name="pref_flattr_auth_title">Accesso a Flattr</string>
+ <string name="pref_flattr_auth_sum">Collega il tuo account flattr per utilizzare flattr direttamente dall\'app</string>
+ <string name="pref_flattr_this_app_title">Supporta con flattr questa app</string>
+ <string name="pref_flattr_this_app_sum">Supporta lo sviluppo di AntennaPod tramite flattr. Grazie!</string>
<string name="pref_revokeAccess_title">Revoca l\'accesso</string>
+ <string name="pref_revokeAccess_sum">Revoca il permesso, a questa applicazione, di accedere al tuo account flattr.</string>
+ <string name="pref_auto_flattr_title">Flattr Automatico</string>
+ <string name="pref_auto_flattr_sum">Configura l\'esecuzione automatica di Flattr</string>
<string name="user_interface_label">Interfaccia utente</string>
+ <string name="pref_set_theme_title">Seleziona un Tema</string>
+ <string name="pref_nav_drawer_feed_order_title">Imposta l\'ordine delle Iscrizioni</string>
<string name="pref_set_theme_sum">Cambia l\'aspetto di AntennaPod</string>
+ <string name="pref_automatic_download_title">Download Automatico</string>
+ <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_automatic_download_on_battery_sum">Scarica automaticamente quando la batteria non è in caricamento</string>
+ <string name="pref_autodl_wifi_filter_sum">Abilita il download automatico solo per alcune reti Wi-Fi selezionate.</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 Contemporanei</string>
+ <string name="pref_episode_cache_title">Cache degli Episodi</string>
<string name="pref_theme_title_light">Chiaro</string>
<string name="pref_theme_title_dark">Scuro</string>
<string name="pref_episode_cache_unlimited">Illimitato</string>
@@ -191,34 +332,67 @@
<string name="pref_update_interval_hours_singular">ora</string>
<string name="pref_update_interval_hours_manual">Manuale</string>
<string name="pref_gpodnet_authenticate_title">Accesso</string>
- <string name="pref_gpodnet_logout_title">Esci</string>
+ <string name="pref_gpodnet_authenticate_sum">Effettua il login con il tuo account gpodder.net per sincronizzare le tue sottoscrizioni.</string>
+ <string name="pref_gpodnet_logout_title">Logout</string>
+ <string name="pref_gpodnet_logout_toast">Logout effettuato</string>
<string name="pref_gpodnet_setlogin_information_title">Cambia le informazioni di accesso</string>
+ <string name="pref_gpodnet_setlogin_information_sum">Cambia le informazioni di login per il tuo account gpodder.net.</string>
+ <string name="pref_gpodnet_sync_started">Sincronizzazione avviata</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_gpodnet_sethostname_title">Imposta il nome dell\'host</string>
<string name="pref_gpodnet_sethostname_use_default_host">Utilizza l\'host predefinito</string>
<string name="pref_expandNotify_title">Espandi le notifiche</string>
+ <string name="pref_expandNotify_sum">Espandi sempre le notifiche per mostrare i pulsanti di riproduzione.</string>
+ <string name="pref_persistNotify_title">Controlli di riproduzione persistenti</string>
+ <string name="pref_persistNotify_sum">Mantieni le notifiche e i controlli del blocco dello schermo quando la riproduzione è in pausa.</string>
+ <string name="pref_showDownloadReport_title">Mostra il Rapporto del Download</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 un nuovo episodio in testa alla coda.</string>
+ <string name="pref_queueAddToFront_title">Aggiungi in cima alla coda</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="send_email">Invia e-mail</string>
+ <string name="experimental_pref">Sperimentale</string>
+ <string name="pref_current_value">Valore corrente: %1$s</string>
+ <string name="pref_proxy_title">Proxy</string>
+ <string name="pref_faq">FAQ</string>
+ <string name="pref_known_issues">Problemi noti</string>
+ <string name="pref_cast_title">Supporto a Chromecast</string>
<!--Auto-Flattr dialog-->
+ <string name="auto_flattr_enable">Abilita l\'esecuzione automatica di Flattr</string>
+ <string name="auto_flattr_after_percent">Carica l\'episodio su Flattr appena è stato riprodotto al %d percento</string>
+ <string name="auto_flattr_ater_beginning">Carica l\'episodio su Flattr appena comincia la riproduzione</string>
+ <string name="auto_flattr_ater_end">Carica l\'episodio su Flattr appena finisce la riproduzione</string>
<!--Search-->
<string name="found_in_chapters_label">Trovato nei capitoli</string>
<string name="search_status_no_results">Nessun risultato trovato</string>
<string name="search_label">Cerca</string>
<string name="found_in_title_label">Trovato nel titolo</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="start_import_label">Avvia l\'importazione</string>
+ <string name="opml_import_label">Importazione OPML</string>
<string name="opml_directory_error">ERRORE!</string>
+ <string name="reading_opml_label">Lettura OPML file in corso</string>
+ <string name="opml_import_error_no_file">Nessun file selezionato!</string>
<string name="select_all_label">Seleziona tutto</string>
<string name="deselect_all_label">Deseleziona tutto</string>
+ <string name="choose_file_from_filesystem">Dal filesystem locale</string>
<string name="choose_file_from_external_application">Usa un\'applicazione esterna</string>
<string name="opml_export_label">Esportazione OPML</string>
<string name="export_error_label">Errore di esportazione</string>
<!--Sleep timer-->
- <string name="set_sleeptimer_label">Imposta timer per lo spegnimento</string>
- <string name="disable_sleeptimer_label">Disabilta timer per lo spegnimento</string>
+ <string name="set_sleeptimer_label">Imposta timer di spegnimento</string>
+ <string name="disable_sleeptimer_label">Disabilita il timer di spegnimento</string>
+ <string name="enter_time_here_label">Tempo di spegnimento</string>
<string name="sleep_timer_label">Timer per lo spegnimento</string>
- <string name="time_left_label">Tempo rimasto:\u0020</string>
+ <string name="time_left_label">Tempo rimanente:\u0020</string>
<string name="time_dialog_invalid_input">Input non valido, il tempo deve essere un numero intero</string>
+ <string name="timer_vibration_label">Vibra</string>
+ <string name="time_seconds">secondi</string>
+ <string name="time_minutes">minuti</string>
+ <string name="time_hours">ore</string>
<plurals name="time_seconds_quantified">
<item quantity="one">1 secondo</item>
<item quantity="other">%d secondi</item>
@@ -244,6 +418,7 @@
<string name="username_label">Nome utente</string>
<string name="password_label">Password</string>
<string name="gpodnetauth_device_title">Selezione del dispositivo</string>
+ <string name="gpodnetauth_device_descr">Crea un nuovo dispositivo per utilizzare il tuo account gpodder.net o scegline uno esistente:</string>
<string name="gpodnetauth_device_deviceID">ID del dispositivo:\u0020</string>
<string name="gpodnetauth_device_butCreateNewDevice">Crea un nuovo dispositivo</string>
<string name="gpodnetauth_device_chooseExistingDevice">Scegli un dispositivo esistente:</string>
@@ -251,6 +426,7 @@
<string name="gpodnetauth_device_errorAlreadyUsed">ID del dispositivo già in uso</string>
<string name="gpodnetauth_device_butChoose">Scegli</string>
<string name="gpodnetauth_finish_title">Accesso avvenuto con successo!</string>
+ <string name="gpodnetauth_finish_descr">Congraturazioni! Il tuo account gpodder.net è stato collegato con il tuo dispositivo. Da ora AntennaPod sincronizzerà automaticamente le sottoscrizioni sul tuo dispositivo con il tuo account gpodder.net.</string>
<string name="gpodnetauth_finish_butsyncnow">Avvia ora la sincronizzazione</string>
<string name="gpodnetauth_finish_butgomainscreen">Vai alla schermata principale</string>
<string name="gpodnetsync_auth_error_title">errore di autenticazione di gpodder.net</string>
@@ -260,6 +436,7 @@
<!--Directory chooser-->
<string name="selected_folder_label">Cartella selezionata:</string>
<string name="create_folder_label">Crea una cartella</string>
+ <string name="choose_data_directory">Scegli la directory per i dati</string>
<string name="create_folder_msg">Creare una nuova cartella dal nome \"%1$s\"?</string>
<string name="create_folder_success">Creata una nuova cartella</string>
<string name="create_folder_error_no_write_access">Non è possibile scrivere in questa cartella</string>
@@ -269,7 +446,11 @@
<string name="folder_not_readable_error">\"%1$s\" non è leggibile</string>
<string name="folder_not_writable_error">\"%1$s\" non è scrivibile</string>
<string name="folder_not_empty_dialog_title">La cartella non è vuota</string>
+ <string name="folder_not_empty_dialog_msg">La cartella che hai selezionato non è vuota. I download dei media e altri file saranno creati in questa cartella. Continuare?</string>
<string name="set_to_default_folder">Scegli la cartella predefinita</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">Sospendi la riproduzione invece di abbassare il volume quando un\'altra app emette un suono</string>
+ <string name="pref_pausePlaybackForFocusLoss_title">Pausa su interruzione</string>
+ <string name="pref_resumeAfterCall_title">Riprendi dopo la chiamata</string>
<!--Online feed view-->
<string name="subscribe_label">Abbonati</string>
<string name="subscribed_label">Abbonato</string>
@@ -279,52 +460,75 @@
<string name="fast_forward_label">Avanti veloce</string>
<string name="media_type_audio_label">Audio</string>
<string name="media_type_video_label">Video</string>
+ <string name="navigate_upwards_label">Naviga verso l\'alto</string>
<string name="status_downloading_label">L\'episodio sta venendo scaricato</string>
<string name="in_queue_label">L\'episodio è in coda</string>
+ <string name="drag_handle_content_description">Trascina per cambiare la posizione di questo oggetto</string>
<string name="load_next_page_label">Carica la pagina successiva</string>
<!--Feed information screen-->
<string name="authentication_label">Autenticazione</string>
+ <string name="authentication_descr">Cambia il tuo nome utente e la tua password per questo podcast e i suoi episodi.</string>
<string name="episode_filters_include">Includi</string>
<string name="episode_filters_exclude">Escludi</string>
- <string name="keep_updated">Tieni aggiornato</string>
+ <string name="keep_updated">Mantieni Aggiornato</string>
<!--Progress information-->
<!--AntennaPodSP-->
+ <string name="sp_apps_importing_feeds_msg">Importazione di sottoscrizioni da applicazioni monouso in corso...</string>
<string name="search_itunes_label">Cerca su iTunes</string>
+ <string name="filter">Filtro</string>
<!--Episodes apply actions-->
<string name="all_label">Tutto</string>
- <string name="none_label">Nessuno</string>
+ <string name="selected_all_label">Seleziona tutti gli Episodi</string>
+ <string name="none_label">Nulla</string>
<string name="deselected_all_label">De-seleziona tutti gli episodi</string>
<string name="played_label">Riprodotti</string>
<string name="selected_played_label">Selezionati gli episodi riprodotti</string>
<string name="unplayed_label">Non riprodotti</string>
<string name="selected_unplayed_label">Selezionati gli episodi non riprodotti</string>
<string name="downloaded_label">Scaricati</string>
- <string name="selected_downloaded_label">Selezionati gli episodi scaricati</string>
+ <string name="selected_downloaded_label">Seleziona gli episodi scaricati</string>
<string name="not_downloaded_label">Non scaricati</string>
- <string name="selected_not_downloaded_label">Selezionati gli episodi non scaricati</string>
+ <string name="selected_not_downloaded_label">Seleziona gli episodi non scaricati</string>
<string name="queued_label">In coda</string>
- <string name="selected_queued_label">Selezionati gli episodi in coda</string>
+ <string name="selected_queued_label">Seleziona gli episodi in coda</string>
<string name="not_queued_label">Non in coda</string>
- <string name="selected_not_queued_label">Selezionati gli episodi non in coda</string>
- <string name="selected_has_media_label">Selezionati gli episodi con elementi multimediali</string>
+ <string name="selected_not_queued_label">Seleziona gli episodi non in coda</string>
+ <string name="selected_has_media_label">Seleziona gli episodi con elementi multimediali</string>
<!--Sort-->
<string name="sort_title_a_z">Titolo (A \u2192 Z)</string>
<string name="sort_title_z_a">Titolo (Z \u2192 A)</string>
<string name="sort_date_new_old">Data (Nuovi \u2192 Vecchi)</string>
<string name="sort_date_old_new">Data (Vecchi \u2192 Nuovi)</string>
+ <string name="sort_duration_short_long">Durata (Corti \u2192 Lunghi)</string>
+ <string name="sort_duration_long_short">Durata (Lunghi \u2192 Corti)</string>
<!--Rating dialog-->
<string name="rating_title">Ti piace AntennaPod?</string>
+ <string name="rating_never_label">Lasciami solo</string>
<string name="rating_later_label">Ricordamelo più tardi</string>
+ <string name="rating_now_label">Certo, facciamolo!</string>
<!--Audio controls-->
<string name="audio_controls">Controlli audio</string>
<string name="playback_speed">Velocità di riproduzione</string>
<string name="volume">Volume</string>
+ <string name="left_short">Sx</string>
+ <string name="right_short">Dx</string>
+ <string name="audio_effects">Effetti Audio</string>
<!--proxy settings-->
<string name="proxy_type_label">Tipo</string>
+ <string name="host_label">Host</string>
+ <string name="port_label">Porta</string>
+ <string name="optional_hint">(Opzionale)</string>
<string name="proxy_test_label">Test</string>
<string name="proxy_checking">Controllo in corso...</string>
<string name="proxy_test_successful">Test avvenuto con successo</string>
<string name="proxy_test_failed">Test fallito</string>
+ <string name="proxy_host_empty_error">Host non può essere vuoto</string>
+ <string name="proxy_port_invalid_error">Porta non valida</string>
+ <!--Database import/export-->
+ <string name="import_select_file">Seleziona il file da importare</string>
+ <string name="export_ok">Esportato con successo.</string>
<!--Casting-->
+ <string name="cast_media_route_menu_title">Riproduci su...</string>
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-iw-rIL/strings.xml b/core/src/main/res/values-iw-rIL/strings.xml
index 9a835b9f0..3860518d4 100644
--- a/core/src/main/res/values-iw-rIL/strings.xml
+++ b/core/src/main/res/values-iw-rIL/strings.xml
@@ -2,52 +2,71 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
<string name="feeds_label">הזנות</string>
- <string name="statistics_label">סטטיסטיקות</string>
- <string name="add_feed_label">הוסף פודקאסט</string>
+ <string name="statistics_label">סטטיסטיקה</string>
+ <string name="add_feed_label">הוספת פודקאסט</string>
<string name="episodes_label">פרקים</string>
- <string name="all_episodes_short_label">הכל</string>
+ <string name="all_episodes_short_label">הכול</string>
<string name="favorite_episodes_label">מועדפים</string>
<string name="new_label">חדש</string>
<string name="settings_label">הגדרות</string>
- <string name="add_new_feed_label">הוסף פודקאסט</string>
<string name="downloads_label">הורדות</string>
- <string name="downloads_running_label">פועל</string>
- <string name="downloads_completed_label">סיים</string>
+ <string name="downloads_running_label">פעיל</string>
+ <string name="downloads_completed_label">הושלם</string>
<string name="downloads_log_label">יומן</string>
- <string name="cancel_download_label">בטל הורדה</string>
+ <string name="subscriptions_label">פודקאסטים</string>
+ <string name="subscriptions_list_label">רשימת פודקאסטים</string>
+ <string name="cancel_download_label">ביטול\nהורדה</string>
<string name="playback_history_label">היסטוריית ניגון</string>
<string name="gpodnet_main_label">gpodder.net</string>
- <string name="gpodnet_auth_label">התחברות אל gpodder.net</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>
<!--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>
+ <string name="statistics_mode">מצב סטטיסטיקה</string>
+ <string name="statistics_mode_normal">חישוב זמן שנוגן בפועל. נגינה כפולה נספרת פעמיים, בעוד שסימון כנוגן לא נחשב.</string>
+ <string name="statistics_mode_count_all">סיכום כל הפודקאסטים שסומנו כנוגנו</string>
+ <string name="statistics_speed_not_counted">לתשומת לבך: אין התייחסות למהירות הנגינה.</string>
<!--Main activity-->
- <string name="drawer_open">פתח תפריט</string>
- <string name="drawer_close">סגור תפריט</string>
- <string name="drawer_feed_order_alphabetical">מיין בסדר אלפביתי</string>
- <string name="drawer_feed_order_last_update">מיין לפי תאריך פרסום</string>
- <string name="drawer_feed_order_most_played">מיין לפי מספר פרקים שהושמעו </string>
+ <string name="drawer_open">פתיחת תפריט</string>
+ <string name="drawer_close">סגירת תפריט</string>
+ <string name="drawer_preferences">העדפות מגירה</string>
+ <string name="drawer_feed_order_unplayed_episodes">מיון לפי מונה</string>
+ <string name="drawer_feed_order_alphabetical">מיון בסדר אלפביתי</string>
+ <string name="drawer_feed_order_last_update">מיון לפי תאריך פרסום</string>
+ <string name="drawer_feed_order_most_played">מיון לפי מספר פרקים שהושמעו </string>
+ <string name="drawer_feed_counter_new_unplayed">מספר פרקים חדשים וכאלו שעדיין לא התנגנו</string>
+ <string name="drawer_feed_counter_new">מספר פרקים חדשים</string>
+ <string name="drawer_feed_counter_unplayed">מספר פרקים שעוד לא התנגנו</string>
+ <string name="drawer_feed_counter_downloaded">מספר פרקים שהתקבלו</string>
+ <string name="drawer_feed_counter_none">ללא</string>
<!--Webview actions-->
- <string name="open_in_browser_label">פתח בדפדפן</string>
- <string name="copy_url_label">העתק כתובת אתר</string>
- <string name="share_url_label">שתף כתובת אתר</string>
- <string name="copied_url_msg">כתובת אתרהועתקה ללוח.</string>
- <string name="go_to_position_label">עבור למיקום זה</string>
+ <string name="open_in_browser_label">פתיחה בדפדפן</string>
+ <string name="copy_url_label">העתקת כתובת</string>
+ <string name="share_url_label">שיתוף כתובת</string>
+ <string name="copied_url_msg">הכתובת הועתקה ללוח הגזירים</string>
+ <string name="go_to_position_label">מעבר למיקום זה</string>
<!--Playback history-->
- <string name="clear_history_label">נקה היסטוריה</string>
+ <string name="clear_history_label">ניקוי היסטוריה</string>
<!--Other-->
<string name="confirm_label">אישור</string>
- <string name="cancel_label">בטל</string>
+ <string name="cancel_label">ביטול</string>
<string name="yes">כן</string>
<string name="no">לא</string>
- <string name="author_label">מחבר</string>
+ <string name="reset">איפוס</string>
+ <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>
- <string name="refresh_label">רענן</string>
- <string name="external_storage_error_msg">אין אחסון חיצוני זמין. אנא ודא כי אחסון חיצוני הוא מותקן כך שהאפליקציה תוכל לעבוד כמו שצריך.</string>
+ <string name="refresh_label">רענון</string>
+ <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>
@@ -56,187 +75,283 @@
<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>
- <string name="auto_download_label">כלול בהורדות אוטומטיות</string>
+ <string name="save_username_password_label">שמירת שם משתמש וססמה</string>
+ <string name="close_label">סגירה</string>
+ <string name="retry_label">לנסות שוב</string>
+ <string name="auto_download_label">לכלול בהורדות אוטומטיות</string>
+ <string name="auto_download_apply_to_items_title">החלה על פרקים קודמים</string>
+ <string name="auto_download_apply_to_items_message">הגדרות ה<i>הורדה האוטומטית</i> החדשות יחולו אוטומטית על פרקים חדשים.\nלהחיל אותן גם על פרקים שפורסמו בעבר?</string>
+ <string name="auto_delete_label">מחיקת פרק באופן אוטומטי</string>
<string name="parallel_downloads_suffix">\u0020הורדות במקביל</string>
+ <string name="feed_auto_download_global">בררת מחדל גלובלי</string>
<string name="feed_auto_download_always">תמיד</string>
<string name="feed_auto_download_never">אף פעם</string>
- <string name="send_label">שלח...</string>
+ <string name="send_label">שליחה…</string>
<string name="episode_cleanup_never">אף פעם</string>
+ <string name="episode_cleanup_queue_removal">כאשר לא בתור</string>
<string name="episode_cleanup_after_listening">אחרי סיום</string>
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">יום אחרי סיום</item>
+ <item quantity="two">%d ימים לאחר סיום </item>
+ <item quantity="many">%d ימים לאחר סיום </item>
<item quantity="other">%d ימים לאחר סיום </item>
</plurals>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">כתובת הזנה</string>
- <string name="etxtFeedurlHint">כתובת של הזנה או אתר אינטרנט</string>
- <string name="txtvfeedurl_label">הוסף פודקאסט לפי כתובת אתר</string>
+ <string name="etxtFeedurlHint">www.example.com/feed</string>
+ <string name="txtvfeedurl_label">הוספת פודקאסט לפי כתובת</string>
<string name="podcastdirectories_label">חפש פודקאסט בספריה</string>
- <string name="browse_gpoddernet_label">עיין בgpodder.net</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>
- <string name="mark_all_read_msg">סמן את כל הפרקים כנקרא</string>
- <string name="mark_all_read_confirmation_msg">אנא אשר שאתה רוצה לסמן את כל פרקים כנקראים.</string>
- <string name="mark_all_read_feed_confirmation_msg">אנא אשר שאתה רוצה לסמן את כל פרקים בהזנה זו כנקראים.</string>
- <string name="mark_all_seen_label">סמן את כולם כנראו</string>
- <string name="mark_all_seen_msg">סמן את כל הפרקים כנראו</string>
- <string name="mark_all_seen_confirmation_msg">אנא אשר שאתה רוצה לסמן את כל פרקים כנראו.</string>
- <string name="show_info_label">הצג מידע</string>
- <string name="rename_feed_label">שנה שם פודקאסט</string>
- <string name="remove_feed_label">הסר פודקאסט</string>
- <string name="share_label">שתף...</string>
- <string name="share_link_label">שתף קישור אתר</string>
- <string name="feed_remover_msg">הסר הזנה</string>
- <string name="load_complete_feed">רענן את כל ההזנה</string>
- <string name="hide_episodes_title">הסתר פרקים</string>
+ <string name="mark_all_read_label">סימון הכול כנוגנו</string>
+ <string name="mark_all_read_msg">סימון את כל הפרקים כנוגנו</string>
+ <string name="mark_all_read_confirmation_msg">נא לאשר שברצונך לסמן את כל הפרקים כנוגנו.</string>
+ <string name="mark_all_read_feed_confirmation_msg">נא לאשר שברצונך לסמן את כל הפרקים בהזנה הזאת כנוגנו.</string>
+ <string name="mark_all_seen_label">סימון כולם כנצפו</string>
+ <string name="mark_all_seen_msg">סימון כל הפרקים כנצפו</string>
+ <string name="mark_all_seen_confirmation_msg">נא לאשר שברצונך לסמן את כל הפרקים כנצפו.</string>
+ <string name="show_info_label">הצגת מידע</string>
+ <string name="rename_feed_label">שינוי שם פודקאסט</string>
+ <string name="remove_feed_label">הסרת פודקאסט</string>
+ <string name="share_label">שיתוף…</string>
+ <string name="share_link_label">שיתוף קישור</string>
+ <string name="share_file_label">שיתוף כתובת</string>
+ <string name="share_link_with_position_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="hide_unplayed_episodes_label">לא נוגן</string>
+ <string name="hide_paused_episodes_label">מושהה</string>
+ <string name="hide_played_episodes_label">נוגן</string>
+ <string name="hide_queued_episodes_label">בתור</string>
+ <string name="hide_not_queued_episodes_label">לא בתור</string>
+ <string name="hide_downloaded_episodes_label">הורד</string>
+ <string name="hide_not_downloaded_episodes_label">לא הורד</string>
+ <string name="hide_has_media_label">יש מדיה</string>
+ <string name="filtered_label">מסונן</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} הרענון האחרון נכשל</string>
+ <string name="open_podcast">פתיחת פודקאסט</string>
<!--actions on feeditems-->
- <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="remove_episode_lable">הסר פרק</string>
- <string name="mark_read_label">סמן כנקרא</string>
- <string name="marked_as_read_label">סומן כנקרא</string>
- <string name="mark_unread_label">סמן כלא נקרא</string>
- <string name="add_to_queue_label">הוסף לתור</string>
+ <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="remove_episode_lable">הסרת פרק</string>
+ <string name="marked_as_seen_label">סימון כנצפה</string>
+ <string name="mark_read_label">סימון כנצפה</string>
+ <string name="marked_as_read_label">סימון כנוגן</string>
+ <string name="mark_unread_label">סימון כלא נוגן</string>
+ <string name="add_to_queue_label">הוספה לתור</string>
<string name="added_to_queue_label">התווסף לתור</string>
- <string name="remove_from_queue_label">הסר מהתור</string>
+ <string name="remove_from_queue_label">הסרה מהתור</string>
<string name="add_to_favorite_label">התווסף למועדפים</string>
- <string name="visit_website_label">בקר באתר</string>
- <string name="support_label">תרום באמצעות Flattr</string>
- <string name="skip_episode_label">דלג על הפרק</string>
+ <string name="added_to_favorites">התווסף למועדפים</string>
+ <string name="remove_from_favorite_label">הסרה מהמועדפים</string>
+ <string name="removed_from_favorites">הוסר מהמועדפים</string>
+ <string name="visit_website_label">ביקור באתר</string>
+ <string name="support_label">תרומה עם Flattr</string>
+ <string name="skip_episode_label">דילוג על פרק</string>
+ <string name="activate_auto_download">הפעלת הורדה אוטומטית</string>
+ <string name="deactivate_auto_download">השבתת הורדה אוטומטית</string>
+ <string name="reset_position">איפוס מיקום הנגינה</string>
+ <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_failed">כשל</string>
+ <string name="download_pending">הורדה ממתינה</string>
<string name="download_running">הורדה מתבצעת</string>
- <string name="download_error_device_not_found">התקן איחסון לא נמצא</string>
- <string name="download_error_insufficient_space">אין די שטח איחסון</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_http_data_error">שגיאת נתוני HTTP</string>
<string name="download_error_error_unknown">שגיאה לא ידועה</string>
- <string name="download_error_parser_exception">שגיאת תוכנית ניתוח</string>
+ <string name="download_error_parser_exception">שגיאת מפענח</string>
<string name="download_error_unsupported_type">סוג ההזנה אינו נתמך</string>
<string name="download_error_connection_error">שגיאת חיבור</string>
<string name="download_error_unknown_host">שרת לא ידוע</string>
<string name="download_error_unauthorized">שגיאת אימות</string>
- <string name="cancel_all_downloads_label">בטל את כל ההורדות</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_report_title">הורדות הושלמו</string>
- <string name="download_error_malformed_url">כתובת אתר שגויה</string>
+ <string name="download_canceled_autodownload_enabled_msg">ההורדה בוטלה\nה<i>הורדה האוטומטית</i> הושבתה עבור פריט זה</string>
+ <string name="download_report_title">הורדות הושלמו עם שגיאה אחת או יותר</string>
+ <string name="download_report_content_title">דוח הורדה</string>
+ <string name="download_error_malformed_url">כתובת שגויה</string>
<string name="download_error_io_error">שגיאת קלט פלט</string>
<string name="download_error_request_error">שגיאת בקשה</string>
<string name="download_error_db_access">שגיאת גישה למסד הנתונים</string>
- <string name="downloads_processing">מעבד הורדות</string>
- <string name="download_notification_title">מוריד פודקאסט</string>
- <string name="download_report_content">%1$d הורדות הצליחו, %2$d ניכשלו</string>
+ <plurals name="downloads_left">
+ <item quantity="one">נותרה הורדה %d</item>
+ <item quantity="two">נותרו %d הורדות</item>
+ <item quantity="many">נותרו %d הורדות</item>
+ <item quantity="other">נותרו %d הורדות</item>
+ </plurals>
+ <string name="downloads_processing">ההורדות בהליכי עיבוד</string>
+ <string name="download_notification_title">נתוני הפודקאסט מתקבלים</string>
+ <string name="download_report_content">%1$d הורדות הצליחו, %2$d נכשלו</string>
<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>
+ <string name="download_request_error_dialog_message_prefix">אירעה שגיאה בעת הניסיון הורדת הקובץ:\u0020</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לאפשר את ההורדה באופן זמני?\n\n<small>הבחירה שלך תישמר למשך 10 דקות.</small></string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">הוספה לתור</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">לאפשר לבינתיים</string>
<!--Mediaplayer messages-->
<string name="player_error_msg">שגיאה!</string>
- <string name="player_stopped_msg">מדיה לא מתנגנת</string>
- <string name="player_preparing_msg">מתכונן</string>
- <string name="player_ready_msg">מוכן</string>
- <string name="player_seeking_msg">מחפש</string>
- <string name="playback_error_server_died">שרת מת</string>
+ <string name="player_stopped_msg">אין מדיה מתנגנת</string>
+ <string name="player_preparing_msg">בהכנה</string>
+ <string name="player_ready_msg">בהמתנה</string>
+ <string name="player_seeking_msg">מתבצע איתור</string>
+ <string name="playback_error_server_died">השרת לא מגיב</string>
<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>
+ <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="sort">מיין</string>
+ <string name="lock_queue">נעילת תור</string>
+ <string name="unlock_queue">שחרור תור</string>
+ <string name="queue_locked">התור ננעל</string>
+ <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>
<string name="date">תאריך</string>
<string name="duration">משך</string>
+ <string name="episode_title">כותרת הפרק</string>
+ <string name="feed_title">כותרת ההזנה</string>
<string name="ascending">בסדר עולה</string>
<string name="descending">בסדר יורד</string>
- <string name="clear_queue_confirmation_msg">אנא אשר שאתה רוצה לנקות את התור מכל הפרקים שבו</string>
+ <string name="clear_queue_confirmation_msg">נא לאשר את פינוי התור מכל הפרקים שבו</string>
<!--Flattr-->
- <string name="flattr_auth_label">כניסה ל-Fattr</string>
- <string name="flattr_auth_explanation">לחץ על הכפתור למטה כדי להתחיל את תהליך האימות. אתה תועבר למסך כניסת flattr בדפדפן שלך ותתבקש לתת לאנטנה-פוד רשות לתרום באמצעות flattr. לאחר שקבלת אישור, תוכל לחזור למסך זה באופן אוטומטי.</string>
+ <string name="flattr_auth_label">כניסה ל־Fattr</string>
+ <string name="flattr_auth_explanation">נא ללחוץ על הכפתור שלמטה כדי להתחיל את תהליך האימות. פעולה זו תעביר אותך למסך הכניסה של Flattr בדפדפן שלך ותופיע בקשה לאפשר לאנטנה־פּוֹד לבצע פעולות ב־Flattr. לאחר מתן ההרשאה, המערכת תחזיר אותך למסך זה אוטומטית.</string>
<string name="authenticate_label">אימות</string>
- <string name="return_home_label">חזור למסך הבית</string>
- <string name="flattr_auth_success">האימות הצליח! עכשיו אתה יכול לתרום באמצעות flattr מתוך האפליקציה.</string>
- <string name="no_flattr_token_title">אסימון flattr לא נמצא</string>
- <string name="no_flattr_token_notification_msg">חשבון ה-flattr שלך אינו מחובר לאנטנה-פוד. הקש כאן לאימות.</string>
- <string name="no_flattr_token_msg">חשבון ה-flattr שלך אינו מחובר לאנטנה-פוד. אתה יכול לקשראת לחשבונך לאנטנה-פוד לתרום באמצעות flattr מתוך האפליקציה או שאתה יכול לבקר באתר האינטרנט של הדבר לו תרצה לתרום.</string>
- <string name="authenticate_now_label">אמת</string>
+ <string name="return_home_label">חזרה למסך הבית</string>
+ <string name="flattr_auth_success">האימות הצליח! כעת יש לך אפשרות לתרום עם Flattr מתוך היישומון.</string>
+ <string name="no_flattr_token_title">לא נמצא אסימון Flattr</string>
+ <string name="no_flattr_token_notification_msg">נראה כי חשבון ה־Flattr שלך אינו מחובר לאנטנה־פּוֹד. יש לגעת כאן לאימות.</string>
+ <string name="no_flattr_token_msg">נראה כי חשבון ה־Flattr שלך אינו מחובר לאנטנה־פּוֹד. יש לך אפשרות לחבר את חשבונך לאנטנה־פּוֹד כדי לתרום עם Flattr מתוך היישומון או לבקר באתר של מה שברצונך לתרום לו עם Flattr.</string>
+ <string name="authenticate_now_label">אימות</string>
<string name="action_forbidden_title">הפעולה אסורה</string>
- <string name="action_forbidden_msg">לאנטנה-פוד אין הרשאה לפעולה זו. הסיבה לכך יכולה להיות שאסימון הגישה של אנטנה-פוד לחשבון שלך בוטל. אתה יכול לבצע אימות מחדש או לבקר באתר האינטרנט של הדבר במקום.</string>
+ <string name="action_forbidden_msg">לאנטנה־פּוֹד אין הרשאה לבצע פעולה זו. הסיבה לכך עשויה להיות שאסימון הגישה של אנטנה־פּוֹד לחשבון שלך נשלל. ניתן לאמת מחדש או לבקר באתר המיועד במקום.</string>
<string name="access_revoked_title">גישה בוטלה</string>
- <string name="access_revoked_info">אסימון הגישה של אנטנה-פוד לחשבונך בוטל. על מנת להשלים את התהליך, אתה צריך להסיר יישום זה מהרשימת היישומים שאושרו בהגדרות החשבונך באתר flattr.</string>
+ <string name="access_revoked_info">אסימון הגישה של אנטנה־פוד לחשבונך בוטל. על מנת להשלים את התהליך, אתה צריך להסיר יישום זה מהרשימת היישומים שאושרו בהגדרות החשבונך באתר flattr.</string>
<!--Flattr-->
- <string name="flattr_click_success">תרמת ב-Flattr!</string>
- <string name="flattr_click_success_count">תרמת ב-Flattr %d פעמים! </string>
+ <string name="flattr_click_success">תרמת ב־Flattr!</string>
+ <string name="flattr_click_success_count">תרמת ב־Flattr %d פעמים!</string>
<string name="flattr_click_success_queue">תרומות Flattr: %s.</string>
- <string name="flattr_click_failure_count">כישלון לתרום ב-Flattr %d!</string>
- <string name="flattr_click_failure">לא נתרם ב-Flattr: %s.</string>
- <string name="flattr_click_enqueued">תרומות ב-Flattr מאוחר יותר</string>
- <string name="flattring_thing">תורם ב-Flattr %s</string>
- <string name="flattring_label">אנטנה-פוד תורם ב-Flattr</string>
- <string name="flattrd_label">אנטנה-פוד תרם ב-Flattr</string>
- <string name="flattrd_failed_label">כישלון תרומת אנטנה-פוד ב-Flattr</string>
- <string name="flattr_retrieving_status">איחזור תרומות Flattr</string>
+ <string name="flattr_click_failure_count">התרומה ב־Flattr %d נכשלה!</string>
+ <string name="flattr_click_failure">לא נתרם ב־Flattr: %s.</string>
+ <string name="flattr_click_enqueued">לתרום ב־Flattr מאוחר יותר</string>
+ <string name="flattring_thing">תרומה ב־Flattr %s</string>
+ <string name="flattring_label">תרומה ב־Flattr עם אנטנה־פּוֹד</string>
+ <string name="flattrd_label">תרומה ב־Flattr עם אנטנה־פּוֹד</string>
+ <string name="flattrd_failed_label">התרומה באנטנה־פּוֹד עם Flattr נכשלה</string>
+ <string name="flattr_retrieving_status">תרומות Flattr מתקבלות</string>
<!--Variable Speed-->
- <string name="download_plugin_label">הורד תוסף</string>
+ <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_label">אין פריטים ברשימה זו.</string>
- <string name="no_feeds_label">לא נרשמת עדיין להזנות.</string>
+ <string name="no_feeds_label">לא נרשמת להזנות עדיין.</string>
+ <string name="no_chapters_label">לפרק זה אין פרקים.</string>
+ <string name="no_shownotes_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="about_pref">על אודות</string>
<string name="queue_label">תור</string>
- <string name="services_label">שירותים</string>
<string name="flattr_label">Flattr</string>
- <string name="pref_unpauseOnHeadsetReconnect_sum">המשך את הניגון כשהאוזניות מחוברות מחדש</string>
- <string name="pref_followQueue_sum">עבור לפריט הבא בתור כאשר הניגון מסתיים</string>
- <string name="pref_auto_delete_sum">מחק פרק כהניגון מסתיים</string>
+ <string name="pref_episode_cleanup_title">ניקוי פרקים</string>
+ <string name="pref_episode_cleanup_summary">פרקים שאינם בתור ואינם במועדפים אמורים לענות לתנאים של הסרה במקרה שההורדה האוטומטית זקוקה למקום לפרקים חדשים</string>
+ <string name="pref_pauseOnDisconnect_sum">השהיית הנגינה כאשר האוזניות או ה־Bluetooth מנותקים</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">להמשיך את הניגון כשהאוזניות מחוברות מחדש</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">להמשיך את הנגינה עם חיבור מחדש של ה־Bluetooth</string>
+ <string name="pref_hardwareForwardButtonSkips_title">כפתור קדימה מדלג</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">לחיצה על כפתור החומרה קדימה מדלג לפרק הבא במקום להאיץ קדימה</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">כפתור אחורה מתחיל מחדש</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">לחיצה על כפתור החומרה אחורה מדלג מתחיל מחדש את נגינת הפרק הנוכחי במקום לחזור אחורה בפרק</string>
+ <string name="pref_followQueue_sum">לעבור לפריט הבא בתור כאשר הניגון מסתיים</string>
+ <string name="pref_auto_delete_sum">מחיקת פרק כשהניגון מסתיים</string>
<string name="pref_auto_delete_title">מחיקה אוטומטית</string>
+ <string name="pref_smart_mark_as_played_sum">סימון פרקים כנוגנו אפילו אם נשארו כמה שניות לנגינה</string>
+ <string name="pref_smart_mark_as_played_title">סימון חכם כנוגנו</string>
+ <string name="pref_skip_keeps_episodes_sum">להשאיר פרקים למרות שדילגת עליהם</string>
+ <string name="pref_skip_keeps_episodes_title">להשאיר פרקים שדולגו</string>
+ <string name="pref_favorite_keeps_episodes_sum">להשאיר פרקים שסומנו כמועדפים</string>
+ <string name="pref_favorite_keeps_episodes_title">להשאיר פרקים מועדפים</string>
<string name="playback_pref">ניגון</string>
<string name="network_pref">רשת</string>
- <string name="pref_downloadMediaOnWifiOnly_sum">הורד קבצי מדיה רק דרך חיבור אינטרנט אלחוטי</string>
+ <string name="pref_autoUpdateIntervallOrTime_title">זמן בין עדכונים או מועד ביום</string>
+ <string name="pref_autoUpdateIntervallOrTime_sum">נא לציין הפרש זמן או מועד ביום לרענון ההזנות אוטומטית</string>
+ <string name="pref_autoUpdateIntervallOrTime_message">ניתן להגדיר <i>הפרש זמן</i> כגון „כל שעתיים”, להגדיר <i>מועד ביום</i> כגון „7:00” או <i>להשבית</i> עדכונים אוטומטיים לאלתר.\n\n<small>לתשומת לבך: מועדי העדכון אינם מדויקים. יתכן עיכוב קל בפעולות.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">השבתה</string>
+ <string name="pref_autoUpdateIntervallOrTime_Interval">הגדרת הפרש זמן</string>
+ <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_downloadMediaOnWifiOnly_title">הורדת מדיה דרך רשת אלחוטית</string>
<string name="pref_pauseOnHeadsetDisconnect_title">ניתוק אוזניות</string>
<string name="pref_unpauseOnHeadsetReconnect_title">חיבור אוזניות מחדש</string>
- <string name="pref_mobileUpdate_title">עידכון דרך רשת סלולרית</string>
- <string name="pref_mobileUpdate_sum">אפשר עידכונים דרך רשת סלולרית</string>
- <string name="refreshing_label">מרענן</string>
+ <string name="pref_unpauseOnBluetoothReconnect_title">חיבור Bluetooth מחדש</string>
+ <string name="pref_mobileUpdate_title">עדכון דרך רשת סלולרית</string>
+ <string name="pref_mobileUpdate_sum">לאפשר עדכונים דרך רשת סלולרית</string>
+ <string name="refreshing_label">מתבצע רענון</string>
<string name="flattr_settings_label">הגדרות Flattr</string>
- <string name="pref_flattr_auth_title">כניסה ל-Fattr</string>
- <string name="pref_flattr_auth_sum">היכנס לחשבון שלך לflattr לתרום ישירות מתוך האפליקציה.</string>
+ <string name="pref_flattr_auth_title">כניסה ל־Fattr</string>
+ <string name="pref_flattr_auth_sum">ניתן להיכנס לחשבון שלך ב־Flattr כדי לתרום ישירות מתוך היישומון.</string>
<string name="pref_flattr_this_app_title">תרום באמצעות Flattr לאפליקציה זו</string>
- <string name="pref_flattr_this_app_sum">תמוך בפיתוח אנטנה-פוד בתרומה עם Flattr. תודה!</string>
- <string name="pref_revokeAccess_title">בטל גישה</string>
- <string name="pref_revokeAccess_sum">בטל הרשאת גישה לחשבון flattr ליישום זה.</string>
+ <string name="pref_flattr_this_app_sum">ניתן לתמוך בפיתוח של אנטנה־פּוֹד על ידי תרומה ב־Flattr. תודה!</string>
+ <string name="pref_revokeAccess_title">ביטול גישה גישה</string>
+ <string name="pref_revokeAccess_sum">שלילת הרשאות הגישה לחשבון ה־Flattr שלך מיישומון זה.</string>
<string name="pref_auto_flattr_title">תרומות Flattr אוטומטיות</string>
- <string name="pref_auto_flattr_sum">הגדר תרומות flattr אוטומטיות</string>
- <string name="user_interface_label">ממשק משתמש</string>
- <string name="pref_set_theme_title">בחר ערכת נושא</string>
- <string name="pref_set_theme_sum">שנה את מראה אנטנה-פוד</string>
+ <string name="pref_auto_flattr_sum">הגדרת תרומות Flattr אוטומטיות</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>
+ <string name="pref_nav_drawer_feed_order_sum">שינוי סדר המינויים שלך</string>
+ <string name="pref_nav_drawer_feed_counter_title">הגדרת מונה מינויים</string>
+ <string name="pref_nav_drawer_feed_counter_sum">החלפת הפרטים שמוצגים על ידי מונה המינויים</string>
+ <string name="pref_set_theme_sum">שינוי המראה של אנטנה־פּוֹד</string>
<string name="pref_automatic_download_title">הורדה אוטומטית</string>
- <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_automatic_download_on_battery_title">הורדה כשלא טוען</string>
- <string name="pref_automatic_download_on_battery_sum">אפשר הורדה אוטומטית כשהסוללה אינה נטענת</string>
+ <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="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_theme_title_light">בהיר</string>
@@ -246,123 +361,295 @@
<string name="pref_update_interval_hours_singular">שעה</string>
<string name="pref_update_interval_hours_manual">ידני</string>
<string name="pref_gpodnet_authenticate_title">כניסה</string>
- <string name="pref_gpodnet_authenticate_sum">כנס עם חשבון gpodder.net שלך על מנת לסנכרן את ההרשמות שלך.</string>
- <string name="pref_gpodnet_logout_title">התנתקות</string>
- <string name="pref_gpodnet_logout_toast">ההתנתקות הייתה מוצלחת</string>
- <string name="pref_gpodnet_setlogin_information_title">שינוי פרטי התחברות</string>
- <string name="pref_gpodnet_setlogin_information_sum">שנה פרטי התחברות של חשבון gpodder.net.</string>
+ <string name="pref_gpodnet_authenticate_sum">ניתן להיכנס לחשבונך ב־gpodder.net כדי לסנכרן את המינויים שלך.</string>
+ <string name="pref_gpodnet_logout_title">יציאה</string>
+ <string name="pref_gpodnet_logout_toast">הצלחת לצאת</string>
+ <string name="pref_gpodnet_setlogin_information_title">שינוי פרטי הכניסה</string>
+ <string name="pref_gpodnet_setlogin_information_sum">שינוי פרטי הכניסה לחשבון ה־gpodder.net שלך.</string>
+ <string name="pref_gpodnet_sync_changes_title">סנכרון השינויים כעת</string>
+ <string name="pref_gpodnet_sync_changes_sum">סנכרון שינויי מצב במינויים ובפרקים מול gpodder.net.</string>
+ <string name="pref_gpodnet_full_sync_title">סנכרון מלא כעת</string>
+ <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_gpodnet_sethostname_title">הגדר שם שרת</string>
- <string name="pref_gpodnet_sethostname_use_default_host">השתמש בשרת ברירת מידל</string>
- <string name="pref_expandNotify_title">הרחב הודעה</string>
- <string name="pref_expandNotify_sum">תמיד הרחב את ההודעה כדי להראות את לחצני הפעלה.</string>
- <string name="pref_persistNotify_title">פקדי הפעלה קבועים</string>
- <string name="pref_persistNotify_sum">שמר בקרי הודעה ומסך נעילה בעת השהיית השמעה.</string>
- <string name="pref_expand_notify_unsupport_toast">גרסאות אנדרויד לפני 4.1 לא תומכות בהודעות מורחבות.</string>
- <string name="pref_queueAddToFront_sum">הוסף פרקים חדשים לראש התור.</string>
- <string name="pref_queueAddToFront_title">הוסף לראש התור.</string>
+ <string name="pref_playback_speed_sum">בחירת המהירויות הזמינות למהירות נגינה משתנה</string>
+ <string name="pref_fast_forward">זמן דילוג בהאצה קדימה</string>
+ <string name="pref_fast_forward_sum">התאמת מספר השניות של הקפיצה קדימה בעת לחיצה על כפתור ההאצה</string>
+ <string name="pref_rewind">זמן בקפיצה אחורה</string>
+ <string name="pref_rewind_sum">התאמת מספר השניות של הקפיצה אחורה בעת לחיצה על כפתור החזרה</string>
+ <string name="pref_gpodnet_sethostname_title">הגדרת שם מארח</string>
+ <string name="pref_gpodnet_sethostname_use_default_host">שימוש בשם מארח כבררת מחדל</string>
+ <string name="pref_expandNotify_title">הרחבת הודעה</string>
+ <string name="pref_expandNotify_sum">תמיד להרחיב את ההודעה כדי להציג את לחצני הנגינה.</string>
+ <string name="pref_persistNotify_title">פקדי נגינה קבועים</string>
+ <string name="pref_persistNotify_sum">להשאיר את פקדי ההתרעות ומסך הנעילה גם כשהנגינה מושהית.</string>
+ <string name="pref_compact_notification_buttons_title">הגדרת כפתורים במסך נעילה</string>
+ <string name="pref_compact_notification_buttons_sum">החלפת כפתור הנגינה במסך הנעילה. הכפתורים נגינה/השהיה תמיד נכללים.</string>
+ <string name="pref_compact_notification_buttons_dialog_title">בחירה עד %1$d פריטים</string>
+ <string name="pref_compact_notification_buttons_dialog_error">ניתן לבחור עד %1$d פריטים.</string>
+ <string name="pref_lockscreen_background_title">הגדרת רקע מסך הנעילה</string>
+ <string name="pref_lockscreen_background_sum">הגדרת רקע מסך הנעילה לתמונה של הפרק שמתנגן כעת. כתופעת לוואי, התמונה תופיע גם ביישומי צד שלישי.</string>
+ <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_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>
+ <string name="pref_cast_message_free_flavor">לתמיכה ב־Chromecast נדרשות ספריות קנייניות מאת צד־שלישי שמושבתות בגרסה זו של אנטנה־פּוֹד</string>
+ <string name="pref_enqueue_downloaded_title">הוספת הורדות לתור</string>
+ <string name="pref_enqueue_downloaded_summary">הוספת פרקים שהתקבלו לתור</string>
<!--Auto-Flattr dialog-->
- <string name="auto_flattr_enable">הפעל תרומות flattr אוטומטיות</string>
- <string name="auto_flattr_after_percent">תרום באמצעות flattr כשנוגן %d אחוזים מהפרק</string>
- <string name="auto_flattr_ater_beginning">תרום באמצעות flattr כשניגון פרק מתחיל</string>
- <string name="auto_flattr_ater_end">תרום באמצעות flattr כשניגון פרק מסתיים</string>
+ <string name="auto_flattr_enable">הפעלת תרומות Flattr אוטומטיות</string>
+ <string name="auto_flattr_after_percent">לתרום לפרק כאשר התנגנו %d אחוזים</string>
+ <string name="auto_flattr_ater_beginning">לתרום עם Flattr עם תחילת נגינת פרק</string>
+ <string name="auto_flattr_ater_end">לתרום עם Flattr עם סיום נגינת פרק</string>
<!--Search-->
+ <string name="search_hint">חיפוש אחר פרקים</string>
+ <string name="found_in_shownotes_label">נמצא בהערות הפרק</string>
<string name="found_in_chapters_label">נמצא בפרקים</string>
- <string name="search_status_no_results">אין תוצאות</string>
+ <string name="found_in_authors_label">נמצא בין היוצרים</string>
+ <string name="found_in_feeds_label">נמצא בערוץ התוכן</string>
+ <string name="search_status_no_results">לא נמצאו תוצאות</string>
<string name="search_label">חיפוש</string>
<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_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_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="select_all_label">בחר הכל</string>
- <string name="deselect_all_label">בטל בחירות</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="export_error_label">שגיאת יצוא</string>
- <string name="opml_export_success_title">יצוא OPML הצליח.</string>
- <string name="opml_export_success_sum">קובץ OPML נכתב ל:\u0020</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>
+ <string name="export_error_label">שגיאת ייצוא</string>
+ <string name="export_success_title">הייצוא הצליח</string>
+ <string name="export_success_sum">הקובץ שייוצא נכתב אל:\n\n%1$s</string>
+ <string name="opml_import_ask_read_permission">נדרשת גישה לאחסון חיצוני כדי לקרוא את קובץ ה־OPML</string>
<!--Sleep timer-->
- <string name="set_sleeptimer_label">קבע טיימר שינה</string>
- <string name="disable_sleeptimer_label">בטל טיימר שינה</string>
- <string name="enter_time_here_label">קבע זמן</string>
- <string name="sleep_timer_label">טיימר שינה</string>
- <string name="time_left_label">זמן נותר:\u0020</string>
- <string name="time_dialog_invalid_input">קלט לא חוקי, זמן חייב להיות מספר שלם</string>
+ <string name="set_sleeptimer_label">הגדרת מתזמן שינה</string>
+ <string name="disable_sleeptimer_label">השבתת מתזמן שינה</string>
+ <string name="enter_time_here_label">הגדרת שעה</string>
+ <string name="sleep_timer_label">מתזמן שינה</string>
+ <string name="time_left_label">זמן שנותר:\u0020</string>
+ <string name="time_dialog_invalid_input">קלט שגוי, השעה חייב להיות מספר שלם וחיובי</string>
+ <string name="timer_about_to_expire_label"><b>כאשר ספירת המתזמן עומדת להסתיים:</b></string>
+ <string name="shake_to_reset_label">לשקשק כדי לאפס את המתזמן</string>
+ <string name="timer_vibration_label">לרטוט</string>
+ <string name="time_seconds">שניות</string>
+ <string name="time_minutes">דקות</string>
+ <string name="time_hours">שעות</string>
+ <plurals name="time_seconds_quantified">
+ <item quantity="one">שנייה אחת</item>
+ <item quantity="two">%d שניות</item>
+ <item quantity="many">%d שניות</item>
+ <item quantity="other">%d שניות</item>
+ </plurals>
+ <plurals name="time_minutes_quantified">
+ <item quantity="one">דקה אחת</item>
+ <item quantity="two">%d דקות</item>
+ <item quantity="many">%d דקות</item>
+ <item quantity="other">%d דקות</item>
+ </plurals>
+ <plurals name="time_hours_quantified">
+ <item quantity="one">שעה</item>
+ <item quantity="two">שעתיים</item>
+ <item quantity="many">%d שעות</item>
+ <item quantity="other">%d שעות</item>
+ </plurals>
+ <string name="auto_enable_label">הפעלה אוטומטית</string>
+ <string name="sleep_timer_enabled_label">מתזמן השינה פעיל</string>
+ <string name="sleep_timer_disabled_label">מתזמן השינה מושבת</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">קטגוריות</string>
- <string name="gpodnet_toplist_header">פודקאסטים בכירים</string>
+ <string name="gpodnet_toplist_header">פודקאסטים מובילים</string>
<string name="gpodnet_suggestions_header">המלצות</string>
- <string name="gpodnet_search_hint">חפש ב-gpodder.net</string>
- <string name="gpodnetauth_login_title">התחברות</string>
- <string name="gpodnetauth_login_descr">ברוך הבא להתחברות ל-gpodder.net. ראשית, הקלד את פרטי הכניסה שלך:</string>
- <string name="gpodnetauth_login_butLabel">התחברות</string>
- <string name="gpodnetauth_login_register">אם אין לך עדיין חשבון, תוכל לפתוח אחד דרך: \nhttps://gpodder.net/register/</string>
- <string name="username_label">שם משתמש:</string>
- <string name="password_label">ססמה:</string>
+ <string name="gpodnet_search_hint">חיפוש ב־gpodder.net</string>
+ <string name="gpodnetauth_login_title">כניסה</string>
+ <string name="gpodnetauth_login_descr">ברוך בואך לתהליך הכניסה ל־gpodder.net. ראשית, עליך להקליד את פרטי הכניסה שלך:</string>
+ <string name="gpodnetauth_login_butLabel">כניסה</string>
+ <string name="gpodnetauth_login_register">אם עדיין אין לך חשבון, ניתן ליצור אחד כאן:‎\nhttps://gpodder.net/register/‎</string>
+ <string name="username_label">שם משתמש</string>
+ <string name="password_label">ססמה</string>
<string name="gpodnetauth_device_title">בחירת מכשיר</string>
- <string name="gpodnetauth_device_descr">צור מכשיר חדש לשימוש עבור חשבון gpodder.net או לבחר אחד קיים:</string>
+ <string name="gpodnetauth_device_descr">ניתן ליצור התקן חדש לשימוש עם החשבון שלך ב־gpodder.net או לבחור בהתקן חדש:</string>
<string name="gpodnetauth_device_deviceID">מזהה מכשיר:\u0020</string>
<string name="gpodnetauth_device_caption">כותרת</string>
- <string name="gpodnetauth_device_butCreateNewDevice">צור מכשיר חדש</string>
- <string name="gpodnetauth_device_chooseExistingDevice">בחר מכשיר קיים:</string>
- <string name="gpodnetauth_device_errorEmpty">מזהה המכשיר אינו יכול להיות ריק</string>
- <string name="gpodnetauth_device_errorAlreadyUsed">מזהה המכשיר בשימוש</string>
- <string name="gpodnetauth_device_butChoose">בחר</string>
- <string name="gpodnetauth_finish_title">התחברות מוצלחת!</string>
- <string name="gpodnetauth_finish_descr">מזל טוב! חשבון gpodder.net שלך מקושר כעת עם המכשיר שלך. אנטנה-פוד מעתה יסנכרן באופן אוטומטי הרשמות במכשיר שלך עם חשבון gpodder.net שלך.</string>
- <string name="gpodnetauth_finish_butsyncnow">התחל סנכרון כעת</string>
- <string name="gpodnetauth_finish_butgomainscreen">עבור למסך הראשי</string>
- <string name="gpodnetsync_auth_error_title">שגיאת אימות של gpodder.net</string>
+ <string name="gpodnetauth_device_butCreateNewDevice">יצירת מכשיר חדש</string>
+ <string name="gpodnetauth_device_chooseExistingDevice">בחירת מכשיר קיים:</string>
+ <string name="gpodnetauth_device_errorEmpty">מזהה המכשיר לא יכול להישאר ריק</string>
+ <string name="gpodnetauth_device_errorAlreadyUsed">מזהה המכשיר כבר בשימוש</string>
+ <string name="gpodnetauth_device_caption_errorEmpty">הכותרת לא יכולה להישאר ריקה</string>
+ <string name="gpodnetauth_device_butChoose">בחירה</string>
+ <string name="gpodnetauth_finish_title">נכנסת בהצלחה!</string>
+ <string name="gpodnetauth_finish_descr">מזל טוב! חשבון ה־gpodder.net שלך מקושר כעת עם המכשיר שלך. מעתה כל המינויים שלך יסונכרנו אוטומטית על ידי אנטנה־פּוֹד מהמכשיר שלך לחשבון ה־gpodder.net שלך.</string>
+ <string name="gpodnetauth_finish_butsyncnow">התחלת סנכרון כעת</string>
+ <string name="gpodnetauth_finish_butgomainscreen">מעבר למסך הראשי</string>
+ <string name="gpodnetsync_auth_error_title">שגיאת אימות מול gpodder.net</string>
<string name="gpodnetsync_auth_error_descr">שם משתמש או ססמה שגויים</string>
- <string name="gpodnetsync_error_title">שגיאת סנכרון של gpodder.net</string>
- <string name="gpodnetsync_error_descr">שגיאה במהל סינכרון:\u0020</string>
+ <string name="gpodnetsync_error_title">שגיאת סנכרון מול gpodder.net</string>
+ <string name="gpodnetsync_error_descr">שגיאה במהלך סינכרון:\u0020</string>
+ <string name="gpodnetsync_pref_report_successful">מוצלח</string>
+ <string name="gpodnetsync_pref_report_failed">נכשל</string>
<!--Directory chooser-->
- <string name="selected_folder_label">תיקיה נבחרת:</string>
- <string name="create_folder_label">צור תיקיה</string>
+ <string name="selected_folder_label">תיקייה נבחרת:</string>
+ <string name="create_folder_label">יצירת תיקייה</string>
<string name="choose_data_directory">בחר תיקיית מידע</string>
- <string name="create_folder_msg">צור תיקיה חדשה בשם \"%1$s\"?</string>
- <string name="create_folder_success">תיקיה חדשה נוצרה</string>
- <string name="create_folder_error_no_write_access">לא ניתן לכתוב לתיקה זו</string>
- <string name="create_folder_error_already_exists">תיקה כבר קיימת</string>
- <string name="create_folder_error">לא ניתן ליצור תיקיה</string>
- <string name="folder_not_empty_dialog_title">התיקיה אינה ריקה</string>
- <string name="folder_not_empty_dialog_msg">התיקייה שבחרת אינה ריקה. הורדות מדיה וקבצים אחרים יהיו ממוקמות ישירות בתיקייה זו. להמשיך בכל זאת?</string>
- <string name="set_to_default_folder">בחר תיקיית ברירת מחדל</string>
- <string name="pref_pausePlaybackForFocusLoss_sum">השהה ניגון במקום החלשת עוצמת שמע כשאפליקציה אחרת מנגנת</string>
- <string name="pref_pausePlaybackForFocusLoss_title">השהה בזמן הפרעה</string>
+ <string name="choose_data_directory_message">נא לבחור את בסיס תיקיית הנתונים שלך. תת־התיקיות תיווצרנה על ידי אנטנה־פּוֹד בהתאם.</string>
+ <string name="choose_data_directory_permission_rationale">נדרשת גישה לאחסון חיצוני כדי לשנות את תיקיית הנתונים</string>
+ <string name="create_folder_msg">ליצור תיקייה חדשה בשם „%1$s”?</string>
+ <string name="create_folder_success">נוצרה תיקייה חדשה</string>
+ <string name="create_folder_error_no_write_access">לא ניתן לכתוב לתיקייה זו</string>
+ <string name="create_folder_error_already_exists">התיקייה כבר קיימת</string>
+ <string name="create_folder_error">לא ניתן ליצור תיקייה</string>
+ <string name="folder_does_not_exist_error">„%1$s” לא קיים</string>
+ <string name="folder_not_readable_error">„%1$s” אינו קריא</string>
+ <string name="folder_not_writable_error">„%1$s” חסום לכתיבה</string>
+ <string name="folder_not_empty_dialog_title">התיקייה אינה ריקה</string>
+ <string name="folder_not_empty_dialog_msg">התיקייה שבחרת אינה ריקה. הורדות מדיה וקבצים אחרים ימוקמו ישירות בתיקייה זו. להמשיך בכל זאת?</string>
+ <string name="set_to_default_folder">נא לבחור תיקיית בררת מחדל</string>
+ <string name="pref_pausePlaybackForFocusLoss_sum">השהיית הניגון במקום הנמכת עצמת השמע כאשר יישומון אחר מעוניין לנגן צלילים</string>
+ <string name="pref_pausePlaybackForFocusLoss_title">להשהות במהלך הפרעות</string>
+ <string name="pref_resumeAfterCall_sum">להמשיך בנגינה לאחר השלמת שיחת הטלפון</string>
+ <string name="pref_resumeAfterCall_title">להמשיך לאחר שיחה</string>
+ <string name="pref_restart_required">יש להפעיל את אנטנה־פּוֹד מחדש כדי שהשינויים ייכנסו לתוקף.</string>
<!--Online feed view-->
- <string name="subscribe_label">הרשם</string>
- <string name="subscribed_label">נרשם</string>
+ <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>
+ <string name="rewind_label">חזרה לאחור</string>
+ <string name="fast_forward_label">הרצה קדימה</string>
<string name="media_type_audio_label">שמע</string>
<string name="media_type_video_label">וידאו</string>
- <string name="navigate_upwards_label">נווט למעלה</string>
+ <string name="navigate_upwards_label">ניווט כלפי מעלה</string>
<string name="status_downloading_label">הפרק יורד</string>
<string name="in_queue_label">הפרק בתור</string>
<string name="drag_handle_content_description">גרור לשינוי מיקום פריט זה</string>
- <string name="load_next_page_label">טען את הדף הבא</string>
+ <string name="load_next_page_label">טעינת הדף הבא</string>
<!--Feed information screen-->
<string name="authentication_label">אימות</string>
- <string name="authentication_descr">שנה את שם המשתמש והסיסמה שלך לפודקאסט ופרקים שלו.</string>
+ <string name="authentication_descr">שינוי שם המשתמש והססמה שלך לפודקאסט הזה ולפרקים שלו.</string>
+ <string name="auto_download_settings_label">הגדרות הורדה אוטומטית</string>
+ <string name="episode_filters_label">מסנן פרקים</string>
+ <string name="episode_filters_description">רשימת המונחים בהם יעשה שימוש כדי להחליט אם להכליל או להחריג פרק כלשהו במהלך הורדה אוטומטית</string>
+ <string name="episode_filters_include">להכליל</string>
+ <string name="episode_filters_exclude">להחריג</string>
+ <string name="episode_filters_hint">מילים בודדות \n\"אוסף מילים\"</string>
+ <string name="keep_updated">לשמור על עדכניות</string>
<!--Progress information-->
+ <string name="progress_upgrading_database">מסד הנתונים משתדרג</string>
<!--AntennaPodSP-->
- <string name="sp_apps_importing_feeds_msg">מייבא רישום מאפליקציות יעודיות...</string>
- <string name="search_itunes_label">חפש בiTunes</string>
+ <string name="sp_apps_importing_feeds_msg">מתבצע ייבוא מינויים מיישומונים ממוקדי מטרה…</string>
+ <string name="search_itunes_label">חיפוש ב־iTunes</string>
+ <string name="filter">מסנן</string>
+ <string name="search_fyyd_label">בחיפוש ב־fyyd</string>
<!--Episodes apply actions-->
+ <string name="all_label">הכול</string>
+ <string name="selected_all_label">בחירת כל הפרקים</string>
+ <string name="none_label">ללא</string>
+ <string name="deselected_all_label">אף פרק לא נבחר</string>
+ <string name="played_label">נוגנו</string>
+ <string name="selected_played_label">בחירת פרקים שנוגנו</string>
+ <string name="unplayed_label">לא נוגנו</string>
+ <string name="selected_unplayed_label">בחירת פרקים שלא נוגנו</string>
+ <string name="downloaded_label">הורדו</string>
+ <string name="selected_downloaded_label">בחירת פרקים שהורדו</string>
+ <string name="not_downloaded_label">לא הורדו</string>
+ <string name="selected_not_downloaded_label">בחירת פרקים שלא הורדו</string>
+ <string name="queued_label">בתור</string>
+ <string name="selected_queued_label">בחירת פרקים בתור</string>
+ <string name="not_queued_label">לא בתור</string>
+ <string name="selected_not_queued_label">בחירת פרטים שאינם בתור</string>
+ <string name="has_media">יש מדיה</string>
+ <string name="selected_has_media_label">בחירת פרקים עם מדיה</string>
<!--Sort-->
+ <string name="sort_title_a_z">כותרת (א \u2192 ת)</string>
+ <string name="sort_title_z_a">כותרת (ת \u2192 א)</string>
+ <string name="sort_date_new_old">תאריך (חדש \u2192 ישן)</string>
+ <string name="sort_date_old_new">תאריך (ישן \u2192 חדש)</string>
+ <string name="sort_duration_short_long">משך (קצר \u2192 ארוך)</string>
+ <string name="sort_duration_long_short">משך (קצר \u2192 ארוך)</string>
<!--Rating dialog-->
+ <string name="rating_title">היישומון אנטנה־פּוֹד נושא חן בעיניך?</string>
+ <string name="rating_message">מאוד נשמח לקבל דירוג על אנטנה־פּוֹד אם יש לך זמן לכך.</string>
+ <string name="rating_never_label">לא תודה</string>
+ <string name="rating_later_label">פעם אחרת</string>
+ <string name="rating_now_label">בטח, נלך על זה!</string>
<!--Audio controls-->
+ <string name="audio_controls">פקדי שמע</string>
+ <string name="playback_speed">מהירות נגינה</string>
+ <string name="volume">עצמת שמע</string>
+ <string name="left_short">L</string>
+ <string name="right_short">R</string>
+ <string name="audio_effects">אפקטים של שמע</string>
+ <string name="stereo_to_mono">איחוד: סטריאו למונו</string>
+ <string name="sonic_only">Sonic בלבד</string>
<!--proxy settings-->
+ <string name="proxy_type_label">סוג</string>
+ <string name="host_label">מארח</string>
+ <string name="port_label">פתחה</string>
+ <string name="optional_hint">(רשות)</string>
+ <string name="proxy_test_label">בדיקה</string>
+ <string name="proxy_checking">מתבצעת בדיקה…</string>
+ <string name="proxy_test_successful">הבדיקה לא הצליחה</string>
+ <string name="proxy_test_failed">הבדיקה נכשלה</string>
+ <string name="proxy_host_empty_error">המארח לא יכול להישאר ריק</string>
+ <string name="proxy_host_invalid_error">כתובת המארח אינה כתובת IP או שם מתחם תקניים</string>
+ <string name="proxy_port_invalid_error">הפתחה אינה תקנית</string>
+ <!--Database import/export-->
+ <string name="import_export">ייבוא/ייצוא מסד נתונים</string>
+ <string name="import_export_warning">תכונה ניסיונית זו יכולה לשמש לטובת העברת המינויים והפרקים שניגנת להתקן אחר.\n\nניתן לייבא מסדי נתונים שיוצאו רק לאותה הגרסה של אנטנה־פּוֹד. אחרת, תכונה זו עשויה לגרור התנהגות בלתי צפויה.\n\nלאחר הייבוא, יתכן שחלק מהפרקים יופיעו כאילו כבר הורדת אותם למרות שבפועל לא עשית זאת. עליך פשוט ללחוץ על כפתור הנגינה של הפרקים כדי שאנטנה־פּוֹד יוכל לזהות זאת.</string>
+ <string name="label_import">ייבוא</string>
+ <string name="label_export">ייצוא</string>
+ <string name="import_select_file">בחירת קובץ לייבוא</string>
+ <string name="export_ok">הייצוא הצליח.</string>
+ <string name="import_ok">הייבוא הצליח.\n\nנא ללחוץ על אישור כדי להפעיל את אנטנה־פּוֹד מחדש</string>
<!--Casting-->
+ <string name="cast_media_route_menu_title">ניגון דרך…</string>
+ <string name="cast_disconnect_label">ניתוק השידור</string>
+ <string name="cast_not_castable">המדיה הנבחרת אינה תואמת להתקן שידור</string>
+ <string name="cast_failed_to_play">התחלת נגינת המדיה נכשלה</string>
+ <string name="cast_failed_to_stop">עצירת נגינת המדיה נכשלה</string>
+ <string name="cast_failed_to_pause">השהיית נגינת המדיה נכשלה</string>
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <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>
+ <string name="cast_failed_media_error_skipping">שגיאה בנגינת המדיה. מתבצע דילוג…</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-ja/strings.xml b/core/src/main/res/values-ja/strings.xml
index 2e70594ec..c6068aa4b 100644
--- a/core/src/main/res/values-ja/strings.xml
+++ b/core/src/main/res/values-ja/strings.xml
@@ -1,15 +1,16 @@
<?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">購読を更新</string>
<string name="feeds_label">フィード</string>
<string name="statistics_label">統計情報</string>
<string name="add_feed_label">フィードを追加</string>
<string name="episodes_label">エピソード</string>
<string name="all_episodes_short_label">すべて</string>
+ <string name="new_episodes_label">新規</string>
<string name="favorite_episodes_label">お気に入り</string>
<string name="new_label">新</string>
<string name="settings_label">設定</string>
- <string name="add_new_feed_label">フィードを追加</string>
<string name="downloads_label">ダウンロード</string>
<string name="downloads_running_label">実行中</string>
<string name="downloads_completed_label">完了</string>
@@ -19,10 +20,12 @@
<string name="cancel_download_label">ダウンロードをキャンセル</string>
<string name="playback_history_label">再生履歴</string>
<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>
@@ -110,10 +113,14 @@
<string name="mark_all_seen_msg">すべてのエピソードを参照済にしました</string>
<string name="mark_all_seen_confirmation_msg">参照済としてマークするすべてのエピソードを確認してください。</string>
<string name="show_info_label">情報を表示</string>
+ <string name="show_feed_settings_label">フィード設定を表示</string>
+ <string name="feed_info_label">フィード情報</string>
+ <string name="feed_settings_label">フィード設定</string>
<string name="rename_feed_label">ポッドキャストの名前を変更</string>
<string name="remove_feed_label">ポッドキャストを削除</string>
<string name="share_label">共有…</string>
<string name="share_link_label">Webサイトのリンクを共有</string>
+ <string name="share_file_label">ファイルを共有</string>
<string name="share_link_with_position_label">場所とリンクを共有</string>
<string name="share_feed_url_label">フィード URLを共有</string>
<string name="share_item_url_label">エピソードファイル URLを共有</string>
@@ -122,7 +129,7 @@
<string name="feed_remover_msg">フィードの削除中</string>
<string name="load_complete_feed">フィードをすべて更新</string>
<string name="hide_episodes_title">エピソードを非表示にする</string>
- <string name="episode_actions">操作を適用</string>
+ <string name="batch_edit">一括編集</string>
<string name="hide_unplayed_episodes_label">未再生</string>
<string name="hide_paused_episodes_label">一時停止しました</string>
<string name="hide_played_episodes_label">再生しました</string>
@@ -142,6 +149,7 @@
<string name="stream_label">ストリーム</string>
<string name="remove_label">削除</string>
<string name="delete_label">削除</string>
+ <string name="delete_failed">ファイルを削除できません。デバイスを再起動してみてください。</string>
<string name="remove_episode_lable">エピソードを削除</string>
<string name="marked_as_seen_label">参照済としてマーク</string>
<string name="mark_read_label">再生済としてマーク</string>
@@ -166,6 +174,8 @@
<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>
@@ -215,6 +225,7 @@
<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-->
@@ -232,6 +243,8 @@
<string name="duration">継続時間</string>
<string name="episode_title">エピソード タイトル</string>
<string name="feed_title">フィード タイトル</string>
+ <string name="random">ランダム</string>
+ <string name="smart_shuffle">スマートシャッフル</string>
<string name="ascending">昇順</string>
<string name="descending">降順</string>
<string name="clear_queue_confirmation_msg">クリアする、キューに含まれるすべてのエピソードを確認してください。</string>
@@ -278,8 +291,17 @@
<string name="other_pref">その他</string>
<string name="about_pref">について</string>
<string name="queue_label">キュー</string>
- <string name="services_label">サービス</string>
+ <string name="integrations_label">統合</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">マイクロペイメント サービス</string>
+ <string name="automation">自動</string>
+ <string name="download_pref_details">詳細</string>
+ <string name="import_export_pref">インポート/エクスポート</string>
+ <string name="appearance">外観</string>
+ <string name="external_elements">外部要素</string>
+ <string name="interruptions">割り込み</string>
+ <string name="buttons">ボタン</string>
+ <string name="media_player">メディアプレーヤー</string>
<string name="pref_episode_cleanup_title">エピソード クリーンアップ</string>
<string name="pref_episode_cleanup_summary">キューに含まれておらず、お気に入りではないエピソードは、自動ダウンロードで新しいエピソードのためにスペースが必要な場合、除去の対象になります</string>
<string name="pref_pauseOnDisconnect_sum">ヘッドフォンまたはBluetoothの接続が切断された時、再生を一時停止します</string>
@@ -296,6 +318,8 @@
<string name="pref_smart_mark_as_played_title">再生済としてスマートマーク</string>
<string name="pref_skip_keeps_episodes_sum">エピソードをスキップした時に残しておきます</string>
<string name="pref_skip_keeps_episodes_title">エピソードのスキップ時に残す</string>
+ <string name="pref_favorite_keeps_episodes_sum">エピソードをお気に入りに追加した時に残しておきます</string>
+ <string name="pref_favorite_keeps_episodes_title">お気に入りのエピソードを残す</string>
<string name="playback_pref">再生</string>
<string name="network_pref">ネットワーク</string>
<string name="pref_autoUpdateIntervallOrTime_title">間隔または時間を更新</string>
@@ -339,12 +363,15 @@
<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>
<string name="pref_episode_cache_title">エピソードキャッシュ</string>
<string name="pref_theme_title_light">ライト</string>
<string name="pref_theme_title_dark">ダーク</string>
+ <string name="pref_theme_title_trueblack">トゥルーブラック</string>
<string name="pref_episode_cache_unlimited">無制限</string>
<string name="pref_update_interval_hours_plural">時間</string>
<string name="pref_update_interval_hours_singular">時間</string>
@@ -395,8 +422,7 @@
<string name="crash_report_sum">メールで最新のクラッシュレポートを送信します</string>
<string name="send_email">メールを送信</string>
<string name="experimental_pref">実験的</string>
- <string name="pref_sonic_title">Sonic メディアプレーヤー</string>
- <string name="pref_sonic_message">Android 標準のメディアプレーヤーと Prestissimo の代わりに、内蔵のソニックメディアプレーヤーを使用します</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>
@@ -406,8 +432,13 @@
<string name="pref_cast_title">Chromecast サポート</string>
<string name="pref_cast_message_play_flavor">(Chromecast、オーディオスピーカー、Android TV など) キャストデバイス上でリモートメディア再生のサポートを有効にします</string>
<string name="pref_cast_message_free_flavor">Chromecast は AntennaPod のこのバージョンで無効になっているサードパーティ独自のライブラリーが必要です</string>
- <string name="pref_enqueue_downloaded_title">ダウンロード済みをキューに入れる</string>
+ <string name="pref_enqueue_downloaded_title">ダウンロードのキューに入れる</string>
<string name="pref_enqueue_downloaded_summary">ダウンロードしたエピソードをキューに追加します</string>
+ <string name="media_player_builtin">ビルトイン Android プレーヤー</string>
+ <string name="pref_videoBehavior_title">ビデオ動作</string>
+ <string name="pref_videoBehavior_sum">ビデオ再生から遷移時の動作</string>
+ <string name="stop_playback">再生停止</string>
+ <string name="continue_playback">再生継続</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">自動Flattrを有効にする</string>
<string name="auto_flattr_after_percent">%d %再生したらエピソードをFlattr </string>
@@ -444,8 +475,8 @@
<string name="html_export_label">HTML エクスポート</string>
<string name="exporting_label">エクスポート中…</string>
<string name="export_error_label">エクスポートエラー</string>
- <string name="opml_export_success_title">OPMLをエクスポートしました。</string>
- <string name="opml_export_success_sum">.opml ファイルを書き込みました:\u0020</string>
+ <string name="export_success_title">エクスポートしました</string>
+ <string name="export_success_sum">エクスポートしたファイルを書き込みました:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">OPML ファイルを読み込むために、外部ストレージへのアクセスが必要です</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">スリープタイマーをセット</string>
@@ -609,6 +640,14 @@
<string name="proxy_host_empty_error">ホストは空にできません</string>
<string name="proxy_host_invalid_error">ホストが有効なIPアドレスやドメインではありません</string>
<string name="proxy_port_invalid_error">ポートが正しくありません</string>
+ <!--Database import/export-->
+ <string name="import_export">データベースのインポート/エクスポート</string>
+ <string name="import_export_warning">この実験的な機能を使用すると、サブスクリプションと再生したエピソードを別のデバイスに転送できます。\n\nエクスポートされたデータベースは、同じバージョンのAntennaPodを使用する場合にのみインポートできます。 それ以外の場合、この機能は予期しない動作につながります。\n\nインポートした後、エピソードはダウンロードされたものとして表示されることがあります。 エピソードの再生ボタンを押すだけで、AntennaPodがこれを検出します。</string>
+ <string name="label_import">インポート</string>
+ <string name="label_export">エクスポート</string>
+ <string name="import_select_file">インポートするファイルを選択してください</string>
+ <string name="export_ok">エクスポートしました。</string>
+ <string name="import_ok">インポートが成功しました。\n\nOKを押してAntennaPodを再起動してください。</string>
<!--Casting-->
<string name="cast_media_route_menu_title">…で再生</string>
<string name="cast_disconnect_label">キャストセッションを切断</string>
@@ -625,4 +664,13 @@
<string name="cast_failed_seek">キャストデバイスの新しい位置への移動に失敗しました</string>
<string name="cast_failed_receiver_player_error">レシーバープレーヤーで深刻なエラーが発生しました</string>
<string name="cast_failed_media_error_skipping">メディアの再生時にエラーが発生しました。スキップしています…</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">操作が必要</string>
+ <string name="notification_channel_user_action_description">たとえばパスワードを入力する必要がある場合など、操作が必要な場合に表示されます。</string>
+ <string name="notification_channel_downloading">ダウンロード中</string>
+ <string name="notification_channel_downloading_description">現在のダウンロードが表示されます。</string>
+ <string name="notification_channel_playing">現在再生中</string>
+ <string name="notification_channel_playing_description">再生をコントロールできます。これはポッドキャスト再生中のメイン通知です。</string>
+ <string name="notification_channel_error">エラー</string>
+ <string name="notification_channel_error_description">ダウンロードや gpodder の同期に失敗した場合など、何か問題が発生した場合に表示されます。</string>
</resources>
diff --git a/core/src/main/res/values-kn-rIN/strings.xml b/core/src/main/res/values-kn-rIN/strings.xml
index 3d24900c9..14ccf4e42 100644
--- a/core/src/main/res/values-kn-rIN/strings.xml
+++ b/core/src/main/res/values-kn-rIN/strings.xml
@@ -98,6 +98,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-ko-rKR/strings.xml b/core/src/main/res/values-ko-rKR/strings.xml
index 28dfeb6e8..2d9481b84 100644
--- a/core/src/main/res/values-ko-rKR/strings.xml
+++ b/core/src/main/res/values-ko-rKR/strings.xml
@@ -33,6 +33,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-ko/strings.xml b/core/src/main/res/values-ko/strings.xml
index b472db1a7..c776885ea 100644
--- a/core/src/main/res/values-ko/strings.xml
+++ b/core/src/main/res/values-ko/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">즐겨찾기</string>
<string name="new_label">신규</string>
<string name="settings_label">설정</string>
- <string name="add_new_feed_label">팟캐스트 추가</string>
<string name="downloads_label">다운로드</string>
<string name="downloads_running_label">실행 중</string>
<string name="downloads_completed_label">마침</string>
@@ -114,6 +113,7 @@
<string name="remove_feed_label">팟캐스트 제거</string>
<string name="share_label">공유…</string>
<string name="share_link_label">홈페이지 링크 공유</string>
+ <string name="share_file_label">파일 공유</string>
<string name="share_link_with_position_label">위치와 같이 링크 공유</string>
<string name="share_feed_url_label">피드 URL 공유</string>
<string name="share_item_url_label">에피소드 파일 URL 공유</string>
@@ -122,7 +122,6 @@
<string name="feed_remover_msg">피드 삭제하는 중</string>
<string name="load_complete_feed">전체 피드 새로고침</string>
<string name="hide_episodes_title">에피소드 감추기</string>
- <string name="episode_actions">동작 적용</string>
<string name="hide_unplayed_episodes_label">재생 안 함</string>
<string name="hide_paused_episodes_label">일시 중지</string>
<string name="hide_played_episodes_label">재생함</string>
@@ -142,6 +141,7 @@
<string name="stream_label">스트리밍</string>
<string name="remove_label">제거</string>
<string name="delete_label">삭제</string>
+ <string name="delete_failed">파일을 삭제할 수 없습니다. 장치를 재부팅하면 동작할 수도 있습니다.</string>
<string name="remove_episode_lable">에피소드 제거</string>
<string name="marked_as_seen_label">봤다고 표시했습니다</string>
<string name="mark_read_label">재생했다고 표시</string>
@@ -166,6 +166,8 @@
<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>
@@ -278,7 +280,6 @@
<string name="other_pref">기타</string>
<string name="about_pref">정보</string>
<string name="queue_label">대기열</string>
- <string name="services_label">서비스</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">에피소드 정리</string>
<string name="pref_episode_cleanup_summary">대기열에 없고 즐겨찾기에 넣지 않은 에피소드는 자동 다운로드에서 새 에피소드에 공간이 필요할 경우 제거될 수 있습니다.</string>
@@ -296,6 +297,8 @@
<string name="pref_smart_mark_as_played_title">똑똑하게 재생한 것으로 표시</string>
<string name="pref_skip_keeps_episodes_sum">에피소드를 넘겼을 경우에도 유지합니다.</string>
<string name="pref_skip_keeps_episodes_title">넘긴 에피소드 유지 보관</string>
+ <string name="pref_favorite_keeps_episodes_sum">즐겨찾기로 표시하면 에피소드를 유지합니다</string>
+ <string name="pref_favorite_keeps_episodes_title">즐겨찾기 에피소드 유지</string>
<string name="playback_pref">재생</string>
<string name="network_pref">네트워크</string>
<string name="pref_autoUpdateIntervallOrTime_title">업데이트 주기 또는 하루 중 시각</string>
@@ -339,6 +342,8 @@
<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>
@@ -395,8 +400,6 @@
<string name="crash_report_sum">최근의 이상 종료 보고서를 이메일로 보냅니다.</string>
<string name="send_email">이메일 보내기</string>
<string name="experimental_pref">실험적 기능</string>
- <string name="pref_sonic_title">소닉 미디어 플레이어</string>
- <string name="pref_sonic_message">내장 소닉 미디어 플레이어를 안드로이드 고유 미디어 플레이어와 Prestissimo 대신 사용합니다.</string>
<string name="pref_current_value">현재 값: %1$s</string>
<string name="pref_proxy_title">프록시</string>
<string name="pref_proxy_sum">네트워크 프록시 설정</string>
@@ -406,7 +409,7 @@
<string name="pref_cast_title">크롬캐스트 지원</string>
<string name="pref_cast_message_play_flavor">캐스트 장치의 원격 미디어 재생 기능 사용 (예: 크롬캐스트, 안드로이드 TV의 오디오 스피커)</string>
<string name="pref_cast_message_free_flavor">크롬캐스트는 서드파티 라이브러리가 필요하지만, 이 버전의 안테나팟에서는 사용하지 않게 되어 있습니다.</string>
- <string name="pref_enqueue_downloaded_title">다운로드 항목 대기열 넣기</string>
+ <string name="pref_enqueue_downloaded_title">다운로드한 항목 대기열에 추가</string>
<string name="pref_enqueue_downloaded_summary">다운로드한 에피소드를 대기열에 추가</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">자동 flattr 사용</string>
@@ -444,8 +447,8 @@
<string name="html_export_label">HTML 내보내기</string>
<string name="exporting_label">내보내는 중…</string>
<string name="export_error_label">내보내기 오류</string>
- <string name="opml_export_success_title">OPML 내보내기가 성공했습니다.</string>
- <string name="opml_export_success_sum">OPML 파일을 다음에 저장했습니다:\u0020</string>
+ <string name="export_success_title">내보내기 성공</string>
+ <string name="export_success_sum">내보낸 파일을 다음에 저장했습니다:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">OPML 파일을 읽으려면 외부 저장소 접근이 필요합니다</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">취침 타이머 설정</string>
@@ -609,6 +612,14 @@
<string name="proxy_host_empty_error">호스트가 비어 있으면 안 됩니다</string>
<string name="proxy_host_invalid_error">호스트가 올바른 IP 주소 또는 도메인이 아닙니다</string>
<string name="proxy_port_invalid_error">포트가 올바르지 않습니다</string>
+ <!--Database import/export-->
+ <string name="import_export">데이터베이스 가져오기/내보내기</string>
+ <string name="import_export_warning">실험적인 기능으로 구독 정보와 재생한 에피소드 정보를 다른 장치로 옮기는데 사용합니다.\n\n내보낸 데이터베이스는 같은 버전의 안테나팟을 사용할 경우에만 가져올 수 있습니다. 같은 버전이 아니면 예상치 못하게 동작할 수 있습니다.\n\n가져온 후에 다운로드하지 않은 에피소드가 다운로드한 것으로 표시될 수 있습니다. 그 경우 해당 에피소드의 재생 버튼을 누르면 안테나팟에서 다운로드 여부를 확인해 줍니다.</string>
+ <string name="label_import">가져오기</string>
+ <string name="label_export">내보내기</string>
+ <string name="import_select_file">가져올 파일을 선택하십시오</string>
+ <string name="export_ok">내보내기 성공</string>
+ <string name="import_ok">내보내기 성공.\n\n안테나팟을 다시 시작하려면 확인을 누르십시오</string>
<!--Casting-->
<string name="cast_media_route_menu_title">다른 장치에서 재생...</string>
<string name="cast_disconnect_label">캐스트 세션 연결 끊기</string>
@@ -625,4 +636,5 @@
<string name="cast_failed_seek">캐스트 장치에 새 재생 위치로 이동하는데 실패했습니다</string>
<string name="cast_failed_receiver_player_error">리시버 플레이어에서 심각한 오류가 발생했습니다</string>
<string name="cast_failed_media_error_skipping">미디어 재생에 오류. 건너뜁니다...</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-lt/strings.xml b/core/src/main/res/values-lt/strings.xml
index 73d1d90a8..ed8862d11 100644
--- a/core/src/main/res/values-lt/strings.xml
+++ b/core/src/main/res/values-lt/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Mėgiami</string>
<string name="new_label">Nauji</string>
<string name="settings_label">Nustatymai</string>
- <string name="add_new_feed_label">Pridėti tinklalaidę</string>
<string name="downloads_label">Atsiuntimai</string>
<string name="downloads_running_label">Vykdomi</string>
<string name="downloads_completed_label">Užbaigti</string>
@@ -94,6 +93,7 @@
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">Praėjus 1 dienai nuo perklausymo</item>
<item quantity="few">Praėjus %d dienoms nuo perklausymo</item>
+ <item quantity="many">Praėjus %d dienų nuo perklausymo</item>
<item quantity="other">Praėjus %d dienų nuo perklausymo</item>
</plurals>
<!--'Add Feed' Activity labels-->
@@ -116,6 +116,7 @@
<string name="remove_feed_label">Pašalinti tinklalaidę</string>
<string name="share_label">Dalintis...</string>
<string name="share_link_label">Dalintis nuoroda</string>
+ <string name="share_file_label">Dalintis failu</string>
<string name="share_link_with_position_label">Dalintis nuoroda su pozicija</string>
<string name="share_feed_url_label">Dalintis sklaidos kanalo URL</string>
<string name="share_item_url_label">Dalintis epizodo failo URL</string>
@@ -124,7 +125,6 @@
<string name="feed_remover_msg">Šalinamas sklaidos kanalas</string>
<string name="load_complete_feed">Atnaujinti visą sklaidos kanalą</string>
<string name="hide_episodes_title">Slėpti epizodus</string>
- <string name="episode_actions">Pritaikyti veiksmus</string>
<string name="hide_unplayed_episodes_label">Neperklausyti</string>
<string name="hide_paused_episodes_label">Pristabdyti</string>
<string name="hide_played_episodes_label">Perklausyti</string>
@@ -144,6 +144,7 @@
<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="remove_episode_lable">Pašalinti epizodą</string>
<string name="marked_as_seen_label">Pažymėtas kaip matytas</string>
<string name="mark_read_label">Pažymėti kaip perklausytą</string>
@@ -192,6 +193,7 @@
<plurals name="downloads_left">
<item quantity="one">Liko %d atsiuntimas</item>
<item quantity="few">Liko %d atsiuntimai</item>
+ <item quantity="many">Liko %d atsiuntimų</item>
<item quantity="other">Liko %d atsiuntimų</item>
</plurals>
<string name="downloads_processing">Apdorojami atsiuntimai</string>
@@ -282,7 +284,6 @@
<string name="other_pref">Kita</string>
<string name="about_pref">Apie</string>
<string name="queue_label">Eilė</string>
- <string name="services_label">Paslaugos</string>
<string name="flattr_label">Flattr</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>
@@ -300,6 +301,8 @@
<string name="pref_smart_mark_as_played_title">Išmanus perklausų žymėjimas</string>
<string name="pref_skip_keeps_episodes_sum">Palikti epizodus, kai šie praleidžiami</string>
<string name="pref_skip_keeps_episodes_title">Palikti praleistus epizodus</string>
+ <string name="pref_favorite_keeps_episodes_sum">Palikti epizodus, kurie yra pažymėti kaip mėgiami</string>
+ <string name="pref_favorite_keeps_episodes_title">Palikti mėgiamus epizodus</string>
<string name="playback_pref">Atkūrimas</string>
<string name="network_pref">Tinklas</string>
<string name="pref_autoUpdateIntervallOrTime_title">Atnaujinimų intervalas ar dienos metas</string>
@@ -343,6 +346,8 @@
<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>
@@ -399,8 +404,6 @@
<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="experimental_pref">Eksperimentinis</string>
- <string name="pref_sonic_title">„Sonic“ medijos leistuvė</string>
- <string name="pref_sonic_message">Naudoti įtaisytąją „Sonic“ medijos leistuvę vietoje savosios „Android“ medijos leistuvės ir „Prestissimo“ </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>
@@ -448,8 +451,6 @@
<string name="html_export_label">HTML eksportas</string>
<string name="exporting_label">Eksportuojama...</string>
<string name="export_error_label">Eksporto klaida</string>
- <string name="opml_export_success_title">OPML eksportas sėkmingas.</string>
- <string name="opml_export_success_sum">.opml failas išsaugotas į:\u0020</string>
<string name="opml_import_ask_read_permission">Norint nuskaityti OPML failą reikalinga prieiga prie nešiojamos atmintinės</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Nustatyti miego laikmatį</string>
@@ -467,16 +468,19 @@
<plurals name="time_seconds_quantified">
<item quantity="one">1 sekundė</item>
<item quantity="few">%d sekundės</item>
+ <item quantity="many">%d sekundžių</item>
<item quantity="other">%d sekundžių</item>
</plurals>
<plurals name="time_minutes_quantified">
<item quantity="one">1 minutė</item>
<item quantity="few">%d minutės</item>
+ <item quantity="many">%d minučių</item>
<item quantity="other">%d minučių</item>
</plurals>
<plurals name="time_hours_quantified">
<item quantity="one">1 valanda</item>
<item quantity="few">%d valandos</item>
+ <item quantity="many">%d valandų</item>
<item quantity="other">%d valandų</item>
</plurals>
<string name="auto_enable_label">Įjungti automatiškai</string>
@@ -619,6 +623,19 @@
<string name="proxy_host_empty_error">Serverio laukelis negali būti tuščias</string>
<string name="proxy_host_invalid_error">Serverio laukelyje nurodytas netaisyklingas IP adresas ar sritis</string>
<string name="proxy_port_invalid_error">Netinkamas prievadas</string>
+ <!--Database import/export-->
+ <string name="import_export">Duomenų bazės importas/eksportas</string>
+ <string name="import_export_warning">Šis eksperimentinė funkcija leidžia perkelti jūsų prenumeratas bei duomenis apie perklausytus epizodus į kitą įrenginį.
+
+Eksportuota duomenų bazė gali būti importuota tik naudojantis ta pačia „AntennaPod“ versija. Kitu atveju, šios funkcijos naudojimas gali turėti netikėtų padarinių.
+
+Po importavimo, epizodai gali būti per klaidą pažymėti kaip atsisiųsti. Tiesiog spustelėkite atkūrimo mygtuką ir „AntennaPod“ aptiks šią klaidą. </string>
+ <string name="label_import">Importuoti</string>
+ <string name="label_export">Eksportuoti</string>
+ <string name="import_select_file">Pasirinkite failą, kurį norite importuoti</string>
+ <string name="import_ok">Importuota sėkmingai.
+
+Spauskite „OK“, kad paleisti „AntennaPod“ iš naujo.</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Atkurti naudojant...</string>
<string name="cast_disconnect_label">Atjungti „Chromecast“ sesiją</string>
@@ -635,4 +652,5 @@
<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>
<string name="cast_failed_media_error_skipping">Įvyko medijos atkūrimo klaida. Praleidžiama...</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-mk/strings.xml b/core/src/main/res/values-mk/strings.xml
new file mode 100644
index 000000000..1bb5aa651
--- /dev/null
+++ b/core/src/main/res/values-mk/strings.xml
@@ -0,0 +1,54 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<resources xmlns:tools="http://schemas.android.com/tools">
+ <!--Activitiy and fragment titles-->
+ <string name="statistics_label">Статистики</string>
+ <string name="add_feed_label">Стави Подкаст</string>
+ <string name="episodes_label">Епизоди</string>
+ <string name="all_episodes_short_label">Сите</string>
+ <string name="new_episodes_label">Нови</string>
+ <string name="favorite_episodes_label">Омилени</string>
+ <!--Statistics fragment-->
+ <!--Main activity-->
+ <!--Webview actions-->
+ <!--Playback history-->
+ <!--Other-->
+ <string name="no">Не</string>
+ <string name="reset">Ресет</string>
+ <string name="author_label">Автор</string>
+ <string name="language_label">Јазик</string>
+ <string name="url_label">УРЛ</string>
+ <string name="chapters_label">Поглавја</string>
+ <!--'Add Feed' Activity labels-->
+ <!--Actions on feeds-->
+ <!--actions on feeditems-->
+ <string name="delete_label">Избриши</string>
+ <!--Download messages and labels-->
+ <!--Mediaplayer messages-->
+ <!--Queue operations-->
+ <string name="date">Датум</string>
+ <!--Flattr-->
+ <!--Flattr-->
+ <!--Variable Speed-->
+ <!--Empty list labels-->
+ <!--Preferences-->
+ <!--Auto-Flattr dialog-->
+ <!--Search-->
+ <!--OPML import and export-->
+ <!--Sleep timer-->
+ <!--gpodder.net-->
+ <!--Directory chooser-->
+ <!--Online feed view-->
+ <!--Content descriptions for image buttons-->
+ <!--Feed information screen-->
+ <!--Progress information-->
+ <!--AntennaPodSP-->
+ <!--Episodes apply actions-->
+ <!--Sort-->
+ <!--Rating dialog-->
+ <!--Audio controls-->
+ <!--proxy settings-->
+ <!--Database import/export-->
+ <!--Casting-->
+ <!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
+</resources>
diff --git a/core/src/main/res/values-nb/strings.xml b/core/src/main/res/values-nb/strings.xml
index 13f9274ae..545eddc13 100644
--- a/core/src/main/res/values-nb/strings.xml
+++ b/core/src/main/res/values-nb/strings.xml
@@ -8,7 +8,6 @@
<string name="favorite_episodes_label">Favoritter</string>
<string name="new_label">Nye</string>
<string name="settings_label">Innstillinger</string>
- <string name="add_new_feed_label">Legg til podcast</string>
<string name="downloads_label">Nedlastninger</string>
<string name="downloads_running_label">Kjører</string>
<string name="downloads_completed_label">Fullført</string>
@@ -96,7 +95,6 @@
<string name="feed_remover_msg">Fjerner strøm</string>
<string name="load_complete_feed">Oppdater hele strømmen</string>
<string name="hide_episodes_title">Skjul episoder</string>
- <string name="episode_actions">Lagre handlinger</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>
@@ -232,7 +230,6 @@
<string name="other_pref">Annet</string>
<string name="about_pref">Om</string>
<string name="queue_label">Queue</string>
- <string name="services_label">Tjenester</string>
<string name="flattr_label">Flattr</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>
@@ -351,8 +348,6 @@
<string name="choose_file_from_external_application">Bruk ekstern applikasjon</string>
<string name="opml_export_label">OPML-eksportering</string>
<string name="export_error_label">Eksporteringserror</string>
- <string name="opml_export_success_title">OPML-import vellykket.</string>
- <string name="opml_export_success_sum">.opml-filen ble skrevet til:\u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Sett opp sovetimer</string>
<string name="disable_sleeptimer_label">Deaktiver sovetimer</string>
@@ -478,6 +473,8 @@
<string name="rating_now_label">Naturligvis, kom igjen!</string>
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-nl/strings.xml b/core/src/main/res/values-nl/strings.xml
index ce80569e4..daa64d90c 100644
--- a/core/src/main/res/values-nl/strings.xml
+++ b/core/src/main/res/values-nl/strings.xml
@@ -1,15 +1,16 @@
<?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">Feeds updaten</string>
<string name="feeds_label">Feeds</string>
<string name="statistics_label">Statistieken</string>
<string name="add_feed_label">Podcast toevoegen</string>
<string name="episodes_label">Afleveringen</string>
<string name="all_episodes_short_label">Alle</string>
+ <string name="new_episodes_label">Nieuw</string>
<string name="favorite_episodes_label">Favorieten</string>
<string name="new_label">Nieuw</string>
<string name="settings_label">Instellingen</string>
- <string name="add_new_feed_label">Podcast toevoegen</string>
<string name="downloads_label">Downloads</string>
<string name="downloads_running_label">Bezig</string>
<string name="downloads_completed_label">Voltooid</string>
@@ -19,10 +20,12 @@
<string name="cancel_download_label">Annuleer download</string>
<string name="playback_history_label">Afspeelgeschiedenis</string>
<string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_summary">Met andere apparaten synchroniseren</string>
<string name="gpodnet_auth_label">gpodder.net login</string>
<string name="free_space_label">%1$s beschikbaar</string>
<string name="episode_cache_full_title">Afleveringen cache is vol</string>
<string name="episode_cache_full_message">Het maximum aantal gecachte afleveringen is bereikt. U kunt het maximum verhogen in de instellingen.</string>
+ <string name="synchronizing">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 de %2$d afleveringen gestart.\n\n%3$s van de %4$s afgespeeld.</string>
@@ -57,7 +60,7 @@
<string name="yes">Ja</string>
<string name="no">Nee</string>
<string name="reset">Reset</string>
- <string name="author_label">Auteur</string>
+ <string name="author_label">Auteur(s)</string>
<string name="language_label">Taal</string>
<string name="url_label">URL</string>
<string name="podcast_settings_label">Instellingen</string>
@@ -111,19 +114,23 @@
<string name="mark_all_seen_msg">\'Nieuw\' label van alle afleveringen verwijderend</string>
<string name="mark_all_seen_confirmation_msg">Bevestig aub dat u het \'nieuw\' label van alle afleveringen wilt verwijderen.</string>
<string name="show_info_label">Toon informatie</string>
+ <string name="show_feed_settings_label">Feed-instellingen tonen</string>
+ <string name="feed_info_label">Feed-informatie</string>
+ <string name="feed_settings_label">Feed-instellingen</string>
<string name="rename_feed_label">Podcast hernoemen</string>
<string name="remove_feed_label">Podcast verwijderen</string>
<string name="share_label">Delen…</string>
<string name="share_link_label">Link van de aflevering delen</string>
+ <string name="share_file_label">Deel bestand</string>
<string name="share_link_with_position_label">Link van de aflevering met tijdstip delen</string>
<string name="share_feed_url_label">URL van de feed delen</string>
- <string name="share_item_url_label">URL van het mediabestand delen</string>
- <string name="share_item_url_with_position_label">URL van mediabestand met tijdstip delen</string>
+ <string name="share_item_url_label">URL v/h mediabestand delen</string>
+ <string name="share_item_url_with_position_label">URL v/h mediabestand met tijdstip delen</string>
<string name="feed_delete_confirmation_msg">Bevestig dat u de feed <i>%1$s</i> en ALLE (ook gedownloade) afleveringen van deze podcast wilt verwijderen.</string>
<string name="feed_remover_msg">Feed verwijderen</string>
<string name="load_complete_feed">Hele feed vernieuwen</string>
<string name="hide_episodes_title">Afleveringen verbergen</string>
- <string name="episode_actions">Afleveringen beheren</string>
+ <string name="batch_edit">Bulkbewerking</string>
<string name="hide_unplayed_episodes_label">Niet afgespeeld</string>
<string name="hide_paused_episodes_label">Gepauzeerd</string>
<string name="hide_played_episodes_label">Afgespeeld</string>
@@ -143,6 +150,7 @@
<string name="stream_label">Stream</string>
<string name="remove_label">Verwijderen</string>
<string name="delete_label">Verwijderen</string>
+ <string name="delete_failed">Kan bestand niet verwijderen. Het kan misschien helpen om je apparaat opnieuw op te starten.</string>
<string name="remove_episode_lable">Aflevering(en) verwijderen</string>
<string name="marked_as_seen_label">Verwijder \'nieuw\' label</string>
<string name="mark_read_label">Als afgespeeld markeren</string>
@@ -167,13 +175,15 @@
<string name="download_failed">mislukt</string>
<string name="download_pending">Download in afwachting</string>
<string name="download_running">Aan het downloaden</string>
+ <string name="download_error_details">Details</string>
+ <string name="download_error_details_message">%1$s \n\nURL bestand:\n%2$s</string>
<string name="download_error_device_not_found">Opslagmedium niet gevonden</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 data fout</string>
<string name="download_error_error_unknown">Onbekende fout</string>
<string name="download_error_parser_exception">Parser Exception</string>
- <string name="download_error_unsupported_type">Niet ondersteunde feed soort</string>
+ <string name="download_error_unsupported_type">Niet ondersteunde type feed</string>
<string name="download_error_connection_error">Verbindingsfout</string>
<string name="download_error_unknown_host">Onbekende host</string>
<string name="download_error_unauthorized">Authenticatie fout</string>
@@ -217,6 +227,7 @@
<string name="playback_error_unknown">Onbekende fout</string>
<string name="no_media_playing_label">Geen media aan het afspelen</string>
<string name="player_buffering_msg">Buffering</string>
+ <string name="player_go_to_picture_in_picture">Picture-in-picture modus</string>
<string name="playbackservice_notification_title">Podcast aan het afspelen</string>
<string name="unknown_media_key">AntennaPod - Mediaknop onbekend: %1$d</string>
<!--Queue operations-->
@@ -234,6 +245,8 @@
<string name="duration">Lengte</string>
<string name="episode_title">Afleveringtitel</string>
<string name="feed_title">Feed-titel</string>
+ <string name="random">Willekeurig</string>
+ <string name="smart_shuffle">Slim Shuffelen</string>
<string name="ascending">Oplopend</string>
<string name="descending">Aflopend</string>
<string name="clear_queue_confirmation_msg">Bevestig aub dat u alle afleveringen uit de wachtrij wilt verwijderen</string>
@@ -280,16 +293,25 @@
<string name="other_pref">Overig</string>
<string name="about_pref">Over AntennaPod</string>
<string name="queue_label">Wachtrij</string>
- <string name="services_label">Services</string>
+ <string name="integrations_label">Integraties</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Service voor microbetalingen</string>
+ <string name="automation">Automatische acties</string>
+ <string name="download_pref_details">Details</string>
+ <string name="import_export_pref">Importeren/exporteren</string>
+ <string name="appearance">Uiterlijk</string>
+ <string name="external_elements">Externe elementen</string>
+ <string name="interruptions">Onderbrekingen</string>
+ <string name="buttons">Knoppen</string>
+ <string name="media_player">Mediaspeler</string>
<string name="pref_episode_cleanup_title">Automatisch opschonen</string>
<string name="pref_episode_cleanup_summary">Afleveringen die niet in de wachtrij staan én niet als favoriet gemarkeerd zijn, mogen verwijderd worden als Automatisch Downloaden ruimte nodig heeft voor nieuwe afleveringen</string>
<string name="pref_pauseOnDisconnect_sum">Afspelen pauzeren wanneer de koptelefoon wordt losgekoppeld of de bluetooth verbinding wordt verbroken</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Afspelen hervatten wanneer de koptelefoon opnieuw wordt aangesloten</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Afspelen hervatten wanneer de bluetooth verbinding hervat wordt</string>
- <string name="pref_hardwareForwardButtonSkips_title">\'Volgende\' knop voor overslaan</string>
+ <string name="pref_hardwareForwardButtonSkips_title">\'Volgende\' voor overslaan</string>
<string name="pref_hardwareForwardButtonSkips_sum">Aflevering overslaan ipv vooruitspoelen wanneer op een fysieke \'volgende\' knop wordt gedrukt</string>
- <string name="pref_hardwarePreviousButtonRestarts_title">Vorige voor opnieuw afspelen</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">\'Vorige\' voor opnieuw afspelen</string>
<string name="pref_hardwarePreviousButtonRestarts_sum">Aflevering afspelen vanaf het begin ipv terugspoelen wanneer op een fysieke \'vorige\' knop wordt gedrukt</string>
<string name="pref_followQueue_sum">Volgende item in de wachtrij afspelen als de aflevering voltooid is</string>
<string name="pref_auto_delete_sum">Afleveringen verwijderen als ze zijn afgespeeld</string>
@@ -298,6 +320,8 @@
<string name="pref_smart_mark_as_played_title">Slimme afgespeeld markering</string>
<string name="pref_skip_keeps_episodes_sum">Afleveringen bewaren en in de wachtrij houden als u op \'overslaan\' klikt</string>
<string name="pref_skip_keeps_episodes_title">Overgeslagen afleveringen bewaren</string>
+ <string name="pref_favorite_keeps_episodes_sum">Afleveringen bewaren als ze als favoriet gemarkeerd zijn</string>
+ <string name="pref_favorite_keeps_episodes_title">Favoriete afleveringen bewaren</string>
<string name="playback_pref">Afspelen</string>
<string name="network_pref">Netwerk</string>
<string name="pref_autoUpdateIntervallOrTime_title">Feed update interval of tijdstip</string>
@@ -330,7 +354,7 @@
<string name="pref_set_theme_title">Kies kleurschema</string>
<string name="pref_nav_drawer_title">Menu aanpassen</string>
<string name="pref_nav_drawer_sum">Het uiterlijk en andere instellingen van het menu aanpassen.</string>
- <string name="pref_nav_drawer_items_title">Menu-items instellen</string>
+ <string name="pref_nav_drawer_items_title">Selecteer menu-items</string>
<string name="pref_nav_drawer_items_sum">Aanpassen welke items in het menu worden getoond</string>
<string name="pref_nav_drawer_feed_order_title">Feed volgorde instellen</string>
<string name="pref_nav_drawer_feed_order_sum">De volgorde van uw feeds instellen</string>
@@ -341,12 +365,15 @@
<string name="pref_automatic_download_sum">Configureer het automatisch downloaden van afleveringen.</string>
<string name="pref_autodl_wifi_filter_title">Wi-Fi filter inschakelen</string>
<string name="pref_autodl_wifi_filter_sum">Automatisch downloaden alleen toestaan voor geselecteerde Wi-Fi-netwerken.</string>
+ <string name="pref_autodl_allow_on_mobile_title">Downloaden via mobiele verbinding</string>
+ <string name="pref_autodl_allow_on_mobile_sum">Het automatisch downloaden van afleveringen via een mobiele internetverbinding toestaan.</string>
<string name="pref_automatic_download_on_battery_title">Downloaden zonder opladen</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>
<string name="pref_episode_cache_title">Afleveringen cache</string>
<string name="pref_theme_title_light">Licht</string>
<string name="pref_theme_title_dark">Donker</string>
+ <string name="pref_theme_title_trueblack">Echt zwart</string>
<string name="pref_episode_cache_unlimited">Onbeperkt</string>
<string name="pref_update_interval_hours_plural">uur</string>
<string name="pref_update_interval_hours_singular">uur</string>
@@ -371,7 +398,7 @@
<string name="pref_playback_speed_sum">Pas de beschikbare snelheden aan voor de variabele audio afspeelsnelheid</string>
<string name="pref_fast_forward">Snelheid van vooruitspoelen</string>
<string name="pref_fast_forward_sum">Pas het aantal seconden aan waarmee wordt vooruitgespoeld per klik op de knop</string>
- <string name="pref_rewind">Snelheid terugspoelen</string>
+ <string name="pref_rewind">Snelheid van terugspoelen</string>
<string name="pref_rewind_sum">Pas het aantal seconden aan waarmee wordt teruggespoeld per klik op de knop</string>
<string name="pref_gpodnet_sethostname_title">Definieer hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Gebruik standaard host</string>
@@ -396,9 +423,8 @@
<string name="crash_report_title">Crashreport</string>
<string name="crash_report_sum">Verstuur laatste crashreport via email</string>
<string name="send_email">Verstuur email</string>
- <string name="experimental_pref">Experimentele functie</string>
- <string name="pref_sonic_title">Sonic mediaspeler</string>
- <string name="pref_sonic_message">Gebruik AntennaPod\'s ingebouwde Sonic mediaspeler als een alternatief voor Prestissimo en de mediaspeler van Android.</string>
+ <string name="experimental_pref">Experimentele functie(s)</string>
+ <string name="pref_media_player_message">Selecteer 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>
@@ -408,8 +434,13 @@
<string name="pref_cast_title">Chromecast</string>
<string name="pref_cast_message_play_flavor">Ondersteuning activeren voor draadloos afspelen via Cast apparaten (zoals Chromecast, Audio speakers en Android TV)</string>
<string name="pref_cast_message_free_flavor">Voor Chromecast is software van derden vereist die niet beschikbaar zijn in deze versie van AntennaPod</string>
- <string name="pref_enqueue_downloaded_title">Downloads in de wachtrij</string>
+ <string name="pref_enqueue_downloaded_title">Gedownloade afleveringen in wachtrij</string>
<string name="pref_enqueue_downloaded_summary">Voeg gedownloade afleveringen toe aan de wachtrij</string>
+ <string name="media_player_builtin">Ingebouwde speler van Android</string>
+ <string name="pref_videoBehavior_title">Bij verlaten video</string>
+ <string name="pref_videoBehavior_sum">Wat te doen bij het verlaten van spelende video</string>
+ <string name="stop_playback">Afspelen stoppen</string>
+ <string name="continue_playback">Blijf geluid afspelen</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Automatisch flattr\'en aanzetten</string>
<string name="auto_flattr_after_percent">Flattr een aflevering zodra %d procent is afgespeeld</string>
@@ -442,12 +473,12 @@
<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 export</string>
- <string name="html_export_label">HTML export</string>
+ <string name="opml_export_label">OPML exporteren</string>
+ <string name="html_export_label">HTML exporteren</string>
<string name="exporting_label">Exporteren…</string>
<string name="export_error_label">Export fout</string>
- <string name="opml_export_success_title">OPML bestand succesvol geëxporteerd.</string>
- <string name="opml_export_success_sum"> Het OPML-bestand is in \u0020 geplaatst</string>
+ <string name="export_success_title">Export succesvol</string>
+ <string name="export_success_sum">Het geëxporteerde bestand is hier opgeslagen:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Toegang tot externe locaties is nodig om het OPML-bestand te kunnen lezen</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Slaap timer instellen</string>
@@ -511,7 +542,7 @@
<!--Directory chooser-->
<string name="selected_folder_label">Geselecteerde map:</string>
<string name="create_folder_label">Map aanmaken</string>
- <string name="choose_data_directory">Kies data map</string>
+ <string name="choose_data_directory">Kies datamap</string>
<string name="choose_data_directory_message">Kies de hoofdmap voor uw data. AntennaPod zal de benodigde submappen creeëren.</string>
<string name="choose_data_directory_permission_rationale">Toegang tot de externe opslag is nodig om de data-map aan te passen</string>
<string name="create_folder_msg">Maak een nieuwe map aan met de naam \"%1$s\"?</string>
@@ -614,6 +645,14 @@
<string name="proxy_host_empty_error">Host kan niet leeg zijn</string>
<string name="proxy_host_invalid_error">Host is geen geldig IP-adres of domein</string>
<string name="proxy_port_invalid_error">Poortnummer ongeldig</string>
+ <!--Database import/export-->
+ <string name="import_export">Database im-/exporteren</string>
+ <string name="import_export_warning">Hiermee kun je de podcasts waarop je geabonneerd bent en de afgespeelde afleveringen naar een ander apparaat kopiëren\n\nGeëxporteerde databases kunnen alleen geïmporteerd worden in dezelfde versie van AntennaPod (zie \'Over AntennaPod\' in de instellingen). Het importeren naar een andere versie kan onverwachte problemen opleveren.\n\nNa het importeren kunnen afleveringen als \'gedownload\' aangemerkt zijn, terwijl dit niet het geval is. Druk op de afspeelknop naast de afleveringen om AntennaPod dit te laten detecteren.</string>
+ <string name="label_import">Importeren</string>
+ <string name="label_export">Exporteren</string>
+ <string name="import_select_file">Selecteer bestand om te importeren</string>
+ <string name="export_ok">Export succesvol.</string>
+ <string name="import_ok">Succesvol geïmporteerd\n\nDruk op OK om AntennaPod te herstarten.</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Afspelen op…</string>
<string name="cast_disconnect_label">Cast loskoppelen</string>
@@ -630,4 +669,13 @@
<string name="cast_failed_seek">Het opzoeken van het nieuwe tijdstip op het Cast apparaat is mislukt</string>
<string name="cast_failed_receiver_player_error">Ernstige fout opgetreden bij het afspelende Cast apparaat</string>
<string name="cast_failed_media_error_skipping">Er was een fout bij het afspelen; de aflevering wordt overgeslagen…</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Actie vereist</string>
+ <string name="notification_channel_user_action_description">Tonen als actie vereist is, bijvoorbeeld als je een wachtwoord moet invoeren.</string>
+ <string name="notification_channel_downloading">Aan het downloaden</string>
+ <string name="notification_channel_downloading_description">Tonen als er iets wordt gedownload.</string>
+ <string name="notification_channel_playing">Wordt momenteel afgespeeld</string>
+ <string name="notification_channel_playing_description">Hiermee kun je het afspelen controleren. Dit is de voornaamste notificatie tijdens het afspelen van een podcast.</string>
+ <string name="notification_channel_error">Foutmeldingen</string>
+ <string name="notification_channel_error_description">Tonen wanneer er iets fout is gegaan, bijvoorbeeld als downloaden of synchroniseren mislukt.</string>
</resources>
diff --git a/core/src/main/res/values-no-rNB/strings.xml b/core/src/main/res/values-no-rNB/strings.xml
index 56bff080b..166d93f47 100644
--- a/core/src/main/res/values-no-rNB/strings.xml
+++ b/core/src/main/res/values-no-rNB/strings.xml
@@ -2,23 +2,31 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
<string name="feeds_label">Strømmer</string>
+ <string name="statistics_label">Statistikk</string>
<string name="add_feed_label">Legg til podcast</string>
<string name="episodes_label">Episoder</string>
<string name="all_episodes_short_label">Alle</string>
<string name="favorite_episodes_label">Favoritter</string>
<string name="new_label">Nye</string>
<string name="settings_label">Innstillinger</string>
- <string name="add_new_feed_label">Legg til podcast</string>
<string name="downloads_label">Nedlastninger</string>
<string name="downloads_running_label">Kjører</string>
<string name="downloads_completed_label">Fullført</string>
<string name="downloads_log_label">Logg</string>
+ <string name="subscriptions_label">Abonnement</string>
+ <string name="subscriptions_list_label">Abonnementliste</string>
<string name="cancel_download_label">Avbryt\nLast ned</string>
<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-->
+ <string name="total_time_listened_to_podcasts">Sum av avspilte podcaster:</string>
+ <string name="statistics_details_dialog">Startet %1$d av %2$d episoder.\n\nAvspilt %3$s av %4$s.</string>
+ <string name="statistics_mode">Statistikk modus</string>
+ <string name="statistics_mode_normal">Kalkuler faktisk avspilt varighet. Dobbel avspilling telles to ganger, mens markering som avspilt telles ikke</string>
<!--Main activity-->
<string name="drawer_open">Åpne menyen</string>
<string name="drawer_close">Lukk menyen</string>
@@ -26,9 +34,11 @@
<string name="drawer_feed_order_unplayed_episodes">Sorter på teller</string>
<string name="drawer_feed_order_alphabetical">Sorter alfabetisk</string>
<string name="drawer_feed_order_last_update">Sorter på utgivelsesdato</string>
+ <string name="drawer_feed_order_most_played">Sorter etter antall avspilte episoder</string>
<string name="drawer_feed_counter_new_unplayed">Antall nye og uavspilte episoder</string>
<string name="drawer_feed_counter_new">Antall nye episoder</string>
<string name="drawer_feed_counter_unplayed">Antall uavspilte episoder</string>
+ <string name="drawer_feed_counter_downloaded">Antall nedlastede episoder</string>
<string name="drawer_feed_counter_none">Ingen</string>
<!--Webview actions-->
<string name="open_in_browser_label">Åpne i nettleser</string>
@@ -43,6 +53,7 @@
<string name="cancel_label">Avbryt</string>
<string name="yes">Ja</string>
<string name="no">Nei</string>
+ <string name="reset">Tilbakestill</string>
<string name="author_label">Opphavsperson</string>
<string name="language_label">Språk</string>
<string name="url_label">URL</string>
@@ -53,6 +64,7 @@
<string name="refresh_label">Oppdater</string>
<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>
@@ -60,16 +72,19 @@
<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>
<string name="auto_download_label">Inkluder i automatiske nedlastninger</string>
<string name="auto_download_apply_to_items_title">Angi for tidligere episoder</string>
<string name="auto_download_apply_to_items_message">Den nye <i>Automatisk nedlasting</i>-innstillingen vil automatisk aktiveres for nye episoder.\nØnsker du å aktivere den for tidligere utgitte episoder også?</string>
+ <string name="auto_delete_label">Slett episode automatisk</string>
<string name="parallel_downloads_suffix">\u0020samtidige nedlastinger</string>
<string name="feed_auto_download_always">Alltid</string>
<string name="feed_auto_download_never">Aldri</string>
- <string name="episode_cleanup_never">AldriAldri</string>
+ <string name="send_label">Send...</string>
+ <string name="episode_cleanup_never">Aldri</string>
<string name="episode_cleanup_queue_removal">Når ikke i kø</string>
<string name="episode_cleanup_after_listening">Etter den er ferdig</string>
<plurals name="episode_cleanup_days_after_listening">
@@ -81,6 +96,7 @@
<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>
@@ -88,15 +104,22 @@
<string name="mark_all_read_confirmation_msg">Vennligst bekreft at du ønsker å markere alle episoder som avspilt.</string>
<string name="mark_all_read_feed_confirmation_msg">Vennligst bekreft at du ønsker å markere alle episoder i denne strømmen som avspilt.</string>
<string name="mark_all_seen_label">Marker alle som sett</string>
+ <string name="mark_all_seen_msg">Marker alle episoder som sett</string>
+ <string name="mark_all_seen_confirmation_msg">Vennligst bekreft at du ønsker å markere alle episoder som sett.</string>
<string name="show_info_label">Vis informasjon</string>
+ <string name="rename_feed_label">Endre navn på podcast</string>
<string name="remove_feed_label">Fjern podcast</string>
+ <string name="share_label">Del ...</string>
<string name="share_link_label">Del lenke</string>
+ <string name="share_file_label">Del fil</string>
<string name="share_link_with_position_label">Del lenke med plassering</string>
<string name="share_feed_url_label">Del strømmens URL</string>
+ <string name="share_item_url_label">Del episodens URL</string>
+ <string name="share_item_url_with_position_label">Del episodens URL med posisjon</string>
+ <string name="feed_delete_confirmation_msg">Vennligst bekreft at du ønsker å slette strømmen \"%1$s\" og alle episoder fra denne strømmen som du har lastet ned.</string>
<string name="feed_remover_msg">Fjerner strøm</string>
<string name="load_complete_feed">Oppdater hele strømmen</string>
<string name="hide_episodes_title">Skjul episoder</string>
- <string name="episode_actions">Lagre handlinger</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>
@@ -104,8 +127,10 @@
<string name="hide_not_queued_episodes_label">Ikke i kø</string>
<string name="hide_downloaded_episodes_label">Nedlastet</string>
<string name="hide_not_downloaded_episodes_label">Ikke nedlastet</string>
+ <string name="hide_has_media_label">Har media</string>
<string name="filtered_label">Filtrert</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Siste oppdatering mislyktes</string>
+ <string name="open_podcast">Åpne podcast</string>
<!--actions on feeditems-->
<string name="download_label">Last ned</string>
<string name="play_label">Spill</string>
@@ -113,7 +138,10 @@
<string name="stop_label">Stopp</string>
<string name="stream_label">Stream</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="remove_episode_lable">Fjern episode</string>
+ <string name="marked_as_seen_label">Marker som sett</string>
<string name="mark_read_label">Marker som avspilt</string>
<string name="marked_as_read_label">Marker som avspilt</string>
<string name="mark_unread_label">Marker som ikke avspilt</string>
@@ -121,7 +149,9 @@
<string name="added_to_queue_label">Lagt til i kø</string>
<string name="remove_from_queue_label">Fjern fra queue</string>
<string name="add_to_favorite_label">Legg til i favoritter</string>
+ <string name="added_to_favorites">Legg til i favoritter</string>
<string name="remove_from_favorite_label">Fjern fra favoritter</string>
+ <string name="removed_from_favorites">Fjernet fra favoritter</string>
<string name="visit_website_label">Besøk nettside</string>
<string name="support_label">Flattr\'e dette</string>
<string name="skip_episode_label">Skip episode</string>
@@ -144,6 +174,8 @@
<string name="download_error_connection_error">Tilkoblingsfeil</string>
<string name="download_error_unknown_host">Ukjent vert</string>
<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>
@@ -183,6 +215,8 @@
<!--Queue operations-->
<string name="lock_queue">Lås køen</string>
<string name="unlock_queue">Lås opp køen</string>
+ <string name="queue_locked">Kø låst</string>
+ <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>
@@ -191,6 +225,8 @@
<string name="sort">Sortér</string>
<string name="date">På dato</string>
<string name="duration">På varighet</string>
+ <string name="episode_title">Episodetittel</string>
+ <string name="feed_title">Strømtittel</string>
<string name="ascending">Økende</string>
<string name="descending">Synkende</string>
<string name="clear_queue_confirmation_msg">Vennligst bekreft at du ønsker å slette ALLE elementer i køen</string>
@@ -228,23 +264,30 @@
<!--Empty list labels-->
<string name="no_items_label">Det er ingen objekter på denne listen.</string>
<string name="no_feeds_label">Du abonnerer ikke på noen strømmer enda.</string>
+ <string name="no_chapters_label">Denne episoden har ingen kapitler.</string>
+ <string name="no_shownotes_label">Denne episoden har ingen shownotater</string>
<!--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">Queue</string>
- <string name="services_label">Tjenester</string>
<string name="flattr_label">Flattr</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>
<string name="pref_unpauseOnHeadsetReconnect_sum">Gjenoppta avspilling når hodetelefoner gjeninnkoples</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Fortsett avspilling når bluetooth er tilkoblet igjen</string>
<string name="pref_hardwareForwardButtonSkips_sum">Ved pressing av hardware forover-knapp hopp til neste episode istedenfor forover-spoling</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">Forriv</string>
<string name="pref_followQueue_sum">Hopp til neste element i køen når avspillingen er ferdig</string>
<string name="pref_auto_delete_sum">Slett episode når avspillingen er ferdig</string>
<string name="pref_auto_delete_title">Automatisk sletting</string>
<string name="pref_smart_mark_as_played_sum">Marker episoder som avspilt selv om det er X antall sekunder igjen av avspillingen</string>
+ <string name="pref_smart_mark_as_played_title">Smart markering som avspilt</string>
<string name="pref_skip_keeps_episodes_sum">Behold episoder når de hoppes over</string>
<string name="pref_skip_keeps_episodes_title">Behold episoder som er hoppet over</string>
+ <string name="pref_favorite_keeps_episodes_sum">Behold episoder når de er merket som favoritt</string>
+ <string name="pref_favorite_keeps_episodes_title">Behold favorittepisoder</string>
<string name="playback_pref">Avspilling</string>
<string name="network_pref">Nettverk</string>
<string name="pref_autoUpdateIntervallOrTime_title">Oppdateringsintervall eler tidspunkt</string>
@@ -253,6 +296,8 @@
<string name="pref_autoUpdateIntervallOrTime_Disable">Skru av</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Sett intervall</string>
<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>
@@ -286,6 +331,8 @@
<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>
@@ -302,6 +349,13 @@
<string name="pref_gpodnet_logout_toast">Utloggelse lyktes</string>
<string name="pref_gpodnet_setlogin_information_title">Endre innloggingsinformasjon</string>
<string name="pref_gpodnet_setlogin_information_sum">Endre innlogginsinformasjonen til din gpodder.net konto</string>
+ <string name="pref_gpodnet_sync_changes_title">Synkroniser endringer nå</string>
+ <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>
<string name="pref_playback_speed_sum">Egendefiner hastighetene tilgjengelig for variabel avspillingshastighet</string>
<string name="pref_gpodnet_sethostname_title">Sett vertsnavn</string>
@@ -310,6 +364,7 @@
<string name="pref_expandNotify_sum">Utvider alltid varselet for å inkludere avspillingsknapper.</string>
<string name="pref_persistNotify_title">Vedvarende avspillingskontroller</string>
<string name="pref_persistNotify_sum">Behold varsel- og låseskjermkontroller når avspilling er satt på pause.</string>
+ <string name="pref_compact_notification_buttons_title">Velg låseskjermsknapper</string>
<string name="pref_lockscreen_background_title">Angi som bakgrunn på låseskjermen</string>
<string name="pref_lockscreen_background_sum">Angir låseskjermbakgrunnsbildet til å være den nåværende episodens bilde. Som en sideeffekt vil dette også vise bildet i tredjepartsapper.</string>
<string name="pref_showDownloadReport_title">Vis nedlastingsrapport</string>
@@ -324,16 +379,23 @@
<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>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Aktiver automatisk flattring</string>
<string name="auto_flattr_after_percent">Flattre episode så snart %d prosent er avspilt</string>
<string name="auto_flattr_ater_beginning">Flattre episode når avspillingen starter</string>
<string name="auto_flattr_ater_end">Flattre episode når avspillingen tar slutt</string>
<!--Search-->
+ <string name="search_hint">Søk etter episoder</string>
<string name="found_in_chapters_label">Funnet i kapitler</string>
<string name="search_status_no_results">Ingen resultater ble funnet</string>
<string name="search_label">Søk</string>
<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>
@@ -344,14 +406,18 @@
<string name="opml_import_label">OPML-import</string>
<string name="opml_directory_error">ERROR!</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>
<string name="export_error_label">Eksporteringserror</string>
- <string name="opml_export_success_title">OPML-import vellykket.</string>
- <string name="opml_export_success_sum">.opml-filen ble skrevet til:\u0020</string>
+ <string name="opml_import_ask_read_permission">Tilgang til ekstern lagring er nødvendig for å lese OPML filen</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Sett opp sovetimer</string>
<string name="disable_sleeptimer_label">Deaktiver sovetimer</string>
@@ -377,6 +443,9 @@
<item quantity="one">1 time</item>
<item quantity="other">%d timer</item>
</plurals>
+ <string name="auto_enable_label">Auto-aktiver</string>
+ <string name="sleep_timer_enabled_label">Sovetimer aktivert</string>
+ <string name="sleep_timer_disabled_label">Sovetimer deaktivert</string>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">KATEGORIER</string>
<string name="gpodnet_toplist_header">TOPP-PODCASTER</string>
@@ -396,6 +465,7 @@
<string name="gpodnetauth_device_chooseExistingDevice">Velg eksisterende enhet:</string>
<string name="gpodnetauth_device_errorEmpty">Device ID kan ikke være tom</string>
<string name="gpodnetauth_device_errorAlreadyUsed">EnhetsID er allerede i bruk</string>
+ <string name="gpodnetauth_device_caption_errorEmpty">Tittel kan ikke være tom</string>
<string name="gpodnetauth_device_butChoose">Velg</string>
<string name="gpodnetauth_finish_title">Innlogging lyktes!</string>
<string name="gpodnetauth_finish_descr">Gratulerer! Din gpodder.net konto er nå linket opp med din enhet. AntennaPod vil nå automatisk synkronisere abonnementer på din enhet med din gpodder.net konto.</string>
@@ -405,11 +475,14 @@
<string name="gpodnetsync_auth_error_descr">Feil brukernavn eller passord.</string>
<string name="gpodnetsync_error_title">gpodder.net synkroniseringserror</string>
<string name="gpodnetsync_error_descr">En feil oppsto under synkronisering av:\u0020</string>
+ <string name="gpodnetsync_pref_report_successful">Vellykket</string>
+ <string name="gpodnetsync_pref_report_failed">Mislyktes</string>
<!--Directory chooser-->
<string name="selected_folder_label">Valgt mappe</string>
<string name="create_folder_label">Lag mappe</string>
<string name="choose_data_directory">Velg datamappe</string>
<string name="choose_data_directory_message">Vennligst velg basisen for datamappen din. AntennaPod vil lage de nødvendige submappene dine.</string>
+ <string name="choose_data_directory_permission_rationale">Tilgang til ekstern lagring er nødvendig for å endre data mappe</string>
<string name="create_folder_msg">Lag en ny mappe med navn \"%1$s\"?</string>
<string name="create_folder_success">Lagde en ny mappe</string>
<string name="create_folder_error_no_write_access">Kan ikke skrive til denne mappen</string>
@@ -429,6 +502,7 @@
<!--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>
@@ -442,11 +516,17 @@
<!--Feed information screen-->
<string name="authentication_label">Autentisering</string>
<string name="authentication_descr">Endre brukernavnet og passordet for denne podcasten og dens episoder.</string>
+ <string name="auto_download_settings_label">Innstillinger for automatisk nedlastning</string>
+ <string name="episode_filters_label">Episodefilter</string>
+ <string name="episode_filters_include">Inkluder</string>
+ <string name="episode_filters_exclude">Utelukk</string>
+ <string name="keep_updated">Hold oppdatert</string>
<!--Progress information-->
<string name="progress_upgrading_database">Oppgraderer databasen</string>
<!--AntennaPodSP-->
<string name="sp_apps_importing_feeds_msg">Importerer abbonementer fra enkeltstående applikasjoner ...</string>
<string name="search_itunes_label">Søk på iTunes</string>
+ <string name="filter">Filter</string>
<!--Episodes apply actions-->
<string name="all_label">Alle</string>
<string name="selected_all_label">Valgte alle episoder</string>
@@ -460,6 +540,9 @@
<string name="selected_downloaded_label">Valgte nedlastede episoder</string>
<string name="not_downloaded_label">Ikke nedlastet</string>
<string name="selected_not_downloaded_label">Valgte ikke-nedlastede episoder</string>
+ <string name="queued_label">I kø</string>
+ <string name="not_queued_label">Ikke i kø</string>
+ <string name="selected_not_queued_label">Valgte episoder som ikke er i kø</string>
<!--Sort-->
<string name="sort_title_a_z">Tittel (A \u2192 Z)</string>
<string name="sort_title_z_a">Tittel (Z \u2192 A)</string>
@@ -474,7 +557,34 @@
<string name="rating_later_label">Minn meg på dette senere</string>
<string name="rating_now_label">Naturligvis, kom igjen!</string>
<!--Audio controls-->
+ <string name="playback_speed">Avspillingshastighet</string>
+ <string name="volume">Volum</string>
+ <string name="audio_effects">Lydeffekter</string>
+ <string name="stereo_to_mono">Nedmiksing: Stereo til mono</string>
+ <string name="sonic_only">Kun Sonic</string>
<!--proxy settings-->
+ <string name="proxy_type_label">Type</string>
+ <string name="host_label">Vert</string>
+ <string name="port_label">Port</string>
+ <string name="optional_hint">(Valgfri)</string>
+ <string name="proxy_test_label">Test</string>
+ <string name="proxy_checking">Kontrollerer...</string>
+ <string name="proxy_test_successful">Test vellykket</string>
+ <string name="proxy_test_failed">Test mislykket</string>
+ <string name="proxy_host_empty_error">Vert kan ikke være tom</string>
+ <string name="proxy_host_invalid_error">Vert er ikke en gyldig IP adresse eller domene</string>
+ <string name="proxy_port_invalid_error">Ikke gyldig port</string>
+ <!--Database import/export-->
+ <string name="import_export">Database import/eksport</string>
+ <string name="label_import">Importer</string>
+ <string name="label_export">Eksporter</string>
+ <string name="import_select_file">Velg fil å importere</string>
<!--Casting-->
+ <string name="cast_media_route_menu_title">Spill på...</string>
+ <string name="cast_not_castable">Valgt media er ikke kompatibel med strømmingsenhet</string>
+ <string name="cast_failed_to_play">Feilet å starte avspilling av media</string>
+ <string name="cast_failed_to_stop">Feilter å stoppe avspillingen av media</string>
+ <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-->
</resources>
diff --git a/core/src/main/res/values-no/strings.xml b/core/src/main/res/values-no/strings.xml
index 28dfeb6e8..2d9481b84 100644
--- a/core/src/main/res/values-no/strings.xml
+++ b/core/src/main/res/values-no/strings.xml
@@ -33,6 +33,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-pl-rPL/strings.xml b/core/src/main/res/values-pl-rPL/strings.xml
index 194f387bd..f75590077 100644
--- a/core/src/main/res/values-pl-rPL/strings.xml
+++ b/core/src/main/res/values-pl-rPL/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Ulubione</string>
<string name="new_label">Nowy</string>
<string name="settings_label">Ustawienia</string>
- <string name="add_new_feed_label">Dodaj podcast</string>
<string name="downloads_label">Pobrane</string>
<string name="downloads_running_label">W toku</string>
<string name="downloads_completed_label">Ukończone</string>
@@ -23,6 +22,7 @@
<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>
@@ -117,6 +117,7 @@
<string name="remove_feed_label">Usuń podcast</string>
<string name="share_label">Udostępnij...</string>
<string name="share_link_label">Udostępnij stronę</string>
+ <string name="share_file_label">Udostępnij plik</string>
<string name="share_link_with_position_label">Udostępnij link z aktualną pozycją</string>
<string name="share_feed_url_label">Udostępnij adres kanału</string>
<string name="share_item_url_label">Udostępnij plik URL odcinka</string>
@@ -125,7 +126,6 @@
<string name="feed_remover_msg">Usuwanie kanału</string>
<string name="load_complete_feed">Odśwież cały kanał</string>
<string name="hide_episodes_title">Ukryj odcinki</string>
- <string name="episode_actions">Zatwierdź czynności</string>
<string name="hide_unplayed_episodes_label">Nieodtworzone</string>
<string name="hide_paused_episodes_label">Zatrzymane</string>
<string name="hide_played_episodes_label">Odtworzone</string>
@@ -145,6 +145,7 @@
<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="remove_episode_lable">Usuń odcinek</string>
<string name="marked_as_seen_label">Zaznaczono jako wyświetlone</string>
<string name="mark_read_label">Oznacz jako odtworzone</string>
@@ -169,6 +170,9 @@
<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>
+ <string name="download_error_details_message">%1$s \n\nAdres pliku:\n%2$s
+</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>
@@ -284,7 +288,6 @@
<string name="other_pref">Inne</string>
<string name="about_pref">O...</string>
<string name="queue_label">Kolejka</string>
- <string name="services_label">Usługi</string>
<string name="flattr_label">Flattr</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>
@@ -302,6 +305,8 @@
<string name="pref_smart_mark_as_played_title">Inteligentnie oznacz jako odtworzone</string>
<string name="pref_skip_keeps_episodes_sum">Zachowuje pominięte odcinki w kolejce</string>
<string name="pref_skip_keeps_episodes_title">Zachowaj pominięte odcinki</string>
+ <string name="pref_favorite_keeps_episodes_sum">Zachowaj odcinki gdy są oznaczone jako Ulubione</string>
+ <string name="pref_favorite_keeps_episodes_title">Zachowaj Ulubione Odcinki</string>
<string name="playback_pref">Odtwarzanie</string>
<string name="network_pref">Sieć</string>
<string name="pref_autoUpdateIntervallOrTime_title">Częstotliwość aktualizacji lub Czas dnia</string>
@@ -345,6 +350,8 @@
<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="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>
@@ -401,8 +408,6 @@
<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="experimental_pref">Eksperymentalne</string>
- <string name="pref_sonic_title">Odtwarzacz mediów Sonic</string>
- <string name="pref_sonic_message">Użyj wbudowanego odtwarzacza Sonic jako oprogramowanie zastępcze do natywnego odtwarzacza Android i Prestissimo</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>
@@ -412,7 +417,7 @@
<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 zostały pobrane</string>
+ <string name="pref_enqueue_downloaded_title">Rzeczy z kolejki pobrane</string>
<string name="pref_enqueue_downloaded_summary">Dodaj pobrane odcinki do kolejki</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Włącz automatyczne wspieranie na flattr.</string>
@@ -450,8 +455,8 @@
<string name="html_export_label">Eksport HTML</string>
<string name="exporting_label">Eksportowanie...</string>
<string name="export_error_label">Błąd eksportu</string>
- <string name="opml_export_success_title">Eksport OPML udany.</string>
- <string name="opml_export_success_sum">Plik .opml został zapisany do:\u0020</string>
+ <string name="export_success_title">Export zakończony powodzeniem</string>
+ <string name="export_success_sum">Wyeksportowany plik został zapisany w:\n\n %1$s</string>
<string name="opml_import_ask_read_permission">Dostęp do zewnętrznej pamięci jest potrzebny do odczytywania plików OPML</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Ustaw czas do wyłączenia</string>
@@ -625,6 +630,14 @@ https://gpodder.net/register/</string>
<string name="proxy_host_empty_error">Host nie może być pusty!</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>
+ <!--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>
+ <string name="label_import">Import</string>
+ <string name="label_export">Export</string>
+ <string name="import_select_file">Wybierz plik do Importowania</string>
+ <string name="export_ok">Export zakończony powodzeniem</string>
+ <string name="import_ok">Import zakończony powodzeniem.\n\nNaciśnij OK aby zrestartować AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Odtwarzaj na...</string>
<string name="cast_disconnect_label">Rozłącz sesję</string>
@@ -641,4 +654,5 @@ https://gpodder.net/register/</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-->
</resources>
diff --git a/core/src/main/res/values-pl/strings.xml b/core/src/main/res/values-pl/strings.xml
index 975aa5263..6a640a0ee 100644
--- a/core/src/main/res/values-pl/strings.xml
+++ b/core/src/main/res/values-pl/strings.xml
@@ -67,7 +67,6 @@
<!--Empty list labels-->
<!--Preferences-->
<string name="queue_label">Kolejka</string>
- <string name="services_label">Usługi</string>
<string name="pref_downloadMediaOnWifiOnly_sum">Pobieraj pliki tylko przez WiFi</string>
<string name="refreshing_label">Odświeżanie</string>
<string name="user_interface_label">Interfejs Użytkownika</string>
@@ -94,6 +93,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-pt-rBR/strings.xml b/core/src/main/res/values-pt-rBR/strings.xml
index 7606f99f3..30b69432a 100644
--- a/core/src/main/res/values-pt-rBR/strings.xml
+++ b/core/src/main/res/values-pt-rBR/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Favoritos</string>
<string name="new_label">Novo</string>
<string name="settings_label">Configurações</string>
- <string name="add_new_feed_label">Adicionar podcast</string>
<string name="downloads_label">Downloads</string>
<string name="downloads_running_label">Rodando</string>
<string name="downloads_completed_label">Finalizado</string>
@@ -123,7 +122,6 @@
<string name="feed_remover_msg">Removendo feed</string>
<string name="load_complete_feed">Atualizar feed completamente</string>
<string name="hide_episodes_title">Ocultar Episódios</string>
- <string name="episode_actions">Aplicar ações</string>
<string name="hide_unplayed_episodes_label">Não reproduzido</string>
<string name="hide_paused_episodes_label">Pausado</string>
<string name="hide_played_episodes_label">Reproduzido</string>
@@ -280,7 +278,6 @@
<string name="other_pref">Outros</string>
<string name="about_pref">Sobre</string>
<string name="queue_label">Fila</string>
- <string name="services_label">Serviços</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Limpar Episódio</string>
<string name="pref_episode_cleanup_summary">Episódios que não estão na fila e não estão nos favoritos podem ser removidos se o Download Automático precisar de espaço para novos episódios</string>
@@ -397,8 +394,6 @@
<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_sonic_title">Sonic Media Player</string>
- <string name="pref_sonic_message">Utilizar o reprodutor de mídia Sonic no lugar do reprodutor de mídia nativo do Android e do Prestissimo</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>
@@ -408,7 +403,6 @@
<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>
<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_enqueue_downloaded_title">Adicionar baixado à fila</string>
<string name="pref_enqueue_downloaded_summary">Adicionar episódios baixados à fila</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Abilitar automaticamente o flattr</string>
@@ -446,8 +440,6 @@
<string name="html_export_label">Exportar HTML</string>
<string name="exporting_label">Exportando...</string>
<string name="export_error_label">Erro na exportação</string>
- <string name="opml_export_success_title">Exportação do OPML realizada com sucesso.</string>
- <string name="opml_export_success_sum">O arquivo .opml foi gravado em:\u0020</string>
<string name="opml_import_ask_read_permission">Acesso ao armazenamento externo é necessária para ler o arquivo OPML</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Configura desligamento automático</string>
@@ -614,6 +606,7 @@
<string name="proxy_host_empty_error">Host não pode ser vazio</string>
<string name="proxy_host_invalid_error">Host não possui um endereço de IP ou domínio válidos</string>
<string name="proxy_port_invalid_error">Porta inválida</string>
+ <!--Database import/export-->
<!--Casting-->
<string name="cast_media_route_menu_title">Reproduzir em...</string>
<string name="cast_disconnect_label">Desconectar a sessão do cast</string>
@@ -630,4 +623,5 @@
<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>
<string name="cast_failed_media_error_skipping">Erro ao reproduzir mídia. Pulando...</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-pt/strings.xml b/core/src/main/res/values-pt/strings.xml
index 154b451b6..17f04c860 100644
--- a/core/src/main/res/values-pt/strings.xml
+++ b/core/src/main/res/values-pt/strings.xml
@@ -1,15 +1,16 @@
<?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">Atualizar subscrições</string>
<string name="feeds_label">Fontes</string>
<string name="statistics_label">Estatísticas</string>
<string name="add_feed_label">Adicionar podcast</string>
<string name="episodes_label">Episódios</string>
<string name="all_episodes_short_label">Todos</string>
+ <string name="new_episodes_label">Novos</string>
<string name="favorite_episodes_label">Favoritos</string>
<string name="new_label">Novos</string>
<string name="settings_label">Definições</string>
- <string name="add_new_feed_label">Adicionar podcast</string>
<string name="downloads_label">Descargas</string>
<string name="downloads_running_label">Em curso</string>
<string name="downloads_completed_label">Terminada</string>
@@ -19,10 +20,12 @@
<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_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>
@@ -111,10 +114,14 @@
<string name="mark_all_seen_msg">Marcar todos os episódios como vistos</string>
<string name="mark_all_seen_confirmation_msg">Confirma de que deseja marcar todos os episódios como vistos?</string>
<string name="show_info_label">Mostrar informações</string>
+ <string name="show_feed_settings_label">Mostrar definições da fonte</string>
+ <string name="feed_info_label">Informação da fonte</string>
+ <string name="feed_settings_label">Definições da fonte</string>
<string name="rename_feed_label">Renomear podcast</string>
<string name="remove_feed_label">Remover podcast</string>
<string name="share_label">Partilhar...</string>
<string name="share_link_label">Partilhar ligação</string>
+ <string name="share_file_label">Partilhar ficheiro</string>
<string name="share_link_with_position_label">Partilhar ligação com posição</string>
<string name="share_feed_url_label">Partilhar URL da fonte</string>
<string name="share_item_url_label">Partilhar URL do episódio</string>
@@ -123,7 +130,7 @@
<string name="feed_remover_msg">Remover fonte</string>
<string name="load_complete_feed">Atualizar todas as páginas da fonte</string>
<string name="hide_episodes_title">Ocultar episódios</string>
- <string name="episode_actions">Aplicar ações</string>
+ <string name="batch_edit">Edição em lote</string>
<string name="hide_unplayed_episodes_label">Não reproduzidos</string>
<string name="hide_paused_episodes_label">Em pausa</string>
<string name="hide_played_episodes_label">Reproduzidos</string>
@@ -143,6 +150,7 @@
<string name="stream_label">Emitir</string>
<string name="remove_label">Remover</string>
<string name="delete_label">Apagar</string>
+ <string name="delete_failed">Episódio não apagado. Experimente reiniciar o dispositivo.</string>
<string name="remove_episode_lable">Remover episódio</string>
<string name="marked_as_seen_label">Marcar como visto</string>
<string name="mark_read_label">Marcar como reproduzido</string>
@@ -167,6 +175,8 @@
<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>
@@ -217,6 +227,7 @@
<string name="playback_error_unknown">Erro desconhecido</string>
<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-->
@@ -234,6 +245,8 @@
<string name="duration">Duração</string>
<string name="episode_title">Título do episódio</string>
<string name="feed_title">Título da fonte</string>
+ <string name="random">Aleatório</string>
+ <string name="smart_shuffle">Mistura inteligente</string>
<string name="ascending">Crescente</string>
<string name="descending">Decrescente</string>
<string name="clear_queue_confirmation_msg">Tem a certeza de que deseja remover todos os episódios da fila de reprodução?</string>
@@ -266,7 +279,7 @@
<!--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 transferir 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="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-->
@@ -280,8 +293,17 @@
<string name="other_pref">Outras</string>
<string name="about_pref">Sobre</string>
<string name="queue_label">Fila</string>
- <string name="services_label">Serviços</string>
+ <string name="integrations_label">Integrações</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Serviço de micro pagamentos</string>
+ <string name="automation">Automatização</string>
+ <string name="download_pref_details">Detalhes</string>
+ <string name="import_export_pref">Importar/Exportar</string>
+ <string name="appearance">Aparência</string>
+ <string name="external_elements">Elementos externos</string>
+ <string name="interruptions">Interrupções</string>
+ <string name="buttons">Botões</string>
+ <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>
@@ -298,6 +320,8 @@
<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_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_title">Manter episódios favoritos</string>
<string name="playback_pref">Reprodução</string>
<string name="network_pref">Rede</string>
<string name="pref_autoUpdateIntervallOrTime_title">Intervalo de atualização ou hora do dia</string>
@@ -341,12 +365,15 @@
<string name="pref_automatic_download_sum">Configurar a descarga automática dos 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="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_theme_title_light">Claro</string>
<string name="pref_theme_title_dark">Escuro</string>
+ <string name="pref_theme_title_trueblack">Muito escuro</string>
<string name="pref_episode_cache_unlimited">Sem limite</string>
<string name="pref_update_interval_hours_plural">horas</string>
<string name="pref_update_interval_hours_singular">hora</string>
@@ -397,8 +424,7 @@
<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="experimental_pref">Experimental</string>
- <string name="pref_sonic_title">Reprodutor multimédia Sonic</string>
- <string name="pref_sonic_message">Utilizar o Sonic Media Player como substituto do reprodutor nativo do Android e do Prestissimo</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>
@@ -408,8 +434,13 @@
<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_enqueue_downloaded_title">Colocar descarregados na fila</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="media_player_builtin">Reprodutor nativo Android</string>
+ <string name="pref_videoBehavior_title">Comportamento 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</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Ativar flattr automático</string>
<string name="auto_flattr_after_percent">Flattr de episódios ao atingir %d porcento de reprodução</string>
@@ -446,8 +477,8 @@
<string name="html_export_label">Exportação HTML</string>
<string name="exporting_label">A exportar...</string>
<string name="export_error_label">Erro de exportação</string>
- <string name="opml_export_success_title">Exportação efetuada</string>
- <string name="opml_export_success_sum">O ficheiro .opml foi guardado em:\u0020</string>
+ <string name="export_success_title">Exportação com sucesso</string>
+ <string name="export_success_sum">O ficheiro exportado foi guardado em:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Requer acesso ao armazenamento externo para ler o ficheiro OPML</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Definir temporizador</string>
@@ -614,6 +645,14 @@
<string name="proxy_host_empty_error">Servidor não pode estar vazio</string>
<string name="proxy_host_invalid_error">O servidor não tem um endereço ou domínio válido</string>
<string name="proxy_port_invalid_error">Porta inválido</string>
+ <!--Database import/export-->
+ <string name="import_export">Importação/exportação da base de dados</string>
+ <string name="import_export_warning">Esta função experimental pode ser usada para transferir as suas subscrições e episódios para outros dispositivos.\n\nAs bases de dados exportadas apenas podem ser importadas se usar a mesma versão do AntennaPod. Caso contrário, esta função poderá produzir efeitos inesperados.\n\nDepois da importação, os episódios poderão ser apresentados como tendo sido descarregados mesmo que não seja verdade. Prima o botão de reprodução dos episódios para que o AntennaPod detete a situação.</string>
+ <string name="label_import">Importar</string>
+ <string name="label_export">Exportar</string>
+ <string name="import_select_file">Selecione o ficheiro a importar</string>
+ <string name="export_ok">Exportação com sucesso.</string>
+ <string name="import_ok">Importação bem sucedida.\n\nPor favor prima OK para reiniciar o AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Reproduzir em...</string>
<string name="cast_disconnect_label">Desligar da última sessão</string>
@@ -630,4 +669,13 @@
<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>
<string name="cast_failed_media_error_skipping">Erro de reprodução. A ignorar...</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Requer ação</string>
+ <string name="notification_channel_user_action_description">Mostrar se for necessária uma ação como, por exemplo, digitar uma palavra-passe.</string>
+ <string name="notification_channel_downloading">A descarregar</string>
+ <string name="notification_channel_downloading_description">Mostrar durante a descarga.</string>
+ <string name="notification_channel_playing">Reprodução atual</string>
+ <string name="notification_channel_playing_description">Permite o controlo da reprodução. Esta será a notificação que verá ao reproduzir um podcast.</string>
+ <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>
</resources>
diff --git a/core/src/main/res/values-ro-rRO/strings.xml b/core/src/main/res/values-ro-rRO/strings.xml
index ba3f0ed4c..92b2da8f9 100644
--- a/core/src/main/res/values-ro-rRO/strings.xml
+++ b/core/src/main/res/values-ro-rRO/strings.xml
@@ -2,25 +2,38 @@
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
<string name="feeds_label">Feeduri</string>
+ <string name="statistics_label">Statistici</string>
+ <string name="add_feed_label">Adaugă podcast</string>
+ <string name="episodes_label">Episoade</string>
+ <string name="all_episodes_short_label">Toate</string>
+ <string name="favorite_episodes_label">Favorite</string>
<string name="new_label">Nou</string>
<string name="settings_label">Setări</string>
<string name="downloads_label">Descărcări</string>
+ <string name="downloads_running_label">Active</string>
+ <string name="downloads_completed_label">Complete</string>
+ <string name="downloads_log_label">Jurnal</string>
<string name="cancel_download_label">Anulează descărcare</string>
<string name="playback_history_label">Istorie ascultare</string>
<string name="gpodnet_main_label">gpodder.net</string>
<string name="gpodnet_auth_label">autentificare gpodder.net</string>
<!--Statistics fragment-->
<!--Main activity-->
+ <string name="drawer_open">Deschide meniul</string>
+ <string name="drawer_close">Închide meniul</string>
<!--Webview actions-->
<string name="open_in_browser_label">Deschide în browser</string>
<string name="copy_url_label">Copiază URL</string>
<string name="share_url_label">Împarte URL</string>
<string name="copied_url_msg">URL copiat în clipboard</string>
+ <string name="go_to_position_label">Mergi la poziția</string>
<!--Playback history-->
<string name="clear_history_label">Golește istoric</string>
<!--Other-->
<string name="confirm_label">Confirmă</string>
<string name="cancel_label">Anulează</string>
+ <string name="yes">Da</string>
+ <string name="no">Nu</string>
<string name="author_label">Autor</string>
<string name="language_label">Limbă</string>
<string name="podcast_settings_label">Setări</string>
@@ -29,6 +42,7 @@
<string name="refresh_label">Reîncarcă</string>
<string name="external_storage_error_msg">Nu exista stocare externă. Asigurați-vă că stocarea externă este conectată pentru ca aplicația să funcționeze corespunzător.</string>
<string name="chapters_label">Capitole</string>
+ <string name="chapter_duration">Durata: %1$s</string>
<string name="shownotes_label">Notițe</string>
<string name="description_label">Descriere</string>
<string name="most_recent_prefix">Cel mai recent episod:\u0020</string>
@@ -39,6 +53,13 @@
<string name="save_username_password_label">Salvează numele de utilizator și parola</string>
<string name="close_label">închide</string>
<string name="retry_label">Reîncearcă</string>
+ <string name="auto_delete_label">Șterge epidosul automat</string>
+ <string name="feed_auto_download_always">Întotdeauna</string>
+ <string name="feed_auto_download_never">Niciodată</string>
+ <string name="send_label">Trimite...</string>
+ <string name="episode_cleanup_never">Niciodată</string>
+ <string name="episode_cleanup_queue_removal">Când nu e în coadă</string>
+ <string name="episode_cleanup_after_listening">După terminare</string>
<!--'Add Feed' Activity labels-->
<string name="feedurl_label">Adresă feed</string>
<!--Actions on feeds-->
@@ -123,7 +144,6 @@
<string name="other_pref">Altele</string>
<string name="about_pref">Despre</string>
<string name="queue_label">Coadă</string>
- <string name="services_label">Servicii</string>
<string name="flattr_label">Flattr</string>
<string name="pref_followQueue_sum">Sari la următorul element din coadă cand se termină ascultarea</string>
<string name="playback_pref">Ascultare</string>
@@ -174,7 +194,6 @@
<string name="deselect_all_label">Deselectează toate</string>
<string name="opml_export_label">Exportă OPML</string>
<string name="export_error_label">Eroare exportare</string>
- <string name="opml_export_success_sum">Fișierul .opml a fost scris în:\u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Setează cronometru somn</string>
<string name="disable_sleeptimer_label">Oprește cronometru somn</string>
@@ -224,6 +243,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-ru/strings.xml b/core/src/main/res/values-ru/strings.xml
index 57a801304..63823cc51 100644
--- a/core/src/main/res/values-ru/strings.xml
+++ b/core/src/main/res/values-ru/strings.xml
@@ -1,15 +1,16 @@
<?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">Обновить подписки</string>
<string name="feeds_label">Каналы</string>
<string name="statistics_label">Статистика</string>
<string name="add_feed_label">Добавить подкаст</string>
<string name="episodes_label">Выпуски</string>
<string name="all_episodes_short_label">Все</string>
+ <string name="new_episodes_label">Новые</string>
<string name="favorite_episodes_label">Избранное</string>
<string name="new_label">Новые</string>
<string name="settings_label">Настройки</string>
- <string name="add_new_feed_label">Добавить подкаст</string>
<string name="downloads_label">Загрузки</string>
<string name="downloads_running_label">Выполняется</string>
<string name="downloads_completed_label">Завершено</string>
@@ -19,15 +20,17 @@
<string name="cancel_download_label">Отменить загрузку</string>
<string name="playback_history_label">Журнал</string>
<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>
<string name="statistics_mode">Режим подсчёта статистики</string>
- <string name="statistics_mode_normal">Рассчитывать длительность воспроизведённого на самом деле. Повторное воспроизведение засчитывается, а отметка о прослушивании — нет</string>
+ <string name="statistics_mode_normal">Рассчитывать длительность действительного воспроизведения. Повторное воспроизведение засчитывается, а отметка о прослушивании — нет</string>
<string name="statistics_mode_count_all">Прибавлять также все подкасты, отмеченные как прослушанные</string>
<string name="statistics_speed_not_counted">Замечание: Скорость воспроизведения не учитывается никогда.</string>
<!--Main activity-->
@@ -113,10 +116,14 @@
<string name="mark_all_seen_msg">Все выпуски отмечены как просмотренные</string>
<string name="mark_all_seen_confirmation_msg">Пожалуйста, подтвердите намерение отметить все выпуски как просмотренные.</string>
<string name="show_info_label">Показать информацию</string>
+ <string name="show_feed_settings_label">Показать настройки канала</string>
+ <string name="feed_info_label">О канале</string>
+ <string name="feed_settings_label">Настройки канала</string>
<string name="rename_feed_label">Переименовать подкаст</string>
<string name="remove_feed_label">Удалить подкаст</string>
<string name="share_label">Поделиться…</string>
<string name="share_link_label">Поделиться ссылкой</string>
+ <string name="share_file_label">Поделиться файлом</string>
<string name="share_link_with_position_label">Поделиться ссылкой с отметкой времени</string>
<string name="share_feed_url_label">Поделиться ссылкой на канал</string>
<string name="share_item_url_label">Поделиться ссылкой на файл выпуска</string>
@@ -125,7 +132,7 @@
<string name="feed_remover_msg">Удаление канала</string>
<string name="load_complete_feed">Обновить весь канал</string>
<string name="hide_episodes_title">Скрыть выпуски</string>
- <string name="episode_actions">Применить действия</string>
+ <string name="batch_edit">Групповая обработка</string>
<string name="hide_unplayed_episodes_label">Непрослушанное</string>
<string name="hide_paused_episodes_label">Приостановленное</string>
<string name="hide_played_episodes_label">Прослушанное</string>
@@ -145,6 +152,7 @@
<string name="stream_label">Воспроизвести из сети</string>
<string name="remove_label">Удалить</string>
<string name="delete_label">Удалить</string>
+ <string name="delete_failed">Невозможно удалить файл. Попробуйте перезагрузить устройство.</string>
<string name="remove_episode_lable">Удалить выпуск</string>
<string name="marked_as_seen_label">Отмечено как просмотренное</string>
<string name="mark_read_label">Отметить как прослушанное</string>
@@ -169,6 +177,11 @@
<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
+
+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>
@@ -221,6 +234,7 @@
<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-->
@@ -238,6 +252,8 @@
<string name="duration">По продолжительности</string>
<string name="episode_title">Название выпуска</string>
<string name="feed_title">Название канала</string>
+ <string name="random">Случайно</string>
+ <string name="smart_shuffle">Умное перемешивание</string>
<string name="ascending">По возрастанию</string>
<string name="descending">По убыванию</string>
<string name="clear_queue_confirmation_msg">Подтвердите, что хотите очистить очередь от ВСЕХ эпизодов.</string>
@@ -284,8 +300,17 @@
<string name="other_pref">Прочее</string>
<string name="about_pref">О программе</string>
<string name="queue_label">Очередь</string>
- <string name="services_label">Сервисы</string>
+ <string name="integrations_label">Интеграция</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Услуга микроплатежей</string>
+ <string name="automation">Автоматизация</string>
+ <string name="download_pref_details">Подробнее</string>
+ <string name="import_export_pref">Импорт/экспорт</string>
+ <string name="appearance">Внешний вид</string>
+ <string name="external_elements">Внешние органы управления</string>
+ <string name="interruptions">Прерывания</string>
+ <string name="buttons">Кнопки</string>
+ <string name="media_player">Проигрыватель</string>
<string name="pref_episode_cleanup_title">Удаление выпусков</string>
<string name="pref_episode_cleanup_summary">Выпуски, которые не стоят в очереди и не отмечены как избранные могут быть удалены для освобождения места под Автозагрузку.</string>
<string name="pref_pauseOnDisconnect_sum">Приостановить воспроизведение, когда наушники или bluetooth отключены</string>
@@ -302,11 +327,13 @@
<string name="pref_smart_mark_as_played_title">Предварительная отметка \"прослушано\"</string>
<string name="pref_skip_keeps_episodes_sum">Сохранять выпуски, помеченные как пропущенные</string>
<string name="pref_skip_keeps_episodes_title">Сохранять пропущенные выпуски</string>
+ <string name="pref_favorite_keeps_episodes_sum">Хранить выпуски, помеченные как избранное</string>
+ <string name="pref_favorite_keeps_episodes_title">Хранить избранные выпуски</string>
<string name="playback_pref">Воспроизведение</string>
<string name="network_pref">Сеть</string>
<string name="pref_autoUpdateIntervallOrTime_title">Время для обновлений</string>
<string name="pref_autoUpdateIntervallOrTime_sum">Выбрать часы или время суток для автоматического обновления каналов</string>
- <string name="pref_autoUpdateIntervallOrTime_message">Вы можете задать <i>интервал</i>, например \"каждые 2 часа\", выбрать <i>время</i>, например \"7:00\" или <i>отключить</i> автоматическое обновление. \n\n<small>Обратите внимание: время для обновлений приблизительное. Вы можете заметить небольшие задержки.</small></string>
+ <string name="pref_autoUpdateIntervallOrTime_message">Вы можете задать <i>интервал</i>, например, \"каждые 2 часа\", выбрать <i>время</i>, например, \"в 7:00\" или <i>отключить</i> автоматическое обновление. \n\n<small>Обратите внимание: время для обновлений приблизительное. Могут быть небольшие задержки.</small></string>
<string name="pref_autoUpdateIntervallOrTime_Disable">Отключить</string>
<string name="pref_autoUpdateIntervallOrTime_Interval">Задать интервал</string>
<string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Задать время</string>
@@ -345,12 +372,15 @@
<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>
<string name="pref_episode_cache_title">Кэш выпусков</string>
<string name="pref_theme_title_light">Светлая</string>
<string name="pref_theme_title_dark">Тёмная</string>
+ <string name="pref_theme_title_trueblack">Совсем чёрная</string>
<string name="pref_episode_cache_unlimited">Неограничен</string>
<string name="pref_update_interval_hours_plural">ч.</string>
<string name="pref_update_interval_hours_singular">ч.</string>
@@ -401,8 +431,7 @@
<string name="crash_report_sum">Отослать последний отчёт о сбое по e-mail</string>
<string name="send_email">Отправить Email</string>
<string name="experimental_pref">Экспериментальные настройки</string>
- <string name="pref_sonic_title">Проигрывать через Sonic</string>
- <string name="pref_sonic_message">Задействовать встроенный медиа проигрыватель Sonic вместо стандартного из ОС Android и Prestissimo</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>
@@ -414,6 +443,11 @@
<string name="pref_cast_message_free_flavor">Для работы Chromecast требуются собственнические библиотеки третьей стороны, которые не включены в данную версию AntennaPod</string>
<string name="pref_enqueue_downloaded_title">Добавлять загруженные в очередь</string>
<string name="pref_enqueue_downloaded_summary">Добавлять загруженные выпуски в очередь</string>
+ <string name="media_player_builtin">Встроенный в Android проигрыватель</string>
+ <string name="pref_videoBehavior_title">Поведение видео</string>
+ <string name="pref_videoBehavior_sum">При сворачивании проигрывателя видео</string>
+ <string name="stop_playback">остановить воспроизведение</string>
+ <string name="continue_playback">продолжить воспроизведение</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Включить автоматическую поддержку через Flattr</string>
<string name="auto_flattr_after_percent">Поддерживать через Flattr эпизоды, прослушанные на %d процентов</string>
@@ -450,8 +484,10 @@
<string name="html_export_label">Экспорт в HTML</string>
<string name="exporting_label">Экспортируется...</string>
<string name="export_error_label">Ошибка экспорта</string>
- <string name="opml_export_success_title">OPML успешно экспортирован.</string>
- <string name="opml_export_success_sum">Файл OPML был записан в:\u0020</string>
+ <string name="export_success_title">Экспорт завершён успешно</string>
+ <string name="export_success_sum">Экспорт осуществлён в файл:
+
+%1$s</string>
<string name="opml_import_ask_read_permission">Для чтения файла OPML необходим доступ к внешнему хранилищу</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Установить таймер сна</string>
@@ -624,6 +660,14 @@
<string name="proxy_host_empty_error">Обязательно укажите хост</string>
<string name="proxy_host_invalid_error">Неверный адрес или домен хоста</string>
<string name="proxy_port_invalid_error">Неверный порт</string>
+ <!--Database import/export-->
+ <string name="import_export">Импорт/экспорт базы данных</string>
+ <string name="import_export_warning">Данная опытная функция поможет перенести Ваши подписки и прослушанные выпуски на другое устройство.\n\nЭкспортированные базы данных можно импортировать только в ту же версию AntennaPod. В противном случае может произойти непредвиденное.\n\nПосле импорта выпуски могут быть помечены как загруженные, хотя это и не так. Простого нажатия на кнопку воспроизведения достаточно, чтобы AntennaPod распознала такой случай.</string>
+ <string name="label_import">Импорт</string>
+ <string name="label_export">Экспорт</string>
+ <string name="import_select_file">Выберите импортируемый файл</string>
+ <string name="export_ok">Успешно экспортировано.</string>
+ <string name="import_ok">Успешно импортировано.\n\nПожалуйста, нажмите OK, чтобы перезапустить AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Воспроизвести на…</string>
<string name="cast_disconnect_label">Закрыть сеанс передачи Google cast</string>
@@ -640,4 +684,13 @@
<string name="cast_failed_seek">Не удалось выполнить перемотку на устройстве Google cast</string>
<string name="cast_failed_receiver_player_error">Серьёзная ошибка воспроизведения в устройстве Google cast</string>
<string name="cast_failed_media_error_skipping">Ошибка воспроизведения. Пропускаю…</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Требуется действие</string>
+ <string name="notification_channel_user_action_description">Показывается, когда требуется действие, например, для ввода пароля.</string>
+ <string name="notification_channel_downloading">Идёт загрузка</string>
+ <string name="notification_channel_downloading_description">Показывается во время загрузки.</string>
+ <string name="notification_channel_playing">Идёт воспроизведение</string>
+ <string name="notification_channel_playing_description">Позволяет управлять воспроизведением. Основное уведомление, показывается при воспроизведении подкаста.</string>
+ <string name="notification_channel_error">Ошибки</string>
+ <string name="notification_channel_error_description">Показывается если что-то пошло не так, к примеру, неудавшаяся загрузка или синхронизация с gpodder.</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 83f618d48..8e147d033 100644
--- a/core/src/main/res/values-sv-rSE/strings.xml
+++ b/core/src/main/res/values-sv-rSE/strings.xml
@@ -1,15 +1,16 @@
<?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">Uppdatera Prenumerationer</string>
<string name="feeds_label">Flöden</string>
<string name="statistics_label">Statistik</string>
<string name="add_feed_label">Lägg till Podcast</string>
<string name="episodes_label">Episoder</string>
<string name="all_episodes_short_label">Alla</string>
+ <string name="new_episodes_label">Nytt</string>
<string name="favorite_episodes_label">Favoriter</string>
<string name="new_label">Nya</string>
<string name="settings_label">Inställningar</string>
- <string name="add_new_feed_label">Lägg till Podcast</string>
<string name="downloads_label">Nedladdningar</string>
<string name="downloads_running_label">Pågående</string>
<string name="downloads_completed_label">Färdiga</string>
@@ -19,10 +20,12 @@
<string name="cancel_download_label">Avbryt\nNedladdning</string>
<string name="playback_history_label">Uppspelningshistorik</string>
<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>
@@ -111,10 +114,14 @@
<string name="mark_all_seen_msg">Markera alla Episoder som sedda</string>
<string name="mark_all_seen_confirmation_msg">Bekräfta att du vill markera alla episoder som sedda.</string>
<string name="show_info_label">Visa information</string>
+ <string name="show_feed_settings_label">Visa flödesinställningar</string>
+ <string name="feed_info_label">Flödesinfo</string>
+ <string name="feed_settings_label">Flödesinställningar</string>
<string name="rename_feed_label">Byt namn på Podcast</string>
<string name="remove_feed_label">Ta bort Podcast</string>
<string name="share_label">Dela…</string>
<string name="share_link_label">Dela Länk</string>
+ <string name="share_file_label">Dela Fil</string>
<string name="share_link_with_position_label">Dela Länk med Position</string>
<string name="share_feed_url_label">Dela Flödets URL</string>
<string name="share_item_url_label">Dela Episoden Fil-URL</string>
@@ -123,7 +130,7 @@
<string name="feed_remover_msg">Tar bort Flöde</string>
<string name="load_complete_feed">Uppdatera hela Flödet</string>
<string name="hide_episodes_title">Dölj Episoder</string>
- <string name="episode_actions">Applicera åtgärder</string>
+ <string name="batch_edit">Batchredigering</string>
<string name="hide_unplayed_episodes_label">Ospelade</string>
<string name="hide_paused_episodes_label">Pausade</string>
<string name="hide_played_episodes_label">Spelad</string>
@@ -143,6 +150,7 @@
<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="remove_episode_lable">Ta bort Episod</string>
<string name="marked_as_seen_label">Markera som sedd</string>
<string name="mark_read_label">Markera som spelad</string>
@@ -167,6 +175,8 @@
<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>
@@ -217,6 +227,7 @@
<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-->
@@ -234,6 +245,8 @@
<string name="duration">Längd</string>
<string name="episode_title">Episodtitel</string>
<string name="feed_title">Flödestitel</string>
+ <string name="random">Slumpa</string>
+ <string name="smart_shuffle">Smart Blandning</string>
<string name="ascending">Stigande</string>
<string name="descending">Fallande</string>
<string name="clear_queue_confirmation_msg">Bekräfta att du vill rensa kön från ALLA episoder.</string>
@@ -280,8 +293,17 @@
<string name="other_pref">Annat</string>
<string name="about_pref">Om</string>
<string name="queue_label">Kö</string>
- <string name="services_label">Tjänster</string>
+ <string name="integrations_label">Integrationer</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Mikrobetalningstjänst</string>
+ <string name="automation">Automatisering</string>
+ <string name="download_pref_details">Detaljer</string>
+ <string name="import_export_pref">Importera/Exportera</string>
+ <string name="appearance">Utseende</string>
+ <string name="external_elements">Externa element</string>
+ <string name="interruptions">Avbrott</string>
+ <string name="buttons">Knappar</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>
<string name="pref_pauseOnDisconnect_sum">Pausa uppspelningen när hörlurar eller bluetooth kopplas ifrån.</string>
@@ -298,6 +320,8 @@
<string name="pref_smart_mark_as_played_title">Smart markera som spelad</string>
<string name="pref_skip_keeps_episodes_sum">Ta inte bort episoder när de hoppas över</string>
<string name="pref_skip_keeps_episodes_title">Behåll Överhoppade Episoder</string>
+ <string name="pref_favorite_keeps_episodes_sum">Behåll episoder när de är favoritmarkerade</string>
+ <string name="pref_favorite_keeps_episodes_title">Behåll Favoritepisoder</string>
<string name="playback_pref">Uppspelning</string>
<string name="network_pref">Nätverk </string>
<string name="pref_autoUpdateIntervallOrTime_title">Uppdateringsintervall eller Tid på Dagen</string>
@@ -341,12 +365,15 @@
<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>
<string name="pref_episode_cache_title">Episodcache</string>
<string name="pref_theme_title_light">Ljust</string>
<string name="pref_theme_title_dark">Mörkt</string>
+ <string name="pref_theme_title_trueblack">Riktig Svart</string>
<string name="pref_episode_cache_unlimited">Obegränsat</string>
<string name="pref_update_interval_hours_plural">timmar</string>
<string name="pref_update_interval_hours_singular">timme</string>
@@ -397,8 +424,7 @@
<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_sonic_title">Sonic Mediaspelare</string>
- <string name="pref_sonic_message">Använd den inbyggda Sonic mediaspelare som ersättning för Androids egna mediaspelare och Prestissimo</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>
@@ -408,8 +434,13 @@
<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>
<string name="pref_cast_message_free_flavor">Chromecast kräver propretiära tredjepartsbibliotek som inte är inkluderade i denna version av AntennaPod</string>
- <string name="pref_enqueue_downloaded_title">Lägg nedladdade i kön</string>
+ <string name="pref_enqueue_downloaded_title">Köa Nedladdade</string>
<string name="pref_enqueue_downloaded_summary">Lägg nedladdade episoder i uppspelningskön</string>
+ <string name="media_player_builtin">Andriods inbyggda spelare</string>
+ <string name="pref_videoBehavior_title">Videobeteende</string>
+ <string name="pref_videoBehavior_sum">Beteende när videouppspelning avslutas</string>
+ <string name="stop_playback">Stoppa uppspelning</string>
+ <string name="continue_playback">Fortsätt uppspelning</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Aktivera automatisk Flattring</string>
<string name="auto_flattr_after_percent">Flattra episoden så snart %d procent har spelats</string>
@@ -446,8 +477,8 @@
<string name="html_export_label">HTML export</string>
<string name="exporting_label">Exporterar…</string>
<string name="export_error_label">Exporteringsfel</string>
- <string name="opml_export_success_title">OPML Exportering lyckades.</string>
- <string name="opml_export_success_sum">.opml filen skrevs till:\u0020</string>
+ <string name="export_success_title">Exporten lyckades</string>
+ <string name="export_success_sum">Den exporterade filen skrevs till:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Tillgång till extern lagring krävs för att läsa OPML-filen</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Ställ in sömntimer</string>
@@ -614,6 +645,14 @@
<string name="proxy_host_empty_error">Värd måste fyllas i</string>
<string name="proxy_host_invalid_error">Värd är inte en giltig IP adress eller domän</string>
<string name="proxy_port_invalid_error">Porten är inte giltig</string>
+ <!--Database import/export-->
+ <string name="import_export">Databas-Import/Export</string>
+ <string name="import_export_warning">Denna experimentella funktion kan användas för att föra över dina prenumerationer och spelade episoder till en annan enhet.\n\nExporterade databaser kan bara importeras med samma version av AntennaPod. Annars kommer en import att leda till oförutsägbara konsekvenser.\n\nEfter en import kan episoder visas som nedladdade även om de inte är det. Tryck bara på uppspelningsknappen på episoden för att AntennaPod ska kolla det igen.</string>
+ <string name="label_import">Import</string>
+ <string name="label_export">Export</string>
+ <string name="import_select_file">Välj fil att importera</string>
+ <string name="export_ok">Exporten lyckades.</string>
+ <string name="import_ok">Importen lyckades.\n\nTryck OK för att starta om AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Spela på...</string>
<string name="cast_disconnect_label">Koppla loss castningen</string>
@@ -630,4 +669,13 @@
<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>
<string name="cast_failed_media_error_skipping">Fel vid uppspelning av media. Hoppar över...</string>
+ <!--Notification channels-->
+ <string name="notification_channel_user_action">Åtgärd krävs</string>
+ <string name="notification_channel_user_action_description">Visas om din åtgärd är obligatorisk, till exempel om du behöver ange ett lösenord.</string>
+ <string name="notification_channel_downloading">Laddar ner</string>
+ <string name="notification_channel_downloading_description">Visas under tiden som nedladdning pågår.</string>
+ <string name="notification_channel_playing">Uppspelning pågår</string>
+ <string name="notification_channel_playing_description">Medger kontroll över uppspelning. Detta är huvudnotifieringen som du ser när en podcast spelas.</string>
+ <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>
</resources>
diff --git a/core/src/main/res/values-sw-rKE/strings.xml b/core/src/main/res/values-sw-rKE/strings.xml
index 28dfeb6e8..2d9481b84 100644
--- a/core/src/main/res/values-sw-rKE/strings.xml
+++ b/core/src/main/res/values-sw-rKE/strings.xml
@@ -33,6 +33,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-te/strings.xml b/core/src/main/res/values-te/strings.xml
index 8afbdb37d..d73db283a 100644
--- a/core/src/main/res/values-te/strings.xml
+++ b/core/src/main/res/values-te/strings.xml
@@ -3,6 +3,7 @@
<!--Activitiy and fragment titles-->
<string name="feeds_label">ఫీడులు</string>
<string name="statistics_label">గణాంకాలు</string>
+ <string name="episodes_label">ఎపిసోడులు</string>
<string name="all_episodes_short_label">అన్నీ</string>
<string name="favorite_episodes_label">ఇష్టాలు</string>
<string name="new_label">కొత్తది</string>
@@ -13,33 +14,63 @@
<string name="downloads_log_label">చిట్టా</string>
<string name="subscriptions_label">చందాలు</string>
<string name="subscriptions_list_label">చందాల జాబితా</string>
+ <string name="cancel_download_label">దింపుకోలును\nరద్దుచేయి</string>
+ <string name="playback_history_label">ఆడింపు చరిత్ర</string>
+ <string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_auth_label">gpodder.net ప్రవేశం</string>
<string name="free_space_label">%1$s ఖాళీ</string>
<!--Statistics fragment-->
+ <string name="statistics_mode">గణాంకాల రీతి</string>
<!--Main activity-->
+ <string name="drawer_open">మెనూని తెరువు</string>
+ <string name="drawer_close">మెనూని మూసివేయి</string>
<string name="drawer_feed_counter_none">ఏమీలేవు</string>
<!--Webview actions-->
<!--Playback history-->
+ <string name="clear_history_label">చరిత్రను తుడిచివేయి</string>
<!--Other-->
+ <string name="confirm_label">నిర్ధారించు</string>
<string name="cancel_label">రద్దుచేయి</string>
<string name="yes">అవును</string>
<string name="no">కాదు</string>
<string name="language_label">భాష</string>
<string name="podcast_settings_label">అమరికలు</string>
+ <string name="cover_label">బొమ్మ</string>
+ <string name="error_label">పొరపాటు</string>
+ <string name="error_msg_prefix">పొరపాటు జరిగింది:</string>
+ <string name="chapters_label">చాప్టర్లు</string>
+ <string name="chapter_duration">నిడివి: %1$s</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="loading_label">వస్తోంది…</string>
+ <string name="save_username_password_label">వాడుకరి పేరునీ సంకేతపదాన్నీ భద్రపరచు</string>
<string name="close_label">మూసివేయి</string>
+ <string name="retry_label">మళ్ళీ ప్రయత్నించు</string>
<string name="feed_auto_download_always">ఎల్లప్పుడూ</string>
<string name="send_label">పంపించు…</string>
+ <plurals name="episode_cleanup_days_after_listening">
+ <item quantity="one">ముగించిన 1 రోజు తర్వాత</item>
+ <item quantity="other">ముగించిన %d రోజుల తర్వాత</item>
+ </plurals>
<!--'Add Feed' Activity labels-->
+ <string name="feedurl_label">ఫీడు చిరునామా</string>
+ <string name="etxtFeedurlHint">www.example.com/feed</string>
<!--Actions on feeds-->
+ <string name="show_info_label">సమాచారం చూపించు</string>
<string name="share_label">పంచుకోండి…</string>
<!--actions on feeditems-->
+ <string name="download_label">దింపుకో</string>
<string name="play_label">ఆడించు</string>
<string name="pause_label">నిలుపు</string>
<string name="stop_label">ఆపివేయి</string>
+ <string name="remove_label">తొలగించు</string>
<string name="delete_label">తొలగించు</string>
<!--Download messages and labels-->
+ <string name="download_error_details">వివరాలు</string>
+ <string name="download_type_feed">ఫీడు</string>
<!--Mediaplayer messages-->
<!--Queue operations-->
<string name="date">తేదీ</string>
@@ -51,15 +82,22 @@
<!--Variable Speed-->
<!--Empty list labels-->
<!--Preferences-->
+ <string name="other_pref">ఇతర</string>
<string name="about_pref">గురించి</string>
- <string name="services_label">సేవలు</string>
<string name="network_pref">నెట్‌వర్క్</string>
+ <string name="pref_autoUpdateIntervallOrTime_Disable">అచేతనించు</string>
+ <string name="pref_autoUpdateIntervallOrTime_every">ప్రతీ %1$s</string>
+ <string name="pref_theme_title_light">లేత</string>
+ <string name="pref_theme_title_dark">నల్లని</string>
+ <string name="pref_episode_cache_unlimited">అపరిమితం</string>
<string name="pref_update_interval_hours_plural">గంటలు</string>
<string name="pref_update_interval_hours_singular">గంట</string>
<string name="experimental_pref">ప్రయోగాత్మకం</string>
<string name="pref_proxy_title">ప్రాక్సీ</string>
+ <string name="pref_known_issues">తెలిసిన సమస్యలు</string>
<!--Auto-Flattr dialog-->
<!--Search-->
+ <string name="search_label">వెతుకు</string>
<!--OPML import and export-->
<string name="select_all_label">అన్నీ ఎంచుకోండి</string>
<!--Sleep timer-->
@@ -80,9 +118,14 @@
</plurals>
<!--gpodder.net-->
<string name="gpodnet_taglist_header">వర్గాలు</string>
+ <string name="gpodnet_toplist_header">మేటి పాడ్‌కాస్టులు</string>
+ <string name="gpodnet_suggestions_header">సలహాలు</string>
<string name="username_label">వాడుకరి పేరు</string>
<string name="password_label">సంకేతపదం</string>
+ <string name="gpodnetsync_pref_report_successful">విజయవంతం</string>
+ <string name="gpodnetsync_pref_report_failed">విఫలమైంది</string>
<!--Directory chooser-->
+ <string name="folder_not_empty_dialog_title">సంచయం ఖాళీగా లేదు</string>
<!--Online feed view-->
<!--Content descriptions for image buttons-->
<!--Feed information screen-->
@@ -98,6 +141,11 @@
<!--proxy settings-->
<string name="proxy_type_label">రకం</string>
<string name="optional_hint">(ఐచ్చికం)</string>
+ <!--Database import/export-->
+ <string name="label_import">దిగుమతి</string>
+ <string name="label_export">దిగుమతి</string>
+ <string name="export_ok">ఎగుమతి విజయవంతం.</string>
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-tr/strings.xml b/core/src/main/res/values-tr/strings.xml
index 8b9bd5f20..ec002ef97 100644
--- a/core/src/main/res/values-tr/strings.xml
+++ b/core/src/main/res/values-tr/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Favoriler</string>
<string name="new_label">Yeni</string>
<string name="settings_label">Ayarlar</string>
- <string name="add_new_feed_label">Cep Yayını ekle</string>
<string name="downloads_label">İndirilenler</string>
<string name="downloads_running_label">Çalışıyor</string>
<string name="downloads_completed_label">Tamamlandı</string>
@@ -111,7 +110,6 @@
<string name="feed_remover_msg">Besleme kaldırılıyor</string>
<string name="load_complete_feed">Tüm beslemeyi yenile</string>
<string name="hide_episodes_title">Bölümleri gizle</string>
- <string name="episode_actions">Eylemleri uygula</string>
<string name="hide_unplayed_episodes_label">Oynatılmadı</string>
<string name="hide_paused_episodes_label">Duraklatıldı</string>
<string name="hide_played_episodes_label">Oynatıldı</string>
@@ -265,7 +263,6 @@
<string name="other_pref">Diğer</string>
<string name="about_pref">Hakkında</string>
<string name="queue_label">Kuyruk</string>
- <string name="services_label">Servisler</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Bölüm Temizliği</string>
<string name="pref_pauseOnDisconnect_sum">Kulaklıklar çıkarıldığında veya bluetooth bağlantısı kesildiğinde çalmayı duraklat</string>
@@ -375,8 +372,6 @@
<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>
- <string name="opml_export_success_title">Opml dışa aktarma başarılı.</string>
- <string name="opml_export_success_sum">.opml dosyasy yazıldı: \u0020</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Zamanlayıcıyı ayarla</string>
<string name="disable_sleeptimer_label">Zamanlayıcıyı devre dışı bırak</string>
@@ -506,6 +501,8 @@
<string name="proxy_test_label">Test</string>
<string name="proxy_test_successful">Test başarılı</string>
<string name="proxy_test_failed">Test başarısız</string>
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-uk-rUA/strings.xml b/core/src/main/res/values-uk-rUA/strings.xml
index 308d390c6..1c6b2588f 100644
--- a/core/src/main/res/values-uk-rUA/strings.xml
+++ b/core/src/main/res/values-uk-rUA/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Улюблені</string>
<string name="new_label">Нові</string>
<string name="settings_label">Налаштування</string>
- <string name="add_new_feed_label">Додати подкаст</string>
<string name="downloads_label">Завантаження</string>
<string name="downloads_running_label">В процесі</string>
<string name="downloads_completed_label">Завершено</string>
@@ -94,6 +93,7 @@
<plurals name="episode_cleanup_days_after_listening">
<item quantity="one">1 день після закінчення</item>
<item quantity="few">%d дні після закінчення</item>
+ <item quantity="many">%d днів після закінчення</item>
<item quantity="other">%d днів після закінчення</item>
</plurals>
<!--'Add Feed' Activity labels-->
@@ -116,6 +116,7 @@
<string name="remove_feed_label">Видалити подкаст</string>
<string name="share_label">Поділитися…</string>
<string name="share_link_label">Поділитися URL сайту</string>
+ <string name="share_file_label">Поділитись файлом</string>
<string name="share_link_with_position_label">Поділитись посиланням на позицію</string>
<string name="share_feed_url_label">Поділитись посиланням на канал</string>
<string name="share_item_url_label">Поділитись посиланням на файл епізода</string>
@@ -124,7 +125,6 @@
<string name="feed_remover_msg">Удаляю канал</string>
<string name="load_complete_feed">Оновити канал цілком</string>
<string name="hide_episodes_title">Приховати епізоди</string>
- <string name="episode_actions">Застосувати дії</string>
<string name="hide_unplayed_episodes_label">Неграні</string>
<string name="hide_paused_episodes_label">На паузі</string>
<string name="hide_played_episodes_label">Грані</string>
@@ -144,6 +144,7 @@
<string name="stream_label">Прослухати без завантаження</string>
<string name="remove_label">Видалити</string>
<string name="delete_label">Видалити</string>
+ <string name="delete_failed">Файл не видалено. Можливо, перезавантаження пристрою допоможе.</string>
<string name="remove_episode_lable">Видалити епізод</string>
<string name="marked_as_seen_label">Позначено як переглянутий</string>
<string name="mark_read_label">Позначити як граний</string>
@@ -168,6 +169,8 @@
<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>
@@ -192,6 +195,7 @@
<plurals name="downloads_left">
<item quantity="one">%d завантаження залишилось</item>
<item quantity="few">%d завантаження залишилось</item>
+ <item quantity="many">%d завантажень залишилось</item>
<item quantity="other">%d завантажень залишилось</item>
</plurals>
<string name="downloads_processing">Обробка завантаженого</string>
@@ -282,7 +286,6 @@
<string name="other_pref">Інше</string>
<string name="about_pref">Про програму</string>
<string name="queue_label">Черга</string>
- <string name="services_label">Сервіси</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">Чищення епізодів</string>
<string name="pref_episode_cleanup_summary">Епізоди що не знаходяться в черзі та не помічені як улюблені можуть бути видалені якщо Автозавантажувач потребуватиме місце для нових епізодів.</string>
@@ -300,6 +303,8 @@
<string name="pref_smart_mark_as_played_title">Розумне позначення прослуханих епізодів</string>
<string name="pref_skip_keeps_episodes_sum">Зберігати епізоди що пропущені при програванні </string>
<string name="pref_skip_keeps_episodes_title">Зберігати пропущені при програванні епізоди </string>
+ <string name="pref_favorite_keeps_episodes_sum">Зберігати епізоди що помічені як улюблені</string>
+ <string name="pref_favorite_keeps_episodes_title">Зберігати улюблені епізоди</string>
<string name="playback_pref">Відтворення</string>
<string name="network_pref">Мережа</string>
<string name="pref_autoUpdateIntervallOrTime_title">Частота оновлень або оновлення в зазначений час</string>
@@ -343,6 +348,8 @@
<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>
@@ -399,8 +406,6 @@
<string name="crash_report_sum">Надіслати е-пошту зі звітом про останній збій</string>
<string name="send_email">Надіслати е-пошту</string>
<string name="experimental_pref">Експериментальні</string>
- <string name="pref_sonic_title">Sonic Media Player</string>
- <string name="pref_sonic_message">Застосувати вбудований програвач sonic замість програвача Android та Prestissimo</string>
<string name="pref_current_value">Поточне значення: %1$s</string>
<string name="pref_proxy_title">Проксі</string>
<string name="pref_proxy_sum">Застосувати проксі сервер</string>
@@ -410,7 +415,7 @@
<string name="pref_cast_title">Підтримка для Chromecast</string>
<string name="pref_cast_message_play_flavor">Включити підтримку програвання на таких пристроях як Chromecast або Android TV</string>
<string name="pref_cast_message_free_flavor">Для підтримки Chromecast потрібні бібліотеки які не включені в цю версію AntennaPod</string>
- <string name="pref_enqueue_downloaded_title">Додавати до черги завантажене</string>
+ <string name="pref_enqueue_downloaded_title">Додати завантаження до черги</string>
<string name="pref_enqueue_downloaded_summary">Додавати завантажені епізоди до черги</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">Включити автоматичне заохочення авторів через сервіс flattr</string>
@@ -448,8 +453,8 @@
<string name="html_export_label">Експорт до HTML</string>
<string name="exporting_label">Експортується…</string>
<string name="export_error_label">Помилка експорту</string>
- <string name="opml_export_success_title">OPML експорт успішний</string>
- <string name="opml_export_success_sum">OPML файл записаний в:\u0020</string>
+ <string name="export_success_title">Успішний експорт</string>
+ <string name="export_success_sum">Файл було записано в:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Щоб прочитати файл OPML потрібен доступ до зовнішньої пам’яти</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">Таймер сну</string>
@@ -467,16 +472,19 @@
<plurals name="time_seconds_quantified">
<item quantity="one">1 секунда</item>
<item quantity="few">%d секунди</item>
+ <item quantity="many">%d секунд</item>
<item quantity="other">%d секунд</item>
</plurals>
<plurals name="time_minutes_quantified">
<item quantity="one">1 хвилина</item>
<item quantity="few">%d хвилини</item>
+ <item quantity="many">%d хвилин</item>
<item quantity="other">%d хвилин</item>
</plurals>
<plurals name="time_hours_quantified">
<item quantity="one">1 година</item>
<item quantity="few">%d години</item>
+ <item quantity="many">%d годин</item>
<item quantity="other">%d годин</item>
</plurals>
<string name="auto_enable_label">Увімкнути автоматично</string>
@@ -619,6 +627,14 @@
<string name="proxy_host_empty_error">Хост не може бути пустим</string>
<string name="proxy_host_invalid_error">Хост не є правильною IP-адресою або доменним ім’ям</string>
<string name="proxy_port_invalid_error">Неправильний порт</string>
+ <!--Database import/export-->
+ <string name="import_export">Імпортувати/Експортувати базу данних</string>
+ <string name="import_export_warning">Ця експериментальна функція може бути використана щоб перенести ваші підпискаи то вже прослухані епізоди до іншого пристрою.\n\nЕкспортована база данних може тільки бути імпортована використоваючи ту саму версію AntennaPod. В противному випадку результат може бути непердбаченним.\n\nПисля импортування, епізоди можут бути відображенним як завантажені, коли на справді воне такими не є. Просто натисніть кнопку грати на цих епізодах щоб видалати іх.</string>
+ <string name="label_import">Імпортувати</string>
+ <string name="label_export">Експортувати</string>
+ <string name="import_select_file">Обрати файл для імпорту</string>
+ <string name="export_ok">Успішний експорт</string>
+ <string name="import_ok">Імпорт пройшов успішно.\n\nБудь ласка натисніть ОК щоб перезапустити AntennaPod</string>
<!--Casting-->
<string name="cast_media_route_menu_title">Грати на…</string>
<string name="cast_disconnect_label">Від’єднатись від пристроя програвання</string>
@@ -635,4 +651,5 @@
<string name="cast_failed_seek">Помилка перехода на нову позицію програвання на пристрої.</string>
<string name="cast_failed_receiver_player_error">Серйозна помилка програвання на пристрої</string>
<string name="cast_failed_media_error_skipping">Помилка програвання файла. Пропускаю...</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-v11/colors.xml b/core/src/main/res/values-v11/colors.xml
deleted file mode 100644
index 520efaa06..000000000
--- a/core/src/main/res/values-v11/colors.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <color name="selection_background_color_dark">#286E8A</color>
- <color name="selection_background_color_light">#81CFEA</color>
-</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-v14/dimens.xml b/core/src/main/res/values-v14/dimens.xml
deleted file mode 100644
index 090a476a8..000000000
--- a/core/src/main/res/values-v14/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <dimen name="widget_margin">0dp</dimen>
-
-</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-v14/styles.xml b/core/src/main/res/values-v14/styles.xml
deleted file mode 100644
index 6a39d6175..000000000
--- a/core/src/main/res/values-v14/styles.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="AntennaPod.TextView.UnreadIndicator" parent="@android:style/TextAppearance.Small">
- <item name="android:textSize">@dimen/text_size_micro</item>
- <item name="android:textColor">@color/new_indicator_green</item>
- <item name="android:text">@string/new_label</item>
- <item name="android:textAllCaps">true</item>
- </style>
-</resources> \ No newline at end of file
diff --git a/core/src/main/res/values-v21/styles.xml b/core/src/main/res/values-v21/styles.xml
index 42bd358c7..c53000c4f 100644
--- a/core/src/main/res/values-v21/styles.xml
+++ b/core/src/main/res/values-v21/styles.xml
@@ -1,5 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+ <style name="Theme.AntennaPod.Light" parent="Theme.Base.AntennaPod.Light">
+ <item name="android:windowContentTransitions">true</item>
+ </style>
+ <style name="Theme.AntennaPod.Dark" parent="Theme.Base.AntennaPod.Dark">
+ <item name="android:windowContentTransitions">true</item>
+ </style>
+ <style name="Theme.AntennaPod.TrueBlack" parent="Theme.Base.AntennaPod.TrueBlack">
+ <item name="android:windowContentTransitions">true</item>
+ <item name="android:navigationBarColor">@color/black</item>
+ <item name="android:colorAccent">@color/white</item>
+ <item name="android:colorPrimary">@color/black</item>
+ <item name="android:colorPrimaryDark">@color/black</item>
+ </style>
+ <style name="Theme.AntennaPod.TrueBlack.NoTitle" parent="Theme.Base.AntennaPod.TrueBlack.NoTitle">
+ <item name="android:navigationBarColor">@color/black</item>
+ <item name="android:colorAccent">@color/white</item>
+ <item name="android:colorPrimary">@color/black</item>
+ <item name="android:colorPrimaryDark">@color/black</item>
+ </style>
<style name="Widget.AntennaPod.Button" parent="Widget.AppCompat.Button">
<item name="textAllCaps">true</item>
diff --git a/core/src/main/res/values-vi-rVN/strings.xml b/core/src/main/res/values-vi-rVN/strings.xml
index 28dfeb6e8..2d9481b84 100644
--- a/core/src/main/res/values-vi-rVN/strings.xml
+++ b/core/src/main/res/values-vi-rVN/strings.xml
@@ -33,6 +33,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-vi/strings.xml b/core/src/main/res/values-vi/strings.xml
index 2c304e07c..7bdaf82af 100644
--- a/core/src/main/res/values-vi/strings.xml
+++ b/core/src/main/res/values-vi/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">Lượt thích</string>
<string name="new_label">Tạo mới</string>
<string name="settings_label">Thiết lập</string>
- <string name="add_new_feed_label">Thêm podcast</string>
<string name="downloads_label">Tải về</string>
<string name="downloads_running_label">Đang tải</string>
<string name="downloads_completed_label">Đã tải</string>
@@ -107,7 +106,6 @@
<string name="share_item_url_with_position_label">Chia sẻ liên kết và vị trí phát tập này</string>
<string name="feed_remover_msg">Đang xoá feed</string>
<string name="hide_episodes_title">Ẩn các tập</string>
- <string name="episode_actions">Áp dụng hành động</string>
<string name="hide_unplayed_episodes_label">Chưa nghe</string>
<string name="hide_paused_episodes_label">Đang tạm dừng</string>
<string name="hide_played_episodes_label">Đã nghe</string>
@@ -307,6 +305,8 @@
<string name="volume">Âm lượng</string>
<string name="audio_effects">Hiệu ứng âm thanh</string>
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-zh-rCN/strings.xml b/core/src/main/res/values-zh-rCN/strings.xml
index bdf9f3484..6c9737828 100644
--- a/core/src/main/res/values-zh-rCN/strings.xml
+++ b/core/src/main/res/values-zh-rCN/strings.xml
@@ -9,7 +9,6 @@
<string name="favorite_episodes_label">收藏</string>
<string name="new_label">最新</string>
<string name="settings_label">设置</string>
- <string name="add_new_feed_label">添加播客</string>
<string name="downloads_label">下载</string>
<string name="downloads_running_label">正在运行</string>
<string name="downloads_completed_label">已完成</string>
@@ -67,7 +66,7 @@
<string name="refresh_label">刷新</string>
<string name="external_storage_error_msg">没有可用的外部存储. 请确保安装外部存储器, 这样本应用才可以正常工作.</string>
<string name="chapters_label">章节</string>
- <string name="chapter_duration">按时长: %1$s</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>
@@ -114,6 +113,7 @@
<string name="remove_feed_label">删除播客</string>
<string name="share_label">分享</string>
<string name="share_link_label">分享网站链接</string>
+ <string name="share_file_label">分享文件</string>
<string name="share_link_with_position_label">分享网站链接与位置</string>
<string name="share_feed_url_label">分享订阅地址</string>
<string name="share_item_url_label">分享剧集文件URL</string>
@@ -122,7 +122,6 @@
<string name="feed_remover_msg">删除订阅</string>
<string name="load_complete_feed">刷新全部订阅</string>
<string name="hide_episodes_title">隐藏曲目</string>
- <string name="episode_actions">启用</string>
<string name="hide_unplayed_episodes_label">未播放</string>
<string name="hide_paused_episodes_label">已暂停</string>
<string name="hide_played_episodes_label">已播放</string>
@@ -141,6 +140,7 @@
<string name="stream_label">流媒体</string>
<string name="remove_label">删除</string>
<string name="delete_label">删除</string>
+ <string name="delete_failed">无法删除文件。重启可能解决该问题。</string>
<string name="remove_episode_lable">移除曲目</string>
<string name="marked_as_seen_label">标记为已读</string>
<string name="mark_read_label">标记已播放</string>
@@ -276,7 +276,6 @@
<string name="other_pref">其他</string>
<string name="about_pref">关于</string>
<string name="queue_label">播放列表</string>
- <string name="services_label">服务</string>
<string name="flattr_label">Flattr</string>
<string name="pref_episode_cleanup_title">清理曲目</string>
<string name="pref_pauseOnDisconnect_sum">暂停播放曲目当耳机或蓝牙重新连接</string>
@@ -383,8 +382,6 @@
<string name="crash_report_sum">通过 E-mail 发送最后崩溃报告</string>
<string name="send_email">发送 E-mail</string>
<string name="experimental_pref">实验性</string>
- <string name="pref_sonic_title">音频媒体播放器</string>
- <string name="pref_sonic_message">使用内置音频媒体播放器代替 Android 原生媒体播放器</string>
<string name="pref_current_value">当前值:%1$s</string>
<string name="pref_proxy_title">代理</string>
<string name="pref_proxy_sum">选择一个网络代理</string>
@@ -394,7 +391,6 @@
<string name="pref_cast_title">Chromecast 支持</string>
<string name="pref_cast_message_play_flavor">启用在 Cast 设备(例如 Chromecast 、 Audio Speakers 和 Android TV )上对于远端媒体回放的支持</string>
<string name="pref_cast_message_free_flavor">Chromecast 所需要的第三方库文件在这个版本的 AntennaPod 中被禁用</string>
- <string name="pref_enqueue_downloaded_title">队列任务下载完毕</string>
<string name="pref_enqueue_downloaded_summary">向队列添加已下载的节目</string>
<!--Auto-Flattr dialog-->
<string name="auto_flattr_enable">启用自动 flattring</string>
@@ -430,8 +426,6 @@
<string name="html_export_label">导出为 HTML 文件</string>
<string name="exporting_label">正在导出</string>
<string name="export_error_label">导出出错</string>
- <string name="opml_export_success_title">OPML 导出成功.</string>
- <string name="opml_export_success_sum">.opml 文件已保存到:\u0020</string>
<string name="opml_import_ask_read_permission">读取 OPML 文件需要访问外部存储的权限</string>
<!--Sleep timer-->
<string name="set_sleeptimer_label">设置休眠计时器</string>
@@ -590,6 +584,7 @@
<string name="proxy_host_empty_error">主机地址不能为空</string>
<string name="proxy_host_invalid_error">主机地址为无效的IP地址或域名</string>
<string name="proxy_port_invalid_error">端口不可用</string>
+ <!--Database import/export-->
<!--Casting-->
<string name="cast_failed_to_play">媒体回放开始失败</string>
<string name="cast_failed_to_stop">媒体回放停止失败</string>
@@ -597,4 +592,5 @@
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
<string name="cast_failed_setting_volume">音量设置失败</string>
<string name="cast_failed_media_error_skipping">媒体播放出错.跳转中...</string>
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-zh-rHK/strings.xml b/core/src/main/res/values-zh-rHK/strings.xml
index 28dfeb6e8..2d9481b84 100644
--- a/core/src/main/res/values-zh-rHK/strings.xml
+++ b/core/src/main/res/values-zh-rHK/strings.xml
@@ -33,6 +33,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values-zh-rTW/strings.xml b/core/src/main/res/values-zh-rTW/strings.xml
index 28dfeb6e8..d642acb0d 100644
--- a/core/src/main/res/values-zh-rTW/strings.xml
+++ b/core/src/main/res/values-zh-rTW/strings.xml
@@ -1,22 +1,303 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources xmlns:tools="http://schemas.android.com/tools">
<!--Activitiy and fragment titles-->
+ <string name="feeds_label">源</string>
+ <string name="statistics_label">統計</string>
+ <string name="add_feed_label">添加播客</string>
+ <string name="episodes_label">集</string>
+ <string name="all_episodes_short_label">全部</string>
+ <string name="favorite_episodes_label">最愛</string>
+ <string name="new_label">新</string>
+ <string name="settings_label">設置</string>
+ <string name="downloads_label">下載</string>
+ <string name="downloads_running_label">進行中</string>
+ <string name="downloads_completed_label">已完成</string>
+ <string name="downloads_log_label">日誌</string>
+ <string name="subscriptions_label">訂閱</string>
+ <string name="subscriptions_list_label">訂閱列表</string>
+ <string name="cancel_download_label">取消下載</string>
+ <string name="playback_history_label">播放歷史</string>
+ <string name="gpodnet_main_label">gpodder.net</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>
<!--Statistics fragment-->
+ <string name="total_time_listened_to_podcasts">播客總播放時長:</string>
+ <string name="statistics_details_dialog">%1$d/%2$d個劇集已開始。%3$s/%4$s已播放。</string>
+ <string name="statistics_mode">統計模式</string>
+ <string name="statistics_mode_normal">計算真實的播放時長。如果播放同一劇集兩遍,則會記錄兩遍的市場。如果只是標記為已播放,則不會被計入播放時長。</string>
+ <string name="statistics_mode_count_all">累加所有標記為已播放的播客</string>
+ <string name="statistics_speed_not_counted">注意:播放速度不被計入。</string>
<!--Main activity-->
+ <string name="drawer_open">打開菜單</string>
+ <string name="drawer_close">關閉菜單</string>
+ <string name="drawer_preferences">抽屜偏好設定</string>
+ <string name="drawer_feed_order_unplayed_episodes">按數量排序</string>
+ <string name="drawer_feed_order_alphabetical">按字母表排序</string>
+ <string name="drawer_feed_order_last_update">按發布日期排序</string>
+ <string name="drawer_feed_order_most_played">按已播放的劇集數排序</string>
+ <string name="drawer_feed_counter_new_unplayed">新增的未播放劇集數</string>
+ <string name="drawer_feed_counter_new">新劇集數</string>
+ <string name="drawer_feed_counter_unplayed">未播放劇集數</string>
+ <string name="drawer_feed_counter_downloaded">已下載的劇集數</string>
+ <string name="drawer_feed_counter_none">沒有</string>
<!--Webview actions-->
+ <string name="open_in_browser_label">在瀏覽器中打開</string>
+ <string name="copy_url_label">複製鏈接</string>
+ <string name="share_url_label">分析鏈接</string>
+ <string name="copied_url_msg">複製鏈接到剪貼板</string>
+ <string name="go_to_position_label">跳至此處</string>
<!--Playback history-->
+ <string name="clear_history_label">清除歷史</string>
<!--Other-->
+ <string name="confirm_label">確定</string>
+ <string name="cancel_label">取消</string>
+ <string name="yes">是</string>
+ <string name="no">否</string>
+ <string name="reset">重置</string>
+ <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>
+ <string name="refresh_label">刷新</string>
+ <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>
+ <string name="auto_download_label">加入自動下載</string>
+ <string name="auto_download_apply_to_items_title">應用至先前的劇集</string>
+ <string name="auto_download_apply_to_items_message">新的 <i>自動下載</i> 設置將會自動套用值新的劇集。\n你想要將此設置套用至之前已發布的劇集嗎?</string>
+ <string name="auto_delete_label">自動刪除劇集</string>
+ <string name="parallel_downloads_suffix">\u0020同時下載</string>
+ <string name="feed_auto_download_global">初始設置</string>
+ <string name="feed_auto_download_always">總是</string>
+ <string name="feed_auto_download_never">不要</string>
+ <string name="send_label">發送中……</string>
+ <string name="episode_cleanup_never">不要</string>
+ <string name="episode_cleanup_queue_removal">若不在隊列中</string>
+ <string name="episode_cleanup_after_listening">執行完畢後</string>
+ <plurals name="episode_cleanup_days_after_listening">
+ <item quantity="other">完成後%d 天</item>
+ </plurals>
<!--'Add Feed' Activity labels-->
+ <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>
+ <string name="mark_all_read_msg">全部劇集標記為已播放</string>
+ <string name="mark_all_read_confirmation_msg">請確認你要將所有劇集標記為已播放。</string>
+ <string name="mark_all_read_feed_confirmation_msg">請確認你要將此播放源的所有劇集標記為已播放。</string>
+ <string name="mark_all_seen_label">標記為已讀</string>
+ <string name="mark_all_seen_msg">全部劇集標記為已讀</string>
+ <string name="mark_all_seen_confirmation_msg">請確認你要將所有劇集標記為已讀。</string>
+ <string name="show_info_label">顯示資料</string>
+ <string name="rename_feed_label">重命名播客</string>
+ <string name="remove_feed_label">移除播客</string>
+ <string name="share_label">分享</string>
+ <string name="share_link_label">分享鏈接</string>
+ <string name="share_file_label">分享文件</string>
+ <string name="share_link_with_position_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="hide_unplayed_episodes_label">未播放</string>
+ <string name="hide_paused_episodes_label">暫停</string>
+ <string name="hide_played_episodes_label">已播放</string>
+ <string name="hide_queued_episodes_label">隊列</string>
+ <string name="hide_not_queued_episodes_label">不在隊列</string>
+ <string name="hide_downloaded_episodes_label">已下載</string>
+ <string name="hide_not_downloaded_episodes_label">未下載</string>
+ <string name="hide_has_media_label">包含媒體</string>
+ <string name="filtered_label">已過濾</string>
+ <string name="refresh_failed_msg">{fa-exclamation-circle} 上次刷新失敗</string>
+ <string name="open_podcast">打開播客</string>
<!--actions on feeditems-->
+ <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="remove_episode_lable">移除劇集</string>
+ <string name="marked_as_seen_label">標記為已讀</string>
+ <string name="mark_read_label">標記為已播放</string>
+ <string name="marked_as_read_label">已標記為已播放</string>
+ <string name="mark_unread_label">標記為未播放</string>
+ <string name="add_to_queue_label">加入隊列</string>
+ <string name="added_to_queue_label">已加入隊列</string>
+ <string name="remove_from_queue_label">從隊列移除</string>
+ <string name="add_to_favorite_label">加入最愛</string>
+ <string name="added_to_favorites">已加入最愛</string>
+ <string name="remove_from_favorite_label">移出最愛</string>
+ <string name="removed_from_favorites">已從最愛移除</string>
+ <string name="visit_website_label">訪問網站</string>
+ <string name="support_label">Flattr this</string>
+ <string name="skip_episode_label">跳過劇集</string>
+ <string name="activate_auto_download">激活自動下載</string>
+ <string name="deactivate_auto_download">禁用自動下載</string>
+ <string name="reset_position">重置播放位置</string>
+ <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_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>
+ <string name="download_error_unsupported_type">未支援的播客源類型</string>
+ <string name="download_error_connection_error">鏈接錯誤</string>
+ <string name="download_error_unknown_host">未知域名</string>
+ <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>
+ <string name="download_report_content_title">下載報告</string>
+ <string name="download_error_malformed_url">鏈接格式不正確</string>
+ <string name="download_error_io_error">IO 錯誤</string>
+ <string name="download_error_request_error">請求錯誤</string>
+ <string name="download_error_db_access">數據庫存取錯誤</string>
+ <plurals name="downloads_left">
+ <item quantity="other">剩餘%d 個下載</item>
+ </plurals>
+ <string name="downloads_processing">正在處理下載</string>
+ <string name="download_notification_title">播客數據下載中</string>
+ <string name="download_report_content">%1$d 個成功下載, %2$d 個失敗</string>
+ <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>
+ <string name="confirm_mobile_download_dialog_title">確定使用移動數據下載</string>
+ <string name="confirm_mobile_download_dialog_message_not_in_queue">通過移動數據下載是禁用的。\n\n您可以選擇添加本劇集到隊列或臨時允許通過移動數據下載。\n\n<small>您的臨時決定有效期為10分鐘</small></string>
+ <string name="confirm_mobile_download_dialog_message">通過移動數據下載是禁用的。\n\n您是否要臨時允許通過移動數據下載。\n\n<small>您的臨時決定有效期為10分鐘</small></string>
+ <string name="confirm_mobile_download_dialog_only_add_to_queue">加入隊列</string>
+ <string name="confirm_mobile_download_dialog_enable_temporarily">臨時允許</string>
<!--Mediaplayer messages-->
+ <string name="player_error_msg">錯誤!</string>
+ <string name="player_stopped_msg">播放完畢</string>
+ <string name="player_preparing_msg">準備中</string>
+ <string name="player_ready_msg">準備</string>
+ <string name="player_seeking_msg">搜索中</string>
+ <string name="playback_error_server_died">服務器未響應</string>
+ <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">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="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="date">日期</string>
+ <string name="duration">時長</string>
+ <string name="episode_title">劇集標題</string>
+ <string name="feed_title">源標題</string>
+ <string name="ascending">升序</string>
+ <string name="descending">降序</string>
+ <string name="clear_queue_confirmation_msg">請確認您要清除隊列以及其中所有的劇集</string>
<!--Flattr-->
+ <string name="flattr_auth_label">Flattr 登錄</string>
+ <string name="flattr_auth_explanation">輕按下方按鈕開始授權流程。您將會在瀏覽器打開flattr的登錄頁並被要求授予AntennaPod關於flattr的權限。完成授權後,您將會自動回到本頁面。</string>
+ <string name="authenticate_label">授權</string>
+ <string name="return_home_label">返回主頁</string>
+ <string name="flattr_auth_success">授權成功!您可以在本應用使用flatter的功能了。</string>
+ <string name="no_flattr_token_title">未找到Flattr密鑰</string>
+ <string name="no_flattr_token_notification_msg">您的flattr賬戶似乎還未關聯至AntennaPod。請按這裡來進行授權。</string>
+ <string name="no_flattr_token_msg">您的flattr賬戶似乎還未關聯至AntennaPod。您可以通過關聯flattr賬戶至AntennaPod來在本應用內部flatter東西,您也可以去通過網站flattr東西。</string>
+ <string name="authenticate_now_label">授權</string>
+ <string name="action_forbidden_title">操作被禁止</string>
+ <string name="action_forbidden_msg">AntennaPod沒有進行此操作的權限。可能是因為您賬戶與AntennaPod的關聯已被撤銷。您可以重新授權或通過網頁來操作。</string>
+ <string name="access_revoked_title">權限已撤銷</string>
+ <string name="access_revoked_info">您已成功撤銷AntennaPod與您賬戶的關聯。要完成此流程,請您前往flattr的網站,在賬戶設置中也移除本應用的關聯。</string>
<!--Flattr-->
+ <string name="flattr_click_success">Flattr了一個事物!</string>
+ <string name="flattr_click_success_count">Flatter了 %d 個事物!</string>
+ <string name="flattr_click_success_queue">Flattr了: %s。</string>
+ <string name="flattr_click_failure_count">未能 flattr %d 個事物!</string>
+ <string name="flattr_click_failure">未能flattr %s。</string>
+ <string name="flattr_click_enqueued">稍後會被flattr</string>
+ <string name="flattring_thing">Flattring %s</string>
+ <string name="flattring_label">AntennaPod 正在 flattr</string>
+ <string name="flattrd_label">AntennaPod 完成 flattr</string>
+ <string name="flattrd_failed_label">AntennaPod未能完成flattr</string>
+ <string name="flattr_retrieving_status">正在接收flattr的事物</string>
<!--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> 。\nPrestissimo與AntennaPod無關.</string>
+ <string name="set_playback_speed_label">播放速度</string>
+ <string name="enable_sonic">啟用Sonic</string>
<!--Empty list labels-->
+ <string name="no_items_label">列表裡沒有項目。</string>
+ <string name="no_feeds_label">您還未訂閱任何源。</string>
+ <string name="no_chapters_label">本劇集沒有章節。</string>
+ <string name="no_shownotes_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="flattr_label">Flattr</string>
+ <string name="pref_episode_cleanup_title">劇集清理</string>
+ <string name="pref_episode_cleanup_summary">如果自動下載需要空間用於新的劇集,則不在隊列中並且不是最愛的劇集將被移除。</string>
+ <string name="pref_pauseOnDisconnect_sum">耳機或藍牙斷開連接時暫停播放</string>
+ <string name="pref_unpauseOnHeadsetReconnect_sum">當耳機再次連接時繼續播放</string>
+ <string name="pref_unpauseOnBluetoothReconnect_sum">當藍牙再次連接時繼續播放</string>
+ <string name="pref_hardwareForwardButtonSkips_title">快進按鈕跳過</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">當按下一個實體的快進按鈕時,跳過下一個劇集而不進行快進</string>
+ <string name="pref_hardwarePreviousButtonRestarts_title">後退鍵重新播放</string>
+ <string name="pref_hardwarePreviousButtonRestarts_sum">當按下一個實體鍵的後退按鈕時,重新播放本劇集而不是後退</string>
+ <string name="pref_followQueue_sum">當播放完畢時自動跳至列表中的下一個項目</string>
+ <string name="pref_auto_delete_sum">播放完畢後刪除劇集</string>
+ <string name="pref_auto_delete_title">自動刪除</string>
+ <string name="pref_smart_mark_as_played_sum">即使播放時間少於特定秒數就離開,也標記劇集為已播放過。</string>
+ <string name="pref_smart_mark_as_played_title">智慧標記為已播放過</string>
+ <string name="pref_skip_keeps_episodes_sum">當跳過時,保持劇集</string>
+ <string name="pref_skip_keeps_episodes_title">保持跳過的聚集</string>
+ <string name="pref_favorite_keeps_episodes_sum">當標記為喜歡的,保持劇集</string>
+ <string name="pref_favorite_keeps_episodes_title">保持喜歡的劇集</string>
+ <string name="playback_pref">播放</string>
<!--Auto-Flattr dialog-->
<!--Search-->
<!--OPML import and export-->
@@ -33,6 +314,8 @@
<!--Rating dialog-->
<!--Audio controls-->
<!--proxy settings-->
+ <!--Database import/export-->
<!--Casting-->
<!--<string name="cast_failed_to_connect">Could not connect to the device</string>-->
+ <!--Notification channels-->
</resources>
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 45650495c..12961504c 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -137,10 +137,12 @@
<string-array name="theme_options">
<item>@string/pref_theme_title_light</item>
<item>@string/pref_theme_title_dark</item>
+ <item>@string/pref_theme_title_trueblack</item>
</string-array>
<string-array name="theme_values">
<item>0</item>
<item>1</item>
+ <item>2</item>
</string-array>
<string-array name="nav_drawer_titles">
@@ -181,6 +183,28 @@
<item>3</item>
</string-array>
+ <string-array name="media_player_options">
+ <item>@string/media_player_builtin</item>
+ <item>@string/media_player_sonic</item>
+ <item>@string/media_player_exoplayer</item>
+ </string-array>
+
+ <string-array name="media_player_values">
+ <item>builtin</item>
+ <item>sonic</item>
+ <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>
@@ -190,6 +214,7 @@
<item>@string/hide_downloaded_episodes_label</item>
<item>@string/hide_not_downloaded_episodes_label</item>
<item>@string/hide_has_media_label</item>
+ <item>@string/hide_is_favorite_label</item>
</string-array>
<string-array name="episode_filter_values">
@@ -201,6 +226,7 @@
<item>downloaded</item>
<item>not_downloaded</item>
<item>has_media</item>
+ <item>is_favorite</item>
</string-array>
<string-array name="image_cache_size_options">
@@ -224,4 +250,59 @@
<item>@string/fast_forward_label</item>
<item>@string/skip_episode_label</item>
</string-array>
+
+ <string-array name="video_background_behavior_options">
+ <item>@string/stop_playback</item>
+ <item>@string/player_go_to_picture_in_picture</item>
+ <item>@string/continue_playback</item>
+ </string-array>
+
+ <string-array name="video_background_behavior_values">
+ <item>stop</item>
+ <item>pip</item>
+ <item>continue</item>
+ </string-array>
+
+ <string-array name="video_background_behavior_options_without_pip">
+ <item>@string/stop_playback</item>
+ <item>@string/continue_playback</item>
+ </string-array>
+
+ <string-array name="video_background_behavior_values_without_pip">
+ <item>stop</item>
+ <item>continue</item>
+ </string-array>
+
+ <string-array name="batch_long_press_options">
+ <item>@string/select_all_above</item>
+ <item>@string/select_all_below</item>
+ </string-array>
+
+ <string-array name="back_button_behavior_options">
+ <item>@string/back_button_default</item>
+ <item>@string/back_button_go_to_page</item>
+ <item>@string/back_button_open_drawer</item>
+ <item>@string/back_button_double_tap</item>
+ <item>@string/back_button_show_prompt</item>
+ </string-array>
+
+ <string-array name="back_button_behavior_values">
+ <item>default</item>
+ <item>page</item>
+ <item>drawer</item>
+ <item>doubletap</item>
+ <item>prompt</item>
+ </string-array>
+
+ <string-array name="back_button_go_to_pages">
+ <item>@string/queue_label</item>
+ <item>@string/episodes_label</item>
+ <item>@string/subscriptions_label</item>
+ </string-array>
+
+ <string-array name="back_button_go_to_pages_tags">
+ <item>QueueFragment</item>
+ <item>EpisodesFragment</item>
+ <item>SubscriptionFragment</item>
+ </string-array>
</resources>
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 04ad7ea64..16a6d9185 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -12,6 +12,8 @@
<attr name="av_rewind" format="reference"/>
<attr name="content_discard" format="reference"/>
<attr name="content_new" format="reference"/>
+ <attr name="storage" format="reference"/>
+ <attr name="statistics" format="reference"/>
<attr name="feed" format="reference"/>
<attr name="location_web_site" format="reference"/>
<attr name="navigation_accept" format="reference"/>
@@ -44,6 +46,7 @@
<attr name="ic_unfav" format="reference"/>
<attr name="ic_sleep" format="reference"/>
<attr name="ic_sleep_off" format="reference"/>
+ <attr name="checkbox_multiple" format="reference"/>
<attr name="ic_check_box" format="reference"/>
<attr name="ic_check_box_outline" format="reference"/>
<attr name="ic_indeterminate_check_box" format="reference"/>
@@ -51,10 +54,22 @@
<attr name="ic_sd_storage" format="reference"/>
<attr name="ic_create_new_folder" format="reference"/>
<attr name="ic_cast_disconnect" format="reference"/>
+ <attr name="ic_swap" format="reference"/>
+ <attr name="ic_cellphone_text" format="reference"/>
+ <attr name="ic_question_answer" format="reference" />
+ <attr name="ic_bug" format="reference" />
+ <attr name="ic_known_issues" format="reference" />
+ <attr name="master_switch_background" format="color"/>
+ <attr name="currently_playing_background" format="color"/>
<!-- Used in itemdescription -->
<attr name="non_transparent_background" format="reference"/>
<attr name="overlay_background" format="color"/>
<attr name="nav_drawer_background" format="color"/>
+
+ <attr name="about_screen_background" format="color"/>
+ <attr name="about_screen_card_background" format="color"/>
+ <attr name="about_screen_card_border" format="color"/>
+ <attr name="about_screen_font_color" format="color"/>
</resources>
diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml
index a570a3fcb..f172b0f6d 100644
--- a/core/src/main/res/values/colors.xml
+++ b/core/src/main/res/values/colors.xml
@@ -20,17 +20,22 @@
<color name="swipe_refresh_secondary_color_dark">#060708</color>
<color name="new_indicator_green">#669900</color>
<color name="image_readability_tint">#80000000</color>
+ <color name="feed_image_bg">#50000000</color>
- <!-- Use Gingerbread-orange -->
- <color name="selection_background_color_dark">#FEBB20</color>
- <color name="selection_background_color_light">#FEBB20</color>
+ <color name="selection_background_color_trueblack">#286E8A</color>
+ <color name="selection_background_color_dark">#286E8A</color>
+ <color name="selection_background_color_light">#81CFEA</color>
<!-- Theme colors -->
<color name="primary_light">#FFFFFF</color>
<color name="highlight_light">#DDDDDD</color>
<color name="highlight_dark">#414141</color>
+ <color name="highlight_trueblack">#414141</color>
<color name="antennapod_blue">#147BAF</color>
+ <color name="master_switch_background_light">#DDDDDD</color>
+ <color name="master_switch_background_dark">#191919</color>
+
</resources>
diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml
index 01dce6a1c..46da1d68e 100644
--- a/core/src/main/res/values/dimens.xml
+++ b/core/src/main/res/values/dimens.xml
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <dimen name="widget_margin">8dp</dimen>
+ <dimen name="widget_margin">0dp</dimen>
<dimen name="thumbnail_length">70dp</dimen>
<dimen name="external_player_height">56dp</dimen>
<dimen name="enc_icons_size">20dp</dimen>
@@ -20,11 +20,10 @@
<dimen name="listview_secondary_button_width">48dp</dimen>
<dimen name="drawer_width">280dp</dimen>
<dimen name="listitem_iconwithtext_height">48dp</dimen>
- <dimen name="listitem_iconwithtext_textleftpadding">14dp</dimen>
+ <dimen name="listitem_iconwithtext_textleftpadding">16dp</dimen>
<dimen name="listitem_iconwithtext_textverticalpadding">16dp</dimen>
- <dimen name="listitem_threeline_height">96dp</dimen>
- <dimen name="listitem_threeline_textleftpadding">14dp</dimen>
+ <dimen name="listitem_threeline_textleftpadding">16dp</dimen>
<dimen name="listitem_threeline_textrightpadding">8dp</dimen>
<dimen name="listitem_threeline_verticalpadding">16dp</dimen>
<dimen name="listitem_threeline_horizontalpadding">16dp</dimen>
diff --git a/core/src/main/res/values/ic_launcher_background.xml b/core/src/main/res/values/ic_launcher_background.xml
new file mode 100644
index 000000000..3df03b8f4
--- /dev/null
+++ b/core/src/main/res/values/ic_launcher_background.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <color name="ic_launcher_background">#008AB8</color>
+</resources> \ No newline at end of file
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index a940c031b..9dd928311 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -6,11 +6,13 @@
<!-- Activitiy and fragment titles -->
<string name="app_name" translate="false">AntennaPod</string>
<string name="provider_authority" translate="false">de.danoeh.antennapod.provider</string>
+ <string name="feed_update_receiver_name">Update Subscriptions</string>
<string name="feeds_label">Feeds</string>
<string name="statistics_label">Statistics</string>
<string name="add_feed_label">Add Podcast</string>
<string name="episodes_label">Episodes</string>
<string name="all_episodes_short_label">All</string>
+ <string name="new_episodes_label">New</string>
<string name="favorite_episodes_label">Favorites</string>
<string name="new_label">New</string>
<string name="settings_label">Settings</string>
@@ -23,10 +25,12 @@
<string name="cancel_download_label">Cancel\nDownload</string>
<string name="playback_history_label">Playback History</string>
<string name="gpodnet_main_label">gpodder.net</string>
+ <string name="gpodnet_summary">Synchronize with other devices</string>
<string name="gpodnet_auth_label">gpodder.net Login</string>
<string name="free_space_label">%1$s free</string>
<string name="episode_cache_full_title">Episode cache full</string>
<string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string>
+ <string name="synchronizing">Synchronizing…</string>
<!-- Statistics fragment -->
<string name="total_time_listened_to_podcasts">Total time of podcasts played:</string>
@@ -66,13 +70,14 @@
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="reset">Reset</string>
- <string name="author_label">Author</string>
+ <string name="author_label">Author(s)</string>
<string name="language_label">Language</string>
<string name="url_label">URL</string>
<string name="podcast_settings_label">Settings</string>
<string name="cover_label">Picture</string>
<string name="error_label">Error</string>
<string name="error_msg_prefix">An error occurred:</string>
+ <string name="needs_storage_permission">Storage permission is needed for this operation</string>
<string name="refresh_label">Refresh</string>
<string name="external_storage_error_msg">No external storage is available. Please make sure that external storage is mounted so that the app can work properly.</string>
<string name="chapters_label">Chapters</string>
@@ -117,25 +122,30 @@
<string name="mark_all_read_label">Mark all as played</string>
<string name="mark_all_read_msg">Marked all Episodes as played</string>
<string name="mark_all_read_confirmation_msg">Please confirm that you want to mark all episodes as being played.</string>
- <string name="mark_all_read_feed_confirmation_msg">Please confirm that you want to mark all episodes in this feed as being played.</string>
+ <string name="mark_all_read_feed_confirmation_msg">Please confirm that you want to mark all episodes in this podcast as being played.</string>
<string name="mark_all_seen_label">Mark all as seen</string>
- <string name="mark_all_seen_msg">Marked all Episodes as seen</string>
+ <string name="mark_all_seen_msg">Marked all episodes as seen</string>
<string name="mark_all_seen_confirmation_msg">Please confirm that you want to mark all episodes as seen.</string>
<string name="show_info_label">Show information</string>
- <string name="rename_feed_label">Rename Podcast</string>
- <string name="remove_feed_label">Remove Podcast</string>
+ <string name="show_feed_settings_label">Show podcast settings</string>
+ <string name="feed_info_label">Podcast info</string>
+ <string name="feed_settings_label">Podcast settings</string>
+ <string name="rename_feed_label">Rename podcast</string>
+ <string name="remove_feed_label">Remove podcast</string>
<string name="share_label">Share&#8230;</string>
- <string name="share_link_label">Share Link</string>
+ <string name="share_link_label">Share Episode URL</string>
+ <string name="share_link_with_position_label">Share Episode URL with Position</string>
<string name="share_file_label">Share File</string>
- <string name="share_link_with_position_label">Share Link with Position</string>
<string name="share_feed_url_label">Share Feed URL</string>
- <string name="share_item_url_label">Share Episode File URL</string>
- <string name="share_item_url_with_position_label">Share Episode File URL with Position</string>
- <string name="feed_delete_confirmation_msg">Please confirm that you want to delete the feed \"%1$s\" and ALL episodes of this feed that you have downloaded.</string>
- <string name="feed_remover_msg">Removing Feed</string>
- <string name="load_complete_feed">Refresh complete Feed</string>
+ <string name="share_item_url_label">Share Media File URL</string>
+ <string name="share_item_url_with_position_label">Share Media File URL with Position</string>
+ <string name="feed_delete_confirmation_msg">Please confirm that you want to delete the podcast \"%1$s\" and ALL its episodes (including downloaded episodes).</string>
+ <string name="feed_remover_msg">Removing podcast</string>
+ <string name="load_complete_feed">Refresh complete podcast</string>
<string name="hide_episodes_title">Hide Episodes</string>
- <string name="episode_actions">Apply actions</string>
+ <string name="batch_edit">Batch edit</string>
+ <string name="select_all_above">Select all above</string>
+ <string name="select_all_below">Select all below</string>
<string name="hide_unplayed_episodes_label">Unplayed</string>
<string name="hide_paused_episodes_label">Paused</string>
<string name="hide_played_episodes_label">Played</string>
@@ -144,6 +154,7 @@
<string name="hide_downloaded_episodes_label">Downloaded</string>
<string name="hide_not_downloaded_episodes_label">Not downloaded</string>
<string name="hide_has_media_label">Has media</string>
+ <string name="hide_is_favorite_label">Is favorite</string>
<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>
@@ -158,6 +169,7 @@
<string name="delete_label">Delete</string>
<string name="delete_failed">Unable to delete file. Rebooting the device could help.</string>
<string name="remove_episode_lable">Remove Episode</string>
+ <string name="mark_as_seen_label">Mark as seen</string>
<string name="marked_as_seen_label">Marked as seen</string>
<string name="mark_read_label">Mark as played</string>
<string name="marked_as_read_label">Marked as played</string>
@@ -182,6 +194,8 @@
<string name="download_failed">failed</string>
<string name="download_pending">Download pending</string>
<string name="download_running">Download running</string>
+ <string name="download_error_details">Details</string>
+ <string name="download_error_details_message">%1$s \n\nFile URL:\n%2$s</string>
<string name="download_error_device_not_found">Storage Device not found</string>
<string name="download_error_insufficient_space">Insufficient Space</string>
<string name="download_error_file_error">File Error</string>
@@ -234,6 +248,7 @@
<string name="no_media_playing_label">No media playing</string>
<string name="position_default_label" translate="false">00:00:00</string>
<string name="player_buffering_msg">Buffering</string>
+ <string name="player_go_to_picture_in_picture">Picture-in-picture mode</string>
<string name="playbackservice_notification_title">Playing podcast</string>
<string name="unknown_media_key">AntennaPod - Unknown media key: %1$d</string>
@@ -251,7 +266,9 @@
<string name="date">Date</string>
<string name="duration">Duration</string>
<string name="episode_title">Episode title</string>
- <string name="feed_title">Feed title</string>
+ <string name="feed_title">Podcast title</string>
+ <string name="random">Random</string>
+ <string name="smart_shuffle">Smart Shuffle</string>
<string name="ascending">Ascending</string>
<string name="descending">Descending</string>
<string name="clear_queue_confirmation_msg">Please confirm that you want to clear the queue of ALL of the episodes in it</string>
@@ -293,7 +310,7 @@
<!-- Empty list labels -->
<string name="no_items_label">There are no items in this list.</string>
- <string name="no_feeds_label">You haven\'t subscribed to any feeds yet.</string>
+ <string name="no_feeds_label">You haven\'t subscribed to any podcasts yet.</string>
<string name="no_chapters_label">This episode has no chapters.</string>
<string name="no_shownotes_label">This episode has no shownotes.</string>
@@ -303,15 +320,24 @@
<string name="other_pref">Other</string>
<string name="about_pref">About</string>
<string name="queue_label">Queue</string>
- <string name="services_label">Services</string>
+ <string name="integrations_label">Integrations</string>
<string name="flattr_label">Flattr</string>
+ <string name="flattr_summary">Micropayment service</string>
+ <string name="automation">Automation</string>
+ <string name="download_pref_details">Details</string>
+ <string name="import_export_pref">Import/Export</string>
+ <string name="appearance">Appearance</string>
+ <string name="external_elements">External elements</string>
+ <string name="interruptions">Interruptions</string>
+ <string name="buttons">Playback control buttons</string>
+ <string name="media_player">Media player</string>
<string name="pref_episode_cleanup_title">Episode Cleanup</string>
<string name="pref_episode_cleanup_summary">Episodes that aren\'t in the queue and aren\'t favorites should be eligible for removal if Auto Download needs space for new episodes</string>
<string name="pref_pauseOnDisconnect_sum">Pause playback when headphones or bluetooth are disconnected</string>
<string name="pref_unpauseOnHeadsetReconnect_sum">Resume playback when the headphones are reconnected</string>
<string name="pref_unpauseOnBluetoothReconnect_sum">Resume playback when bluetooth reconnects</string>
<string name="pref_hardwareForwardButtonSkips_title">Forward Button Skips</string>
- <string name="pref_hardwareForwardButtonSkips_sum">When pressing a hardware forward button skip to the next episode instead of fast-forwarding</string>
+ <string name="pref_hardwareForwardButtonSkips_sum">When pressing a forward button on a bluetooth-connected device skip to the next episode instead of fast-forwarding</string>
<string name="pref_hardwarePreviousButtonRestarts_title">Previous button restarts</string>
<string name="pref_hardwarePreviousButtonRestarts_sum">When pressing a hardware previous button restart playing the current episode instead of rewinding</string>
<string name="pref_followQueue_sum">Jump to next queue item when playback completes</string>
@@ -360,7 +386,7 @@
<string name="pref_nav_drawer_feed_order_title">Set Subscription Order</string>
<string name="pref_nav_drawer_feed_order_sum">Change the order of your subscriptions</string>
<string name="pref_nav_drawer_feed_counter_title">Set Subscription Counter</string>
- <string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter</string>
+ <string name="pref_nav_drawer_feed_counter_sum">Change the information displayed by the subscription counter. Also affects the sorting of subscriptions if \'Subscription Order\' is set to \'Counter\'.</string>
<string name="pref_set_theme_sum">Change the appearance of AntennaPod.</string>
<string name="pref_automatic_download_title">Automatic Download</string>
<string name="pref_automatic_download_sum">Configure the automatic download of episodes.</string>
@@ -374,6 +400,7 @@
<string name="pref_episode_cache_title">Episode Cache</string>
<string name="pref_theme_title_light">Light</string>
<string name="pref_theme_title_dark">Dark</string>
+ <string name="pref_theme_title_trueblack">Black (AMOLED ready)</string>
<string name="pref_episode_cache_unlimited">Unlimited</string>
<string name="pref_update_interval_hours_plural">hours</string>
<string name="pref_update_interval_hours_singular">hour</string>
@@ -402,8 +429,8 @@
<string name="pref_rewind_sum">Customize the number of seconds to jump backwards when the rewind button is clicked</string>
<string name="pref_gpodnet_sethostname_title">Set hostname</string>
<string name="pref_gpodnet_sethostname_use_default_host">Use default host</string>
- <string name="pref_expandNotify_title">Expand Notification</string>
- <string name="pref_expandNotify_sum">Always expand the notification to show playback buttons.</string>
+ <string name="pref_expandNotify_title">High Notification priority</string>
+ <string name="pref_expandNotify_sum">This usually expands the notification to show playback buttons.</string>
<string name="pref_persistNotify_title">Persistent Playback Controls</string>
<string name="pref_persistNotify_sum">Keep notification and lockscreen controls when playback is paused.</string>
<string name="pref_compact_notification_buttons_title">Set Lockscreen Buttons</string>
@@ -424,8 +451,7 @@
<string name="crash_report_sum">Send the latest crash report via e-mail</string>
<string name="send_email">Send e-mail</string>
<string name="experimental_pref">Experimental</string>
- <string name="pref_sonic_title">Sonic Media Player</string>
- <string name="pref_sonic_message">Use built-in sonic media player as a replacement for Android\'s native mediaplayer and Prestissimo</string>
+ <string name="pref_media_player_message">Select which media player to use to play files</string>
<string name="pref_current_value">Current value: %1$s</string>
<string name="pref_proxy_title">Proxy</string>
<string name="pref_proxy_sum">Set a network proxy</string>
@@ -437,6 +463,24 @@
<string name="pref_cast_message_free_flavor">Chromecast requires third party proprietary libraries that are disabled in this version of AntennaPod</string>
<string name="pref_enqueue_downloaded_title">Enqueue Downloaded</string>
<string name="pref_enqueue_downloaded_summary">Add downloaded episodes to the queue</string>
+ <string name="media_player_builtin">Built-in Android player</string>
+ <string name="media_player_sonic" translatable="false">Sonic Media Player</string>
+ <string name="media_player_exoplayer" translatable="false">ExoPlayer</string>
+ <string name="pref_videoBehavior_title">Upon exiting video</string>
+ <string name="pref_videoBehavior_sum">Behavior when leaving video playback</string>
+ <string name="stop_playback">Stop playback</string>
+ <string name="continue_playback">Continue audio playback</string>
+ <string name="behavior">Behavior</string>
+ <string name="pref_back_button_behavior_title">Back Button Behavior</string>
+ <string name="pref_back_button_behavior_sum">Change behavior of the back button.</string>
+ <string name="back_button_default">Default</string>
+ <string name="back_button_open_drawer">Open navigation drawer</string>
+ <string name="back_button_double_tap">Double tap to exit</string>
+ <string name="back_button_show_prompt">Confirm to exit</string>
+ <string name="close_prompt">Are you sure you want to close AntennaPod?</string>
+ <string name="double_tap_toast">Tap back button again to exit</string>
+ <string name="back_button_go_to_page">Go to page…</string>
+ <string name="back_button_go_to_page_title">Select page</string>
<!-- Auto-Flattr dialog -->
<string name="auto_flattr_enable">Enable automatic flattring</string>
@@ -448,8 +492,8 @@
<string name="search_hint">Search for episodes</string>
<string name="found_in_shownotes_label">Found in show notes</string>
<string name="found_in_chapters_label">Found in chapters</string>
- <string name="found_in_authors_label">Found in authors</string>
- <string name="found_in_feeds_label">Found in feeds</string>
+ <string name="found_in_authors_label">Found in author(s)</string>
+ <string name="found_in_feeds_label">Found in podcast</string>
<string name="search_status_no_results">No results were found</string>
<string name="search_label">Search</string>
<string name="found_in_title_label">Found in title</string>
@@ -475,8 +519,8 @@
<string name="html_export_label">HTML export</string>
<string name="exporting_label">Exporting&#8230;</string>
<string name="export_error_label">Export error</string>
- <string name="opml_export_success_title">OPML Export successful.</string>
- <string name="opml_export_success_sum">The .opml file was written to:\u0020</string>
+ <string name="export_success_title">Export successful</string>
+ <string name="export_success_sum">The exported file was written to:\n\n%1$s</string>
<string name="opml_import_ask_read_permission">Access to external storage is required to read the OPML file</string>
<!-- Sleep timer -->
@@ -661,6 +705,15 @@
<string name="proxy_host_invalid_error">Host is not a valid IP address or domain</string>
<string name="proxy_port_invalid_error">Port not valid</string>
+ <!-- Database import/export -->
+ <string name="import_export">Database import/export</string>
+ <string name="import_export_warning">This experimental function can be used to transfer your subscriptions and played episodes to another device.\n\nExported databases can only be imported when using the same version of AntennaPod. Otherwise, this function will lead to unexpected behavior.\n\nAfter importing, episodes might be displayed as downloaded even though they are not. Just press the play button of the episodes to make AntennaPod detect this.</string>
+ <string name="label_import">Import</string>
+ <string name="label_export">Export</string>
+ <string name="import_select_file">Select file to import</string>
+ <string name="export_ok">Export successful.</string>
+ <string name="import_ok">Import successful.\n\nPlease press OK to restart AntennaPod</string>
+
<!-- Casting -->
<string name="cast_media_route_menu_title">Play on&#8230;</string>
<string name="cast_disconnect_label">Disconnect the cast session</string>
@@ -677,4 +730,14 @@
<string name="cast_failed_seek">Failed to seek to the new position on the cast device</string>
<string name="cast_failed_receiver_player_error">Receiver player has encountered a severe error</string>
<string name="cast_failed_media_error_skipping">Error playing media. Skipping&#8230;</string>
+
+ <!-- Notification channels -->
+ <string name="notification_channel_user_action">Action required</string>
+ <string name="notification_channel_user_action_description">Shown if your action is required, for example if you need to enter a password.</string>
+ <string name="notification_channel_downloading">Downloading</string>
+ <string name="notification_channel_downloading_description">Shown while currently downloading.</string>
+ <string name="notification_channel_playing">Currently playing</string>
+ <string name="notification_channel_playing_description">Allows to control playback. This is the main notification you see while playing a podcast.</string>
+ <string name="notification_channel_error">Errors</string>
+ <string name="notification_channel_error_description">Shown if something went wrong, for example if download or gpodder sync fails.</string>
</resources>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index 4f228f8f1..60296c64f 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -1,125 +1,193 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="Theme.AntennaPod.Light" parent="Theme.AppCompat.Light">
+ <style name="Theme.AntennaPod.Light" parent="Theme.Base.AntennaPod.Light">
+ <!-- Room for API dependent attributes -->
+ </style>
+
+ <style name="Theme.Base.AntennaPod.Light" parent="Theme.AppCompat.Light">
<item name="colorPrimary">@color/primary_light</item>
<item name="colorAccent">@color/holo_blue_light</item>
<item name="progressBarTheme">@style/ProgressBarLight</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
<item name="alertDialogTheme">@style/AntennaPod.Dialog.Light</item>
- <item name="attr/action_bar_icon_color">@color/grey600</item>
- <item name="attr/action_about">@drawable/ic_info_grey600_24dp</item>
- <item name="attr/action_search">@drawable/ic_search_grey600_24dp</item>
- <item name="attr/action_stream">@drawable/ic_settings_input_antenna_grey600_24dp</item>
- <item name="attr/av_download">@drawable/ic_file_download_grey600_24dp</item>
- <item name="attr/av_fast_forward">@drawable/ic_fast_forward_grey600_24dp</item>
- <item name="attr/av_pause">@drawable/ic_pause_grey600_24dp</item>
- <item name="attr/av_play">@drawable/ic_play_arrow_grey600_24dp</item>
- <item name="attr/av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
- <item name="attr/content_discard">@drawable/ic_delete_grey600_24dp</item>
- <item name="attr/content_new">@drawable/ic_add_grey600_24dp</item>
- <item name="attr/feed">@drawable/ic_feed_grey600_24dp</item>
- <item name="attr/location_web_site">@drawable/ic_web_grey600_24dp</item>
- <item name="attr/navigation_accept">@drawable/ic_done_grey600_24dp</item>
- <item name="attr/navigation_cancel">@drawable/ic_cancel_grey600_24dp</item>
- <item name="attr/navigation_expand">@drawable/ic_expand_more_grey600_36dp</item>
- <item name="attr/navigation_refresh">@drawable/ic_refresh_grey600_24dp</item>
- <item name="attr/navigation_up">@drawable/navigation_up</item>
- <item name="attr/social_share">@drawable/ic_share_grey600_24dp</item>
- <item name="attr/stat_playlist">@drawable/ic_list_grey600_24dp</item>
- <item name="attr/type_audio">@drawable/ic_hearing_grey600_18dp</item>
- <item name="attr/type_video">@drawable/ic_remove_red_eye_grey600_18dp</item>
- <item name="attr/non_transparent_background">@color/white</item>
- <item name="attr/overlay_background">@color/overlay_light</item>
- <item name="attr/overlay_drawable">@drawable/overlay_drawable</item>
- <item name="attr/dragview_background">@drawable/ic_drag_vertical_grey600_48dp</item>
- <item name="attr/dragview_float_background">@color/white</item>
- <item name="attr/nav_drawer_background">@color/white</item>
- <item name="attr/ic_new">@drawable/ic_new_releases_grey600_24dp</item>
- <item name="attr/ic_history">@drawable/ic_history_grey600_24dp</item>
- <item name="attr/ic_folder">@drawable/ic_folder_grey600_24dp</item>
- <item name="attr/av_play_big">@drawable/ic_play_arrow_grey600_36dp</item>
- <item name="attr/av_pause_big">@drawable/ic_pause_grey600_36dp</item>
- <item name="attr/av_ff_big">@drawable/ic_fast_forward_grey600_36dp</item>
- <item name="attr/av_rew_big">@drawable/ic_fast_rewind_grey600_36dp</item>
- <item name="attr/av_skip_big">@drawable/ic_skip_grey600_36dp</item>
- <item name="attr/ic_fav">@drawable/ic_star_border_grey600_24dp</item>
- <item name="attr/ic_unfav">@drawable/ic_star_grey600_24dp</item>
- <item name="attr/ic_settings">@drawable/ic_settings_grey600_24dp</item>
- <item name="attr/ic_lock_open">@drawable/ic_lock_open_grey600_24dp</item>
- <item name="attr/ic_lock_closed">@drawable/ic_lock_closed_grey600_24dp</item>
- <item name="attr/ic_filter">@drawable/ic_filter_grey600_24dp</item>
- <item name="attr/ic_sleep">@drawable/ic_sleep_grey600_24dp</item>
- <item name="attr/ic_sleep_off">@drawable/ic_sleep_off_grey600_24dp</item>
- <item name="attr/ic_check_box">@drawable/ic_check_box_grey600_24dp</item>
- <item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
- <item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
- <item name="attr/ic_sort">@drawable/ic_sort_grey600_24dp</item>
- <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
- <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
- <item name="attr/ic_cast_disconnect">@drawable/ic_cast_disconnect_grey600_36dp</item>
+ <item type="attr" name="action_bar_icon_color">@color/grey600</item>
+ <item type="attr" name="storage">@drawable/ic_sd_grey600_24dp</item>
+ <item type="attr" name="ic_swap">@drawable/ic_swap_vertical_grey600_24dp</item>
+ <item type="attr" name="statistics">@drawable/ic_poll_box_grey600_24dp</item>
+ <item type="attr" name="action_about">@drawable/ic_info_grey600_24dp</item>
+ <item type="attr" name="checkbox_multiple">@drawable/ic_checkbox_multiple_marked_outline_grey600_24dp</item>
+ <item type="attr" name="action_search">@drawable/ic_search_grey600_24dp</item>
+ <item type="attr" name="action_stream">@drawable/ic_settings_input_antenna_grey600_24dp</item>
+ <item type="attr" name="av_download">@drawable/ic_file_download_grey600_24dp</item>
+ <item type="attr" name="av_fast_forward">@drawable/ic_fast_forward_grey600_24dp</item>
+ <item type="attr" name="av_pause">@drawable/ic_pause_grey600_24dp</item>
+ <item type="attr" name="av_play">@drawable/ic_play_arrow_grey600_24dp</item>
+ <item type="attr" name="av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
+ <item type="attr" name="content_discard">@drawable/ic_delete_grey600_24dp</item>
+ <item type="attr" name="content_new">@drawable/ic_add_grey600_24dp</item>
+ <item type="attr" name="feed">@drawable/ic_feed_grey600_24dp</item>
+ <item type="attr" name="location_web_site">@drawable/ic_web_grey600_24dp</item>
+ <item type="attr" name="navigation_accept">@drawable/ic_done_grey600_24dp</item>
+ <item type="attr" name="navigation_cancel">@drawable/ic_cancel_grey600_24dp</item>
+ <item type="attr" name="navigation_expand">@drawable/ic_expand_more_grey600_36dp</item>
+ <item type="attr" name="navigation_refresh">@drawable/ic_refresh_grey600_24dp</item>
+ <item type="attr" name="navigation_up">@drawable/navigation_up</item>
+ <item type="attr" name="social_share">@drawable/ic_share_grey600_24dp</item>
+ <item type="attr" name="stat_playlist">@drawable/ic_list_grey600_24dp</item>
+ <item type="attr" name="type_audio">@drawable/ic_hearing_grey600_18dp</item>
+ <item type="attr" name="type_video">@drawable/ic_remove_red_eye_grey600_18dp</item>
+ <item type="attr" name="non_transparent_background">@color/white</item>
+ <item type="attr" name="overlay_background">@color/overlay_light</item>
+ <item type="attr" name="overlay_drawable">@drawable/overlay_drawable</item>
+ <item type="attr" name="dragview_background">@drawable/ic_drag_vertical_grey600_48dp</item>
+ <item type="attr" name="dragview_float_background">@color/white</item>
+ <item type="attr" name="nav_drawer_background">@color/white</item>
+ <item type="attr" name="ic_new">@drawable/ic_new_releases_grey600_24dp</item>
+ <item type="attr" name="ic_history">@drawable/ic_history_grey600_24dp</item>
+ <item type="attr" name="ic_folder">@drawable/ic_folder_grey600_24dp</item>
+ <item type="attr" name="av_play_big">@drawable/ic_play_arrow_grey600_36dp</item>
+ <item type="attr" name="av_pause_big">@drawable/ic_pause_grey600_36dp</item>
+ <item type="attr" name="av_ff_big">@drawable/ic_fast_forward_grey600_36dp</item>
+ <item type="attr" name="av_rew_big">@drawable/ic_fast_rewind_grey600_36dp</item>
+ <item type="attr" name="av_skip_big">@drawable/ic_skip_grey600_36dp</item>
+ <item type="attr" name="ic_fav">@drawable/ic_star_border_grey600_24dp</item>
+ <item type="attr" name="ic_unfav">@drawable/ic_star_grey600_24dp</item>
+ <item type="attr" name="ic_settings">@drawable/ic_settings_grey600_24dp</item>
+ <item type="attr" name="ic_lock_open">@drawable/ic_lock_open_grey600_24dp</item>
+ <item type="attr" name="ic_lock_closed">@drawable/ic_lock_closed_grey600_24dp</item>
+ <item type="attr" name="ic_filter">@drawable/ic_filter_grey600_24dp</item>
+ <item type="attr" name="ic_sleep">@drawable/ic_sleep_grey600_24dp</item>
+ <item type="attr" name="ic_sleep_off">@drawable/ic_sleep_off_grey600_24dp</item>
+ <item type="attr" name="ic_check_box">@drawable/ic_check_box_grey600_24dp</item>
+ <item type="attr" name="ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
+ <item type="attr" name="ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
+ <item type="attr" name="ic_sort">@drawable/ic_sort_grey600_24dp</item>
+ <item type="attr" name="ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
+ <item type="attr" name="ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
+ <item type="attr" name="ic_cast_disconnect">@drawable/ic_cast_disconnect_grey600_36dp</item>
+ <item type="attr" name="ic_cellphone_text">@drawable/ic_cellphone_text_grey600_24dp</item>
+ <item type="attr" name="ic_question_answer">@drawable/ic_forum_grey600_24dp</item>
+ <item type="attr" name="ic_bug">@drawable/ic_bug_grey600_24dp</item>
+ <item type="attr" name="ic_known_issues">@drawable/ic_format_list_bulleted_grey600_24dp</item>
+
+ <item type="attr" name="master_switch_background">@color/master_switch_background_light</item>
+ <item type="attr" name="currently_playing_background">@color/highlight_light</item>
+
+ <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
+
+ <item type="attr" name="about_screen_background">#e5e5e5</item>
+ <item type="attr" name="about_screen_card_background">#ffffff</item>
+ <item type="attr" name="about_screen_card_border">#d2d2d2</item>
+ <item type="attr" name="about_screen_font_color">#000000</item>
+ </style>
+
+ <style name="Theme.AntennaPod.Dark" parent="Theme.Base.AntennaPod.Dark">
+ <!-- Room for API dependent attributes -->
</style>
- <style name="Theme.AntennaPod.Dark" parent="Theme.AppCompat">
+ <style name="Theme.Base.AntennaPod.Dark" parent="Theme.AppCompat">
<item name="colorAccent">@color/holo_blue_dark</item>
<item name="colorControlNormal">@color/white</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
<item name="progressBarTheme">@style/ProgressBarDark</item>
<item name="alertDialogTheme">@style/AntennaPod.Dialog.Dark</item>
- <item name="attr/action_bar_icon_color">@color/white</item>
- <item name="attr/action_about">@drawable/ic_info_white_24dp</item>g
- <item name="attr/action_search">@drawable/ic_search_white_24dp</item>
- <item name="attr/action_stream">@drawable/ic_settings_input_antenna_white_24dp</item>
- <item name="attr/av_download">@drawable/ic_file_download_white_24dp</item>
- <item name="attr/av_fast_forward">@drawable/ic_fast_forward_white_24dp</item>
- <item name="attr/av_pause">@drawable/ic_pause_white_24dp</item>
- <item name="attr/av_play">@drawable/ic_play_arrow_white_24dp</item>
- <item name="attr/av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
- <item name="attr/content_discard">@drawable/ic_delete_white_24dp</item>
- <item name="attr/content_new">@drawable/ic_add_white_24dp</item>
- <item name="attr/feed">@drawable/ic_feed_white_24dp</item>
- <item name="attr/location_web_site">@drawable/ic_web_white_24dp</item>
- <item name="attr/navigation_accept">@drawable/ic_done_white_24dp</item>
- <item name="attr/navigation_cancel">@drawable/ic_cancel_white_24dp</item>
- <item name="attr/navigation_expand">@drawable/ic_expand_more_white_36dp</item>
- <item name="attr/navigation_refresh">@drawable/ic_refresh_white_24dp</item>
- <item name="attr/navigation_up">@drawable/navigation_up_dark</item>
- <item name="attr/social_share">@drawable/ic_share_white_24dp</item>
- <item name="attr/stat_playlist">@drawable/ic_list_white_24dp</item>
- <item name="attr/type_audio">@drawable/ic_hearing_white_18dp</item>
- <item name="attr/type_video">@drawable/ic_remove_red_eye_white_18dp</item>
- <item name="attr/non_transparent_background">@color/black</item>
- <item name="attr/overlay_background">@color/overlay_dark</item>
- <item name="attr/overlay_drawable">@drawable/overlay_drawable_dark</item>
- <item name="attr/dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
- <item name="attr/dragview_float_background">@color/black</item>
- <item name="attr/nav_drawer_background">#3B3B3B</item>
- <item name="attr/ic_new">@drawable/ic_new_releases_white_24dp</item>
- <item name="attr/ic_history">@drawable/ic_history_white_24dp</item>
- <item name="attr/ic_folder">@drawable/ic_folder_white_24dp</item>
- <item name="attr/av_play_big">@drawable/ic_play_arrow_white_36dp</item>
- <item name="attr/av_pause_big">@drawable/ic_pause_white_36dp</item>
- <item name="attr/av_ff_big">@drawable/ic_fast_forward_white_36dp</item>
- <item name="attr/av_rew_big">@drawable/ic_fast_rewind_white_36dp</item>
- <item name="attr/av_skip_big">@drawable/ic_skip_white_36dp</item>
- <item name="attr/ic_fav">@drawable/ic_star_border_white_24dp</item>
- <item name="attr/ic_unfav">@drawable/ic_star_white_24dp</item>
- <item name="attr/ic_settings">@drawable/ic_settings_white_24dp</item>
- <item name="attr/ic_lock_open">@drawable/ic_lock_open_white_24dp</item>
- <item name="attr/ic_lock_closed">@drawable/ic_lock_closed_white_24dp</item>
- <item name="attr/ic_filter">@drawable/ic_filter_white_24dp</item>
- <item name="attr/ic_sleep">@drawable/ic_sleep_white_24dp</item>
- <item name="attr/ic_sleep_off">@drawable/ic_sleep_off_white_24dp</item>
- <item name="attr/ic_check_box">@drawable/ic_check_box_white_24dp</item>
- <item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
- <item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
- <item name="attr/ic_sort">@drawable/ic_sort_white_24dp</item>
- <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
- <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
- <item name="attr/ic_cast_disconnect">@drawable/ic_cast_disconnect_white_36dp</item>
+ <item type="attr" name="action_bar_icon_color">@color/white</item>
+ <item type="attr" name="storage">@drawable/ic_sd_white_24dp</item>
+ <item type="attr" name="ic_swap">@drawable/ic_swap_vertical_white_24dp</item>
+ <item type="attr" name="statistics">@drawable/ic_poll_box_white_24dp</item>
+ <item type="attr" name="action_about">@drawable/ic_info_white_24dp</item>
+ <item type="attr" name="checkbox_multiple">@drawable/ic_checkbox_multiple_marked_outline_white_24dp</item>
+ <item type="attr" name="action_search">@drawable/ic_search_white_24dp</item>
+ <item type="attr" name="action_stream">@drawable/ic_settings_input_antenna_white_24dp</item>
+ <item type="attr" name="av_download">@drawable/ic_file_download_white_24dp</item>
+ <item type="attr" name="av_fast_forward">@drawable/ic_fast_forward_white_24dp</item>
+ <item type="attr" name="av_pause">@drawable/ic_pause_white_24dp</item>
+ <item type="attr" name="av_play">@drawable/ic_play_arrow_white_24dp</item>
+ <item type="attr" name="av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
+ <item type="attr" name="content_discard">@drawable/ic_delete_white_24dp</item>
+ <item type="attr" name="content_new">@drawable/ic_add_white_24dp</item>
+ <item type="attr" name="feed">@drawable/ic_feed_white_24dp</item>
+ <item type="attr" name="location_web_site">@drawable/ic_web_white_24dp</item>
+ <item type="attr" name="navigation_accept">@drawable/ic_done_white_24dp</item>
+ <item type="attr" name="navigation_cancel">@drawable/ic_cancel_white_24dp</item>
+ <item type="attr" name="navigation_expand">@drawable/ic_expand_more_white_36dp</item>
+ <item type="attr" name="navigation_refresh">@drawable/ic_refresh_white_24dp</item>
+ <item type="attr" name="navigation_up">@drawable/navigation_up_dark</item>
+ <item type="attr" name="social_share">@drawable/ic_share_white_24dp</item>
+ <item type="attr" name="stat_playlist">@drawable/ic_list_white_24dp</item>
+ <item type="attr" name="type_audio">@drawable/ic_hearing_white_18dp</item>
+ <item type="attr" name="type_video">@drawable/ic_remove_red_eye_white_18dp</item>
+ <item type="attr" name="non_transparent_background">@color/black</item>
+ <item type="attr" name="overlay_background">@color/overlay_dark</item>
+ <item type="attr" name="overlay_drawable">@drawable/overlay_drawable_dark</item>
+ <item type="attr" name="dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
+ <item type="attr" name="dragview_float_background">@color/black</item>
+ <item type="attr" name="nav_drawer_background">#3B3B3B</item>
+ <item type="attr" name="ic_new">@drawable/ic_new_releases_white_24dp</item>
+ <item type="attr" name="ic_history">@drawable/ic_history_white_24dp</item>
+ <item type="attr" name="ic_folder">@drawable/ic_folder_white_24dp</item>
+ <item type="attr" name="av_play_big">@drawable/ic_play_arrow_white_36dp</item>
+ <item type="attr" name="av_pause_big">@drawable/ic_pause_white_36dp</item>
+ <item type="attr" name="av_ff_big">@drawable/ic_fast_forward_white_36dp</item>
+ <item type="attr" name="av_rew_big">@drawable/ic_fast_rewind_white_36dp</item>
+ <item type="attr" name="av_skip_big">@drawable/ic_skip_white_36dp</item>
+ <item type="attr" name="ic_fav">@drawable/ic_star_border_white_24dp</item>
+ <item type="attr" name="ic_unfav">@drawable/ic_star_white_24dp</item>
+ <item type="attr" name="ic_settings">@drawable/ic_settings_white_24dp</item>
+ <item type="attr" name="ic_lock_open">@drawable/ic_lock_open_white_24dp</item>
+ <item type="attr" name="ic_lock_closed">@drawable/ic_lock_closed_white_24dp</item>
+ <item type="attr" name="ic_filter">@drawable/ic_filter_white_24dp</item>
+ <item type="attr" name="ic_sleep">@drawable/ic_sleep_white_24dp</item>
+ <item type="attr" name="ic_sleep_off">@drawable/ic_sleep_off_white_24dp</item>
+ <item type="attr" name="ic_check_box">@drawable/ic_check_box_white_24dp</item>
+ <item type="attr" name="ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
+ <item type="attr" name="ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
+ <item type="attr" name="ic_sort">@drawable/ic_sort_white_24dp</item>
+ <item type="attr" name="ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
+ <item type="attr" name="ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
+ <item type="attr" name="ic_cast_disconnect">@drawable/ic_cast_disconnect_white_36dp</item>
+ <item type="attr" name="ic_cellphone_text">@drawable/ic_cellphone_text_white_24dp</item>
+ <item type="attr" name="ic_question_answer">@drawable/ic_baseline_question_answer_white_24dp</item>
+ <item type="attr" name="ic_bug">@drawable/ic_bug_white_24dp</item>
+ <item type="attr" name="ic_known_issues">@drawable/ic_format_list_bulleted_white_24dp</item>
+ <item type="attr" name="master_switch_background">@color/master_switch_background_dark</item>
+ <item type="attr" name="currently_playing_background">@color/highlight_dark</item>
+ <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
+
+ <item type="attr" name="about_screen_background">#303030</item>
+ <item type="attr" name="about_screen_card_background">#424242</item>
+ <item type="attr" name="about_screen_card_border">#262626</item>
+ <item type="attr" name="about_screen_font_color">#ffffff</item>
+ </style>
+
+ <style name="Theme.AntennaPod.TrueBlack" parent="Theme.Base.AntennaPod.TrueBlack">
+ <!-- Room for API dependent attributes -->
</style>
- <style name="Theme.AntennaPod.Light.NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
+ <style name="Theme.Base.AntennaPod.TrueBlack" parent="Theme.Base.AntennaPod.Dark">
+ <item name="progressBarTheme">@style/ProgressBarTrueBlack</item>
+ <item type="attr" name="non_transparent_background">@color/black</item>
+ <item type="attr" name="overlay_background">@color/black</item>
+ <item type="attr" name="overlay_drawable">@drawable/overlay_drawable_dark_trueblack</item>
+ <item type="attr" name="dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
+ <item type="attr" name="dragview_float_background">@color/black</item>
+ <item type="attr" name="nav_drawer_background">@color/black</item>
+ <item name="android:textColorPrimary">@color/white</item>
+ <item name="android:color">@color/white</item>
+ <item name="android:colorBackground">@color/black</item>
+ <item name="android:windowBackground">@color/black</item>
+ <item name="android:actionBarStyle">@color/black</item>
+ <item name="colorPrimary">@color/black</item>
+ <item name="colorPrimaryDark">@color/black</item>
+ </style>
+
+
+ <style name="Theme.AntennaPod.Light.NoTitle" parent="Theme.Base.AntennaPod.Light.NoTitle">
+ <!-- Room for API dependent attributes -->
+ </style>
+
+ <style name="Theme.Base.AntennaPod.Light.NoTitle" parent="Theme.AppCompat.Light.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowActionModeOverlay">true</item>
<item name="progressBarTheme">@style/ProgressBarLight</item>
@@ -127,59 +195,79 @@
<item name="colorAccent">@color/holo_blue_light</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
<item name="alertDialogTheme">@style/AntennaPod.Dialog.Light</item>
- <item name="attr/action_about">@drawable/ic_info_grey600_24dp</item>
- <item name="attr/action_search">@drawable/ic_search_grey600_24dp</item>
- <item name="attr/action_stream">@drawable/ic_settings_input_antenna_grey600_24dp</item>
- <item name="attr/av_download">@drawable/ic_file_download_grey600_24dp</item>
- <item name="attr/av_fast_forward">@drawable/ic_fast_forward_grey600_24dp</item>
- <item name="attr/av_pause">@drawable/ic_pause_grey600_24dp</item>
- <item name="attr/av_play">@drawable/ic_play_arrow_grey600_24dp</item>
- <item name="attr/av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
- <item name="attr/content_discard">@drawable/ic_delete_grey600_24dp</item>
- <item name="attr/content_new">@drawable/ic_add_grey600_24dp</item>
- <item name="attr/feed">@drawable/ic_feed_grey600_24dp</item>
- <item name="attr/location_web_site">@drawable/ic_web_grey600_24dp</item>
- <item name="attr/navigation_accept">@drawable/ic_done_grey600_24dp</item>
- <item name="attr/navigation_cancel">@drawable/ic_cancel_grey600_24dp</item>
- <item name="attr/navigation_expand">@drawable/ic_expand_more_grey600_36dp</item>
- <item name="attr/navigation_refresh">@drawable/ic_refresh_grey600_24dp</item>
- <item name="attr/navigation_up">@drawable/navigation_up</item>
- <item name="attr/social_share">@drawable/ic_share_grey600_24dp</item>
- <item name="attr/stat_playlist">@drawable/ic_list_grey600_24dp</item>
- <item name="attr/type_audio">@drawable/ic_hearing_grey600_18dp</item>
- <item name="attr/type_video">@drawable/ic_remove_red_eye_grey600_18dp</item>
- <item name="attr/non_transparent_background">@color/white</item>
- <item name="attr/overlay_background">@color/overlay_light</item>
- <item name="attr/overlay_drawable">@drawable/overlay_drawable</item>
- <item name="attr/dragview_background">@drawable/ic_drag_vertical_grey600_48dp</item>
- <item name="attr/dragview_float_background">@color/white</item>
- <item name="attr/nav_drawer_background">@color/white</item>
- <item name="attr/ic_new">@drawable/ic_new_releases_grey600_24dp</item>
- <item name="attr/ic_history">@drawable/ic_history_grey600_24dp</item>
- <item name="attr/ic_folder">@drawable/ic_folder_grey600_24dp</item>
- <item name="attr/av_play_big">@drawable/ic_play_arrow_grey600_36dp</item>
- <item name="attr/av_pause_big">@drawable/ic_pause_grey600_36dp</item>
- <item name="attr/av_ff_big">@drawable/ic_fast_forward_grey600_36dp</item>
- <item name="attr/av_rew_big">@drawable/ic_fast_rewind_grey600_36dp</item>
- <item name="attr/av_skip_big">@drawable/ic_skip_grey600_36dp</item>
- <item name="attr/ic_fav">@drawable/ic_star_border_grey600_24dp</item>
- <item name="attr/ic_unfav">@drawable/ic_star_grey600_24dp</item>
- <item name="attr/ic_settings">@drawable/ic_settings_grey600_24dp</item>
- <item name="attr/ic_lock_open">@drawable/ic_lock_open_grey600_24dp</item>
- <item name="attr/ic_lock_closed">@drawable/ic_lock_closed_grey600_24dp</item>
- <item name="attr/ic_filter">@drawable/ic_filter_grey600_24dp</item>
- <item name="attr/ic_sleep">@drawable/ic_sleep_grey600_24dp</item>
- <item name="attr/ic_sleep_off">@drawable/ic_sleep_off_grey600_24dp</item>
- <item name="attr/ic_check_box">@drawable/ic_check_box_grey600_24dp</item>
- <item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
- <item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
- <item name="attr/ic_sort">@drawable/ic_sort_grey600_24dp</item>
- <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
- <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
- <item name="attr/ic_cast_disconnect">@drawable/ic_cast_disconnect_grey600_36dp</item>
+ <item type="attr" name="storage">@drawable/ic_sd_grey600_24dp</item>
+ <item type="attr" name="ic_swap">@drawable/ic_swap_vertical_grey600_24dp</item>
+ <item type="attr" name="statistics">@drawable/ic_poll_box_grey600_24dp</item>
+ <item type="attr" name="action_about">@drawable/ic_info_grey600_24dp</item>
+ <item type="attr" name="checkbox_multiple">@drawable/ic_checkbox_multiple_marked_outline_grey600_24dp</item>
+ <item type="attr" name="action_search">@drawable/ic_search_grey600_24dp</item>
+ <item type="attr" name="action_stream">@drawable/ic_settings_input_antenna_grey600_24dp</item>
+ <item type="attr" name="av_download">@drawable/ic_file_download_grey600_24dp</item>
+ <item type="attr" name="av_fast_forward">@drawable/ic_fast_forward_grey600_24dp</item>
+ <item type="attr" name="av_pause">@drawable/ic_pause_grey600_24dp</item>
+ <item type="attr" name="av_play">@drawable/ic_play_arrow_grey600_24dp</item>
+ <item type="attr" name="av_rewind">@drawable/ic_fast_rewind_grey600_24dp</item>
+ <item type="attr" name="content_discard">@drawable/ic_delete_grey600_24dp</item>
+ <item type="attr" name="content_new">@drawable/ic_add_grey600_24dp</item>
+ <item type="attr" name="feed">@drawable/ic_feed_grey600_24dp</item>
+ <item type="attr" name="location_web_site">@drawable/ic_web_grey600_24dp</item>
+ <item type="attr" name="navigation_accept">@drawable/ic_done_grey600_24dp</item>
+ <item type="attr" name="navigation_cancel">@drawable/ic_cancel_grey600_24dp</item>
+ <item type="attr" name="navigation_expand">@drawable/ic_expand_more_grey600_36dp</item>
+ <item type="attr" name="navigation_refresh">@drawable/ic_refresh_grey600_24dp</item>
+ <item type="attr" name="navigation_up">@drawable/navigation_up</item>
+ <item type="attr" name="social_share">@drawable/ic_share_grey600_24dp</item>
+ <item type="attr" name="stat_playlist">@drawable/ic_list_grey600_24dp</item>
+ <item type="attr" name="type_audio">@drawable/ic_hearing_grey600_18dp</item>
+ <item type="attr" name="type_video">@drawable/ic_remove_red_eye_grey600_18dp</item>
+ <item type="attr" name="non_transparent_background">@color/white</item>
+ <item type="attr" name="overlay_background">@color/overlay_light</item>
+ <item type="attr" name="overlay_drawable">@drawable/overlay_drawable</item>
+ <item type="attr" name="dragview_background">@drawable/ic_drag_vertical_grey600_48dp</item>
+ <item type="attr" name="dragview_float_background">@color/white</item>
+ <item type="attr" name="nav_drawer_background">@color/white</item>
+ <item type="attr" name="ic_new">@drawable/ic_new_releases_grey600_24dp</item>
+ <item type="attr" name="ic_history">@drawable/ic_history_grey600_24dp</item>
+ <item type="attr" name="ic_folder">@drawable/ic_folder_grey600_24dp</item>
+ <item type="attr" name="av_play_big">@drawable/ic_play_arrow_grey600_36dp</item>
+ <item type="attr" name="av_pause_big">@drawable/ic_pause_grey600_36dp</item>
+ <item type="attr" name="av_ff_big">@drawable/ic_fast_forward_grey600_36dp</item>
+ <item type="attr" name="av_rew_big">@drawable/ic_fast_rewind_grey600_36dp</item>
+ <item type="attr" name="av_skip_big">@drawable/ic_skip_grey600_36dp</item>
+ <item type="attr" name="ic_fav">@drawable/ic_star_border_grey600_24dp</item>
+ <item type="attr" name="ic_unfav">@drawable/ic_star_grey600_24dp</item>
+ <item type="attr" name="ic_settings">@drawable/ic_settings_grey600_24dp</item>
+ <item type="attr" name="ic_lock_open">@drawable/ic_lock_open_grey600_24dp</item>
+ <item type="attr" name="ic_lock_closed">@drawable/ic_lock_closed_grey600_24dp</item>
+ <item type="attr" name="ic_filter">@drawable/ic_filter_grey600_24dp</item>
+ <item type="attr" name="ic_sleep">@drawable/ic_sleep_grey600_24dp</item>
+ <item type="attr" name="ic_sleep_off">@drawable/ic_sleep_off_grey600_24dp</item>
+ <item type="attr" name="ic_check_box">@drawable/ic_check_box_grey600_24dp</item>
+ <item type="attr" name="ic_check_box_outline">@drawable/ic_check_box_outline_blank_grey600_24dp</item>
+ <item type="attr" name="ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_grey600_24dp</item>
+ <item type="attr" name="ic_sort">@drawable/ic_sort_grey600_24dp</item>
+ <item type="attr" name="ic_sd_storage">@drawable/ic_sd_storage_grey600_36dp</item>
+ <item type="attr" name="ic_create_new_folder">@drawable/ic_create_new_folder_grey600_24dp</item>
+ <item type="attr" name="ic_cast_disconnect">@drawable/ic_cast_disconnect_grey600_36dp</item>
+ <item type="attr" name="ic_cellphone_text">@drawable/ic_cellphone_text_grey600_24dp</item>
+ <item type="attr" name="ic_question_answer">@drawable/ic_forum_grey600_24dp</item>
+ <item type="attr" name="ic_bug">@drawable/ic_bug_grey600_24dp</item>
+ <item type="attr" name="ic_known_issues">@drawable/ic_format_list_bulleted_grey600_24dp</item>
+ <item type="attr" name="master_switch_background">@color/master_switch_background_light</item>
+ <item type="attr" name="currently_playing_background">@color/highlight_light</item>
+ <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
+
+ <item type="attr" name="about_screen_background">#e5e5e5</item>
+ <item type="attr" name="about_screen_card_background">#ffffff</item>
+ <item type="attr" name="about_screen_card_border">#d2d2d2</item>
+ <item type="attr" name="about_screen_font_color">#000000</item>
+ </style>
+
+ <style name="Theme.AntennaPod.Dark.NoTitle" parent="Theme.Base.AntennaPod.Dark.NoTitle">
+ <!-- Room for API dependent attributes -->
</style>
- <style name="Theme.AntennaPod.Dark.NoTitle" parent="Theme.AppCompat.NoActionBar">
+ <style name="Theme.Base.AntennaPod.Dark.NoTitle" parent="Theme.AppCompat.NoActionBar">
<item name="windowActionBar">false</item>
<item name="windowActionModeOverlay">true</item>
<item name="progressBarTheme">@style/ProgressBarDark</item>
@@ -187,58 +275,97 @@
<item name="colorControlNormal">@color/white</item>
<item name="buttonStyle">@style/Widget.AntennaPod.Button</item>
<item name="alertDialogTheme">@style/AntennaPod.Dialog.Dark</item>
- <item name="attr/action_about">@drawable/ic_info_white_24dp</item>
- <item name="attr/action_search">@drawable/ic_search_white_24dp</item>
- <item name="attr/action_stream">@drawable/ic_settings_input_antenna_white_24dp</item>
- <item name="attr/av_download">@drawable/ic_file_download_white_24dp</item>
- <item name="attr/av_fast_forward">@drawable/ic_fast_forward_white_24dp</item>
- <item name="attr/av_pause">@drawable/ic_pause_white_24dp</item>
- <item name="attr/av_play">@drawable/ic_play_arrow_white_24dp</item>
- <item name="attr/av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
- <item name="attr/content_discard">@drawable/ic_delete_white_24dp</item>
- <item name="attr/content_new">@drawable/ic_add_white_24dp</item>
- <item name="attr/feed">@drawable/ic_feed_white_24dp</item>
- <item name="attr/location_web_site">@drawable/ic_web_white_24dp</item>
- <item name="attr/navigation_accept">@drawable/ic_done_white_24dp</item>
- <item name="attr/navigation_cancel">@drawable/ic_cancel_white_24dp</item>
- <item name="attr/navigation_expand">@drawable/ic_expand_more_white_36dp</item>
- <item name="attr/navigation_refresh">@drawable/ic_refresh_white_24dp</item>
- <item name="attr/navigation_up">@drawable/navigation_up_dark</item>
- <item name="attr/social_share">@drawable/ic_share_white_24dp</item>
- <item name="attr/stat_playlist">@drawable/ic_list_white_24dp</item>
- <item name="attr/type_audio">@drawable/ic_hearing_white_18dp</item>
- <item name="attr/type_video">@drawable/ic_remove_red_eye_white_18dp</item>
- <item name="attr/non_transparent_background">@color/black</item>
- <item name="attr/overlay_background">@color/overlay_dark</item>
- <item name="attr/overlay_drawable">@drawable/overlay_drawable_dark</item>
- <item name="attr/dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
- <item name="attr/dragview_float_background">@color/black</item>
- <item name="attr/nav_drawer_background">#3B3B3B</item>
- <item name="attr/ic_new">@drawable/ic_new_releases_white_24dp</item>
- <item name="attr/ic_history">@drawable/ic_history_white_24dp</item>
- <item name="attr/ic_folder">@drawable/ic_folder_white_24dp</item>
- <item name="attr/av_play_big">@drawable/ic_play_arrow_white_36dp</item>
- <item name="attr/av_pause_big">@drawable/ic_pause_white_36dp</item>
- <item name="attr/av_ff_big">@drawable/ic_fast_forward_white_36dp</item>
- <item name="attr/av_rew_big">@drawable/ic_fast_rewind_white_36dp</item>
- <item name="attr/av_skip_big">@drawable/ic_skip_white_36dp</item>
- <item name="attr/ic_fav">@drawable/ic_star_border_white_24dp</item>
- <item name="attr/ic_unfav">@drawable/ic_star_white_24dp</item>
- <item name="attr/ic_settings">@drawable/ic_settings_white_24dp</item>
- <item name="attr/ic_lock_open">@drawable/ic_lock_open_white_24dp</item>
- <item name="attr/ic_lock_closed">@drawable/ic_lock_closed_white_24dp</item>
- <item name="attr/ic_filter">@drawable/ic_filter_white_24dp</item>
- <item name="attr/ic_sleep">@drawable/ic_sleep_white_24dp</item>
- <item name="attr/ic_sleep_off">@drawable/ic_sleep_off_white_24dp</item>
- <item name="attr/ic_check_box">@drawable/ic_check_box_white_24dp</item>
- <item name="attr/ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
- <item name="attr/ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
- <item name="attr/ic_sort">@drawable/ic_sort_white_24dp</item>
- <item name="attr/ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
- <item name="attr/ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
- <item name="attr/ic_cast_disconnect">@drawable/ic_cast_disconnect_white_36dp</item>
+ <item type="attr" name="storage">@drawable/ic_sd_white_24dp</item>
+ <item type="attr" name="ic_swap">@drawable/ic_swap_vertical_white_24dp</item>
+ <item type="attr" name="statistics">@drawable/ic_poll_box_white_24dp</item>
+ <item type="attr" name="action_about">@drawable/ic_info_white_24dp</item>
+ <item type="attr" name="checkbox_multiple">@drawable/ic_checkbox_multiple_marked_outline_white_24dp</item>
+ <item type="attr" name="action_search">@drawable/ic_search_white_24dp</item>
+ <item type="attr" name="action_stream">@drawable/ic_settings_input_antenna_white_24dp</item>
+ <item type="attr" name="av_download">@drawable/ic_file_download_white_24dp</item>
+ <item type="attr" name="av_fast_forward">@drawable/ic_fast_forward_white_24dp</item>
+ <item type="attr" name="av_pause">@drawable/ic_pause_white_24dp</item>
+ <item type="attr" name="av_play">@drawable/ic_play_arrow_white_24dp</item>
+ <item type="attr" name="av_rewind">@drawable/ic_fast_rewind_white_24dp</item>
+ <item type="attr" name="content_discard">@drawable/ic_delete_white_24dp</item>
+ <item type="attr" name="content_new">@drawable/ic_add_white_24dp</item>
+ <item type="attr" name="feed">@drawable/ic_feed_white_24dp</item>
+ <item type="attr" name="location_web_site">@drawable/ic_web_white_24dp</item>
+ <item type="attr" name="navigation_accept">@drawable/ic_done_white_24dp</item>
+ <item type="attr" name="navigation_cancel">@drawable/ic_cancel_white_24dp</item>
+ <item type="attr" name="navigation_expand">@drawable/ic_expand_more_white_36dp</item>
+ <item type="attr" name="navigation_refresh">@drawable/ic_refresh_white_24dp</item>
+ <item type="attr" name="navigation_up">@drawable/navigation_up_dark</item>
+ <item type="attr" name="social_share">@drawable/ic_share_white_24dp</item>
+ <item type="attr" name="stat_playlist">@drawable/ic_list_white_24dp</item>
+ <item type="attr" name="type_audio">@drawable/ic_hearing_white_18dp</item>
+ <item type="attr" name="type_video">@drawable/ic_remove_red_eye_white_18dp</item>
+ <item type="attr" name="non_transparent_background">@color/black</item>
+ <item type="attr" name="overlay_background">@color/overlay_dark</item>
+ <item type="attr" name="overlay_drawable">@drawable/overlay_drawable_dark</item>
+ <item type="attr" name="dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
+ <item type="attr" name="dragview_float_background">@color/black</item>
+ <item type="attr" name="nav_drawer_background">#3B3B3B</item>
+ <item type="attr" name="ic_new">@drawable/ic_new_releases_white_24dp</item>
+ <item type="attr" name="ic_history">@drawable/ic_history_white_24dp</item>
+ <item type="attr" name="ic_folder">@drawable/ic_folder_white_24dp</item>
+ <item type="attr" name="av_play_big">@drawable/ic_play_arrow_white_36dp</item>
+ <item type="attr" name="av_pause_big">@drawable/ic_pause_white_36dp</item>
+ <item type="attr" name="av_ff_big">@drawable/ic_fast_forward_white_36dp</item>
+ <item type="attr" name="av_rew_big">@drawable/ic_fast_rewind_white_36dp</item>
+ <item type="attr" name="av_skip_big">@drawable/ic_skip_white_36dp</item>
+ <item type="attr" name="ic_fav">@drawable/ic_star_border_white_24dp</item>
+ <item type="attr" name="ic_unfav">@drawable/ic_star_white_24dp</item>
+ <item type="attr" name="ic_settings">@drawable/ic_settings_white_24dp</item>
+ <item type="attr" name="ic_lock_open">@drawable/ic_lock_open_white_24dp</item>
+ <item type="attr" name="ic_lock_closed">@drawable/ic_lock_closed_white_24dp</item>
+ <item type="attr" name="ic_filter">@drawable/ic_filter_white_24dp</item>
+ <item type="attr" name="ic_sleep">@drawable/ic_sleep_white_24dp</item>
+ <item type="attr" name="ic_sleep_off">@drawable/ic_sleep_off_white_24dp</item>
+ <item type="attr" name="ic_check_box">@drawable/ic_check_box_white_24dp</item>
+ <item type="attr" name="ic_check_box_outline">@drawable/ic_check_box_outline_blank_white_24dp</item>
+ <item type="attr" name="ic_indeterminate_check_box">@drawable/ic_indeterminate_check_box_white_24dp</item>
+ <item type="attr" name="ic_sort">@drawable/ic_sort_white_24dp</item>
+ <item type="attr" name="ic_sd_storage">@drawable/ic_sd_storage_white_36dp</item>
+ <item type="attr" name="ic_create_new_folder">@drawable/ic_create_new_folder_white_24dp</item>
+ <item type="attr" name="ic_cast_disconnect">@drawable/ic_cast_disconnect_white_36dp</item>
+ <item type="attr" name="ic_cellphone_text">@drawable/ic_cellphone_text_white_24dp</item>
+ <item type="attr" name="ic_question_answer">@drawable/ic_baseline_question_answer_white_24dp</item>
+ <item type="attr" name="ic_bug">@drawable/ic_bug_white_24dp</item>
+ <item type="attr" name="ic_known_issues">@drawable/ic_format_list_bulleted_white_24dp</item>
+ <item type="attr" name="master_switch_background">@color/master_switch_background_dark</item>
+ <item type="attr" name="currently_playing_background">@color/highlight_dark</item>
+ <item name="preferenceTheme">@style/PreferenceThemeOverlay.v14.Material</item>
+
+ <item type="attr" name="about_screen_background">#303030</item>
+ <item type="attr" name="about_screen_card_background">#424242</item>
+ <item type="attr" name="about_screen_card_border">#262626</item>
+ <item type="attr" name="about_screen_font_color">#ffffff</item>
+ </style>
+
+ <style name="Theme.AntennaPod.TrueBlack.NoTitle" parent="Theme.Base.AntennaPod.TrueBlack.NoTitle">
+ <!-- Room for API dependent attributes -->
</style>
+ <style name="Theme.Base.AntennaPod.TrueBlack.NoTitle" parent="Theme.Base.AntennaPod.Dark.NoTitle">
+ <item name="progressBarTheme">@style/ProgressBarTrueBlack</item>
+ <item type="attr" name="non_transparent_background">@color/black</item>
+ <item type="attr" name="overlay_background">@color/black</item>
+ <item type="attr" name="overlay_drawable">@drawable/overlay_drawable_dark_trueblack</item>
+ <item type="attr" name="dragview_background">@drawable/ic_drag_vertical_white_48dp</item>
+ <item type="attr" name="dragview_float_background">@color/black</item>
+ <item type="attr" name="nav_drawer_background">@color/black</item>
+ <item type="attr" name="currently_playing_background">@color/highlight_trueblack</item>
+ <item name="android:textColorPrimary">@color/white</item>
+ <item name="android:color">@color/white</item>
+ <item name="android:colorBackground">@color/black</item>
+ <item name="android:windowBackground">@color/black</item>
+ <item name="android:actionBarStyle">@color/black</item>
+ <item name="colorPrimary">@color/black</item>
+ <item name="colorPrimaryDark">@color/black</item>
+ </style>
+
+
<style name="Theme.AntennaPod.Dark.Splash" parent="Theme.AppCompat.NoActionBar">
<item name="android:windowBackground">@drawable/bg_splash</item>
</style>
@@ -283,6 +410,7 @@
<item name="android:textSize">@dimen/text_size_micro</item>
<item name="android:textColor">@color/new_indicator_green</item>
<item name="android:text">@string/new_label</item>
+ <item name="android:textAllCaps">true</item>
</style>
<style name="Widget.AntennaPod.Button" parent="Widget.AppCompat.Button">
@@ -325,4 +453,9 @@
<item name="android:progressDrawable">@drawable/progress_bar_horizontal_dark</item>
</style>
+ <style name="ProgressBarTrueBlack">
+ <item name="android:indeterminateOnly">false</item>
+ <item name="android:progressDrawable">@drawable/progress_bar_horizontal_trueblack</item>
+ </style>
+
</resources>
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 f12f1d806..9af76cf86 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -14,6 +14,7 @@ import de.danoeh.antennapod.core.util.NetworkUtils;
* Apps using the core module of AntennaPod should register implementations of all interfaces here.
*/
public class ClientConfig {
+ private ClientConfig(){}
/**
* Should be used when setting User-Agent header for HTTP-requests.
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
index 27fb7344d..5198a76bd 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
@@ -1440,12 +1440,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
return idleReason;
}
- private void onMessageSendFailed(int errorCode) {
- for (CastConsumer consumer : castConsumers) {
- consumer.onDataMessageSendFailed(errorCode);
- }
- }
-
/*
* This is called by onStatusUpdated() of the RemoteMediaPlayer
*/
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java
index f0a7214c9..88da6a0ec 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java
@@ -13,7 +13,6 @@ import java.util.Calendar;
import java.util.List;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -24,6 +23,8 @@ import de.danoeh.antennapod.core.util.playback.Playable;
* Helper functions for Cast support.
*/
public class CastUtils {
+ private CastUtils(){}
+
private static final String TAG = "CastUtils";
public static final String KEY_MEDIA_ID = "de.danoeh.antennapod.core.cast.MediaId";
@@ -97,9 +98,9 @@ public class CastUtils {
if (subtitle != null) {
metadata.putString(MediaMetadata.KEY_SUBTITLE, subtitle);
}
- FeedImage image = feedItem.getImage();
- if (image != null && !TextUtils.isEmpty(image.getDownload_url())) {
- metadata.addImage(new WebImage(Uri.parse(image.getDownload_url())));
+
+ if (!TextUtils.isEmpty(feedItem.getImageUrl())) {
+ metadata.addImage(new WebImage(Uri.parse(feedItem.getImageUrl())));
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(media.getItem().getPubDate());
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/RemoteMedia.java b/core/src/play/java/de/danoeh/antennapod/core/cast/RemoteMedia.java
index a3ac87062..1c6dd30c4 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/RemoteMedia.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/RemoteMedia.java
@@ -12,6 +12,8 @@ import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.common.images.WebImage;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
+
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@@ -22,12 +24,9 @@ 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.storage.DBReader;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-
/**
* Playable implementation for media on a Cast Device for which a local version of
* {@link de.danoeh.antennapod.core.feed.FeedMedia} hasn't been found.
@@ -131,14 +130,6 @@ public class RemoteMedia implements Playable {
return feedUrl;
}
- public FeedMedia lookForFeedMedia() {
- FeedItem feedItem = DBReader.getFeedItem(feedUrl, itemIdentifier);
- if (feedItem == null) {
- return null;
- }
- return feedItem.getMedia();
- }
-
@Override
public void writeToPreferences(SharedPreferences.Editor prefEditor) {
//it seems pointless to do it, since the session should be kept by the remote device.
diff --git a/core/src/test/java/android/text/TextUtils.java b/core/src/test/java/android/text/TextUtils.java
new file mode 100644
index 000000000..c31234171
--- /dev/null
+++ b/core/src/test/java/android/text/TextUtils.java
@@ -0,0 +1,32 @@
+package android.text;
+
+/**
+ * A slim-down version of standard {@link android.text.TextUtils} to be used in unit tests.
+ */
+public class TextUtils {
+
+ /**
+ * Returns true if a and b are equal, including if they are both null.
+ * <p><i>Note: In platform versions 1.1 and earlier, this method only worked well if
+ * both the arguments were instances of String.</i></p>
+ * @param a first CharSequence to check
+ * @param b second CharSequence to check
+ * @return true if a and b are equal
+ */
+ public static boolean equals(CharSequence a, CharSequence b) {
+ if (a == b) return true;
+ int length;
+ if (a != null && b != null && (length = a.length()) == b.length()) {
+ if (a instanceof String && b instanceof String) {
+ return a.equals(b);
+ } else {
+ for (int i = 0; i < length; i++) {
+ if (a.charAt(i) != b.charAt(i)) return false;
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/core/src/test/java/android/util/Log.java b/core/src/test/java/android/util/Log.java
new file mode 100644
index 000000000..881d10209
--- /dev/null
+++ b/core/src/test/java/android/util/Log.java
@@ -0,0 +1,245 @@
+package android.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * A stub for {@link android.util.Log} to be used in unit tests.
+ *
+ * It outputs the log statements to standard error.
+ */
+public final class Log {
+
+ /**
+ * Priority constant for the println method; use Log.v.
+ */
+ public static final int VERBOSE = 2;
+
+ /**
+ * Priority constant for the println method; use Log.d.
+ */
+ public static final int DEBUG = 3;
+
+ /**
+ * Priority constant for the println method; use Log.i.
+ */
+ public static final int INFO = 4;
+
+ /**
+ * Priority constant for the println method; use Log.w.
+ */
+ public static final int WARN = 5;
+
+ /**
+ * Priority constant for the println method; use Log.e.
+ */
+ public static final int ERROR = 6;
+
+ /**
+ * Priority constant for the println method.
+ */
+ public static final int ASSERT = 7;
+
+ private Log() {
+ }
+
+ /**
+ * Send a {@link #VERBOSE} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ public static int v(String tag, String msg) {
+ return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
+ }
+
+ /**
+ * Send a {@link #VERBOSE} log message and log the exception.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ public static int v(String tag, String msg, Throwable tr) {
+ return printlns(LOG_ID_MAIN, VERBOSE, tag, msg, tr);
+ }
+
+ /**
+ * Send a {@link #DEBUG} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ public static int d(String tag, String msg) {
+ return println_native(LOG_ID_MAIN, DEBUG, tag, msg);
+ }
+
+ /**
+ * Send a {@link #DEBUG} log message and log the exception.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ public static int d(String tag, String msg, Throwable tr) {
+ return printlns(LOG_ID_MAIN, DEBUG, tag, msg, tr);
+ }
+
+ /**
+ * Send an {@link #INFO} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ public static int i(String tag, String msg) {
+ return println_native(LOG_ID_MAIN, INFO, tag, msg);
+ }
+
+ /**
+ * Send a {@link #INFO} log message and log the exception.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ public static int i(String tag, String msg, Throwable tr) {
+ return printlns(LOG_ID_MAIN, INFO, tag, msg, tr);
+ }
+
+ /**
+ * Send a {@link #WARN} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ public static int w(String tag, String msg) {
+ return println_native(LOG_ID_MAIN, WARN, tag, msg);
+ }
+
+ /**
+ * Send a {@link #WARN} log message and log the exception.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ public static int w(String tag, String msg, Throwable tr) {
+ return printlns(LOG_ID_MAIN, WARN, tag, msg, tr);
+ }
+
+ /**
+ * Checks to see whether or not a log for the specified tag is loggable at the specified level.
+ *
+ * @return true in all cases (for unit test environment)
+ */
+ public static boolean isLoggable(String tag, int level) {
+ return true;
+ }
+
+ /*
+ * Send a {@link #WARN} log message and log the exception.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param tr An exception to log
+ */
+ public static int w(String tag, Throwable tr) {
+ return printlns(LOG_ID_MAIN, WARN, tag, "", tr);
+ }
+
+ /**
+ * Send an {@link #ERROR} log message.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ */
+ public static int e(String tag, String msg) {
+ return println_native(LOG_ID_MAIN, ERROR, tag, msg);
+ }
+
+ /**
+ * Send a {@link #ERROR} log message and log the exception.
+ * @param tag Used to identify the source of a log message. It usually identifies
+ * the class or activity where the log call occurs.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log
+ */
+ public static int e(String tag, String msg, Throwable tr) {
+ return printlns(LOG_ID_MAIN, ERROR, tag, msg, tr);
+ }
+
+ /**
+ * What a Terrible Failure: Report a condition that should never happen.
+ * The error will always be logged at level ASSERT with the call stack.
+ * Depending on system configuration, a report may be added to the
+ * {@link android.os.DropBoxManager} and/or the process may be terminated
+ * immediately with an error dialog.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ */
+ public static int wtf(String tag, String msg) {
+ return wtf(LOG_ID_MAIN, tag, msg, null, false, false);
+ }
+
+ /**
+ * Like {@link #wtf(String, String)}, but also writes to the log the full
+ * call stack.
+ * @hide
+ */
+ public static int wtfStack(String tag, String msg) {
+ return wtf(LOG_ID_MAIN, tag, msg, null, true, false);
+ }
+
+ /**
+ * What a Terrible Failure: Report an exception that should never happen.
+ * Similar to {@link #wtf(String, String)}, with an exception to log.
+ * @param tag Used to identify the source of a log message.
+ * @param tr An exception to log.
+ */
+ public static int wtf(String tag, Throwable tr) {
+ return wtf(LOG_ID_MAIN, tag, tr.getMessage(), tr, false, false);
+ }
+
+ /**
+ * What a Terrible Failure: Report an exception that should never happen.
+ * Similar to {@link #wtf(String, Throwable)}, with a message as well.
+ * @param tag Used to identify the source of a log message.
+ * @param msg The message you would like logged.
+ * @param tr An exception to log. May be null.
+ */
+ public static int wtf(String tag, String msg, Throwable tr) {
+ return wtf(LOG_ID_MAIN, tag, msg, tr, false, false);
+ }
+
+ /**
+ * Priority Constant for wtf.
+ * Added for this custom Log implementation, not in android sources.
+ */
+ private static final int WTF = 8;
+ static int wtf(int logId, String tag, String msg, Throwable tr, boolean localStack,
+ boolean system) {
+ return printlns(LOG_ID_MAIN, WTF, tag, msg, tr);
+ }
+
+ private static final int LOG_ID_MAIN = 0;
+
+ private static final String[] PRIORITY_ABBREV = { "0", "1", "V", "D", "I", "W", "E", "A", "WTF" };
+
+ private static int println_native(int bufID, int priority, String tag, String msg) {
+ String res = PRIORITY_ABBREV[priority] + "/" + tag + " " + msg + System.lineSeparator();
+ System.err.print(res);
+ return res.length();
+ }
+
+ private static int printlns(int bufID, int priority, String tag, String msg,
+ Throwable tr) {
+ StringWriter trSW = new StringWriter();
+ if (tr != null) {
+ trSW.append(" , Exception: ");
+ PrintWriter trPW = new PrintWriter(trSW);
+ tr.printStackTrace(trPW);
+ trPW.flush();
+ }
+ return println_native(bufID, priority, tag, msg + trSW.toString());
+ }
+
+}
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedItemMother.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java
index 3d7c4fe5f..b78cecc23 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedItemMother.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java
@@ -2,14 +2,14 @@ package de.danoeh.antennapod.core.feed;
import java.util.Date;
-import static de.danoeh.antennapod.core.feed.FeedImageMother.anyFeedImage;
import static de.danoeh.antennapod.core.feed.FeedMother.anyFeed;
class FeedItemMother {
+ private static final String IMAGE_URL = "http://example.com/image";
static FeedItem anyFeedItemWithImage() {
FeedItem item = new FeedItem(0, "Item", "Item", "url", new Date(), FeedItem.PLAYED, anyFeed());
- item.setImage(anyFeedImage());
+ item.setImageUrl(IMAGE_URL);
return item;
}
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedItemTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
index 9e12e8ae0..b9cba05b4 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
@@ -1,65 +1,58 @@
package de.danoeh.antennapod.core.feed;
-import android.test.AndroidTestCase;
+import org.junit.Before;
+import org.junit.Test;
import static de.danoeh.antennapod.core.feed.FeedItemMother.anyFeedItemWithImage;
+import static org.junit.Assert.assertEquals;
-public class FeedItemTest extends AndroidTestCase {
+public class FeedItemTest {
private FeedItem original;
- private FeedImage originalImage;
private FeedItem changedFeedItem;
- @Override
- protected void setUp() {
+ @Before
+ public void setUp() {
original = anyFeedItemWithImage();
- originalImage = original.getImage();
changedFeedItem = anyFeedItemWithImage();
}
+ @Test
public void testUpdateFromOther_feedItemImageDownloadUrlChanged() throws Exception {
setNewFeedItemImageDownloadUrl();
-
original.updateFromOther(changedFeedItem);
-
- feedItemImageWasUpdated();
+ assertFeedItemImageWasUpdated();
}
+ @Test
public void testUpdateFromOther_feedItemImageRemoved() throws Exception {
feedItemImageRemoved();
-
original.updateFromOther(changedFeedItem);
-
- feedItemImageWasNotUpdated();
+ assertFeedItemImageWasNotUpdated();
}
+ @Test
public void testUpdateFromOther_feedItemImageAdded() throws Exception {
- feedItemHadNoImage();
+ original.setImageUrl(null);
setNewFeedItemImageDownloadUrl();
-
original.updateFromOther(changedFeedItem);
-
- feedItemImageWasUpdated();
- }
-
- private void feedItemHadNoImage() {
- original.setImage(null);
+ assertFeedItemImageWasUpdated();
}
private void setNewFeedItemImageDownloadUrl() {
- changedFeedItem.getImage().setDownload_url("http://example.com/new_picture");
+ changedFeedItem.setImageUrl("http://example.com/new_picture");
}
private void feedItemImageRemoved() {
- changedFeedItem.setImage(null);
+ changedFeedItem.setImageUrl(null);
}
- private void feedItemImageWasUpdated() {
- assertEquals(original.getImage().getDownload_url(), changedFeedItem.getImage().getDownload_url());
+ private void assertFeedItemImageWasUpdated() {
+ assertEquals(original.getImageUrl(), changedFeedItem.getImageUrl());
}
- private void feedItemImageWasNotUpdated() {
- assertTrue(originalImage == original.getImage());
+ private void assertFeedItemImageWasNotUpdated() {
+ assertEquals(anyFeedItemWithImage().getImageUrl(), original.getImageUrl());
}
} \ No newline at end of file
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedMother.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
index 1cea6b9c9..f46797d28 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedMother.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
@@ -1,13 +1,11 @@
package de.danoeh.antennapod.core.feed;
-import static de.danoeh.antennapod.core.feed.FeedImageMother.anyFeedImage;
-
-public class FeedMother {
+class FeedMother {
+ public static final String IMAGE_URL = "http://example.com/image";
public static Feed anyFeed() {
- FeedImage image = anyFeedImage();
return new Feed(0, null, "title", "http://example.com", "This is the description",
- "http://example.com/payment", "Daniel", "en", null, "http://example.com/feed", image,
+ "http://example.com/payment", "Daniel", "en", null, "http://example.com/feed", IMAGE_URL,
null, "http://example.com/feed", true);
}
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
index 8067ec93f..4717041f4 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/feed/FeedTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
@@ -1,63 +1,61 @@
package de.danoeh.antennapod.core.feed;
-import android.test.AndroidTestCase;
+import org.junit.Before;
+import org.junit.Test;
-import static de.danoeh.antennapod.core.feed.FeedImageMother.anyFeedImage;
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;
-public class FeedTest extends AndroidTestCase {
+public class FeedTest {
private Feed original;
- private FeedImage originalImage;
private Feed changedFeed;
- @Override
- protected void setUp() {
+ @Before
+ public void setUp() {
original = anyFeed();
- originalImage = original.getImage();
changedFeed = anyFeed();
}
+ @Test
public void testCompareWithOther_feedImageDownloadUrlChanged() throws Exception {
setNewFeedImageDownloadUrl();
-
feedHasChanged();
}
+ @Test
public void testCompareWithOther_sameFeedImage() throws Exception {
- changedFeed.setImage(anyFeedImage());
-
+ changedFeed.setImageUrl(FeedMother.IMAGE_URL);
feedHasNotChanged();
}
+ @Test
public void testCompareWithOther_feedImageRemoved() throws Exception {
feedImageRemoved();
-
feedHasNotChanged();
}
+ @Test
public void testUpdateFromOther_feedImageDownloadUrlChanged() throws Exception {
setNewFeedImageDownloadUrl();
-
original.updateFromOther(changedFeed);
-
feedImageWasUpdated();
}
+ @Test
public void testUpdateFromOther_feedImageRemoved() throws Exception {
feedImageRemoved();
-
original.updateFromOther(changedFeed);
-
feedImageWasNotUpdated();
}
+ @Test
public void testUpdateFromOther_feedImageAdded() throws Exception {
feedHadNoImage();
setNewFeedImageDownloadUrl();
-
original.updateFromOther(changedFeed);
-
feedImageWasUpdated();
}
@@ -66,11 +64,11 @@ public class FeedTest extends AndroidTestCase {
}
private void feedHadNoImage() {
- original.setImage(null);
+ original.setImageUrl(null);
}
private void setNewFeedImageDownloadUrl() {
- changedFeed.getImage().setDownload_url("http://example.com/new_picture");
+ changedFeed.setImageUrl("http://example.com/new_picture");
}
private void feedHasChanged() {
@@ -78,15 +76,15 @@ public class FeedTest extends AndroidTestCase {
}
private void feedImageRemoved() {
- changedFeed.setImage(null);
+ changedFeed.setImageUrl(null);
}
private void feedImageWasUpdated() {
- assertEquals(original.getImage().getDownload_url(), changedFeed.getImage().getDownload_url());
+ assertEquals(original.getImageUrl(), changedFeed.getImageUrl());
}
private void feedImageWasNotUpdated() {
- assertTrue(originalImage == original.getImage());
+ assertEquals(anyFeed().getImageUrl(), original.getImageUrl());
}
} \ No newline at end of file
diff --git a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/LongLongMapTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/LongLongMapTest.java
index 50c2a9c3c..0ed77eb9f 100644
--- a/core/src/androidTest/java/de/danoeh/antennapod/core/tests/util/LongLongMapTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/LongLongMapTest.java
@@ -1,11 +1,12 @@
-package de.danoeh.antennapod.core.tests.util;
+package de.danoeh.antennapod.core.util;
-import android.test.AndroidTestCase;
+import org.junit.Test;
-import de.danoeh.antennapod.core.util.LongIntMap;
+import static org.junit.Assert.assertEquals;
-public class LongLongMapTest extends AndroidTestCase {
+public class LongLongMapTest {
+ @Test
public void testEmptyMap() {
LongIntMap map = new LongIntMap();
assertEquals(0, map.size());
@@ -18,6 +19,7 @@ public class LongLongMapTest extends AndroidTestCase {
assertEquals(1, map.hashCode());
}
+ @Test
public void testSingleElement() {
LongIntMap map = new LongIntMap();
map.put(17, 42);
@@ -30,6 +32,7 @@ public class LongLongMapTest extends AndroidTestCase {
assertEquals(true, map.delete(17));
}
+ @Test
public void testAddAndDelete() {
LongIntMap map = new LongIntMap();
for(int i=0; i < 100; i++) {
@@ -46,6 +49,7 @@ public class LongLongMapTest extends AndroidTestCase {
}
}
+ @Test
public void testOverwrite() {
LongIntMap map = new LongIntMap();
map.put(17, 42);
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 736fb7d3f..29953ea14 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 52dd1f044..e0b3fb8d7 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-4.10.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-4.2-bin.zip
diff --git a/ic_launcher-web.png b/ic_launcher-web.png
index 7a7518d73..3868fc03b 100644
--- a/ic_launcher-web.png
+++ b/ic_launcher-web.png
Binary files differ
diff --git a/project.properties b/project.properties
deleted file mode 100644
index a8561fa99..000000000
--- a/project.properties
+++ /dev/null
@@ -1,13 +0,0 @@
-# This file is automatically generated by Android Tools.
-# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
-#
-# This file must be checked in Version Control Systems.
-#
-# To customize properties used by the Ant build system use,
-# "ant.properties", and override values to adapt the script to your
-# project structure.
-
-# Project target.
-proguard.config=proguard.cfg
-target=android-19
-