summaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java52
-rw-r--r--app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java44
-rw-r--r--app/src/androidTest/java/de/test/antennapod/NthMatcher.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java5
-rw-r--r--app/src/androidTest/java/de/test/antennapod/playback/PlaybackBuiltinTest.java17
-rw-r--r--app/src/androidTest/java/de/test/antennapod/playback/PlaybackExoplayerTest.java17
-rw-r--r--app/src/androidTest/java/de/test/antennapod/playback/PlaybackSonicTest.java17
-rw-r--r--app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java326
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java210
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java68
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java166
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBQueueCleanupAlgorithmTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java95
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java34
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java35
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java37
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java278
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java272
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java346
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java24
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java6
-rw-r--r--app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java12
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java130
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java2
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java46
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java4
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/service/download/HTTPBin.java14
-rw-r--r--app/src/androidTest/java/de/test/antennapod/util/syndication/FeedDiscovererTest.java3
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java205
-rw-r--r--app/src/main/AndroidManifest.xml49
-rw-r--r--app/src/main/assets/developers.csv119
-rw-r--r--app/src/main/assets/licenses.xml105
-rwxr-xr-xapp/src/main/assets/logo.pngbin60183 -> 0 bytes
-rw-r--r--app/src/main/assets/translators.csv46
-rw-r--r--app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java22
-rw-r--r--app/src/main/java/de/danoeh/antennapod/PodcastApp.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java160
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java55
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java1
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java225
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java130
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java117
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java156
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java45
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java181
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java145
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java153
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java137
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java107
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java3
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java31
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java46
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java59
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java133
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java49
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java15
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java24
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java21
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java72
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java112
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java51
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java47
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java122
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java73
-rw-r--r--app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java63
-rw-r--r--app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java41
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java30
-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.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java152
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java12
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java226
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java142
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java112
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java20
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java247
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java193
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java34
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java197
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java36
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java81
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/TransitionEffect.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java10
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java9
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java7
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java65
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java56
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java126
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java64
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java88
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java11
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java8
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java77
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java33
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java65
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java180
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java195
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java97
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java44
-rw-r--r--app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java18
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java25
-rw-r--r--app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java27
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/PieChartView.java121
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java4
-rw-r--r--app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java2
-rw-r--r--app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java30
-rw-r--r--app/src/main/play/listings/de-DE/full-description.txt65
-rw-r--r--app/src/main/play/listings/de-DE/graphics/phone-screenshots/1.pngbin0 -> 988741 bytes
-rw-r--r--app/src/main/play/listings/de-DE/graphics/phone-screenshots/2.pngbin0 -> 995809 bytes
-rw-r--r--app/src/main/play/listings/de-DE/graphics/phone-screenshots/3.pngbin0 -> 254192 bytes
-rw-r--r--app/src/main/play/listings/de-DE/graphics/phone-screenshots/4.pngbin0 -> 495972 bytes
-rw-r--r--app/src/main/play/listings/de-DE/graphics/phone-screenshots/5.pngbin0 -> 473491 bytes
-rw-r--r--app/src/main/play/listings/de-DE/graphics/phone-screenshots/6.pngbin0 -> 704178 bytes
-rw-r--r--app/src/main/play/listings/en-US/full-description.txt63
-rw-r--r--app/src/main/play/listings/en-US/graphics/icon/icon.png (renamed from app/src/main/play/listings/en-US/graphics/icon/icon_play.png)bin25302 -> 25302 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/00.pngbin300470 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/01.pngbin385379 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/02.pngbin150991 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/03.pngbin378048 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/04.pngbin167307 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/05.pngbin93763 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/06.pngbin173137 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/07.pngbin54530 -> 0 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/1.pngbin0 -> 952560 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/2.pngbin0 -> 97710 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/3.pngbin0 -> 250923 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/4.pngbin0 -> 502083 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/5.pngbin0 -> 380087 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/phone-screenshots/6.pngbin0 -> 759276 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/promo-graphic/promo-graphic.pngbin0 -> 10717 bytes
-rw-r--r--app/src/main/play/listings/en-US/graphics/tv-banner/tv-banner.pngbin0 -> 81268 bytes
-rw-r--r--app/src/main/play/listings/es-ES/full-description.txt53
-rw-r--r--app/src/main/play/listings/fr-FR/full-description.txt65
-rw-r--r--app/src/main/play/listings/fr-FR/graphics/phone-screenshots/1.pngbin0 -> 951594 bytes
-rw-r--r--app/src/main/play/listings/fr-FR/graphics/phone-screenshots/2.pngbin0 -> 96555 bytes
-rw-r--r--app/src/main/play/listings/fr-FR/graphics/phone-screenshots/3.pngbin0 -> 254441 bytes
-rw-r--r--app/src/main/play/listings/fr-FR/graphics/phone-screenshots/4.pngbin0 -> 511650 bytes
-rw-r--r--app/src/main/play/listings/fr-FR/graphics/phone-screenshots/5.pngbin0 -> 379047 bytes
-rw-r--r--app/src/main/play/listings/fr-FR/graphics/phone-screenshots/6.pngbin0 -> 726651 bytes
-rw-r--r--app/src/main/play/release-notes/en-US/default.txt15
-rw-r--r--app/src/main/res/anim/card_flip_left_in.xml25
-rw-r--r--app/src/main/res/anim/card_flip_left_out.xml18
-rw-r--r--app/src/main/res/anim/card_flip_right_in.xml25
-rw-r--r--app/src/main/res/anim/card_flip_right_out.xml18
-rw-r--r--app/src/main/res/layout/about_teaser.xml7
-rw-r--r--app/src/main/res/layout/activity_widget_config.xml80
-rw-r--r--app/src/main/res/layout/addfeed.xml21
-rw-r--r--app/src/main/res/layout/all_episodes_fragment.xml2
-rw-r--r--app/src/main/res/layout/audio_controls.xml3
-rw-r--r--app/src/main/res/layout/bug_report.xml28
-rw-r--r--app/src/main/res/layout/checkbox_do_not_show_again.xml17
-rw-r--r--app/src/main/res/layout/choose_data_folder_dialog.xml12
-rw-r--r--app/src/main/res/layout/choose_data_folder_dialog_entry.xml7
-rw-r--r--app/src/main/res/layout/downloaded_episodeslist_item.xml2
-rw-r--r--app/src/main/res/layout/edit_text_dialog.xml15
-rw-r--r--app/src/main/res/layout/feedinfo.xml4
-rw-r--r--app/src/main/res/layout/feeditem_fragment.xml43
-rw-r--r--app/src/main/res/layout/feeditem_pager_fragment.xml6
-rw-r--r--app/src/main/res/layout/feeditemlist_header.xml2
-rw-r--r--app/src/main/res/layout/fragment_itunes_search.xml2
-rw-r--r--app/src/main/res/layout/fragment_subscriptions.xml18
-rw-r--r--app/src/main/res/layout/gpodnet_podcast_listitem.xml2
-rw-r--r--app/src/main/res/layout/itunes_podcast_listitem.xml2
-rw-r--r--app/src/main/res/layout/main.xml6
-rw-r--r--app/src/main/res/layout/mediaplayerinfo_activity.xml41
-rw-r--r--app/src/main/res/layout/nav_feedlistitem.xml2
-rw-r--r--app/src/main/res/layout/numberpicker.xml2
-rw-r--r--app/src/main/res/layout/onlinefeedview_activity.xml63
-rw-r--r--app/src/main/res/layout/onlinefeedview_header.xml24
-rw-r--r--app/src/main/res/layout/pager_fragment.xml4
-rw-r--r--app/src/main/res/layout/proxy_settings.xml6
-rw-r--r--app/src/main/res/layout/queue_fragment.xml2
-rw-r--r--app/src/main/res/layout/queue_listitem.xml2
-rw-r--r--app/src/main/res/layout/quick_feed_discovery.xml39
-rw-r--r--app/src/main/res/layout/quick_feed_discovery_item.xml6
-rw-r--r--app/src/main/res/layout/searchlist_item.xml2
-rw-r--r--app/src/main/res/layout/simple_icon_list_item.xml41
-rw-r--r--app/src/main/res/layout/statistics_activity.xml55
-rw-r--r--app/src/main/res/layout/statistics_listitem.xml96
-rw-r--r--app/src/main/res/layout/statistics_listitem_total_time.xml42
-rw-r--r--app/src/main/res/layout/time_dialog.xml9
-rw-r--r--app/src/main/res/layout/videoplayer_activity.xml13
-rw-r--r--app/src/main/res/menu/allepisodes_context.xml86
-rw-r--r--app/src/main/res/menu/episodes.xml6
-rw-r--r--app/src/main/res/menu/feedinfo.xml2
-rw-r--r--app/src/main/res/menu/feeditem_options.xml5
-rw-r--r--app/src/main/res/menu/feeditemlist_context.xml5
-rw-r--r--app/src/main/res/menu/feedlist.xml23
-rw-r--r--app/src/main/res/menu/gpodder_podcasts.xml2
-rw-r--r--app/src/main/res/menu/itunes_search.xml2
-rw-r--r--app/src/main/res/menu/mediaplayer.xml8
-rw-r--r--app/src/main/res/menu/queue.xml2
-rw-r--r--app/src/main/res/menu/queue_context.xml73
-rw-r--r--app/src/main/res/menu/statistics.xml6
-rw-r--r--app/src/main/res/menu/subscriptions.xml7
-rw-r--r--app/src/main/res/values/integers.xml5
-rw-r--r--app/src/main/res/xml/feed_settings.xml9
-rw-r--r--app/src/main/res/xml/player_widget_info.xml3
-rw-r--r--app/src/main/res/xml/preferences.xml14
-rw-r--r--app/src/main/res/xml/preferences_about.xml33
-rw-r--r--app/src/main/res/xml/preferences_autodownload.xml6
-rw-r--r--app/src/main/res/xml/preferences_playback.xml30
-rw-r--r--app/src/main/res/xml/preferences_user_interface.xml18
-rw-r--r--app/src/main/templates/about.html183
-rw-r--r--app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java60
-rw-r--r--app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java6
-rw-r--r--app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java38
-rw-r--r--app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java4
-rw-r--r--app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java15
272 files changed, 6108 insertions, 4492 deletions
diff --git a/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java b/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
new file mode 100644
index 000000000..e7fe079a6
--- /dev/null
+++ b/app/src/androidTest/java/de/danoeh/antennapod/core/service/download/StubDownloader.java
@@ -0,0 +1,52 @@
+package de.danoeh.antennapod.core.service.download;
+
+import androidx.annotation.NonNull;
+
+import de.danoeh.antennapod.core.util.Consumer;
+
+public class StubDownloader extends Downloader {
+
+ private final long downloadTime;
+
+ @NonNull
+ private final Consumer<DownloadStatus> onDownloadComplete;
+
+ public StubDownloader(@NonNull DownloadRequest request, long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
+ super(request);
+ this.downloadTime = downloadTime;
+ this.onDownloadComplete = onDownloadComplete;
+ }
+
+ @Override
+ protected void download() {
+ try {
+ Thread.sleep(downloadTime);
+ } catch (Throwable t) {
+ t.printStackTrace();
+ }
+ onDownloadComplete.accept(result);
+ }
+
+ @NonNull
+ @Override
+ public DownloadRequest getDownloadRequest() {
+ return super.getDownloadRequest();
+ }
+
+ @NonNull
+ @Override
+ public DownloadStatus getResult() {
+ return super.getResult();
+ }
+
+ @Override
+ public boolean isFinished() {
+ return super.isFinished();
+ }
+
+ @Override
+ public void cancel() {
+ super.cancel();
+ result.setCancelled();
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
index 9e86275fc..c76cd2032 100644
--- a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java
@@ -1,33 +1,33 @@
package de.test.antennapod;
import android.content.Context;
-import android.content.SharedPreferences;
-import android.support.annotation.StringRes;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.PerformException;
-import android.support.test.espresso.UiController;
-import android.support.test.espresso.ViewAction;
-import android.support.test.espresso.contrib.DrawerActions;
-import android.support.test.espresso.contrib.RecyclerViewActions;
-import android.support.test.espresso.util.HumanReadables;
-import android.support.test.espresso.util.TreeIterables;
+import androidx.annotation.StringRes;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.PerformException;
+import androidx.test.espresso.UiController;
+import androidx.test.espresso.ViewAction;
+import androidx.test.espresso.ViewInteraction;
+import androidx.test.espresso.contrib.DrawerActions;
+import androidx.test.espresso.contrib.RecyclerViewActions;
+import androidx.test.espresso.util.HumanReadables;
+import androidx.test.espresso.util.TreeIterables;
import android.view.View;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.dialog.RatingDialog;
-import de.danoeh.antennapod.fragment.QueueFragment;
import org.hamcrest.Matcher;
import java.io.File;
import java.util.concurrent.TimeoutException;
-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.isRoot;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
public class EspressoTestUtils {
/**
@@ -115,8 +115,10 @@ public class EspressoTestUtils {
}
public static void clickPreference(@StringRes int title) {
- onView(withId(R.id.list)).perform(
- RecyclerViewActions.actionOnItem(hasDescendant(withText(title)),
+ onView(withId(R.id.recycler_view)).perform(
+ RecyclerViewActions.actionOnItem(
+ allOf(hasDescendant(withText(title)),
+ hasDescendant(withId(android.R.id.widget_frame))),
click()));
}
@@ -129,4 +131,8 @@ public class EspressoTestUtils {
onView(isRoot()).perform(waitForView(withId(R.id.drawer_layout), 1000));
onView(withId(R.id.drawer_layout)).perform(DrawerActions.close());
}
+
+ public static ViewInteraction onDrawerItem(Matcher<View> viewMatcher) {
+ return onView(allOf(viewMatcher, withId(R.id.txtvTitle)));
+ }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/NthMatcher.java b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
index f9ecacda5..3f2b83a26 100644
--- a/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
+++ b/app/src/androidTest/java/de/test/antennapod/NthMatcher.java
@@ -15,7 +15,7 @@ public class NthMatcher {
return nth(matcher, 2);
}
- private static <T> Matcher<T> nth(final Matcher<T> matcher, final int index) {
+ public static <T> Matcher<T> nth(final Matcher<T> matcher, final int index) {
return new BaseMatcher<T>() {
AtomicInteger count = new AtomicInteger(0);
diff --git a/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
index e8a87ecf1..b3c79367f 100644
--- a/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/entities/ExternalMediaTest.java
@@ -4,9 +4,9 @@ import android.annotation.SuppressLint;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import org.junit.After;
diff --git a/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java b/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
index 8cd9b0fa1..4b81a4f2b 100644
--- a/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/feed/FeedFilterTest.java
@@ -1,6 +1,6 @@
package de.test.antennapod.feed;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.FeedFilter;
import de.danoeh.antennapod.core.feed.FeedItem;
import org.junit.Test;
diff --git a/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java b/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
index 9779b32a5..0b9a67d0a 100644
--- a/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/feed/FeedItemTest.java
@@ -1,6 +1,6 @@
package de.test.antennapod.feed;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.FeedItem;
import org.junit.Test;
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 91e31e73c..161393b8a 100644
--- a/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/gpodnet/GPodnetServiceTest.java
@@ -4,7 +4,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.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;
@@ -29,7 +29,7 @@ public class GPodnetServiceTest {
private static final String PW = "";
@Before
- protected void setUp() {
+ public void setUp() {
service = new GpodnetService();
}
diff --git a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
index dfb78d5a9..4f6bf0c1f 100644
--- a/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/handler/FeedHandlerTest.java
@@ -1,8 +1,8 @@
package de.test.antennapod.handler;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -12,7 +12,6 @@ import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
-import java.nio.file.Files;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
diff --git a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackBuiltinTest.java b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackBuiltinTest.java
new file mode 100644
index 000000000..a80ee41d7
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackBuiltinTest.java
@@ -0,0 +1,17 @@
+package de.test.antennapod.playback;
+
+import androidx.test.filters.LargeTest;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import org.junit.Before;
+
+/**
+ * Test cases for starting and ending playback from the MainActivity and AudioPlayerActivity.
+ */
+@LargeTest
+public class PlaybackBuiltinTest extends PlaybackTest {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ UserPreferences.enableBuiltin();
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackExoplayerTest.java b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackExoplayerTest.java
new file mode 100644
index 000000000..0cf73f069
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackExoplayerTest.java
@@ -0,0 +1,17 @@
+package de.test.antennapod.playback;
+
+import androidx.test.filters.LargeTest;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import org.junit.Before;
+
+/**
+ * Test cases for starting and ending playback from the MainActivity and AudioPlayerActivity.
+ */
+@LargeTest
+public class PlaybackExoplayerTest extends PlaybackTest {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ UserPreferences.enableExoplayer();
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackSonicTest.java
new file mode 100644
index 000000000..a0fb74809
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackSonicTest.java
@@ -0,0 +1,17 @@
+package de.test.antennapod.playback;
+
+import androidx.test.filters.LargeTest;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import org.junit.Before;
+
+/**
+ * Test cases for starting and ending playback from the MainActivity and AudioPlayerActivity.
+ */
+@LargeTest
+public class PlaybackSonicTest extends PlaybackTest {
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ UserPreferences.enableSonic();
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
new file mode 100644
index 000000000..f9cd3aeeb
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/playback/PlaybackTest.java
@@ -0,0 +1,326 @@
+package de.test.antennapod.playback;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.service.playback.PlayerStatus;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.test.antennapod.EspressoTestUtils;
+import de.test.antennapod.ui.UITestUtils;
+import org.awaitility.Awaitility;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
+import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static de.test.antennapod.NthMatcher.first;
+import static de.test.antennapod.NthMatcher.nth;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.hamcrest.Matchers.hasItems;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+
+/**
+ * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
+ */
+public abstract class PlaybackTest {
+
+ @Rule
+ public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
+
+ private UITestUtils uiTestUtils;
+ protected Context context;
+
+ @Before
+ public void setUp() throws Exception {
+ context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ EspressoTestUtils.clearPreferences();
+ EspressoTestUtils.clearDatabase();
+ EspressoTestUtils.makeNotFirstRun();
+
+ uiTestUtils = new UITestUtils(context);
+ uiTestUtils.setup();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ activityTestRule.finishActivity();
+ uiTestUtils.tearDown();
+ // shut down playback service
+ context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ Awaitility.await().until(() -> !PlaybackService.isRunning);
+ }
+
+ @Test
+ public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ List<FeedItem> queue = DBReader.getQueue();
+ final FeedItem first = queue.get(0);
+ playFromQueue(0);
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> {
+ if (uiTestUtils.getPlaybackController(getActivity()).getStatus()
+ != PlayerStatus.PLAYING) {
+ return true;
+ } else if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ != first.getMedia().getId();
+ } else {
+ return true;
+ }
+ });
+
+ Thread.sleep(1000);
+ assertNotEquals(PlayerStatus.PLAYING, uiTestUtils.getPlaybackController(getActivity()).getStatus());
+ }
+
+ @Test
+ public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
+ setContinuousPlaybackPreference(true);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+
+ List<FeedItem> queue = DBReader.getQueue();
+ final FeedItem first = queue.get(0);
+ final FeedItem second = queue.get(1);
+
+ playFromQueue(0);
+ Awaitility.await().atMost(2, TimeUnit.SECONDS).until(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ == first.getMedia().getId();
+ } else {
+ return false;
+ }
+ });
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId()
+ == second.getMedia().getId();
+ } else {
+ return false;
+ }
+ });
+ }
+
+
+ @Test
+ public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
+ replayEpisodeCheck(true);
+ }
+
+ @Test
+ public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
+ replayEpisodeCheck(false);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Skip_Average() throws Exception {
+ doTestSmartMarkAsPlayed_Skip_ForEpisode(0);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Skip_LastEpisodeInQueue() throws Exception {
+ doTestSmartMarkAsPlayed_Skip_ForEpisode(-1);
+ }
+
+ @Test
+ public void testSmartMarkAsPlayed_Pause_WontAffectItem() throws Exception {
+ setSmartMarkAsPlayedPreference(60);
+
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+
+ final int fiIdx = 0;
+ final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
+
+ playFromQueue(fiIdx);
+
+ // let playback run a bit then pause
+ Awaitility.await()
+ .atMost(1000, MILLISECONDS)
+ .until(() -> PlayerStatus.PLAYING == uiTestUtils.getPlaybackController(getActivity()).getStatus());
+ pauseEpisode();
+ Awaitility.await()
+ .atMost(1000, MILLISECONDS)
+ .until(() -> PlayerStatus.PAUSED == uiTestUtils.getPlaybackController(getActivity()).getStatus());
+
+ assertThat("Ensure even with smart mark as play, after pause, the item remains in the queue.",
+ DBReader.getQueue(), hasItems(feedItem));
+ assertThat("Ensure even with smart mark as play, after pause, the item played status remains false.",
+ DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(false));
+ }
+
+ @Test
+ public void testStartLocal() throws Exception {
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ @Test
+ public void testContinousPlaybackOffSingleEpisode() throws Exception {
+ setContinuousPlaybackPreference(false);
+ uiTestUtils.addLocalFeedData(true);
+ activityTestRule.launchActivity(new Intent());
+ DBWriter.clearQueue().get();
+ startLocalPlayback();
+ }
+
+ protected MainActivity getActivity() {
+ return activityTestRule.getActivity();
+ }
+
+ protected void setContinuousPlaybackPreference(boolean value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
+ }
+
+ protected void setSkipKeepsEpisodePreference(boolean value) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putBoolean(UserPreferences.PREF_SKIP_KEEPS_EPISODE, value).commit();
+ }
+
+ protected void setSmartMarkAsPlayedPreference(int smartMarkAsPlayedSecs) {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ prefs.edit().putString(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS,
+ Integer.toString(smartMarkAsPlayedSecs, 10))
+ .commit();
+ }
+
+ private void skipEpisode() {
+ Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
+ context.sendBroadcast(skipIntent);
+ }
+
+ protected void pauseEpisode() {
+ Intent pauseIntent = new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ context.sendBroadcast(pauseIntent);
+ }
+
+ protected void startLocalPlayback() {
+ openNavDrawer();
+ onDrawerItem(withText(R.string.episodes_label)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(R.id.emptyViewTitle), 1000));
+ onView(withText(R.string.all_episodes_short_label)).perform(click());
+
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
+ onView(isRoot()).perform(waitForView(withId(R.id.butSecondaryAction), 1000));
+
+ onView(first(withId(R.id.butSecondaryAction))).perform(click());
+ long mediaId = episodes.get(0).getMedia().getId();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ });
+ }
+
+ /**
+ *
+ * @param itemIdx The 0-based index of the episode to be played in the queue.
+ */
+ protected void playFromQueue(int itemIdx) {
+ final List<FeedItem> queue = DBReader.getQueue();
+
+ onView(nth(withId(R.id.butSecondaryAction), itemIdx + 1)).perform(click());
+ onView(isRoot()).perform(waitForView(withId(R.id.butPlay), 1000));
+ long mediaId = queue.get(itemIdx).getMedia().getId();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ });
+ }
+
+ /**
+ * Check if an episode can be played twice without problems.
+ */
+ protected void replayEpisodeCheck(boolean followQueue) throws Exception {
+ setContinuousPlaybackPreference(followQueue);
+ uiTestUtils.addLocalFeedData(true);
+ DBWriter.clearQueue().get();
+ activityTestRule.launchActivity(new Intent());
+ final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
+
+ startLocalPlayback();
+ long mediaId = episodes.get(0).getMedia().getId();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ });
+
+ Awaitility.await().atMost(5, TimeUnit.SECONDS).until(() ->
+ uiTestUtils.getCurrentMedia(getActivity()) == null
+ || uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId);
+
+ startLocalPlayback();
+ Awaitility.await().atMost(1, TimeUnit.SECONDS).until(() -> {
+ if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
+ return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
+ } else {
+ return false;
+ }
+ });
+ }
+
+ protected void doTestSmartMarkAsPlayed_Skip_ForEpisode(int itemIdxNegAllowed) throws Exception {
+ setSmartMarkAsPlayedPreference(60);
+ // ensure when an episode is skipped, it is removed due to smart as played
+ setSkipKeepsEpisodePreference(false);
+ uiTestUtils.addLocalFeedData(true);
+
+ int fiIdx;
+ if (itemIdxNegAllowed >= 0) {
+ fiIdx = itemIdxNegAllowed;
+ } else { // negative index: count from the end, with -1 being the last one, etc.
+ fiIdx = DBReader.getQueue().size() + itemIdxNegAllowed;
+ }
+ final FeedItem feedItem = DBReader.getQueue().get(fiIdx);
+
+ activityTestRule.launchActivity(new Intent());
+ playFromQueue(fiIdx);
+
+ skipEpisode();
+
+ // assert item no longer in queue (needs to wait till skip is asynchronously processed)
+ Awaitility.await()
+ .atMost(1000, MILLISECONDS)
+ .untilAsserted(() -> {
+ assertThat("Ensure smart mark as play will lead to the item removed from the queue",
+ DBReader.getQueue(), not(hasItems(feedItem)));
+ });
+ assertThat(DBReader.getFeedItem(feedItem.getId()).isPlayed(), is(true));
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
new file mode 100644
index 000000000..d0e14d70a
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/DownloadServiceTest.java
@@ -0,0 +1,210 @@
+package de.test.antennapod.service.download;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.runner.AndroidJUnit4;
+
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadRequest;
+import de.danoeh.antennapod.core.service.download.DownloadService;
+import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.service.download.DownloaderFactory;
+import de.danoeh.antennapod.core.service.download.StubDownloader;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Consumer;
+
+import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @see HttpDownloaderTest for the test of actual download (and saving the file)
+ */
+@RunWith(AndroidJUnit4.class)
+public class DownloadServiceTest {
+
+ private CountDownLatch latch = null;
+ private Feed testFeed = null;
+ private FeedMedia testMedia11 = null;
+
+ private DownloaderFactory origFactory = null;
+
+ @Before
+ public void setUp() throws Exception {
+ origFactory = DownloadService.getDownloaderFactory();
+ testFeed = setUpTestFeeds();
+ testMedia11 = testFeed.getItemAtIndex(0).getMedia();
+ }
+
+ private Feed setUpTestFeeds() throws Exception {
+ // To avoid complication in case of test failures, leaving behind orphaned
+ // media files: add a timestamp so that each test run will have its own directory for media files.
+ Feed feed = new Feed("url", null, "Test Feed title 1 " + System.currentTimeMillis());
+ List<FeedItem> items = new ArrayList<>();
+ feed.setItems(items);
+ FeedItem item1 = new FeedItem(0, "Item 1-1", "Item 1-1", "url", new Date(), FeedItem.NEW, feed);
+ items.add(item1);
+ FeedMedia media1 = new FeedMedia(0, item1, 123, 1, 1, "audio/mp3", null, "http://example.com/episode.mp3", false, null, 0, 0);
+ item1.setMedia(media1);
+
+ DBWriter.setFeedItem(item1).get();
+ return feed;
+ }
+
+
+ @After
+ public void tearDown() throws Exception {
+ DownloadService.setDownloaderFactory(origFactory);
+ }
+
+ @Test
+ public void testEventsGeneratedCaseMediaDownloadSuccess_noEnqueue() throws Exception {
+ doTestEventsGeneratedCaseMediaDownloadSuccess(false, 1);
+ }
+
+ @Test
+ public void testEventsGeneratedCaseMediaDownloadSuccess_withEnqueue() throws Exception {
+ // enqueue itself generates additional FeedItem event
+ doTestEventsGeneratedCaseMediaDownloadSuccess(true, 2);
+ }
+
+ private void doTestEventsGeneratedCaseMediaDownloadSuccess(boolean enqueueDownloaded,
+ int numEventsExpected)
+ throws Exception {
+ // create a stub download that returns successful
+ //
+ // OPEN: Ideally, I'd like the download time long enough so that multiple in-progress DownloadEvents
+ // are generated (to simulate typical download), but it'll make download time quite long (1-2 seconds)
+ // to do so
+ DownloadService.setDownloaderFactory(new StubDownloaderFactory(50, downloadStatus -> {
+ downloadStatus.setSuccessful();
+ }));
+
+ UserPreferences.setEnqueueDownloadedEpisodes(enqueueDownloaded);
+ withFeedItemEventListener(feedItemEventListener -> {
+ try {
+ assertEquals(0, feedItemEventListener.getEvents().size());
+ assertFalse("The media in test should not yet been downloaded",
+ DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+
+ DownloadRequester.getInstance().downloadMedia(false, InstrumentationRegistry.getTargetContext(),
+ testMedia11.getItem());Awaitility.await()
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() >= numEventsExpected);
+ assertTrue("After media download has completed, FeedMedia object in db should indicate so.",
+ DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+ assertEquals("The FeedItem should have been " + (enqueueDownloaded ? "" : "not ") + "enqueued",
+ enqueueDownloaded,
+ DBReader.getQueueIDList().contains(testMedia11.getItem().getId()));
+ } catch (ConditionTimeoutException cte) {
+ fail("The expected FeedItemEvent (for media download complete) has not been posted. "
+ + cte.getMessage());
+ }
+ });
+ }
+
+ @Test
+ public void testCancelDownload_UndoEnqueue_Normal() throws Exception {
+ doTestCancelDownload_UndoEnqueue(false);
+ }
+
+ @Test
+ public void testCancelDownload_UndoEnqueue_AlreadyInQueue() throws Exception {
+ doTestCancelDownload_UndoEnqueue(true);
+ }
+
+ private void doTestCancelDownload_UndoEnqueue(boolean itemAlreadyInQueue) throws Exception {
+ // let download takes longer to ensure the test can cancel the download in time
+ DownloadService.setDownloaderFactory(new StubDownloaderFactory(150, downloadStatus -> {
+ downloadStatus.setSuccessful();
+ }));
+ UserPreferences.setEnqueueDownloadedEpisodes(true);
+ UserPreferences.setEnableAutodownload(false);
+
+ final long item1Id = testMedia11.getItem().getId();
+ if (itemAlreadyInQueue) {
+ // simulate item already in queue condition
+ DBWriter.addQueueItem(InstrumentationRegistry.getTargetContext(), false, item1Id).get();
+ assertTrue(DBReader.getQueueIDList().contains(item1Id));
+ } else {
+ assertFalse(DBReader.getQueueIDList().contains(item1Id));
+ }
+
+ withFeedItemEventListener(feedItemEventListener -> {
+ try {
+ DownloadRequester.getInstance().downloadMedia(false, InstrumentationRegistry.getTargetContext(),
+ testMedia11.getItem());
+ if (itemAlreadyInQueue) {
+ Awaitility.await("download service receives the request - "
+ + "no event is expected before cancel is issued")
+ .atLeast(100, TimeUnit.MILLISECONDS)
+ .until(() -> true);
+ } else {
+ Awaitility.await("item enqueue event")
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() >= 1);
+ }
+ DownloadRequester.getInstance().cancelDownload(InstrumentationRegistry.getTargetContext(),
+ testMedia11);
+ final int totalNumEventsExpected = itemAlreadyInQueue ? 1 : 3;
+ Awaitility.await("item dequeue event + download termination event")
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() ->feedItemEventListener.getEvents().size() >= totalNumEventsExpected);
+ assertFalse("The download should have been canceled",
+ DBReader.getFeedMedia(testMedia11.getId()).isDownloaded());
+ if (itemAlreadyInQueue) {
+ assertTrue("The FeedItem should still be in the queue after the download is cancelled."
+ + " It's there before download.",
+ DBReader.getQueueIDList().contains(item1Id));
+ } else {
+ assertFalse("The FeedItem should not be in the queue after the download is cancelled.",
+ DBReader.getQueueIDList().contains(item1Id));
+ }
+ } catch (ConditionTimeoutException cte) {
+ fail("The expected FeedItemEvent (for media download complete) has not been posted. "
+ + cte.getMessage());
+ }
+ });
+ }
+
+ private static class StubDownloaderFactory implements DownloaderFactory {
+ private final long downloadTime;
+
+ @NonNull
+ private final Consumer<DownloadStatus> onDownloadComplete;
+
+ StubDownloaderFactory(long downloadTime, @NonNull Consumer<DownloadStatus> onDownloadComplete) {
+ this.downloadTime = downloadTime;
+ this.onDownloadComplete = onDownloadComplete;
+ }
+
+ @Nullable
+ @Override
+ public Downloader create(@NonNull DownloadRequest request) {
+ return new StubDownloader(request, downloadTime, onDownloadComplete);
+ }
+ }
+
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
index 967bf431c..4530b7679 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/download/HttpDownloaderTest.java
@@ -1,7 +1,7 @@
package de.test.antennapod.service.download;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
import android.util.Log;
import java.io.File;
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java b/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
index 085e2c479..6bd519228 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/CancelablePSMPCallback.java
@@ -1,6 +1,6 @@
package de.test.antennapod.service.playback;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.util.playback.Playable;
diff --git a/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java b/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
index 113239908..5a16ab954 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/DefaultPSMPCallback.java
@@ -1,7 +1,7 @@
package de.test.antennapod.service.playback;
-import android.support.annotation.NonNull;
-import android.support.annotation.StringRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.util.playback.Playable;
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 502115cc3..c43818a73 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceMediaPlayerTest.java
@@ -2,8 +2,8 @@ package de.test.antennapod.service.playback;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.MediumTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
import de.test.antennapod.EspressoTestUtils;
import junit.framework.AssertionFailedError;
@@ -32,7 +32,7 @@ import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
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 9c0e90929..dbc86a228 100644
--- a/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/service/playback/PlaybackServiceTaskManagerTest.java
@@ -1,9 +1,15 @@
package de.test.antennapod.service.playback;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.LargeTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.annotation.UiThreadTest;
+import androidx.test.filters.LargeTest;
+
+import org.awaitility.Awaitility;
+import org.greenrobot.eventbus.EventBus;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import java.util.ArrayList;
import java.util.Date;
@@ -12,17 +18,16 @@ import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceTaskManager;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.playback.Playable;
-import org.greenrobot.eventbus.EventBus;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import static de.test.antennapod.util.event.FeedItemEventListener.withFeedItemEventListener;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -100,18 +105,8 @@ public class PlaybackServiceTaskManagerTest {
assertNotNull(testQueue);
assertTrue(testQueue.isEmpty());
-
- final CountDownLatch countDownLatch = new CountDownLatch(1);
- EventDistributor.EventListener queueListener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- countDownLatch.countDown();
- }
- };
- EventDistributor.getInstance().register(queueListener);
List<FeedItem> queue = writeTestQueue("a");
EventBus.getDefault().post(QueueEvent.setQueue(queue));
- countDownLatch.await(5000, TimeUnit.MILLISECONDS);
assertNotNull(queue);
testQueue = pstm.getQueue();
@@ -124,6 +119,43 @@ public class PlaybackServiceTaskManagerTest {
}
@Test
+ public void testQueueUpdatedUponDownloadComplete() throws Exception {
+ final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ { // Setup test data
+ List<FeedItem> queue = writeTestQueue("a");
+ FeedItem item = DBReader.getFeedItem(queue.get(0).getId());
+ FeedMedia media = new FeedMedia(item, "http://example.com/episode.mp3", 12345, "audio/mp3");
+ item.setMedia(media);
+ DBWriter.setFeedMedia(media).get();
+ DBWriter.setFeedItem(item).get();
+ }
+
+ PlaybackServiceTaskManager pstm = new PlaybackServiceTaskManager(c, defaultPSTM);
+ final FeedItem testItem = pstm.getQueue().get(0);
+ assertFalse("The item should not yet be downloaded", testItem.getMedia().isDownloaded());
+
+ withFeedItemEventListener( feedItemEventListener -> {
+ // simulate download complete (in DownloadService.MediaHandlerThread)
+ FeedItem item = DBReader.getFeedItem(testItem.getId());
+ item.getMedia().setDownloaded(true);
+ item.getMedia().setFile_url("file://123");
+ item.setAutoDownload(false);
+ DBWriter.setFeedMedia(item.getMedia()).get();
+ DBWriter.setFeedItem(item).get();
+
+ Awaitility.await()
+ .atMost(1000, TimeUnit.MILLISECONDS)
+ .until(() -> feedItemEventListener.getEvents().size() > 0);
+
+ final FeedItem itemUpdated = pstm.getQueue().get(0);
+ assertTrue("media.isDownloaded() should be true - The queue in PlaybackService should be updated after download is completed",
+ itemUpdated.getMedia().isDownloaded());
+ });
+
+ pstm.shutdown();
+ }
+
+ @Test
public void testStartPositionSaver() throws InterruptedException {
final Context c = InstrumentationRegistry.getInstrumentation().getTargetContext();
final int NUM_COUNTDOWNS = 2;
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java b/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java
new file mode 100644
index 000000000..00eef5c8d
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/storage/AutoDownloadTest.java
@@ -0,0 +1,166 @@
+package de.test.antennapod.storage;
+
+import android.content.Context;
+import android.content.Intent;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.core.app.ApplicationProvider;
+
+import org.awaitility.Awaitility;
+import org.awaitility.core.ConditionTimeoutException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.util.List;
+
+import de.danoeh.antennapod.core.ClientConfig;
+import de.danoeh.antennapod.core.DBTasksCallbacks;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.storage.AutomaticDownloadAlgorithm;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.test.antennapod.ui.UITestUtils;
+
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class AutoDownloadTest {
+
+ private Context context;
+ private UITestUtils stubFeedsServer;
+
+ private DBTasksCallbacks dbTasksCallbacksOrig;
+
+ @Before
+ public void setUp() throws Exception {
+ context = ApplicationProvider.getApplicationContext();
+
+ stubFeedsServer = new UITestUtils(context);
+ stubFeedsServer.setup();
+
+ dbTasksCallbacksOrig = ClientConfig.dbTasksCallbacks;
+
+ // create new database
+ PodDBAdapter.init(context);
+ PodDBAdapter.deleteDatabase();
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.close();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ stubFeedsServer.tearDown();
+ ClientConfig.dbTasksCallbacks = dbTasksCallbacksOrig;
+
+ context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
+ Awaitility.await().until(() -> !PlaybackService.isRunning);
+ }
+
+ /**
+ * A cross-functional test, ensuring playback's behavior works with Auto Download in boundary condition.
+ *
+ * Scenario:
+ * - For setting enqueue location AFTER_CURRENTLY_PLAYING
+ * - when playback of an episode is complete and the app advances to the next episode (continuous playback on)
+ * - when automatic download kicks in,
+ * - ensure the next episode is the current playing one, needed for AFTER_CURRENTLY_PLAYING enqueue location.
+ */
+ @Test
+ public void downloadsEnqueuedToAfterCurrent_CurrentAdvancedToNextOnPlaybackComplete() throws Exception {
+ UserPreferences.setFollowQueue(true); // continuous playback
+
+ // Setup: feeds and queue
+ // downloads 3 of them, leave some in new state (auto-downloadable)
+ stubFeedsServer.addLocalFeedData(false);
+ List<FeedItem> queue = DBReader.getQueue();
+ assertTrue(queue.size() > 1);
+ FeedItem item0 = queue.get(0);
+ FeedItem item1 = queue.get(1);
+
+ // Setup: enable automatic download
+ // it is not needed, as the actual automatic download is stubbed.
+ StubDownloadAlgorithm stubDownloadAlgorithm = new StubDownloadAlgorithm();
+ useDownloadAlgorithm(stubDownloadAlgorithm);
+
+ // Actual test
+ // Play the first one in the queue
+ playEpisode(item0);
+
+ try {
+ // when playback is complete, advances to the next one, and auto download kicks in,
+ // ensure that currently playing has been advanced to the next one by this point.
+ Awaitility.await("advanced to the next episode")
+ .atMost(6000, MILLISECONDS) // the test mp3 media is 3-second long. twice should be enough
+ .until(() -> item1.equals(stubDownloadAlgorithm.getCurrentlyPlayingAtDownload()));
+ } catch (ConditionTimeoutException cte) {
+ FeedItem actual = stubDownloadAlgorithm.getCurrentlyPlayingAtDownload();
+ fail("when auto download is triggered, the next episode should be playing: ("
+ + item1.getId() + ", " + item1.getTitle() + ") . "
+ + "Actual playing: ("
+ + (actual == null ? "" : actual.getId() + ", " + actual.getTitle()) + ")"
+ );
+ }
+
+ }
+
+ private void playEpisode(@NonNull FeedItem item) {
+ FeedMedia media = item.getMedia();
+ DBTasks.playMedia(context, media, false, true, true);
+ Awaitility.await("episode is playing")
+ .atMost(1000, MILLISECONDS)
+ .until(() -> item.equals(getCurrentlyPlaying()));
+ }
+
+ private FeedItem getCurrentlyPlaying() {
+ Playable playable = Playable.PlayableUtils.createInstanceFromPreferences(context);
+ if (playable == null) {
+ return null;
+ }
+ return ((FeedMedia)playable).getItem();
+ }
+
+ private void useDownloadAlgorithm(final AutomaticDownloadAlgorithm downloadAlgorithm) {
+ ClientConfig.dbTasksCallbacks = new DBTasksCallbacks() {
+ @Override
+ public AutomaticDownloadAlgorithm getAutomaticDownloadAlgorithm() {
+ return downloadAlgorithm;
+ }
+
+ @Override
+ public EpisodeCleanupAlgorithm getEpisodeCacheCleanupAlgorithm() {
+ return dbTasksCallbacksOrig.getEpisodeCacheCleanupAlgorithm();
+ }
+ };
+ }
+
+ private class StubDownloadAlgorithm implements AutomaticDownloadAlgorithm {
+ @Nullable
+ private FeedItem currentlyPlaying;
+
+ @Override
+ public Runnable autoDownloadUndownloadedItems(Context context) {
+ return () -> {
+ if (currentlyPlaying == null) {
+ currentlyPlaying = getCurrentlyPlaying();
+ } else {
+ throw new AssertionError("Stub automatic download should be invoked once and only once");
+ }
+ };
+ }
+
+ @Nullable
+ FeedItem getCurrentlyPlayingAtDownload() {
+ return currentlyPlaying;
+ }
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
index e78894e59..1acb96d01 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBCleanupTests.java
@@ -10,9 +10,9 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
index fe5091eb9..24cc80061 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBNullCleanupAlgorithmTest.java
@@ -10,9 +10,9 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
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 3320aa545..de810c701 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,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
index d99a409d3..647ab911f 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBReaderTest.java
@@ -7,9 +7,9 @@ import java.util.Date;
import java.util.List;
import java.util.Random;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
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 6af12315c..090cd2213 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTasksTest.java
@@ -2,26 +2,31 @@ package de.test.antennapod.storage;
import android.content.Context;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import static de.danoeh.antennapod.core.util.FeedItemUtil.getIdList;
import static java.util.Collections.singletonList;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -179,4 +184,82 @@ public class DBTasksTest {
lastDate = item.getPubDate();
}
}
+
+ @Test
+ public void testAddQueueItemsInDownload_EnqueueEnabled() throws Exception {
+ // Setup test data / environment
+ UserPreferences.setEnqueueDownloadedEpisodes(true);
+ UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
+
+ List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
+ List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
+
+ DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
+ // the first item fis1.get(0) is already in the queue
+ FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
+
+ // Expectations:
+ List<FeedItem> expectedEnqueued = Arrays.asList(fis1.get(1), fis2.get(2), fis2.get(1));
+ List<FeedItem> expectedQueue = new ArrayList<>();
+ expectedQueue.addAll(DBReader.getQueue());
+ expectedQueue.addAll(expectedEnqueued);
+
+ // Run actual test and assert results
+ List<? extends FeedItem> actualEnqueued =
+ DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
+
+ assertEqualsByIds("Only items not in the queue are enqueued", expectedEnqueued, actualEnqueued);
+ assertEqualsByIds("Queue has new items appended", expectedQueue, DBReader.getQueue());
+ }
+
+ @Test
+ public void testAddQueueItemsInDownload_EnqueueDisabled() throws Exception {
+ // Setup test data / environment
+ UserPreferences.setEnqueueDownloadedEpisodes(false);
+
+ List<FeedItem> fis1 = createSavedFeed("Feed 1", 2).getItems();
+ List<FeedItem> fis2 = createSavedFeed("Feed 2", 3).getItems();
+
+ DBWriter.addQueueItem(context, fis1.get(0), fis2.get(0)).get();
+ FeedItem[] itemsToDownload = new FeedItem[]{ fis1.get(0), fis1.get(1), fis2.get(2), fis2.get(1) };
+
+ // Expectations:
+ List<FeedItem> expectedEnqueued = Collections.emptyList();
+ List<FeedItem> expectedQueue = DBReader.getQueue();
+
+ // Run actual test and assert results
+ List<? extends FeedItem> actualEnqueued =
+ DBTasks.enqueueFeedItemsToDownload(context, Arrays.asList(itemsToDownload));
+
+ assertEqualsByIds("No item is enqueued", expectedEnqueued, actualEnqueued);
+ assertEqualsByIds("Queue is unchanged", expectedQueue, DBReader.getQueue());
+ }
+
+ private void assertEqualsByIds(String msg, List<? extends FeedItem> expected, List<? extends FeedItem> actual) {
+ // assert only the IDs, so that any differences are easily to spot.
+ List<Long> expectedIds = getIdList(expected);
+ List<Long> actualIds = getIdList(actual);
+ assertEquals(msg, expectedIds, actualIds);
+ }
+
+ private Feed createSavedFeed(String title, int numFeedItems) {
+ final Feed feed = new Feed("url", null, title);
+
+ if (numFeedItems > 0) {
+ List<FeedItem> items = new ArrayList<>(numFeedItems);
+ for (int i = 1; i <= numFeedItems; i++) {
+ FeedItem item = new FeedItem(0, "item " + i + " of " + title, "id", "link",
+ new Date(), FeedItem.UNPLAYED, feed);
+ items.add(item);
+ }
+ feed.setItems(items);
+ }
+
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setCompleteFeed(feed);
+ adapter.close();
+ return feed;
+ }
+
}
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
index 09a8466e6..ebf59fea7 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBTestUtils.java
@@ -44,8 +44,8 @@ class DBTestUtils {
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
for (int i = 0; i < numFeeds; i++) {
- Feed f = new Feed(0, null, "feed " + i, null, "link" + i, "descr", null, null,
- null, null, "id" + i, null, null, "url" + i, false, false, null, null, false);
+ Feed f = new Feed(0, null, "feed " + i, "link" + i, "descr", null, null,
+ null, null, "id" + i, null, null, "url" + i, false);
f.setItems(new ArrayList<>());
for (int j = 0; j < numItems; j++) {
FeedItem item = new FeedItem(0, "item " + j, "id" + j, "link" + j, new Date(),
diff --git a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
index 7663bb3c9..10ca281cb 100644
--- a/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/storage/DBWriterTest.java
@@ -4,12 +4,15 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.preference.PreferenceManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+
import org.awaitility.Awaitility;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
import java.io.File;
import java.io.IOException;
@@ -29,11 +32,9 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.Consumer;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
-import static android.support.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
@@ -321,15 +322,11 @@ public class DBWriterTest {
feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
-
File enc = new File(destFolder, "file " + i);
- itemFiles.add(enc);
-
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
@@ -386,15 +383,11 @@ public class DBWriterTest {
feed.setImageUrl("url");
- List<File> itemFiles = new ArrayList<>();
// create items with downloaded media files
for (int i = 0; i < 10; i++) {
FeedItem item = new FeedItem(0, "Item " + i, "Item" + i, "url", new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
-
File enc = new File(destFolder, "file " + i);
- itemFiles.add(enc);
-
FeedMedia media = new FeedMedia(0, item, 1, 1, 1, "mime_type", enc.getAbsolutePath(), "download_url", false, null, 0, 0);
item.setMedia(media);
}
@@ -475,11 +468,12 @@ public class DBWriterTest {
assertFalse(OLD_DATE == media.getPlaybackCompletionDate().getTime());
}
- private Feed queueTestSetupMultipleItems(final int NUM_ITEMS) throws InterruptedException, ExecutionException, TimeoutException {
+ private Feed queueTestSetupMultipleItems(final int numItems) throws InterruptedException, ExecutionException, TimeoutException {
final Context context = getInstrumentation().getTargetContext();
+ UserPreferences.setEnqueueLocation(UserPreferences.EnqueueLocation.BACK);
Feed feed = new Feed("url", null, "title");
feed.setItems(new ArrayList<>());
- for (int i = 0; i < NUM_ITEMS; i++) {
+ for (int i = 0; i < numItems; i++) {
FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i, new Date(), FeedItem.PLAYED, feed);
feed.getItems().add(item);
}
@@ -573,12 +567,16 @@ public class DBWriterTest {
Cursor cursor = adapter.getQueueIDCursor();
assertTrue(cursor.moveToFirst());
assertTrue(cursor.getCount() == NUM_ITEMS);
+ List<Long> expectedIds = FeedItemUtil.getIdList(feed.getItems());
+ List<Long> actualIds = new ArrayList<>();
for (int i = 0; i < NUM_ITEMS; i++) {
assertTrue(cursor.moveToPosition(i));
- assertTrue(cursor.getLong(0) == feed.getItems().get(i).getId());
+ actualIds.add(cursor.getLong(0));
}
cursor.close();
adapter.close();
+ assertEquals("Bulk add to queue: result order should be the same as the order given",
+ expectedIds, actualIds);
}
@Test
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
index 0c65bb2e6..42c770df9 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java
@@ -1,20 +1,16 @@
package de.test.antennapod.ui;
import android.app.Activity;
-import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.intent.rule.IntentsTestRule;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.Espresso;
+import androidx.test.espresso.intent.rule.IntentsTestRule;
+import androidx.test.runner.AndroidJUnit4;
import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.dialog.RatingDialog;
import de.test.antennapod.EspressoTestUtils;
import org.junit.After;
import org.junit.Before;
@@ -24,11 +20,17 @@ import org.junit.runner.RunWith;
import java.io.IOException;
-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.contrib.ActivityResultMatchers.hasResultCode;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.InstrumentationRegistry.getInstrumentation;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.action.ViewActions.typeText;
+import static androidx.test.espresso.contrib.ActivityResultMatchers.hasResultCode;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.Intents.times;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
import static de.test.antennapod.EspressoTestUtils.openNavDrawer;
import static junit.framework.TestCase.assertTrue;
@@ -73,10 +75,11 @@ public class MainActivityTest {
final Feed feed = uiTestUtils.hostedFeeds.get(0);
openNavDrawer();
onView(withText(R.string.add_feed_label)).perform(click());
- solo.enterText(1, feed.getDownload_url());
- onView(withText(R.string.confirm_label)).perform(click());
+ onView(withId(R.id.etxtFeedurl)).perform(typeText(feed.getDownload_url()));
+ onView(withText(R.string.confirm_label)).perform(scrollTo()).perform(click());
+ Espresso.closeSoftKeyboard();
onView(withText(R.string.subscribe_label)).perform(click());
- assertTrue(solo.waitForText(solo.getString(R.string.open_podcast), 0, Timeout.getLargeTimeout(), false));
+ intended(hasComponent(MainActivity.class.getName()), times(2));
}
private String getActionbarTitle() {
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
index 548843636..963a39064 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java
@@ -1,21 +1,17 @@
package de.test.antennapod.ui;
-import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.espresso.ViewInteraction;
-import android.support.test.espresso.contrib.DrawerActions;
-import android.support.test.espresso.intent.rule.IntentsTestRule;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.espresso.ViewInteraction;
+import androidx.test.espresso.contrib.DrawerActions;
+import androidx.test.espresso.intent.rule.IntentsTestRule;
+import androidx.test.runner.AndroidJUnit4;
import android.view.View;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.DownloadsFragment;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
@@ -33,15 +29,16 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-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.action.ViewActions.scrollTo;
-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.isRoot;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.longClick;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.intent.Intents.intended;
+import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static de.test.antennapod.EspressoTestUtils.onDrawerItem;
import static de.test.antennapod.EspressoTestUtils.waitForView;
import static de.test.antennapod.NthMatcher.first;
import static junit.framework.TestCase.assertTrue;
@@ -79,10 +76,6 @@ public class NavigationDrawerTest {
onView(withId(R.id.drawer_layout)).perform(DrawerActions.open());
}
- private ViewInteraction onDrawerItem(Matcher<View> viewMatcher) {
- return onView(allOf(viewMatcher, withId(R.id.txtvTitle)));
- }
-
@Test
public void testClickNavDrawer() throws Exception {
uiTestUtils.addLocalFeedData(false);
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
deleted file mode 100644
index 22959917d..000000000
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackSonicTest.java
+++ /dev/null
@@ -1,278 +0,0 @@
-package de.test.antennapod.ui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.view.View;
-import android.widget.ListView;
-
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import java.util.List;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.test.antennapod.EspressoTestUtils;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static android.support.test.InstrumentationRegistry.getInstrumentation;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
- */
-@LargeTest
-public class PlaybackSonicTest {
- private static final int EPISODES_DRAWER_LIST_INDEX = 1;
- private static final int QUEUE_DRAWER_LIST_INDEX = 0;
-
- private Solo solo;
- private UITestUtils uiTestUtils;
-
- private Context context;
-
- @Rule
- public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class, false, false);
-
- @Before
- public void setUp() throws Exception {
- EspressoTestUtils.clearPreferences();
- EspressoTestUtils.makeNotFirstRun();
- EspressoTestUtils.clearDatabase();
- context = InstrumentationRegistry.getTargetContext();
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit()
- .clear()
- .putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
- .putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
- .putString(UserPreferences.PREF_MEDIA_PLAYER, "sonic")
- .commit();
-
- activityTestRule.launchActivity(new Intent());
- solo = new Solo(getInstrumentation(), activityTestRule.getActivity());
-
- uiTestUtils = new UITestUtils(context);
- uiTestUtils.setup();
- }
-
- @After
- public void tearDown() throws Exception {
- solo.finishOpenedActivities();
- uiTestUtils.tearDown();
-
- // shut down playback service
- skipEpisode();
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- }
-
- private MainActivity getActivity() {
- return activityTestRule.getActivity();
- }
-
- private void openNavDrawer() {
- solo.clickOnImageButton(0);
- getInstrumentation().waitForIdleSync();
- }
-
- private void setContinuousPlaybackPreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
- }
-
- private void skipEpisode() {
- Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
- context.sendBroadcast(skipIntent);
- }
-
- private void startLocalPlayback() {
- openNavDrawer();
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Episodes'
- View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- solo.clickOnView(targetView);
- getInstrumentation().waitForIdleSync();
- solo.waitForText(solo.getString(R.string.all_episodes_short_label));
- solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
- getInstrumentation().waitForIdleSync();
-
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
-
- solo.clickOnView(solo.getView(R.id.butSecondaryAction));
- long mediaId = episodes.get(0).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(playing);
- }
-
- private void startLocalPlaybackFromQueue() {
- openNavDrawer();
-
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Queue'
- View targetView = drawerView.getChildAt(QUEUE_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- getInstrumentation().waitForIdleSync();
- solo.clickOnView(targetView);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
-
- final List<FeedItem> queue = DBReader.getQueue();
- solo.clickOnImageButton(1);
- assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
- long mediaId = queue.get(0).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(playing);
- }
-
- @Test
- public void testStartLocal() throws Exception {
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffSingleEpisode() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
-
- startLocalPlaybackFromQueue();
- boolean stopped = solo.waitForCondition(() -> {
- if (uiTestUtils.getPlaybackController(getActivity()).getStatus()
- != PlayerStatus.PLAYING) {
- return true;
- } else if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- != first.getMedia().getId();
- } else {
- return true;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(stopped);
- Thread.sleep(1000);
- PlayerStatus status = uiTestUtils.getPlaybackController(getActivity()).getStatus();
- assertFalse(status.equals(PlayerStatus.PLAYING));
- }
-
- @Test
- public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(true);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
- final FeedItem second = queue.get(1);
-
- startLocalPlaybackFromQueue();
- boolean firstPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == first.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(firstPlaying);
- boolean secondPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == second.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(secondPlaying);
- }
-
- /**
- * Check if an episode can be played twice without problems.
- */
- private void replayEpisodeCheck(boolean followQueue) throws Exception {
- setContinuousPlaybackPreference(followQueue);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
-
- startLocalPlayback();
- long mediaId = episodes.get(0).getMedia().getId();
- boolean startedPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(startedPlaying);
-
- boolean stoppedPlaying = solo.waitForCondition(() ->
- uiTestUtils.getCurrentMedia(getActivity()) == null
- || uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId
- , Timeout.getLargeTimeout());
- assertTrue(stoppedPlaying);
-
- startLocalPlayback();
- boolean startedReplay = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(startedReplay);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
- replayEpisodeCheck(true);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
- replayEpisodeCheck(false);
- }
-
-}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
deleted file mode 100644
index b302bcc91..000000000
--- a/app/src/androidTest/java/de/test/antennapod/ui/PlaybackTest.java
+++ /dev/null
@@ -1,272 +0,0 @@
-package de.test.antennapod.ui;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.rule.ActivityTestRule;
-import android.view.View;
-import android.widget.ListView;
-
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import java.util.List;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.MainActivity;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-/**
- * test cases for starting and ending playback from the MainActivity and AudioPlayerActivity
- */
-@LargeTest
-public class PlaybackTest {
- private static final int EPISODES_DRAWER_LIST_INDEX = 1;
- private static final int QUEUE_DRAWER_LIST_INDEX = 0;
-
- @Rule
- public ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);
-
- private Solo solo;
- private UITestUtils uiTestUtils;
- private Context context;
-
- @Before
- public void setUp() throws Exception {
- context = InstrumentationRegistry.getTargetContext();
-
- PodDBAdapter.init(context);
- PodDBAdapter.deleteDatabase();
-
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit()
- .clear()
- .putBoolean(UserPreferences.PREF_UNPAUSE_ON_HEADSET_RECONNECT, false)
- .putBoolean(UserPreferences.PREF_PAUSE_ON_HEADSET_DISCONNECT, false)
- .commit();
-
- solo = new Solo(InstrumentationRegistry.getInstrumentation(), getActivity());
-
- uiTestUtils = new UITestUtils(context);
- uiTestUtils.setup();
-
- // create database
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- adapter.close();
- }
-
- @After
- public void tearDown() throws Exception {
- solo.finishOpenedActivities();
- uiTestUtils.tearDown();
-
- // shut down playback service
- skipEpisode();
- context.sendBroadcast(new Intent(PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- }
-
- private MainActivity getActivity() {
- return activityTestRule.getActivity();
- }
-
- private void openNavDrawer() {
- solo.clickOnImageButton(0);
- InstrumentationRegistry.getInstrumentation().waitForIdleSync();
- }
-
- private void setContinuousPlaybackPreference(boolean value) {
- SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
- prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit();
- }
-
- private void skipEpisode() {
- Intent skipIntent = new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
- context.sendBroadcast(skipIntent);
- }
-
- private void startLocalPlayback() {
- openNavDrawer();
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Episodes'
- View targetView = drawerView.getChildAt(EPISODES_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- solo.clickOnView(targetView);
- solo.waitForText(solo.getString(R.string.all_episodes_short_label));
- solo.clickOnText(solo.getString(R.string.all_episodes_short_label));
-
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
-
- solo.clickOnView(solo.getView(R.id.butSecondaryAction));
- long mediaId = episodes.get(0).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(playing);
- }
-
- private void startLocalPlaybackFromQueue() {
- openNavDrawer();
- // if we try to just click on plain old text then
- // we might wind up clicking on the fragment title and not
- // the drawer element like we want.
- ListView drawerView = (ListView)solo.getView(R.id.nav_list);
- // this should be 'Queue'
- View targetView = drawerView.getChildAt(QUEUE_DRAWER_LIST_INDEX);
- solo.waitForView(targetView);
- solo.clickOnView(targetView);
- assertTrue(solo.waitForView(solo.getView(R.id.butSecondaryAction)));
-
- final List<FeedItem> queue = DBReader.getQueue();
- solo.clickOnImageButton(1);
- assertTrue(solo.waitForView(solo.getView(R.id.butPlay)));
- long mediaId = queue.get(0).getMedia().getId();
- boolean playing = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
-
- assertTrue(playing);
- }
-
- @Test
- public void testStartLocal() throws Exception {
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffSingleEpisode() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- startLocalPlayback();
- }
-
- @Test
- public void testContinousPlaybackOffMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(false);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
- startLocalPlaybackFromQueue();
- boolean stopped = solo.waitForCondition(() -> {
- if (uiTestUtils.getPlaybackController(getActivity()).getStatus()
- != PlayerStatus.PLAYING) {
- return true;
- } else if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- != first.getMedia().getId();
- } else {
- return true;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(stopped);
- Thread.sleep(1000);
- PlayerStatus status = uiTestUtils.getPlaybackController(getActivity()).getStatus();
- assertFalse(status.equals(PlayerStatus.PLAYING));
- }
-
- @Test
- public void testContinuousPlaybackOnMultipleEpisodes() throws Exception {
- setContinuousPlaybackPreference(true);
- uiTestUtils.addLocalFeedData(true);
- List<FeedItem> queue = DBReader.getQueue();
- final FeedItem first = queue.get(0);
- final FeedItem second = queue.get(1);
-
- startLocalPlaybackFromQueue();
- boolean firstPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == first.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(firstPlaying);
- boolean secondPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId()
- == second.getMedia().getId();
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(secondPlaying);
- }
-
- /**
- * Check if an episode can be played twice without problems.
- */
- private void replayEpisodeCheck(boolean followQueue) throws Exception {
- setContinuousPlaybackPreference(followQueue);
- uiTestUtils.addLocalFeedData(true);
- DBWriter.clearQueue().get();
- final List<FeedItem> episodes = DBReader.getRecentlyPublishedEpisodes(0, 10);
-
- startLocalPlayback();
- long mediaId = episodes.get(0).getMedia().getId();
- boolean startedPlaying = solo.waitForCondition(() -> {
- if (uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getSmallTimeout());
- assertTrue(startedPlaying);
-
- boolean stoppedPlaying = solo.waitForCondition(() ->
- uiTestUtils.getCurrentMedia(getActivity()) == null
- || uiTestUtils.getCurrentMedia(getActivity()).getId() != mediaId, Timeout.getLargeTimeout());
- assertTrue(stoppedPlaying);
-
- startLocalPlayback();
- boolean startedReplay = solo.waitForCondition(() -> {
- if(uiTestUtils.getCurrentMedia(getActivity()) != null) {
- return uiTestUtils.getCurrentMedia(getActivity()).getId() == mediaId;
- } else {
- return false;
- }
- }, Timeout.getLargeTimeout());
- assertTrue(startedReplay);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOn() throws Exception {
- replayEpisodeCheck(true);
- }
-
- @Test
- public void testReplayEpisodeContinuousPlaybackOff() throws Exception {
- replayEpisodeCheck(false);
- }
-}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
index 1063ac90a..ffabbfdea 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java
@@ -4,23 +4,14 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
-import android.support.test.filters.LargeTest;
-
-import android.support.test.rule.ActivityTestRule;
-import com.robotium.solo.Solo;
-import com.robotium.solo.Timeout;
-
-import de.test.antennapod.EspressoTestUtils;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.concurrent.TimeUnit;
-
+import androidx.annotation.StringRes;
+import androidx.test.filters.LargeTest;
+import androidx.test.rule.ActivityTestRule;
+import com.google.android.material.snackbar.Snackbar;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
@@ -28,20 +19,36 @@ import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
import de.danoeh.antennapod.fragment.EpisodesFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
+import de.test.antennapod.EspressoTestUtils;
+import org.awaitility.Awaitility;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
-import static 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.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.action.ViewActions.replaceText;
+import static androidx.test.espresso.action.ViewActions.scrollTo;
+import static androidx.test.espresso.assertion.ViewAssertions.doesNotExist;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isChecked;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.isRoot;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static de.test.antennapod.EspressoTestUtils.clickPreference;
-import static junit.framework.TestCase.assertEquals;
+import static de.test.antennapod.EspressoTestUtils.waitForView;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static junit.framework.TestCase.assertTrue;
+import static org.hamcrest.Matchers.allOf;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.not;
@LargeTest
public class PreferencesTest {
- private Solo solo;
private Resources res;
@Rule
@@ -49,14 +56,12 @@ public class PreferencesTest {
@Before
public void setUp() {
+ EspressoTestUtils.clearDatabase();
EspressoTestUtils.clearPreferences();
mActivityRule.launchActivity(new Intent());
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mActivityRule.getActivity());
prefs.edit().putBoolean(UserPreferences.PREF_ENABLE_AUTODL, true).commit();
- solo = new Solo(getInstrumentation(), mActivityRule.getActivity());
- Timeout.setSmallTimeout(500);
- Timeout.setLargeTimeout(1000);
res = mActivityRule.getActivity().getResources();
UserPreferences.init(mActivityRule.getActivity());
}
@@ -65,7 +70,7 @@ public class PreferencesTest {
public void testSwitchTheme() {
final int theme = UserPreferences.getTheme();
int otherTheme;
- if(theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
+ if (theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
otherTheme = R.string.pref_theme_title_dark;
} else {
otherTheme = R.string.pref_theme_title_light;
@@ -73,14 +78,15 @@ public class PreferencesTest {
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getTheme() != theme);
}
@Test
public void testSwitchThemeBack() {
final int theme = UserPreferences.getTheme();
int otherTheme;
- if(theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
+ if (theme == de.danoeh.antennapod.core.R.style.Theme_AntennaPod_Light) {
otherTheme = R.string.pref_theme_title_dark;
} else {
otherTheme = R.string.pref_theme_title_light;
@@ -88,7 +94,8 @@ public class PreferencesTest {
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_set_theme_title);
onView(withText(otherTheme)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getTheme() != theme, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getTheme() != theme);
}
@Test
@@ -96,18 +103,18 @@ public class PreferencesTest {
final boolean persistNotify = UserPreferences.isPersistNotify();
clickPreference(R.string.user_interface_label);
clickPreference(R.string.pref_persistNotify_title);
- assertTrue(solo.waitForCondition(() -> persistNotify != UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> persistNotify != UserPreferences.isPersistNotify());
clickPreference(R.string.pref_persistNotify_title);
- assertTrue(solo.waitForCondition(() -> persistNotify == UserPreferences.isPersistNotify(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> persistNotify == UserPreferences.isPersistNotify());
}
@Test
public void testSetLockscreenButtons() {
- onView(withText(R.string.user_interface_label)).perform(click());
- solo.scrollDown();
+ clickPreference(R.string.user_interface_label);
String[] buttons = res.getStringArray(R.array.compact_notification_buttons_options);
- onView(withText(R.string.pref_compact_notification_buttons_title)).perform(click());
- solo.waitForDialogToOpen(1000);
+ clickPreference(R.string.pref_compact_notification_buttons_title);
// First uncheck checkbox
onView(withText(buttons[2])).perform(click());
@@ -117,24 +124,39 @@ public class PreferencesTest {
onView(withText(buttons[2])).perform(click());
// Make sure that the third checkbox is unchecked
- assertTrue(!solo.isTextChecked(buttons[2]));
+ onView(withText(buttons[2])).check(matches(not(isChecked())));
+
+ String snackBarText = String.format(res.getString(
+ R.string.pref_compact_notification_buttons_dialog_error), 2);
+ Awaitility.await().ignoreExceptions().atMost(4000, MILLISECONDS)
+ .until(() -> {
+ onView(withText(snackBarText)).check(doesNotExist());
+ return true;
+ });
+
onView(withText(R.string.confirm_label)).perform(click());
- solo.waitForDialogToClose(1000);
- assertTrue(solo.waitForCondition(UserPreferences::showRewindOnCompactNotification, Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(UserPreferences::showFastForwardOnCompactNotification, Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> !UserPreferences.showSkipOnCompactNotification(), Timeout.getLargeTimeout()));
+
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::showRewindOnCompactNotification);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::showFastForwardOnCompactNotification);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> !UserPreferences.showSkipOnCompactNotification());
}
@Test
- public void testEnqueueAtFront() {
- onView(withText(R.string.playback_pref)).perform(click());
- final boolean enqueueAtFront = UserPreferences.enqueueAtFront();
- solo.scrollDown();
- solo.scrollDown();
- onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enqueueAtFront != UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_queueAddToFront_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enqueueAtFront == UserPreferences.enqueueAtFront(), Timeout.getLargeTimeout()));
+ public void testEnqueueLocation() {
+ clickPreference(R.string.playback_pref);
+ doTestEnqueueLocation(R.string.enqueue_location_after_current, EnqueueLocation.AFTER_CURRENTLY_PLAYING);
+ doTestEnqueueLocation(R.string.enqueue_location_front, EnqueueLocation.FRONT);
+ doTestEnqueueLocation(R.string.enqueue_location_back, EnqueueLocation.BACK);
+ }
+
+ private void doTestEnqueueLocation(@StringRes int optionResId, EnqueueLocation expected) {
+ clickPreference(R.string.pref_enqueue_location_title);
+ onView(withText(optionResId)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> expected == UserPreferences.getEnqueueLocation());
}
@Test
@@ -142,49 +164,57 @@ public class PreferencesTest {
onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseOnHeadsetDisconnect = UserPreferences.isPauseOnHeadsetDisconnect();
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseOnHeadsetDisconnect != UserPreferences.isPauseOnHeadsetDisconnect());
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseOnHeadsetDisconnect == UserPreferences.isPauseOnHeadsetDisconnect());
}
@Test
public void testHeadPhonesReconnect() {
onView(withText(R.string.playback_pref)).perform(click());
- if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
+ if (!UserPreferences.isPauseOnHeadsetDisconnect()) {
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isPauseOnHeadsetDisconnect);
}
final boolean unpauseOnHeadsetReconnect = UserPreferences.isUnpauseOnHeadsetReconnect();
onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnHeadsetReconnect != UserPreferences.isUnpauseOnHeadsetReconnect());
onView(withText(R.string.pref_unpauseOnHeadsetReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnHeadsetReconnect == UserPreferences.isUnpauseOnHeadsetReconnect());
}
@Test
public void testBluetoothReconnect() {
onView(withText(R.string.playback_pref)).perform(click());
- if(UserPreferences.isPauseOnHeadsetDisconnect() == false) {
+ if (!UserPreferences.isPauseOnHeadsetDisconnect()) {
onView(withText(R.string.pref_pauseOnHeadsetDisconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(UserPreferences::isPauseOnHeadsetDisconnect, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isPauseOnHeadsetDisconnect);
}
final boolean unpauseOnBluetoothReconnect = UserPreferences.isUnpauseOnBluetoothReconnect();
onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnBluetoothReconnect != UserPreferences.isUnpauseOnBluetoothReconnect());
onView(withText(R.string.pref_unpauseOnBluetoothReconnect_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> unpauseOnBluetoothReconnect == UserPreferences.isUnpauseOnBluetoothReconnect());
}
@Test
public void testContinuousPlayback() {
- onView(withText(R.string.playback_pref)).perform(click());
+ clickPreference(R.string.playback_pref);
final boolean continuousPlayback = UserPreferences.isFollowQueue();
- solo.scrollDown();
- solo.scrollDown();
- onView(withText(R.string.pref_followQueue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> continuousPlayback != UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
- onView(withText(R.string.pref_followQueue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> continuousPlayback == UserPreferences.isFollowQueue(), Timeout.getLargeTimeout()));
+ clickPreference(R.string.pref_followQueue_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> continuousPlayback != UserPreferences.isFollowQueue());
+ clickPreference(R.string.pref_followQueue_title);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> continuousPlayback == UserPreferences.isFollowQueue());
}
@Test
@@ -192,9 +222,11 @@ public class PreferencesTest {
onView(withText(R.string.storage_pref)).perform(click());
final boolean autoDelete = UserPreferences.isAutoDelete();
onView(withText(R.string.pref_auto_delete_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> autoDelete != UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> autoDelete != UserPreferences.isAutoDelete());
onView(withText(R.string.pref_auto_delete_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> autoDelete == UserPreferences.isAutoDelete(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> autoDelete == UserPreferences.isAutoDelete());
}
@Test
@@ -203,7 +235,7 @@ public class PreferencesTest {
clickPreference(R.string.media_player);
onView(withText(R.string.media_player_exoplayer)).perform(click());
clickPreference(R.string.pref_playback_speed_title);
- solo.waitForDialogToOpen();
+ onView(isRoot()).perform(waitForView(withText("0.50"), 1000));
onView(withText("0.50")).check(matches(isDisplayed()));
onView(withText(R.string.cancel_label)).perform(click());
}
@@ -213,9 +245,11 @@ public class PreferencesTest {
onView(withText(R.string.playback_pref)).perform(click());
final boolean pauseForFocusLoss = UserPreferences.shouldPauseForFocusLoss();
onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseForFocusLoss != UserPreferences.shouldPauseForFocusLoss());
onView(withText(R.string.pref_pausePlaybackForFocusLoss_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> pauseForFocusLoss == UserPreferences.shouldPauseForFocusLoss());
}
@Test
@@ -223,7 +257,8 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_autoUpdateIntervallOrTime_title)).perform(click());
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Disable)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() == 0, 1000));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getUpdateInterval() == 0);
}
@Test
@@ -231,59 +266,58 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_autoUpdateIntervallOrTime_title);
onView(withText(R.string.pref_autoUpdateIntervallOrTime_Interval)).perform(click());
- String search = "12 " + solo.getString(R.string.pref_update_interval_hours_plural);
+ String search = "12 " + res.getString(R.string.pref_update_interval_hours_plural);
onView(withText(search)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getUpdateInterval() ==
- TimeUnit.HOURS.toMillis(12), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getUpdateInterval() == TimeUnit.HOURS.toMillis(12));
}
@Test
public void testSetSequentialDownload() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "1");
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("1"));
onView(withText(android.R.string.ok)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 1, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getParallelDownloads() == 1);
}
@Test
public void testSetParallelDownloads() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "10");
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("10"));
onView(withText(android.R.string.ok)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getParallelDownloads() == 10, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getParallelDownloads() == 10);
}
@Test
public void testSetParallelDownloadsInvalidInput() {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_parallel_downloads_title);
- solo.waitForDialogToOpen();
- solo.clearEditText(0);
- solo.enterText(0, "0");
- assertEquals("", solo.getEditText(0).getText().toString());
- solo.clearEditText(0);
- solo.enterText(0, "100");
- assertEquals("", solo.getEditText(0).getText().toString());
+ onView(isRoot()).perform(waitForView(withClassName(endsWith("EditText")), 1000));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("0"));
+ onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
+ onView(withClassName(endsWith("EditText"))).perform(replaceText("100"));
+ onView(withClassName(endsWith("EditText"))).check(matches(withText("")));
}
@Test
public void testSetEpisodeCache() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
- String entry = entries[entries.length/2];
- final int value = Integer.valueOf(values[values.length/2]);
+ String entry = entries[entries.length / 2];
+ final int value = Integer.parseInt(values[values.length / 2]);
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cache_title);
- solo.waitForDialogToOpen();
- solo.clickOnText(entry);
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == value, Timeout.getLargeTimeout()));
+ onView(isRoot()).perform(waitForView(withText(entry), 1000));
+ onView(withText(entry)).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == value);
}
@Test
@@ -291,27 +325,28 @@ public class PreferencesTest {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
String minEntry = entries[0];
- final int minValue = Integer.valueOf(values[0]);
+ final int minValue = Integer.parseInt(values[0]);
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cache_title);
- solo.scrollUp();
- onView(withText(minEntry)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == minValue, Timeout.getLargeTimeout()));
+ onView(withText(minEntry)).perform(scrollTo()).perform(click());
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == minValue);
}
@Test
public void testSetEpisodeCacheMax() {
String[] entries = res.getStringArray(R.array.episode_cache_size_entries);
String[] values = res.getStringArray(R.array.episode_cache_size_values);
- String maxEntry = entries[entries.length-1];
- final int maxValue = Integer.valueOf(values[values.length-1]);
+ String maxEntry = entries[entries.length - 1];
+ final int maxValue = Integer.parseInt(values[values.length - 1]);
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cache_title)).perform(click());
onView(withText(maxEntry)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getEpisodeCacheSize() == maxValue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCacheSize() == maxValue);
}
@Test
@@ -320,22 +355,27 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_automatic_download_title);
-
- assertTrue(solo.waitForCondition(() -> automaticDownload != UserPreferences.isEnableAutodownload(), Timeout.getLargeTimeout()));
- if(UserPreferences.isEnableAutodownload() == false) {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> automaticDownload != UserPreferences.isEnableAutodownload());
+ if (!UserPreferences.isEnableAutodownload()) {
clickPreference(R.string.pref_automatic_download_title);
}
- assertTrue(solo.waitForCondition(() -> UserPreferences.isEnableAutodownload() == true, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::isEnableAutodownload);
final boolean enableAutodownloadOnBattery = UserPreferences.isEnableAutodownloadOnBattery();
onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableAutodownloadOnBattery != UserPreferences.isEnableAutodownloadOnBattery());
onView(withText(R.string.pref_automatic_download_on_battery_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableAutodownloadOnBattery == UserPreferences.isEnableAutodownloadOnBattery());
final boolean enableWifiFilter = UserPreferences.isEnableAutodownloadWifiFilter();
onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableWifiFilter != UserPreferences.isEnableAutodownloadWifiFilter());
onView(withText(R.string.pref_autodl_wifi_filter_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> enableWifiFilter == UserPreferences.isEnableAutodownloadWifiFilter());
}
@Test
@@ -343,13 +383,10 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_queue_removal));
+ onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_queue_removal), 1000));
onView(withText(R.string.episode_cleanup_queue_removal)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
- EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
- return alg instanceof APQueueCleanupAlgorithm;
- },
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APQueueCleanupAlgorithm);
}
@Test
@@ -357,13 +394,10 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_never));
+ onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_never), 1000));
onView(withText(R.string.episode_cleanup_never)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
- EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
- return alg instanceof APNullCleanupAlgorithm;
- },
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getEpisodeCleanupAlgorithm() instanceof APNullCleanupAlgorithm);
}
@Test
@@ -371,17 +405,17 @@ public class PreferencesTest {
onView(withText(R.string.network_pref)).perform(click());
onView(withText(R.string.pref_automatic_download_title)).perform(click());
onView(withText(R.string.pref_episode_cleanup_title)).perform(click());
- solo.waitForText(solo.getString(R.string.episode_cleanup_after_listening));
+ onView(isRoot()).perform(waitForView(withText(R.string.episode_cleanup_after_listening), 1000));
onView(withText(R.string.episode_cleanup_after_listening)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
- APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 0;
}
return false;
- },
- Timeout.getLargeTimeout()));
+ });
}
@Test
@@ -389,24 +423,24 @@ public class PreferencesTest {
clickPreference(R.string.network_pref);
clickPreference(R.string.pref_automatic_download_title);
clickPreference(R.string.pref_episode_cleanup_title);
- solo.waitForDialogToOpen();
String search = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, 5, 5);
+ onView(isRoot()).perform(waitForView(withText(search), 1000));
onView(withText(search)).perform(click());
- assertTrue(solo.waitForCondition(() -> {
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> {
EpisodeCleanupAlgorithm alg = UserPreferences.getEpisodeCleanupAlgorithm();
if (alg instanceof APCleanupAlgorithm) {
- APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm)alg;
+ APCleanupAlgorithm cleanupAlg = (APCleanupAlgorithm) alg;
return cleanupAlg.getNumberOfHoursAfterPlayback() == 120; // 5 days
}
return false;
- },
- Timeout.getLargeTimeout()));
+ });
}
@Test
public void testRewindChange() {
int seconds = UserPreferences.getRewindSecs();
- int deltas[] = res.getIntArray(R.array.seek_delta_values);
+ int[] deltas = res.getIntArray(R.array.seek_delta_values);
clickPreference(R.string.playback_pref);
clickPreference(R.string.pref_rewind);
@@ -416,11 +450,11 @@ public class PreferencesTest {
// Find next value (wrapping around to next)
int newIndex = (currentIndex + 1) % deltas.length;
- onView(withText(String.valueOf(deltas[newIndex]) + " seconds")).perform(click());
+ onView(withText(deltas[newIndex] + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getRewindSecs() == deltas[newIndex],
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getRewindSecs() == deltas[newIndex]);
}
@Test
@@ -428,7 +462,7 @@ public class PreferencesTest {
clickPreference(R.string.playback_pref);
for (int i = 2; i > 0; i--) { // repeat twice to catch any error where fastforward is tracking rewind
int seconds = UserPreferences.getFastForwardSecs();
- int deltas[] = res.getIntArray(R.array.seek_delta_values);
+ int[] deltas = res.getIntArray(R.array.seek_delta_values);
clickPreference(R.string.pref_fast_forward);
@@ -441,9 +475,8 @@ public class PreferencesTest {
onView(withText(deltas[newIndex] + " seconds")).perform(click());
onView(withText("Confirm")).perform(click());
- solo.waitForDialogToClose();
- assertTrue(solo.waitForCondition(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex],
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getFastForwardSecs() == deltas[newIndex]);
}
}
@@ -454,26 +487,26 @@ public class PreferencesTest {
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.queue_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(QueueFragment.TAG));
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.episodes_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(EpisodesFragment.TAG));
clickPreference(R.string.pref_back_button_behavior_title);
onView(withText(R.string.back_button_go_to_page)).perform(click());
onView(withText(R.string.subscriptions_label)).perform(click());
onView(withText(R.string.confirm_label)).perform(click());
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE,
- Timeout.getLargeTimeout()));
- assertTrue(solo.waitForCondition(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG),
- Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonBehavior() == UserPreferences.BackButtonBehavior.GO_TO_PAGE);
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> UserPreferences.getBackButtonGoToPage().equals(SubscriptionFragment.TAG));
}
@Test
@@ -481,12 +514,15 @@ public class PreferencesTest {
clickPreference(R.string.storage_pref);
if (!UserPreferences.shouldDeleteRemoveFromQueue()) {
clickPreference(R.string.pref_delete_removes_from_queue_title);
- assertTrue(solo.waitForCondition(UserPreferences::shouldDeleteRemoveFromQueue, Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(UserPreferences::shouldDeleteRemoveFromQueue);
}
final boolean deleteRemovesFromQueue = UserPreferences.shouldDeleteRemoveFromQueue();
onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> deleteRemovesFromQueue != UserPreferences.shouldDeleteRemoveFromQueue());
onView(withText(R.string.pref_delete_removes_from_queue_title)).perform(click());
- assertTrue(solo.waitForCondition(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue(), Timeout.getLargeTimeout()));
+ Awaitility.await().atMost(1000, MILLISECONDS)
+ .until(() -> deleteRemovesFromQueue == UserPreferences.shouldDeleteRemoveFromQueue());
}
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
index d1023dd9e..37d76bb6d 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/QueueFragmentTest.java
@@ -1,9 +1,9 @@
package de.test.antennapod.ui;
import android.content.Intent;
-import android.support.test.espresso.Espresso;
-import android.support.test.espresso.intent.rule.IntentsTestRule;
-import android.support.test.runner.AndroidJUnit4;
+import androidx.test.espresso.Espresso;
+import androidx.test.espresso.intent.rule.IntentsTestRule;
+import androidx.test.runner.AndroidJUnit4;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.fragment.QueueFragment;
@@ -13,19 +13,22 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
-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.withContentDescription;
-import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.action.ViewActions.click;
+import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
+import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
+import static androidx.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.CoreMatchers.endsWith;
/**
- * User interface tests for queue fragment
+ * User interface tests for queue fragment.
*/
@RunWith(AndroidJUnit4.class)
public class QueueFragmentTest {
@Rule
- public IntentsTestRule<MainActivity> mActivityRule = new IntentsTestRule<>(MainActivity.class, false, false);
+ public IntentsTestRule<MainActivity> activityRule = new IntentsTestRule<>(MainActivity.class, false, false);
@Before
public void setUp() {
@@ -33,12 +36,13 @@ public class QueueFragmentTest {
EspressoTestUtils.makeNotFirstRun();
EspressoTestUtils.clearDatabase();
EspressoTestUtils.setLastNavFragment(QueueFragment.TAG);
- mActivityRule.launchActivity(new Intent());
+ activityRule.launchActivity(new Intent());
}
@Test
public void testLockEmptyQueue() {
onView(withContentDescription(R.string.lock_queue)).perform(click());
+ onView(allOf(withClassName(endsWith("Button")), withText(R.string.lock_queue))).perform(click());
onView(withContentDescription(R.string.unlock_queue)).perform(click());
}
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
index 905c65c34..f25159046 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtils.java
@@ -4,6 +4,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
@@ -18,7 +19,6 @@ import java.util.List;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -34,7 +34,7 @@ import org.junit.Assert;
* Utility methods for UI tests.
* Starts a web server that hosts feeds, episodes and images.
*/
-class UITestUtils {
+public class UITestUtils {
private static final String TAG = UITestUtils.class.getSimpleName();
@@ -201,7 +201,7 @@ class UITestUtils {
adapter.setCompleteFeed(hostedFeeds.toArray(new Feed[hostedFeeds.size()]));
adapter.setQueue(queue);
adapter.close();
- EventDistributor.getInstance().sendFeedUpdateBroadcast();
+ EventBus.getDefault().post(new FeedListUpdateEvent(hostedFeeds));
EventBus.getDefault().post(QueueEvent.setQueue(queue));
}
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 7555bb69a..a183b648e 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/UITestUtilsTest.java
@@ -5,9 +5,9 @@ import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.MediumTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.LargeTest;
+import androidx.test.filters.MediumTest;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import org.junit.After;
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java
index c4dd032d7..ab16d7603 100644
--- a/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/ui/VideoplayerActivityTest.java
@@ -1,8 +1,8 @@
package de.test.antennapod.ui;
import android.content.Intent;
-import android.support.test.filters.MediumTest;
-import android.support.test.rule.ActivityTestRule;
+import androidx.test.filters.MediumTest;
+import androidx.test.rule.ActivityTestRule;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.VideoplayerActivity;
@@ -10,10 +10,10 @@ import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
-import static android.support.test.espresso.Espresso.onView;
-import static android.support.test.espresso.assertion.ViewAssertions.matches;
-import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
-import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static androidx.test.espresso.Espresso.onView;
+import static androidx.test.espresso.assertion.ViewAssertions.matches;
+import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static androidx.test.espresso.matcher.ViewMatchers.withId;
/**
* Test class for VideoplayerActivity
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 ac98d2802..d33eb55b8 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/FilenameGeneratorTest.java
@@ -1,8 +1,8 @@
package de.test.antennapod.util;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.LargeTest;
-import android.support.test.filters.SmallTest;
+import android.content.Context;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import android.text.TextUtils;
import java.io.File;
@@ -13,77 +13,79 @@ import org.junit.After;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@SmallTest
public class FilenameGeneratorTest {
- private static final String VALID1 = "abc abc";
- private static final String INVALID1 = "ab/c: <abc";
- private static final String INVALID2 = "abc abc ";
+ private static final String VALID1 = "abc abc";
+ private static final String INVALID1 = "ab/c: <abc";
+ private static final String INVALID2 = "abc abc ";
public FilenameGeneratorTest() {
super();
}
@Test
- public void testGenerateFileName() throws IOException {
- String result = FileNameGenerator.generateFileName(VALID1);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testGenerateFileName1() throws IOException {
- String result = FileNameGenerator.generateFileName(INVALID1);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testGenerateFileName2() throws IOException {
- String result = FileNameGenerator.generateFileName(INVALID2);
- assertEquals(result, VALID1);
- createFiles(result);
- }
-
- @Test
- public void testFeedTitleContainsApostrophe() {
- String result = FileNameGenerator.generateFileName("Feed's Title ...");
- assertEquals("Feeds Title", result);
- }
-
- @Test
- public void testFeedTitleContainsDash() {
- String result = FileNameGenerator.generateFileName("Left - Right");
- assertEquals("Left - Right", result);
- }
-
- @Test
- public void testInvalidInput() {
- String result = FileNameGenerator.generateFileName("???");
- assertTrue(!TextUtils.isEmpty(result));
- }
-
- /**
- * Tests if files can be created.
- *
- * @throws IOException
- */
- private void createFiles(String name) throws IOException {
- File cache = InstrumentationRegistry.getContext().getExternalCacheDir();
- File testFile = new File(cache, name);
- testFile.mkdir();
- assertTrue(testFile.exists());
- testFile.delete();
- assertTrue(testFile.createNewFile());
-
- }
-
- @After
- public void tearDown() throws Exception {
- File f = new File(InstrumentationRegistry.getContext().getExternalCacheDir(), VALID1);
- f.delete();
- }
+ public void testGenerateFileName() throws IOException {
+ String result = FileNameGenerator.generateFileName(VALID1);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testGenerateFileName1() throws IOException {
+ String result = FileNameGenerator.generateFileName(INVALID1);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testGenerateFileName2() throws IOException {
+ String result = FileNameGenerator.generateFileName(INVALID2);
+ assertEquals(result, VALID1);
+ createFiles(result);
+ }
+
+ @Test
+ public void testFeedTitleContainsApostrophe() {
+ String result = FileNameGenerator.generateFileName("Feed's Title ...");
+ assertEquals("Feeds Title", result);
+ }
+
+ @Test
+ public void testFeedTitleContainsDash() {
+ String result = FileNameGenerator.generateFileName("Left - Right");
+ assertEquals("Left - Right", result);
+ }
+
+ @Test
+ public void testInvalidInput() {
+ String result = FileNameGenerator.generateFileName("???");
+ assertFalse(TextUtils.isEmpty(result));
+ }
+
+ /**
+ * Tests if files can be created.
+ *
+ * @throws IOException
+ */
+ private void createFiles(String name) throws IOException {
+ File cache = InstrumentationRegistry.getInstrumentation().getTargetContext().getExternalCacheDir();
+ File testFile = new File(cache, name);
+ testFile.mkdir();
+ assertTrue(testFile.exists());
+ testFile.delete();
+ assertTrue(testFile.createNewFile());
+
+ }
+
+ @After
+ public void tearDown() {
+ Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
+ File f = new File(context.getExternalCacheDir(), VALID1);
+ f.delete();
+ }
}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java b/app/src/androidTest/java/de/test/antennapod/util/URLCheckerTest.java
index b96cb273b..5bc2f7bd8 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,6 @@
package de.test.antennapod.util;
-import android.support.test.filters.SmallTest;
+import androidx.test.filters.SmallTest;
import de.danoeh.antennapod.core.util.URLChecker;
import org.junit.Test;
diff --git a/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java b/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java
new file mode 100644
index 000000000..601bba853
--- /dev/null
+++ b/app/src/androidTest/java/de/test/antennapod/util/event/FeedItemEventListener.java
@@ -0,0 +1,46 @@
+package de.test.antennapod.util.event;
+
+import androidx.annotation.NonNull;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import de.danoeh.antennapod.core.event.FeedItemEvent;
+import io.reactivex.functions.Consumer;
+
+/**
+ * Test helpers to listen {@link FeedItemEvent} and handle them accordingly
+ *
+ */
+public class FeedItemEventListener {
+ private final List<FeedItemEvent> events = new ArrayList<>();
+
+ /**
+ * Provides an listener subscribing to {@link FeedItemEvent} that the callers can use
+ *
+ * Note: it uses RxJava's version of {@link Consumer} because it allows exceptions to be thrown.
+ */
+ public static void withFeedItemEventListener(@NonNull Consumer<FeedItemEventListener> consumer)
+ throws Exception {
+ FeedItemEventListener feedItemEventListener = new FeedItemEventListener();
+ try {
+ EventBus.getDefault().register(feedItemEventListener);
+ consumer.accept(feedItemEventListener);
+ } finally {
+ EventBus.getDefault().unregister(feedItemEventListener);
+ }
+ }
+
+ @Subscribe
+ public void onEvent(FeedItemEvent event) {
+ events.add(event);
+ }
+
+ @NonNull
+ public List<? extends FeedItemEvent> getEvents() {
+ return events;
+ }
+}
diff --git a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
index df69859a0..c42695a57 100644
--- a/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
+++ b/app/src/androidTest/java/de/test/antennapod/util/playback/TimelineTest.java
@@ -2,8 +2,8 @@ package de.test.antennapod.util.playback;
import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.SmallTest;
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.SmallTest;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
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 3d8417bf6..4158fd31c 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
@@ -72,20 +72,6 @@ public class HTTPBin extends NanoHTTPD {
return servedFiles.size() - 1;
}
- /**
- * Removes the file with the given ID from the server.
- *
- * @return True if a file was removed, false otherwise
- */
- public synchronized boolean removeFile(int id) {
- if (id < 0) throw new IllegalArgumentException("ID < 0");
- if (id >= servedFiles.size()) {
- return false;
- } else {
- return servedFiles.remove(id) != null;
- }
- }
-
public synchronized File accessFile(int id) {
if (id < 0 || id >= servedFiles.size()) {
return null;
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 d4837ef60..2dda77524 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,12 +1,11 @@
package de.test.antennapod.util.syndication;
-import android.support.test.InstrumentationRegistry;
+import androidx.test.InstrumentationRegistry;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.File;
import java.io.FileOutputStream;
-import java.nio.file.Files;
import java.util.Map;
import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer;
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index 79d7e02f2..aff1d6ea4 100644
--- a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -1,218 +1,15 @@
package de.danoeh.antennapod.activity;
-import android.support.v7.app.AppCompatActivity;
+import androidx.appcompat.app.AppCompatActivity;
/**
* Activity that allows for showing the MediaRouter button whenever there's a cast device in the
* network.
*/
public abstract class CastEnabledActivity extends AppCompatActivity {
-// implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
-// protected CastManager castManager;
-// protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
-// private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
-//
-// @Override
-// protected void onCreate(Bundle savedInstanceState) {
-// super.onCreate(savedInstanceState);
-//
-// PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
-// registerOnSharedPreferenceChangeListener(this);
-//
-// castManager = CastManager.getInstance();
-// castManager.addCastConsumer(castConsumer);
-// castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
-// onCastConnectionChanged(castManager.isConnected());
-// }
-//
-// @Override
-// protected void onDestroy() {
-// PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
-// .unregisterOnSharedPreferenceChangeListener(this);
-// castManager.removeCastConsumer(castConsumer);
-// super.onDestroy();
-// }
-//
-// @Override
-// @CallSuper
-// public boolean onCreateOptionsMenu(Menu menu) {
-// super.onCreateOptionsMenu(menu);
-// getMenuInflater().inflate(R.menu.cast_enabled, menu);
-// castButtonVisibilityManager.setMenu(menu);
-// return true;
-// }
-//
-// @Override
-// @CallSuper
-// public boolean onPrepareOptionsMenu(Menu menu) {
-// super.onPrepareOptionsMenu(menu);
-// mediaRouteActionProvider = castManager
-// .addMediaRouterButton(menu.findItem(R.id.media_route_menu_item));
-// mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
-// return true;
-// }
-//
-// @Override
-// protected void onResume() {
-// super.onResume();
-// castButtonVisibilityManager.setResumed(true);
-// }
-//
-// @Override
-// protected void onPause() {
-// super.onPause();
-// castButtonVisibilityManager.setResumed(false);
-// }
-//
-// @Override
-// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
-// if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
-// boolean newValue = UserPreferences.isCastEnabled();
-// Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
-// castButtonVisibilityManager.setPrefEnabled(newValue);
-// // PlaybackService has its own listener, so if it's active we don't have to take action here.
-// if (!newValue && !PlaybackService.isRunning) {
-// CastManager.getInstance().disconnect();
-// }
-// }
-// }
-//
-// CastConsumer castConsumer = new DefaultCastConsumer() {
-// @Override
-// public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
-// onCastConnectionChanged(true);
-// }
-//
-// @Override
-// public void onDisconnected() {
-// onCastConnectionChanged(false);
-// }
-// };
-//
-// private void onCastConnectionChanged(boolean connected) {
-// if (connected) {
-// castButtonVisibilityManager.onConnected();
-// setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
-// } else {
-// castButtonVisibilityManager.onDisconnected();
-// setVolumeControlStream(AudioManager.STREAM_MUSIC);
-// }
-// }
-//
-// /**
-// * Should be called by any activity or fragment for which the cast button should be shown.
-// *
-// * @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
-// */
public final void requestCastButton(int showAsAction) {
// no-op
}
-
-// private class CastButtonVisibilityManager {
-// private volatile boolean prefEnabled = false;
-// private volatile boolean viewRequested = false;
-// private volatile boolean resumed = false;
-// private volatile boolean connected = false;
-// private volatile int showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
-// private Menu menu;
-//
-// public synchronized void setPrefEnabled(boolean newValue) {
-// if (prefEnabled != newValue && resumed && (viewRequested || connected)) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// prefEnabled = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized void setResumed(boolean newValue) {
-// if (resumed == newValue) {
-// Log.e(TAG, "resumed should never change to the same value");
-// return;
-// }
-// resumed = newValue;
-// if (prefEnabled && (viewRequested || connected)) {
-// if (resumed) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// }
-//
-// public synchronized void setViewRequested(boolean newValue) {
-// if (viewRequested != newValue && resumed && prefEnabled && !connected) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// viewRequested = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized void setConnected(boolean newValue) {
-// if (connected != newValue && resumed && prefEnabled && !prefEnabled) {
-// if (newValue) {
-// castManager.incrementUiCounter();
-// } else {
-// castManager.decrementUiCounter();
-// }
-// }
-// connected = newValue;
-// if (mediaRouteActionProvider != null) {
-// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
-// }
-// }
-//
-// public synchronized boolean shouldEnable() {
-// return prefEnabled && viewRequested;
-// }
-//
-// public void setMenu(Menu menu) {
-// setViewRequested(false);
-// showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
-// this.menu = menu;
-// setShowAsAction();
-// }
-//
-// public void requestCastButton(int showAsAction) {
-// setViewRequested(true);
-// this.showAsAction = showAsAction;
-// setShowAsAction();
-// }
-//
-// public void onConnected() {
-// setConnected(true);
-// setShowAsAction();
-// }
-//
-// public void onDisconnected() {
-// setConnected(false);
-// setShowAsAction();
-// }
-//
-// private void setShowAsAction() {
-// if (menu == null) {
-// Log.d(TAG, "setShowAsAction() without a menu");
-// return;
-// }
-// MenuItem item = menu.findItem(R.id.media_route_menu_item);
-// if (item == null) {
-// Log.e(TAG, "setShowAsAction(), but cast button not inflated");
-// return;
-// }
-// MenuItemCompat.setShowAsAction(item, connected? MenuItem.SHOW_AS_ACTION_ALWAYS : showAsAction);
-// }
-// }
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 73af654e9..b6e6e290d 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -35,8 +35,17 @@
android:restoreAnyVersion="true"
android:usesCleartextTraffic="true"
android:logo="@mipmap/ic_launcher">
+
+ <activity
+ android:name=".activity.WidgetConfigActivity"
+ android:label="Widget settings">
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_CONFIGUR"/>
+ </intent-filter>
+ </activity>
+
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
- android:resource="@drawable/ic_notification" />
+ android:resource="@drawable/ic_antenna" />
<meta-data
android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
@@ -105,17 +114,6 @@
android:value="de.danoeh.antennapod.activity.MainActivity"/>
</activity>
- <activity
- android:name=".activity.FeedInfoActivity"
- android:label="@string/feed_info_label">
- </activity>
-
- <activity
- android:name=".activity.FeedSettingsActivity"
- android:windowSoftInputMode="stateHidden"
- android:label="@string/feed_settings_label">
- </activity>
-
<service
android:name=".core.service.PlayerWidgetJobService"
android:permission="android.permission.BIND_JOB_SERVICE"
@@ -143,21 +141,6 @@
<activity android:name=".activity.StorageErrorActivity">
</activity>
<activity
- android:name=".activity.AboutActivity"
- android:configChanges="keyboardHidden|orientation|screenSize"
- android:label="@string/about_pref">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
- </activity>
- <activity
- android:name=".activity.StatisticsActivity"
- android:label="@string/statistics_label">
- <meta-data
- android:name="android.support.PARENT_ACTIVITY"
- android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
- </activity>
- <activity
android:name=".activity.ImportExportActivity"
android:label="@string/import_export">
<meta-data
@@ -209,6 +192,13 @@
android:name=".activity.OpmlFeedChooserActivity"
android:label="@string/opml_import_label">
</activity>
+ <activity
+ android:name=".activity.BugReportActivity"
+ android:label="@string/bug_report_title">
+ <meta-data
+ android:name="android.support.PARENT_ACTIVITY"
+ android:value="de.danoeh.antennapod.activity.PreferenceActivity"/>
+ </activity>
<activity
android:name=".activity.VideoplayerActivity"
@@ -239,6 +229,7 @@
<activity
android:name=".activity.OnlineFeedViewActivity"
android:configChanges="orientation|screenSize"
+ android:theme="@style/Theme.AntennaPod.Dark.Translucent"
android:label="@string/add_feed_label">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -347,10 +338,10 @@
<action android:name="de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"/>
</intent-filter>
</receiver>
-
+
<provider
android:authorities="@string/provider_authority"
- android:name="android.support.v4.content.FileProvider"
+ android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
diff --git a/app/src/main/assets/developers.csv b/app/src/main/assets/developers.csv
new file mode 100644
index 000000000..460e896f9
--- /dev/null
+++ b/app/src/main/assets/developers.csv
@@ -0,0 +1,119 @@
+danieloeh;968613;Original creator of AntennaPod (retired)
+mfietz;6860662;Maintainer
+ByteHamster;5811634;Maintainer
+TomHennen;5216560;Maintainer (retired)
+orionlee;250644;Contributor
+domingos86;9538859;Contributor
+andersonvom;69922;Contributor
+patheticpat;16046;Contributor
+spacecowboy;223655;Contributor
+brad;1614;Contributor
+Cj-Malone;10121513;Contributor
+gaul;848247;Contributor
+qkolj;6667105;Contributor
+pachecosf;46357909;Contributor
+ahangarha;11241315;Contributor
+rharriso;570910;Contributor
+xgouchet;818706;Contributor
+damoasda;46045854;Contributor
+sevenmaster;12869538;Contributor
+TheRealFalcon;153674;Contributor
+keunes;11229646;Contributor
+shortspider;5712543;Contributor
+hannesa2;3314607;Contributor
+jas14;569991;Contributor
+udif;809640;Contributor
+dirkmueller;1029152;Contributor
+jatinkumarg;20503830;Contributor
+peschmae0;4450993;Contributor
+orelogo;15976578;Contributor
+ydinath;4193331;Contributor
+CedricCabessa;365097;Contributor
+mchelen;30691;Contributor
+johnjohndoe;144518;Contributor
+dethstar;1239177;Contributor
+drabux;10663142;Contributor
+saqura;1935380;Contributor
+bibz;5141956;Contributor
+hzulla;1705654;Contributor
+deandreamatias;21011641;Contributor
+MeirAtIMDDE;4421079;Contributor
+egsavage;126165;Contributor
+ligi;111600;Contributor
+dreiss;4121;Contributor
+liesen;26872;Contributor
+nereocystis;2257107;Contributor
+rezanejati;16049370;Contributor
+twiceyuan;2619800;Contributor
+HaBaLeS;730902;Contributor
+volhol;11587858;Contributor
+CameronBanga;611354;Contributor
+HolgerJeromin;2410353;Contributor
+xisberto;1914956;Contributor
+jmue;898577;Contributor
+katrinleinweber;9948149;Contributor
+LatinSuD;451487;Contributor
+24hours;650407;Contributor
+SosoTughushi;19908097;Contributor
+fabolhak;20029691;Contributor
+archibishop;36948493;Contributor
+alifeflow;24603829;Contributor
+toggles;14695;Contributor
+matdb;48329535;Contributor
+kingargyle;177042;Contributor
+dsmith47;14109426;Contributor
+hannesaa2;18496079;Contributor
+jhunnius;9149031;Contributor
+ShadowIce;59123;Contributor
+raghulj;57007;Contributor
+raghulrm;5362986;Contributor
+stevomit;205959;Contributor
+skitt;2128935;Contributor
+mr-intj;6268767;Contributor
+tuxayo;2678215;Contributor
+alimemonzx;44647595;Contributor
+alanorth;191754;Contributor
+alexte;7724992;Contributor
+andrey-krutov;1488973;Contributor
+arantius;84729;Contributor
+bws9000;262625;Contributor
+chrissicool;232590;Contributor
+cszucko;1810383;Contributor
+CWftw;1498303;Contributor
+danielm5;66779;Contributor
+ariedov;958646;Contributor
+brettle;118192;Contributor
+eirikv;4076243;Contributor
+eerden;277513;Contributor
+jannic;232606;Contributor
+Foso;5015532;Contributor
+Kaligule;3586246;Contributor
+kvithayathil;1056073;Contributor
+luiscruz;1080714;Contributor
+mlasson;5814258;Contributor
+M-arcel;56698158;Contributor
+mo;7117;Contributor
+mdeveloper20;2319126;Contributor
+mschuetz;108637;Contributor
+MolarAmbiguity;10541979;Contributor
+mounirlamouri;573590;Contributor
+ortylp;470439;Contributor
+PtilopsisLeucotis;54054883;Contributor
+SamWhited;512573;Contributor
+selivan;1208989;Contributor
+sonnayasomnambula;7716779;Contributor
+sethoscope;534043;Contributor
+shantanahardy;26757164;Contributor
+mamehacker;16738348;Contributor
+danners;116551;Contributor
+corecode;177979;Contributor
+vimsick;20211590;Contributor
+edent;837136;Contributor
+atrus6;357881;Contributor
+waylife;3348620;Contributor
+wseemann;2296196;Contributor
+amhokies;3124968;Contributor
+axq;5077221;Contributor
+fossterer;4236021;Contributor
+lightonflux;1377943;Contributor
+minusf;3632883;Contributor
diff --git a/app/src/main/assets/licenses.xml b/app/src/main/assets/licenses.xml
new file mode 100644
index 000000000..c69f692cf
--- /dev/null
+++ b/app/src/main/assets/licenses.xml
@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<libraries>
+ <library
+ name="AntennaPod"
+ author="The AntennaPod team"
+ website="https://github.com/AntennaPod/AntennaPod/"
+ license="MIT"
+ licenseText="LICENSE.txt" />
+ <library
+ name="AntennaPod-AudioPlayer"
+ author="The AntennaPod team"
+ website="https://github.com/AntennaPod/AntennaPod-AudioPlayer/"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Apache Commons"
+ author="The Apache Software Foundation"
+ website="http://commons.apache.org/"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="EventBus"
+ author="greenrobot"
+ website="https://github.com/greenrobot/EventBus"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="ExoPlayer"
+ author="Google"
+ website="https://github.com/google/ExoPlayer"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Floating Action Button Speed Dial"
+ author="Roberto Leinardi"
+ website="https://github.com/leinardi/FloatingActionButtonSpeedDial"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Glide"
+ author="bumptech"
+ website="https://github.com/bumptech/glide/"
+ license="Simplified BSD"
+ licenseText="LICENSE_GLIDE.txt" />
+ <library
+ name="Iconify"
+ author="Joan Zapata"
+ website="https://github.com/JoanZapata/android-iconify"
+ license="Apache 2.0"
+ licenseText="LICENSE_ANDROID_ICONIFY.txt" />
+ <library
+ name="jsoup"
+ author="Jonathan Hedley"
+ website="http://jsoup.org/"
+ license="MIT"
+ licenseText="LICENSE_JSOUP.txt" />
+ <library
+ name="Material Design Icons"
+ author="Google"
+ website="https://github.com/google/material-design-icons"
+ license="Attribution-ShareAlike 4.0 International"
+ licenseText="LICENSE_MATERIAL_DESIGN_ICONS.txt" />
+ <library
+ name="Material Design Icons"
+ author="Templarian"
+ website="https://github.com/Templarian/MaterialDesign"
+ license="SIL Open Font, Version 1.1"
+ licenseText="LICENSE_SIL.txt" />
+ <library
+ name="OkHttp"
+ author="Square"
+ website="https://github.com/square/okhttp"
+ license="Apache 2.0"
+ licenseText="LICENSE_OKHTTP.txt" />
+ <library
+ name="Okio"
+ author="Square"
+ website="https://github.com/square/okio"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="RecyclerView-FlexibleDivider"
+ author="yqritc"
+ website="https://github.com/yqritc/RecyclerView-FlexibleDivider"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="RxAndroid"
+ author="ReactiveX"
+ website="https://github.com/ReactiveX/RxAndroid"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="StackBlur"
+ author="Enrique López Mañas"
+ website="https://github.com/kikoso/android-stackblur"
+ license="Apache 2.0"
+ licenseText="LICENSE_APACHE-2.0.txt" />
+ <library
+ name="Triangle Label View"
+ author="Shota Saito"
+ website="https://github.com/shts/TriangleLabelView"
+ license="Apache 2.0"
+ licenseText="LICENSE_TRIANGLE_LABEL_VIEW.txt" />
+</libraries>
diff --git a/app/src/main/assets/logo.png b/app/src/main/assets/logo.png
deleted file mode 100755
index 3b5261b28..000000000
--- a/app/src/main/assets/logo.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/assets/translators.csv b/app/src/main/assets/translators.csv
new file mode 100644
index 000000000..9670fb652
--- /dev/null
+++ b/app/src/main/assets/translators.csv
@@ -0,0 +1,46 @@
+Arabic;abdelrahman.fahem93, abdunnasir, abuzar3.khalid, desha, iDemo, mohamedagamy, msahouli, nabilMaghura
+Asturian (ast_ES);enolp
+Azerbaijani;danieloeh, kotfenix
+Basque;gaztainalde, pospolos, zakurranputza
+Bulgarian;solusitor
+Catalan;dvd1985, exort12, javiercoll, lambdani, marcmetallextrem, xc70
+Chinese (zh_CN);bebeauties38, cyril3, domingos86, dudeG, ErlichLiu, Felix2yu, gaohongyuan, Guaidaodl, Huck0, iconteral, JayYoung, jhxie, kavdx, kyleehee, linxiangyu, molisiye, owen8877, RainSlide, Sak94664, spice2wolf, stellaxuyi, tupunco, wi24rd, wongsyrone, xukeek, yangyang, yiqiok, YogaGuru
+Chinese (zh_TW);gugod, nigelinux, pggdt, Solomon, ymhuang0808
+Czech (cs_CZ);anotheranonymoususer, elich, Hanzmeister, mcepl, petnek, svetlemodry
+Danish;CasperHN, jhertel
+Dutch;e2jk, glotzbach, rwv, Vistaus
+English;mfietz, sterylmreep
+Estonian;Eraser
+Finnish;danieloeh, elguitar, Sahtor
+French;cactux, ChaoticMind, clombion, e2jk, edewaele, lacouture, LouFex, Matth78, mfietz, Poussinou, repat, Sioul, sterylmreep, TacoTheDank, Tilwa, vcariven, whenrow
+Galician;antiparvos, pikamoku, Raichely
+German;112358, altegedanken, barilla, benedikt.g, bitsunited, Buggi, ByteHamster, ceving, ChaoticMind, Chaquotay, dab0015, dadosch, DerSilly, die_otto, DJaeger, elkangaroo, enz, fidel, finsterwalder, Foso, GNi33, hightower5, HolgerJeromin, kalei, lohmann, LostInWeb, mfietz, moasda, nilso, repat, SAPlayer, schafia, Schroedingberg, sevenmaster, sucaml, Teaspoon, theonlytruth, weltenwort, Wyrrrd, ypid
+Modern Greek (1453-);antonist, danieloeh, hua2016s, jack.ath92, MSavoritias, pavlosv
+Hebrew (he_IL);amir.dafnyman, E1i9, mongoose4004, pinkasey, rellieberman, Yaron, הלוי11
+Hindi (hi_IN);ankitiitb1069, Isaasu, nmabhinandan, purple.coder, siddhusengar
+Hungarian;glatz.balazs, hurrikan, lna91, marthynw, meskobalazs, naren93, tszauer, ttyborg42
+Icelandic;marthjod
+Indonesian;jff, levirs565, luke137, rezafaiza, silvanael16
+Italian (it_IT);aalex70, allin, apanontin, Bonnee, buongiorgio, giuseppep, Guybrush88, ilmanzo, m.chinni, neonsoftware, niccord, nixxo, sevenmaster, theloca95
+Japanese;Naofumi, RACER1, sh3llc4t, TranslatorG
+Kannada (kn_IN);chiraag.nataraj, thejeshgn
+Korean;changwoo, libliboom, seungrye, skcha
+Lithuanian;naglis
+Macedonian;krisfremen
+Malayalam;joice, rashivkp, rubenroy
+Norwegian;timbast
+Norwegian Bokmål (nb_NO);corkie, heraldo, kongk, timbast
+Persian;ahangarha, F7D, sinamoghaddas
+Polish (pl_PL);d6210809, hiro2020, Iwangelion, kRkk, lomapur, mandlus, maniexx, Mephistofeles, shark103, tyle
+Portuguese;andersonvom, domingos86, emansije, smarquespt
+Portuguese (pt_BR);alexupits, alysonborges, andersonvom, arua, caioau, carlo_valente, castrors, deandreamatias, edman, Firmino, jackmiras, Junin, lipefire, lluccia, lucasmotacr, mbaltar, rogervezaro, RubeensVinicius, SamWilliam, silvanael16
+Romanian (ro_RO);corneliu.e, fuzzmz, ralienpp
+Russian (ru_RU);astra1, btimofeev, Duke_Raven, gammja, GaynullinDima, MegMasters98, mercutiy, null, overmind88, PtilopsisLeucotis, s.chebotar, shams4real, skvheadless, un_logic, Vladryyu, whereisthetea, zhenya97
+Slovenian (sl_SI);panter23
+Spanish;AleksSyntek, andersonvom, Atreyu94, coperfix, deandreamatias, domingos86, dvd1985, Fitoschido, frandavid100, hard_ware, javiercoll, Juanmuto, lambdani, LatinSuD, leogrignafini, palopezv, TacoTheDank, tres.14159, wakutiteo
+Swahili (macrolanguage);kmtra
+Swedish (sv_SE);albin.brantin, Bio, bpnilsson, ChaoticMind, jony08, nilso, SharpMelon, TiloWiklund, TwoD
+Telugu;Isaasu, veeven
+Turkish;basarancaner, brsata, Erdy, golcuk, overbite, Slsdem
+Ukrainian (uk_UA);older, paul_sm, sergiyr, zhenya97
+Vietnamese;abnvolk, nguyenvui, ppanhh, vietnamesel10n
diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
index ea2166674..061ea9ae2 100644
--- a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
+++ b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java
@@ -9,6 +9,9 @@ import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -32,13 +35,7 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
PrintWriter out = null;
try {
out = new PrintWriter(new FileWriter(path));
- out.println("[ Environment ]");
- out.println("Android version: " + Build.VERSION.RELEASE);
- out.println("OS version: " + System.getProperty("os.version"));
- out.println("AntennaPod version: " + BuildConfig.VERSION_NAME);
- out.println("Model: " + Build.MODEL);
- out.println("Device: " + Build.DEVICE);
- out.println("Product: " + Build.PRODUCT);
+ out.println(getSystemInfo());
out.println();
out.println("[ StackTrace ]");
ex.printStackTrace(out);
@@ -49,4 +46,15 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler {
}
defaultHandler.uncaughtException(thread, ex);
}
+
+ public static String getSystemInfo() {
+ return "[ Environment ]" +
+ "\nAndroid version: " + Build.VERSION.RELEASE +
+ "\nOS version: " + System.getProperty("os.version") +
+ "\nAntennaPod version: " + BuildConfig.VERSION_NAME +
+ "\nModel: " + Build.MODEL +
+ "\nDevice: " + Build.DEVICE +
+ "\nProduct: " + Build.PRODUCT +
+ "\nTime: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date()) + "\n";
+ }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
index cb2f597d6..4e6b8fa29 100644
--- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
+++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod;
import android.app.Application;
-import android.os.Build;
import android.os.StrictMode;
import com.joanzapata.iconify.Iconify;
@@ -10,7 +9,6 @@ import com.joanzapata.iconify.fonts.MaterialModule;
import de.danoeh.antennapod.core.ApCoreEventBusIndex;
import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.spa.SPAUtil;
import org.greenrobot.eventbus.EventBus;
@@ -26,44 +24,43 @@ public class PodcastApp extends Application {
}
}
- private static PodcastApp singleton;
+ private static PodcastApp singleton;
- public static PodcastApp getInstance() {
- return singleton;
- }
+ public static PodcastApp getInstance() {
+ return singleton;
+ }
- @Override
- public void onCreate() {
- super.onCreate();
+ @Override
+ public void onCreate() {
+ super.onCreate();
- Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter());
+ Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter());
- if(BuildConfig.DEBUG) {
- StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder()
- .detectLeakedSqlLiteObjects()
- .penaltyLog()
- .penaltyDropBox();
- builder.detectActivityLeaks();
- builder.detectLeakedClosableObjects();
- if(Build.VERSION.SDK_INT >= 16) {
- builder.detectLeakedRegistrationObjects();
- }
- StrictMode.setVmPolicy(builder.build());
- }
+ if (BuildConfig.DEBUG) {
+ StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder()
+ .detectLeakedSqlLiteObjects()
+ .penaltyLog()
+ .penaltyDropBox()
+ .detectActivityLeaks()
+ .detectLeakedClosableObjects()
+ .detectLeakedRegistrationObjects();
+ StrictMode.setVmPolicy(builder.build());
+ }
- singleton = this;
+ singleton = this;
- ClientConfig.initialize(this);
+ ClientConfig.initialize(this);
- EventDistributor.getInstance();
- Iconify.with(new FontAwesomeModule());
- Iconify.with(new MaterialModule());
+ Iconify.with(new FontAwesomeModule());
+ Iconify.with(new MaterialModule());
SPAUtil.sendSPAppsQueryFeedsIntent(this);
- EventBus.builder()
- .addIndex(new ApEventBusIndex())
- .addIndex(new ApCoreEventBusIndex())
- .installDefaultEventBus();
+ EventBus.builder()
+ .addIndex(new ApEventBusIndex())
+ .addIndex(new ApCoreEventBusIndex())
+ .logNoSubscriberMessages(false)
+ .sendNoSubscriberEvent(false)
+ .installDefaultEventBus();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
deleted file mode 100644
index 1bcdada44..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.Intent;
-import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.webkit.WebSettings;
-import android.webkit.WebView;
-import android.webkit.WebViewClient;
-import android.widget.LinearLayout;
-
-import org.apache.commons.io.IOUtils;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.nio.charset.Charset;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import io.reactivex.Single;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * Displays the 'about' screen
- */
-public class AboutActivity extends AppCompatActivity {
-
- private static final String TAG = AboutActivity.class.getSimpleName();
-
- private WebView webView;
- private LinearLayout webViewContainer;
- private Disposable disposable;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayShowHomeEnabled(true);
- setContentView(R.layout.about);
- webViewContainer = findViewById(R.id.webViewContainer);
- webView = findViewById(R.id.webViewAbout);
- webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- webView.setBackgroundColor(Color.TRANSPARENT);
- webView.setWebViewClient(new WebViewClient() {
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (url.startsWith("http")) {
- Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(browserIntent);
- return true;
- } else {
- url = url.replace("file:///android_asset/", "");
- loadAsset(url);
- return true;
- }
- }
-
- });
- loadAsset("about.html");
- }
-
- private void loadAsset(String filename) {
- disposable = Single.create(subscriber -> {
- InputStream input = null;
- try {
- TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes(
- new int[] { R.attr.about_screen_font_color, R.attr.about_screen_background,
- R.attr.about_screen_card_background, R.attr.about_screen_card_border});
- String fontColor = String.format("#%06X", 0xFFFFFF & res.getColor(0, 0));
- String backgroundColor = String.format("#%06X", 0xFFFFFF & res.getColor(1, 0));
- String cardBackground = String.format("#%06X", 0xFFFFFF & res.getColor(2, 0));
- String cardBorder = String.format("#%06X", 0xFFFFFF & res.getColor(3, 0));
- res.recycle();
- input = getAssets().open(filename);
- String webViewData = IOUtils.toString(input, Charset.defaultCharset());
- if (!webViewData.startsWith("<!DOCTYPE html>")) {
- webViewData = webViewData.replace("%", "&#37;");
- webViewData =
- "<!DOCTYPE html>" +
- "<html>" +
- "<head>" +
- " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" +
- " <style type=\"text/css\">" +
- " @font-face {" +
- " font-family: 'Roboto-Light';" +
- " src: url('file:///android_asset/Roboto-Light.ttf');" +
- " }" +
- " * {" +
- " color: @fontcolor@;" +
- " font-family: roboto-Light;" +
- " font-size: 8pt;" +
- " }" +
- " </style>" +
- "</head><body><p>" + webViewData + "</p></body></html>";
- webViewData = webViewData.replace("\n", "<br/>");
- }
- webViewData = webViewData.replace("@fontcolor@", fontColor);
- webViewData = webViewData.replace("@background@", backgroundColor);
- webViewData = webViewData.replace("@card_background@", cardBackground);
- webViewData = webViewData.replace("@card_border@", cardBorder);
- subscriber.onSuccess(webViewData);
- } catch (IOException e) {
- Log.e(TAG, Log.getStackTraceString(e));
- subscriber.onError(e);
- } finally {
- IOUtils.closeQuietly(input);
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- webViewData ->
- webView.loadDataWithBaseURL("file:///android_asset/", webViewData.toString(), "text/html", "utf-8", "file:///android_asset/" + filename.toString()),
- error -> Log.e(TAG, Log.getStackTraceString(error))
- );
- }
-
- @Override
- public void onBackPressed() {
- if (webView.canGoBack()) {
- webView.goBack();
- } else {
- super.onBackPressed();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- onBackPressed();
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- @Override
- protected void onDestroy() {
- super.onDestroy();
- if (disposable != null) {
- disposable.dispose();
- }
- if (webViewContainer != null && webView != null) {
- webViewContainer.removeAllViews();
- webView.destroy();
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
index 2321a3602..7f8c14b03 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.activity;
import android.content.Intent;
-import android.support.v4.view.ViewCompat;
+import androidx.core.view.ViewCompat;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -12,6 +12,8 @@ import java.util.Locale;
import java.util.concurrent.atomic.AtomicBoolean;
import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
@@ -60,11 +62,13 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
if (controller == null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
return;
}
updatePlaybackSpeedButtonText();
ViewCompat.setAlpha(butPlaybackSpeed, controller.canSetPlaybackSpeed() ? 1.0f : 0.5f);
butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
@Override
@@ -74,14 +78,15 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
}
if (controller == null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
return;
}
float speed = 1.0f;
if(controller.canSetPlaybackSpeed()) {
- speed = UserPreferences.getPlaybackSpeed();
+ speed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(controller.getMedia());
}
- String speedStr = new DecimalFormat("0.00x").format(speed);
- butPlaybackSpeed.setText(speedStr);
+ String speedStr = new DecimalFormat("0.00").format(speed);
+ txtvPlaybackSpeed.setText(speedStr);
}
@Override
@@ -102,7 +107,9 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
String[] availableSpeeds = UserPreferences.getPlaybackSpeedArray();
DecimalFormatSymbols format = new DecimalFormatSymbols(Locale.US);
format.setDecimalSeparator('.');
- String currentSpeed = new DecimalFormat("0.00", format).format(UserPreferences.getPlaybackSpeed());
+
+ float currentSpeedValue = controller.getCurrentPlaybackSpeedMultiplier();
+ String currentSpeed = new DecimalFormat("0.00", format).format(currentSpeedValue);
// Provide initial value in case the speed list has changed
// out from under us
@@ -124,6 +131,12 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
break;
}
}
+
+ try {
+ PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(Float.parseFloat(newSpeed));
+ } catch (NumberFormatException e) {
+ // Well this was awkward...
+ }
UserPreferences.setPlaybackSpeed(newSpeed);
controller.setPlaybackSpeed(Float.parseFloat(newSpeed));
onPositionObserverUpdate();
@@ -136,6 +149,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity {
return true;
});
butPlaybackSpeed.setVisibility(View.VISIBLE);
+ txtvPlaybackSpeed.setVisibility(View.VISIBLE);
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
new file mode 100644
index 000000000..666eacfa8
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java
@@ -0,0 +1,55 @@
+package de.danoeh.antennapod.activity;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.appcompat.app.AppCompatActivity;
+import android.widget.TextView;
+import de.danoeh.antennapod.CrashReportWriter;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+/**
+ * Displays the 'crash report' screen
+ */
+public class BugReportActivity extends AppCompatActivity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ getSupportActionBar().setDisplayShowHomeEnabled(true);
+ setContentView(R.layout.bug_report);
+
+ TextView crashDetailsText = findViewById(R.id.crash_report_logs);
+
+ try {
+ File crashFile = CrashReportWriter.getFile();
+ String crashReportContent = IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8"));
+ crashDetailsText.setText(crashReportContent);
+ } catch (IOException e) {
+ e.printStackTrace();
+ crashDetailsText.setText("No crash report recorded\n" + CrashReportWriter.getSystemInfo());
+ }
+
+ findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> {
+ IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues");
+ });
+
+ findViewById(R.id.btn_copy_log).setOnClickListener(v -> {
+ ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsText.getText());
+ clipboard.setPrimaryClip(clip);
+ Snackbar.make(findViewById(android.R.id.content), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
+ });
+ }
+}
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 871e9c279..c60c7b769 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java
@@ -49,6 +49,7 @@ public class CastplayerActivity extends MediaplayerInfoActivity {
super.setupGUI();
if (butPlaybackSpeed != null) {
butPlaybackSpeed.setVisibility(View.GONE);
+ txtvPlaybackSpeed.setVisibility(View.GONE);
}
// if (butCastDisconnect != null) {
// butCastDisconnect.setOnClickListener(v -> castManager.disconnect());
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 33def125e..49ce954bc 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java
@@ -5,9 +5,9 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
-import android.support.v4.app.NavUtils;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
+import androidx.core.app.NavUtils;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
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 5e04d743d..08ebc6421 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java
@@ -3,8 +3,8 @@ package de.danoeh.antennapod.activity;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
deleted file mode 100644
index 26e360bd3..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java
+++ /dev/null
@@ -1,225 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.ClipData;
-import android.content.Context;
-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.TextUtils;
-import android.util.Log;
-import android.view.Menu;
-import android.view.MenuInflater;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ImageView;
-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;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
-import de.danoeh.antennapod.core.feed.Feed;
-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.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 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 FeedInfoActivity extends AppCompatActivity {
-
- public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
- private static final String TAG = "FeedInfoActivity";
- private Feed feed;
-
- private ImageView imgvCover;
- private TextView txtvTitle;
- private TextView txtvDescription;
- private TextView lblLanguage;
- private TextView txtvLanguage;
- private TextView lblAuthor;
- private TextView txtvAuthor;
- private TextView txtvUrl;
-
- private Disposable disposable;
-
-
- private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- if(feed != null && feed.getDownload_url() != null) {
- String url = feed.getDownload_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();
- }
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- setContentView(R.layout.feedinfo);
- 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));
-
-
- 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);
-
- 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;
- Log.d(TAG, "Language is " + feed.getLanguage());
- Log.d(TAG, "Author is " + feed.getAuthor());
- Log.d(TAG, "URL is " + feed.getDownload_url());
- Glide.with(FeedInfoActivity.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(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());
-
- String description = feed.getDescription();
- if(description != null) {
- if(Feed.TYPE_ATOM1.equals(feed.getType())) {
- HtmlToPlainText formatter = new HtmlToPlainText();
- Document feedDescription = Jsoup.parse(feed.getDescription());
- description = StringUtils.trim(formatter.getPlainText(feedDescription));
- }
- } else {
- description = "";
- }
- txtvDescription.setText(description);
-
- if (!TextUtils.isEmpty(feed.getAuthor())) {
- txtvAuthor.setText(feed.getAuthor());
- txtvAuthorHeader.setText(feed.getAuthor());
- } else {
- lblAuthor.setVisibility(View.GONE);
- txtvAuthor.setVisibility(View.GONE);
- }
- if (!TextUtils.isEmpty(feed.getLanguage())) {
- txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage()));
- } else {
- lblLanguage.setVisibility(View.GONE);
- txtvLanguage.setVisibility(View.GONE);
- }
- txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
- Iconify.addIcons(txtvUrl);
-
- supportInvalidateOptionsMenu();
- }, error -> {
- Log.d(TAG, Log.getStackTraceString(error));
- finish();
- }, () -> {
- Log.e(TAG, "Activity was started with invalid arguments");
- finish();
- });
- }
-
- @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.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);
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java
deleted file mode 100644
index fbd19f88a..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java
+++ /dev/null
@@ -1,130 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.arch.lifecycle.ViewModelProviders;
-import android.graphics.LightingColorFilter;
-import android.os.Bundle;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v7.app.AppCompatActivity;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.MenuItem;
-import android.view.View;
-import android.widget.ImageView;
-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.Feed;
-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.fragment.FeedSettingsFragment;
-import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel;
-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 Feed feed;
- private Disposable disposable;
- private ImageView imgvCover;
- private TextView txtvTitle;
- private ImageView imgvBackground;
- private TextView txtvAuthorHeader;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- setContentView(R.layout.feedsettings);
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
-
- imgvCover = findViewById(R.id.imgvCover);
- txtvTitle = findViewById(R.id.txtvTitle);
- txtvAuthorHeader = findViewById(R.id.txtvAuthor);
- 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));
-
- long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1);
- disposable = ViewModelProviders.of(this).get(FeedSettingsViewModel.class).getFeed(feedId)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- feed = result;
- showFragment();
- showHeader();
- }, error -> {
- Log.d(TAG, Log.getStackTraceString(error));
- finish();
- }, () -> {
- Log.e(TAG, "Activity was started with invalid arguments");
- finish();
- });
- }
-
- private void showFragment() {
- FeedSettingsFragment fragment = new FeedSettingsFragment();
- fragment.setArguments(getIntent().getExtras());
-
- FragmentManager fragmentManager = getSupportFragmentManager();
- FragmentTransaction fragmentTransaction =
- fragmentManager.beginTransaction();
- fragmentTransaction.replace(R.id.settings_fragment_container, fragment);
- fragmentTransaction.commit();
- }
-
- private void showHeader() {
- txtvTitle.setText(feed.getTitle());
-
- if (!TextUtils.isEmpty(feed.getAuthor())) {
- txtvAuthorHeader.setText(feed.getAuthor());
- }
-
- 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);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (disposable != null) {
- disposable.dispose();
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- switch (item.getItemId()) {
- case android.R.id.home:
- finish();
- return true;
- default:
- return super.onOptionsItemSelected(item);
- }
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
index 9795c1240..f85a1cd77 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java
@@ -7,10 +7,10 @@ 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 com.google.android.material.snackbar.Snackbar;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
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 339ce01c2..0023e6d7f 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.activity;
import android.annotation.TargetApi;
import android.app.ProgressDialog;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -10,15 +11,16 @@ import android.database.DataSetObserver;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
-import android.support.annotation.VisibleForTesting;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentTransaction;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.widget.Toolbar;
+import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentTransaction;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
@@ -32,9 +34,13 @@ import android.widget.Toast;
import com.bumptech.glide.Glide;
-import de.danoeh.antennapod.preferences.PreferenceUpgrader;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.Validate;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
import java.util.List;
@@ -43,9 +49,7 @@ import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.event.ProgressEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -66,14 +70,13 @@ import de.danoeh.antennapod.fragment.FeedItemlistFragment;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.fragment.QueueFragment;
import de.danoeh.antennapod.fragment.SubscriptionFragment;
+import de.danoeh.antennapod.fragment.TransitionEffect;
import de.danoeh.antennapod.menuhandler.NavDrawerActivity;
+import de.danoeh.antennapod.preferences.PreferenceUpgrader;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
/**
* The activity that is shown when the user launches the app.
@@ -82,9 +85,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private static final String TAG = "MainActivity";
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.UNREAD_ITEMS_UPDATE;
-
public static final String PREF_NAME = "MainActivityPrefs";
public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch";
public static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag";
@@ -93,7 +93,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
public static final String EXTRA_NAV_INDEX = "nav_index";
public static final String EXTRA_FRAGMENT_TAG = "fragment_tag";
public static final String EXTRA_FRAGMENT_ARGS = "fragment_args";
- public static final String EXTRA_FEED_ID = "fragment_feed_id";
+ private static final String EXTRA_FEED_ID = "fragment_feed_id";
private static final String SAVE_BACKSTACK_COUNT = "backstackCount";
private static final String SAVE_TITLE = "title";
@@ -127,6 +127,14 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
private long lastBackButtonPressTime = 0;
+ @NonNull
+ public static Intent getIntentToOpenFeed(@NonNull Context context, long feedId) {
+ Intent intent = new Intent(context.getApplicationContext(), MainActivity.class);
+ intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+ return intent;
+ }
+
@Override
public void onCreate(Bundle savedInstanceState) {
setTheme(UserPreferences.getNoTitleTheme());
@@ -240,7 +248,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
SharedPreferences.Editor edit = prefs.edit();
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
- edit.commit();
+ edit.apply();
}
}
@@ -309,8 +317,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
fragment = new AddFeedFragment();
break;
case SubscriptionFragment.TAG:
- SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
- fragment = subscriptionFragment;
+ fragment = new SubscriptionFragment();
break;
default:
// default to the queue
@@ -368,15 +375,34 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
}
- public void loadChildFragment(Fragment fragment) {
+ public void loadChildFragment(Fragment fragment, TransitionEffect transition) {
Validate.notNull(fragment);
- FragmentManager fm = getSupportFragmentManager();
- fm.beginTransaction()
- .replace(R.id.main_view, fragment, "main")
+ FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
+
+ switch (transition) {
+ case FADE:
+ transaction.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
+ break;
+ case FLIP:
+ transaction.setCustomAnimations(
+ R.anim.card_flip_left_in,
+ R.anim.card_flip_left_out,
+ R.anim.card_flip_right_in,
+ R.anim.card_flip_right_out);
+ break;
+ }
+
+ transaction
+ .hide(getSupportFragmentManager().findFragmentByTag("main"))
+ .add(R.id.main_view, fragment, "main")
.addToBackStack(null)
.commit();
}
+ public void loadChildFragment(Fragment fragment) {
+ loadChildFragment(fragment, TransitionEffect.NONE);
+ }
+
public void dismissChildFragment() {
getSupportFragmentManager().popBackStack();
}
@@ -461,7 +487,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
RatingDialog.init(this);
}
@@ -489,7 +514,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@Override
protected void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
if (disposable != null) {
disposable.dispose();
@@ -778,25 +802,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
}
@Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(ProgressEvent event) {
- Log.d(TAG, "onEvent(" + event + ")");
- switch(event.action) {
- case START:
- pd = new ProgressDialog(this);
- pd.setMessage(event.message);
- pd.setIndeterminate(true);
- pd.setCancelable(false);
- pd.show();
- break;
- case END:
- if(pd != null) {
- pd.dismiss();
- }
- break;
- }
- }
-
- @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(MessageEvent event) {
Log.d(TAG, "onEvent(" + event + ")");
View parentLayout = findViewById(R.id.drawer_layout);
@@ -807,16 +812,16 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
snackbar.show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadData();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadData();
- }
- }
- };
+
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadData();
+ }
private void handleNavIntent() {
Log.d(TAG, "handleNavIntent()");
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 52497a27f..538ed1231 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -9,14 +9,14 @@ import android.content.pm.PackageManager;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.PixelFormat;
-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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.app.ActivityCompat;
+import androidx.core.app.ActivityOptionsCompat;
+import androidx.core.content.ContextCompat;
+import androidx.appcompat.app.AlertDialog;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -28,12 +28,12 @@ import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;
-import com.afollestad.materialdialogs.MaterialDialog;
import com.bumptech.glide.Glide;
import com.joanzapata.iconify.IconDrawable;
import com.joanzapata.iconify.fonts.FontAwesomeIcons;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
@@ -63,6 +63,10 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.StringUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
@@ -73,7 +77,8 @@ 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;
+ private static final int REQUEST_CODE_STORAGE_PLAY_VIDEO = 42;
+ private static final int REQUEST_CODE_STORAGE_PLAY_AUDIO = 43;
PlaybackController controller;
@@ -194,6 +199,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
};
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ onPositionObserverUpdate();
+ }
+
private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) {
return activity.txtvFF;
}
@@ -274,6 +284,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
controller.init();
loadMediaInfo();
onPositionObserverUpdate();
+ EventBus.getDefault().register(this);
}
@Override
@@ -286,6 +297,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
if (disposable != null) {
disposable.dispose();
}
+ EventBus.getDefault().unregister(this);
super.onStop();
}
@@ -322,6 +334,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
Playable media = controller.getMedia();
boolean isFeedMedia = media != null && (media instanceof FeedMedia);
+ menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed
+
boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null );
menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
@@ -377,10 +391,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
| Intent.FLAG_ACTIVITY_NEW_TASK);
View cover = findViewById(R.id.imgvCover);
- if (cover != null && Build.VERSION.SDK_INT >= 16) {
- ActivityOptionsCompat options = ActivityOptionsCompat.
- makeSceneTransitionAnimation(MediaplayerActivity.this,
- cover, "coverTransition");
+ if (cover != null) {
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeSceneTransitionAnimation(MediaplayerActivity.this, cover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
@@ -389,47 +402,38 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
return true;
} else {
if (media != null) {
+ final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem
switch (item.getItemId()) {
case R.id.add_to_favorites_item:
- if(media instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia)media).getItem();
- if(feedItem != null) {
- DBWriter.addFavoriteItem(feedItem);
- isFavorite = true;
- invalidateOptionsMenu();
- Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
- .show();
- }
+ if (feedItem != null) {
+ DBWriter.addFavoriteItem(feedItem);
+ isFavorite = true;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
+ .show();
}
break;
case R.id.remove_from_favorites_item:
- if(media instanceof FeedMedia) {
- FeedItem feedItem = ((FeedMedia)media).getItem();
- if(feedItem != null) {
- DBWriter.removeFavoriteItem(feedItem);
- isFavorite = false;
- invalidateOptionsMenu();
- Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
- .show();
- }
+ if (feedItem != null) {
+ DBWriter.removeFavoriteItem(feedItem);
+ isFavorite = false;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
+ .show();
}
break;
case R.id.disable_sleeptimer_item:
if (controller.serviceAvailable()) {
- MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this);
- stDialog.title(R.string.sleep_timer_label);
- stDialog.content(getString(R.string.time_left_label)
- + Converter.getDurationStringLong((int) controller
- .getSleepTimerTimeLeft()));
- stDialog.positiveText(R.string.disable_sleeptimer_label);
- stDialog.negativeText(R.string.cancel_label);
- stDialog.onPositive((dialog, which) -> {
- dialog.dismiss();
- controller.disableSleepTimer();
- });
- stDialog.onNegative((dialog, which) -> dialog.dismiss());
- stDialog.build().show();
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.sleep_timer_label)
+ .setMessage(getString(R.string.time_left_label)
+ + Converter.getDurationStringLong((int) controller
+ .getSleepTimerTimeLeft()))
+ .setPositiveButton(R.string.disable_sleeptimer_label, (dialog, which)
+ -> controller.disableSleepTimer())
+ .setNegativeButton(R.string.cancel_label, null)
+ .show();
}
break;
case R.id.set_sleeptimer_item:
@@ -448,28 +452,33 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(isPlayingVideo);
dialog.show(getSupportFragmentManager(), "playback_controls");
break;
+ case R.id.open_feed_item:
+ if (feedItem != null) {
+ Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId());
+ startActivity(intent);
+ }
+ break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(getWebsiteLinkWithFallback(media));
- startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media));
break;
case R.id.share_link_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem());
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemLink(this, feedItem);
}
break;
case R.id.share_download_url_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem());
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem);
}
break;
case R.id.share_link_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true);
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemLink(this, feedItem, true);
}
break;
case R.id.share_download_url_with_position_item:
- if (media instanceof FeedMedia) {
- ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true);
+ if (feedItem != null) {
+ ShareUtils.shareFeedItemDownloadLink(this, feedItem, true);
}
break;
case R.id.share_file:
@@ -490,7 +499,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
private static String getWebsiteLinkWithFallback(Playable media) {
if (media == null) {
return null;
- } else if (media.getWebsiteLink() != null) {
+ } else if (StringUtils.isNotBlank(media.getWebsiteLink())) {
return media.getWebsiteLink();
} else if (media instanceof FeedMedia) {
return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem());
@@ -635,7 +644,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
}
- static public void showSkipPreference(Activity activity, SkipDirection direction) {
+ public static void showSkipPreference(Activity activity, SkipDirection direction) {
int checked = 0;
int skipSecs = direction.getPrefSkipSeconds();
final int[] values = activity.getResources().getIntArray(R.array.seek_delta_values);
@@ -813,11 +822,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
private void checkFavorite() {
- Playable playable = controller.getMedia();
- if (!(playable instanceof FeedMedia)) {
- return;
- }
- FeedItem feedItem = ((FeedMedia) playable).getItem();
+ FeedItem feedItem = getFeedItem(controller.getMedia());
if (feedItem == null) {
return;
}
@@ -847,10 +852,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
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);
}
+
+ int code = REQUEST_CODE_STORAGE_PLAY_AUDIO;
+ if (type == MediaType.VIDEO) {
+ code = REQUEST_CODE_STORAGE_PLAY_VIDEO;
+ }
+ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, code);
return;
}
@@ -858,6 +866,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
ExternalMedia media = new ExternalMedia(intent.getData().getPath(), type);
new PlaybackServiceStarter(this, media)
+ .callEvenIfRunning(true)
.startWhenPrepared(true)
.shouldStream(false)
.prepareImmediately(true)
@@ -865,11 +874,24 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
}
@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();
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, int[] grantResults) {
+ if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ if (requestCode == REQUEST_CODE_STORAGE_PLAY_AUDIO) {
+ playExternalMedia(getIntent(), MediaType.AUDIO);
+ } else if (requestCode == REQUEST_CODE_STORAGE_PLAY_VIDEO) {
+ playExternalMedia(getIntent(), MediaType.VIDEO);
}
+ } else {
+ Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show();
+ }
+ }
+
+ @Nullable
+ private static FeedItem getFeedItem(@Nullable Playable playable) {
+ if (playable instanceof FeedMedia) {
+ return ((FeedMedia) playable).getItem();
+ } else {
+ return null;
}
}
}
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 4fec1cfc5..21f4ad5be 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java
@@ -7,15 +7,15 @@ import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle;
-import android.support.design.widget.AppBarLayout;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentStatePagerAdapter;
-import android.support.v4.view.ViewPager;
-import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
-import android.support.v7.widget.Toolbar;
+import com.google.android.material.appbar.AppBarLayout;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+import androidx.drawerlayout.widget.DrawerLayout;
+import androidx.appcompat.app.ActionBarDrawerToggle;
+import androidx.appcompat.widget.Toolbar;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextMenu;
@@ -23,9 +23,9 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.AdapterView;
-import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ListView;
+import android.widget.TextView;
import android.widget.Toast;
import com.viewpagerindicator.CirclePageIndicator;
@@ -36,8 +36,8 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -63,7 +63,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -92,7 +91,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
NavListAdapter.SUBSCRIPTION_LIST_TAG
};
- Button butPlaybackSpeed;
+ ImageButton butPlaybackSpeed;
+ TextView txtvPlaybackSpeed;
ImageButton butCastDisconnect;
private DrawerLayout drawerLayout;
private NavListAdapter navAdapter;
@@ -119,8 +119,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
if (disposable != null) {
disposable.dispose();
}
- EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
saveCurrentFragment();
}
@@ -172,8 +170,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
@Override
protected void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
loadData();
}
@@ -258,6 +254,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
});
butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed);
+ txtvPlaybackSpeed = findViewById(R.id.txtvPlaybackSpeed);
butCastDisconnect = findViewById(R.id.butCastDisconnect);
pager = findViewById(R.id.pager);
@@ -446,16 +443,10 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
snackbar.show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
-
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EventDistributor.FEED_LIST_UPDATE & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadData();
- }
- }
- };
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadData();
+ }
private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
index ea7687bc9..0c6c63e73 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java
@@ -7,12 +7,11 @@ import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-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 androidx.annotation.NonNull;
+import androidx.annotation.UiThread;
+import androidx.core.app.NavUtils;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
@@ -22,23 +21,19 @@ import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
-import android.widget.RelativeLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
-import de.danoeh.antennapod.core.glide.FastBlurTransformation;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import org.apache.commons.lang3.StringUtils;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
import java.io.File;
import java.io.IOException;
@@ -50,11 +45,11 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedPreferences;
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.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -94,60 +89,31 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
public static final String ARG_TITLE = "title";
private static final int RESULT_ERROR = 2;
private static final String TAG = "OnlineFeedViewActivity";
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE;
private volatile List<Feed> feeds;
private Feed feed;
private String selectedDownloadUrl;
private Downloader downloader;
private boolean isPaused;
+ private boolean didPressSubscribe = false;
private Dialog dialog;
-
+ private ListView listView;
private Button subscribeButton;
+ private ProgressBar progressBar;
private Disposable download;
private Disposable parser;
private Disposable updater;
- private final EventDistributor.EventListener listener = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) {
- updater = Observable.fromCallable(DBReader::getFeedList)
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(
- feeds -> {
- OnlineFeedViewActivity.this.feeds = feeds;
- setSubscribeButtonState(feed);
- }, error -> Log.e(TAG, Log.getStackTraceString(error))
- );
- } else if ((arg & EVENTS) != 0) {
- setSubscribeButtonState(feed);
- }
- }
- };
-
- @Subscribe(threadMode = ThreadMode.MAIN)
- public void onEventMainThread(DownloadEvent event) {
- Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- setSubscribeButtonState(feed);
- }
@Override
protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
+ setTheme(UserPreferences.getTranslucentTheme());
super.onCreate(savedInstanceState);
- ActionBar actionBar = getSupportActionBar();
- if (actionBar != null) {
- actionBar.setDisplayHomeAsUpEnabled(true);
- }
-
- if (actionBar != null && getIntent() != null && getIntent().hasExtra(ARG_TITLE)) {
- actionBar.setTitle(getIntent().getStringExtra(ARG_TITLE));
- }
-
StorageUtils.checkStorageAvailability(this);
+ setContentView(R.layout.onlinefeedview_activity);
+ listView = findViewById(R.id.listview);
+ progressBar = findViewById(R.id.progressBar);
String feedUrl = null;
if (getIntent().hasExtra(ARG_FEEDURL)) {
@@ -156,9 +122,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
|| TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) {
feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)
? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString();
- if (actionBar != null) {
- actionBar.setTitle(R.string.add_feed_label);
- }
}
if (feedUrl == null) {
@@ -183,26 +146,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* Displays a progress indicator.
*/
private void setLoadingLayout() {
- RelativeLayout rl = new RelativeLayout(this);
- RelativeLayout.LayoutParams rlLayoutParams = new RelativeLayout.LayoutParams(
- LinearLayout.LayoutParams.MATCH_PARENT,
- LinearLayout.LayoutParams.MATCH_PARENT);
-
- ProgressBar pb = new ProgressBar(this);
- pb.setIndeterminate(true);
- RelativeLayout.LayoutParams pbLayoutParams = new RelativeLayout.LayoutParams(
- LinearLayout.LayoutParams.WRAP_CONTENT,
- LinearLayout.LayoutParams.WRAP_CONTENT);
- pbLayoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
- rl.addView(pb, pbLayoutParams);
- addContentView(rl, rlLayoutParams);
+ progressBar.setVisibility(View.VISIBLE);
+ findViewById(R.id.feedDisplay).setVisibility(View.GONE);
}
@Override
protected void onStart() {
super.onStart();
isPaused = false;
- EventDistributor.getInstance().register(listener);
EventBus.getDefault().register(this);
}
@@ -210,7 +161,6 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
protected void onStop() {
super.onStop();
isPaused = true;
- EventDistributor.getInstance().unregister(listener);
EventBus.getDefault().unregister(this);
if (downloader != null && !downloader.isFinished()) {
downloader.cancel();
@@ -251,6 +201,12 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
@Override
+ public void finish() {
+ super.finish();
+ overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
+ }
+
+ @Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
@@ -312,6 +268,25 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
}
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ updater = Observable.fromCallable(DBReader::getFeedList)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ feeds -> {
+ OnlineFeedViewActivity.this.feeds = feeds;
+ handleUpdatedFeedStatus(feed);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error))
+ );
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ handleUpdatedFeedStatus(feed);
+ }
+
private void parseFeed() {
if (feed == null || (feed.getFile_url() == null && feed.isDownloaded())) {
throw new IllegalStateException("feed must be non-null and downloaded when parseFeed is called");
@@ -367,20 +342,14 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* This method is executed on a background thread
*/
private void beforeShowFeedInformation(Feed feed) {
- final HtmlToPlainText formatter = new HtmlToPlainText();
- if(Feed.TYPE_ATOM1.equals(feed.getType()) && feed.getDescription() != null) {
- // remove HTML tags from descriptions
- Log.d(TAG, "Removing HTML from feed description");
- Document feedDescription = Jsoup.parse(feed.getDescription());
- feed.setDescription(StringUtils.trim(formatter.getPlainText(feedDescription)));
- }
+ Log.d(TAG, "Removing HTML from feed description");
+
+ feed.setDescription(HtmlToPlainText.getPlainText(feed.getDescription()));
+
Log.d(TAG, "Removing HTML from shownotes");
if (feed.getItems() != null) {
for (FeedItem item : feed.getItems()) {
- if (item.getDescription() != null) {
- Document itemDescription = Jsoup.parse(item.getDescription());
- item.setDescription(StringUtils.trim(formatter.getPlainText(itemDescription)));
- }
+ item.setDescription(HtmlToPlainText.getPlainText(item.getDescription()));
}
}
}
@@ -390,30 +359,27 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
* This method is executed on the GUI thread.
*/
private void showFeedInformation(final Feed feed, Map<String, String> alternateFeedUrls) {
- setContentView(R.layout.listview_activity);
-
+ progressBar.setVisibility(View.GONE);
+ findViewById(R.id.feedDisplay).setVisibility(View.VISIBLE);
this.feed = feed;
this.selectedDownloadUrl = feed.getDownload_url();
- EventDistributor.getInstance().register(listener);
- ListView listView = findViewById(R.id.listview);
listView.setSelector(android.R.color.transparent);
- LayoutInflater inflater = LayoutInflater.from(this);
- View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false);
- listView.addHeaderView(header);
-
listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems()));
- ImageView cover = header.findViewById(R.id.imgvCover);
- ImageView headerBackground = header.findViewById(R.id.imgvBackground);
- header.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
- header.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
+ ImageView cover = findViewById(R.id.imgvCover);
+ ImageView headerBackground = findViewById(R.id.imgvBackground);
+ findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
+ findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
headerBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
- TextView title = header.findViewById(R.id.txtvTitle);
- TextView author = header.findViewById(R.id.txtvAuthor);
+ TextView title = findViewById(R.id.txtvTitle);
+ TextView author = findViewById(R.id.txtvAuthor);
+ Spinner spAlternateUrls = findViewById(R.id.spinnerAlternateUrls);
+
+ View header = View.inflate(this, R.layout.onlinefeedview_header, null);
+ listView.addHeaderView(header);
TextView description = header.findViewById(R.id.txtvDescription);
- Spinner spAlternateUrls = header.findViewById(R.id.spinnerAlternateUrls);
- subscribeButton = header.findViewById(R.id.butSubscribe);
+ subscribeButton = findViewById(R.id.butSubscribe);
if (StringUtils.isNotBlank(feed.getImageUrl())) {
Glide.with(this)
@@ -441,13 +407,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
description.setText(feed.getDescription());
subscribeButton.setOnClickListener(v -> {
- 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
- intent.putExtra(MainActivity.EXTRA_FEED_ID, getFeedId(feed));
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
- startActivity(intent);
+ if (feedInFeedlist(feed)) {
+ openFeed();
} else {
Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle());
f.setPreferences(feed.getPreferences());
@@ -458,7 +419,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
Log.e(TAG, Log.getStackTraceString(e));
DownloadRequestErrorDialogCreator.newRequestErrorDialog(this, e.getMessage());
}
- setSubscribeButtonState(feed);
+ didPressSubscribe = true;
+ handleUpdatedFeedStatus(feed);
}
});
@@ -504,17 +466,28 @@ public class OnlineFeedViewActivity extends AppCompatActivity {
}
});
}
- setSubscribeButtonState(feed);
+ handleUpdatedFeedStatus(feed);
}
- private void setSubscribeButtonState(Feed feed) {
+ private void openFeed() {
+ // feed.getId() is always 0, we have to retrieve the id from the feed list from
+ // the database
+ Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed));
+ finish();
+ startActivity(intent);
+ }
+
+ private void handleUpdatedFeedStatus(Feed feed) {
if (subscribeButton != null && feed != null) {
if (DownloadRequester.getInstance().isDownloadingFile(feed.getDownload_url())) {
subscribeButton.setEnabled(false);
- subscribeButton.setText(R.string.downloading_label);
+ subscribeButton.setText(R.string.subscribing_label);
} else if (feedInFeedlist(feed)) {
subscribeButton.setEnabled(true);
subscribeButton.setText(R.string.open_podcast);
+ if (didPressSubscribe) {
+ openFeed();
+ }
} else {
subscribeButton.setEnabled(true);
subscribeButton.setText(R.string.subscribe_label);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
index 72759c59c..376074525 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.activity;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
+import androidx.appcompat.app.AppCompatActivity;
import android.util.SparseBooleanArray;
import android.view.Menu;
import android.view.MenuInflater;
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 c04ae051e..d7a4b9517 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java
@@ -5,13 +5,12 @@ import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
-import android.support.annotation.Nullable;
-import android.support.v4.app.ActivityCompat;
-import android.support.v7.app.AppCompatActivity;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.app.ActivityCompat;
+import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import org.apache.commons.lang3.ArrayUtils;
import java.io.InputStreamReader;
@@ -30,55 +29,55 @@ import de.danoeh.antennapod.core.util.LangUtils;
public class OpmlImportBaseActivity extends AppCompatActivity {
private static final String TAG = "OpmlImportBaseActivity";
- private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
+ private static final int PERMISSION_REQUEST_READ_EXTERNAL_STORAGE = 5;
private OpmlImportWorker importWorker;
- @Nullable private Uri uri;
-
- /**
- * Handles the choices made by the user in the OpmlFeedChooserActivity and
- * starts the OpmlFeedQueuer if necessary.
- */
- @Override
- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "Received result");
- if (resultCode == RESULT_CANCELED) {
- Log.d(TAG, "Activity was cancelled");
+ @Nullable private Uri uri;
+
+ /**
+ * Handles the choices made by the user in the OpmlFeedChooserActivity and
+ * starts the OpmlFeedQueuer if necessary.
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ Log.d(TAG, "Received result");
+ if (resultCode == RESULT_CANCELED) {
+ Log.d(TAG, "Activity was cancelled");
if (finishWhenCanceled()) {
- finish();
- }
- } else {
- int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
- if (selected != null && selected.length > 0) {
- OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) {
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(intent);
- }
-
- };
- queuer.executeAsync();
- } else {
- Log.d(TAG, "No items were selected");
- }
- }
- }
-
- void importUri(@Nullable Uri uri) {
+ finish();
+ }
+ } else {
+ int[] selected = data.getIntArrayExtra(OpmlFeedChooserActivity.EXTRA_SELECTED_ITEMS);
+ if (selected != null && selected.length > 0) {
+ OpmlFeedQueuer queuer = new OpmlFeedQueuer(this, selected) {
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ Intent intent = new Intent(OpmlImportBaseActivity.this, MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ }
+
+ };
+ queuer.executeAsync();
+ } else {
+ Log.d(TAG, "No items were selected");
+ }
+ }
+ }
+
+ void importUri(@Nullable Uri uri) {
if(uri == null) {
- new MaterialDialog.Builder(this)
- .content(R.string.opml_import_error_no_file)
- .positiveText(android.R.string.ok)
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.opml_import_error_no_file)
+ .setPositiveButton(android.R.string.ok, null)
.show();
return;
}
- this.uri = uri;
+ this.uri = uri;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
- uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
+ uri.toString().contains(Environment.getExternalStorageDirectory().toString())) {
int permission = ActivityCompat.checkSelfPermission(this, android.Manifest.permission.READ_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
requestPermission();
@@ -88,30 +87,28 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
startImport();
}
- private void requestPermission() {
- String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
- ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
- }
-
- @Override
- public void onRequestPermissionsResult(int requestCode,
- String[] permissions,
- int[] grantResults) {
- if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
- return;
- }
- if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) {
- startImport();
- } else {
- new MaterialDialog.Builder(this)
- .content(R.string.opml_import_ask_read_permission)
- .positiveText(android.R.string.ok)
- .negativeText(R.string.cancel_label)
- .onPositive((dialog, which) -> requestPermission())
- .onNegative((dialog, which) -> finish())
+ private void requestPermission() {
+ String[] permissions = { android.Manifest.permission.READ_EXTERNAL_STORAGE };
+ ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_READ_EXTERNAL_STORAGE);
+ }
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode,
+ String[] permissions,
+ int[] grantResults) {
+ if (requestCode != PERMISSION_REQUEST_READ_EXTERNAL_STORAGE) {
+ return;
+ }
+ if (grantResults.length > 0 && ArrayUtils.contains(grantResults, PackageManager.PERMISSION_GRANTED)) {
+ startImport();
+ } else {
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.opml_import_ask_read_permission)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission())
+ .setNegativeButton(R.string.cancel_label, (dialog, which) -> finish())
.show();
- }
- }
+ }
+ }
/** Starts the import process. */
private void startImport() {
@@ -136,10 +133,10 @@ public class OpmlImportBaseActivity extends AppCompatActivity {
importWorker.executeAsync();
} catch (Exception e) {
Log.d(TAG, Log.getStackTraceString(e));
- String message = getString(R.string.opml_reader_error);
- new MaterialDialog.Builder(this)
- .content(message + " " + e.getMessage())
- .positiveText(android.R.string.ok)
+ String message = getString(R.string.opml_reader_error);
+ new AlertDialog.Builder(this)
+ .setMessage(message + " " + e.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
index 7e0ae173f..a0f9bf6d8 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.activity;
import android.os.Bundle;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.preference.PreferenceFragmentCompat;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.preference.PreferenceFragmentCompat;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
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 52102eee1..bd1ccaea4 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java
@@ -5,9 +5,9 @@ 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 androidx.annotation.Nullable;
+import androidx.core.graphics.drawable.DrawableCompat;
+import androidx.appcompat.app.AppCompatActivity;
import android.widget.ProgressBar;
import de.danoeh.antennapod.R;
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
deleted file mode 100644
index 37199ccf7..000000000
--- a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package de.danoeh.antennapod.activity;
-
-import android.content.SharedPreferences;
-import android.os.Bundle;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
-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.ListView;
-import android.widget.ProgressBar;
-import android.widget.RadioButton;
-import android.widget.TextView;
-
-import de.danoeh.antennapod.R;
-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 io.reactivex.Observable;
-import io.reactivex.android.schedulers.AndroidSchedulers;
-import io.reactivex.disposables.Disposable;
-import io.reactivex.schedulers.Schedulers;
-
-/**
- * Displays the 'statistics' screen
- */
-public class StatisticsActivity extends AppCompatActivity
- implements AdapterView.OnItemClickListener {
-
- private static final String TAG = StatisticsActivity.class.getSimpleName();
- private static final String PREF_NAME = "StatisticsActivityPrefs";
- private static final String PREF_COUNT_ALL = "countAll";
-
- private Disposable disposable;
- private TextView totalTimeTextView;
- private ListView feedStatisticsList;
- private ProgressBar progressBar;
- private StatisticsListAdapter listAdapter;
- private boolean countAll = false;
- private SharedPreferences prefs;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
- getSupportActionBar().setDisplayShowHomeEnabled(true);
- setContentView(R.layout.statistics_activity);
-
- prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
- countAll = prefs.getBoolean(PREF_COUNT_ALL, false);
-
- 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);
- feedStatisticsList.setOnItemClickListener(this);
- }
-
- @Override
- public void onResume() {
- super.onResume();
- refreshStatistics();
- }
-
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- super.onCreateOptionsMenu(menu);
- MenuInflater inflater = getMenuInflater();
- inflater.inflate(R.menu.statistics, menu);
- return true;
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- } else if (item.getItemId() == R.id.statistics_mode) {
- selectStatisticsMode();
- return true;
- } else {
- return super.onOptionsItemSelected(item);
- }
- }
-
- private void selectStatisticsMode() {
- View contentView = View.inflate(this, R.layout.statistics_mode_select_dialog, null);
- AlertDialog.Builder builder = new AlertDialog.Builder(this);
- builder.setView(contentView);
- builder.setTitle(R.string.statistics_mode);
-
- if (countAll) {
- ((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).setChecked(true);
- } else {
- ((RadioButton) contentView.findViewById(R.id.statistics_mode_normal)).setChecked(true);
- }
-
- builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
- countAll = ((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).isChecked();
- listAdapter.setCountAll(countAll);
- prefs.edit().putBoolean(PREF_COUNT_ALL, countAll).apply();
- refreshStatistics();
- });
-
- builder.show();
- }
-
- private void refreshStatistics() {
- progressBar.setVisibility(View.VISIBLE);
- totalTimeTextView.setVisibility(View.GONE);
- feedStatisticsList.setVisibility(View.GONE);
- loadStatistics();
- }
-
- private void loadStatistics() {
- if (disposable != null) {
- disposable.dispose();
- }
- disposable = Observable.fromCallable(() -> DBReader.getStatistics(countAll))
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(result -> {
- 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)));
- }
-
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- DBReader.StatisticsItem stats = listAdapter.getItem(position);
-
- AlertDialog.Builder dialog = new AlertDialog.Builder(this);
- dialog.setTitle(stats.feed.getTitle());
- dialog.setMessage(getString(R.string.statistics_details_dialog,
- countAll ? stats.episodesStartedIncludingMarked : stats.episodesStarted,
- stats.episodes,
- Converter.shortLocalizedDuration(this, countAll ?
- stats.timePlayedCountAll : stats.timePlayed),
- Converter.shortLocalizedDuration(this, stats.time)));
- dialog.setPositiveButton(android.R.string.ok, null);
- dialog.show();
- }
-}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
index 20e34cc52..8527949b0 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java
@@ -9,15 +9,13 @@ import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
-import android.support.v4.app.ActivityCompat;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Button;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.io.File;
import de.danoeh.antennapod.R;
@@ -28,22 +26,22 @@ import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
/** Is show if there is now external storage available. */
public class StorageErrorActivity extends AppCompatActivity {
- private static final String TAG = "StorageErrorActivity";
+ private static final String TAG = "StorageErrorActivity";
private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 42;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- setTheme(UserPreferences.getTheme());
- super.onCreate(savedInstanceState);
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
- setContentView(R.layout.storage_error);
+ setContentView(R.layout.storage_error);
- Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder);
- btnChooseDataFolder.setOnClickListener(v -> {
+ Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder);
+ btnChooseDataFolder.setOnClickListener(v -> {
if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
showChooseDataFolderDialog();
@@ -82,11 +80,10 @@ public class StorageErrorActivity extends AppCompatActivity {
}
if (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
grantResults[1] != PackageManager.PERMISSION_GRANTED) {
- new MaterialDialog.Builder(this)
- .content(R.string.choose_data_directory_permission_rationale)
- .positiveText(android.R.string.ok)
- .onPositive((dialog, which) -> requestPermission())
- .onNegative((dialog, which) -> finish())
+ new AlertDialog.Builder(this)
+ .setMessage(R.string.choose_data_directory_permission_rationale)
+ .setPositiveButton(android.R.string.ok, (dialog, which) -> requestPermission())
+ .setNegativeButton(android.R.string.cancel, (dialog, which) -> finish())
.show();
}
}
@@ -101,15 +98,15 @@ public class StorageErrorActivity extends AppCompatActivity {
}
}
- @Override
- protected void onPause() {
- super.onPause();
- try {
- unregisterReceiver(mediaUpdate);
- } catch (IllegalArgumentException e) {
+ @Override
+ protected void onPause() {
+ super.onPause();
+ try {
+ unregisterReceiver(mediaUpdate);
+ } catch (IllegalArgumentException e) {
Log.e(TAG, Log.getStackTraceString(e));
- }
- }
+ }
+ }
// see PreferenceController.showChooseDataFolderDialog()
private void showChooseDataFolderDialog() {
@@ -123,9 +120,9 @@ public class StorageErrorActivity extends AppCompatActivity {
});
}
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- if (resultCode == Activity.RESULT_OK &&
- requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
File path;
@@ -138,46 +135,46 @@ public class StorageErrorActivity extends AppCompatActivity {
return;
}
String message = null;
- if(!path.exists()) {
- message = String.format(getString(R.string.folder_does_not_exist_error), dir);
- } else if(!path.canRead()) {
- message = String.format(getString(R.string.folder_not_readable_error), dir);
- } else if(!path.canWrite()) {
- message = String.format(getString(R.string.folder_not_writable_error), dir);
- }
-
- if(message == null) {
- Log.d(TAG, "Setting data folder: " + dir);
- UserPreferences.setDataFolder(dir);
- leaveErrorState();
- } else {
- AlertDialog.Builder ab = new AlertDialog.Builder(this);
- ab.setMessage(message);
- ab.setPositiveButton(android.R.string.ok, null);
- ab.show();
- }
- }
- }
-
- private void leaveErrorState() {
- finish();
- startActivity(new Intent(this, MainActivity.class));
- }
-
- private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
- if (intent.getBooleanExtra("read-only", true)) {
- Log.d(TAG, "Media was mounted; Finishing activity");
- leaveErrorState();
- } else {
- Log.d(TAG, "Media seemed to have been mounted read only");
- }
- }
- }
-
- };
+ if(!path.exists()) {
+ message = String.format(getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ leaveErrorState();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(this);
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
+ }
+ }
+
+ private void leaveErrorState() {
+ finish();
+ startActivity(new Intent(this, MainActivity.class));
+ }
+
+ private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (TextUtils.equals(intent.getAction(), Intent.ACTION_MEDIA_MOUNTED)) {
+ if (intent.getBooleanExtra("read-only", true)) {
+ Log.d(TAG, "Media was mounted; Finishing activity");
+ leaveErrorState();
+ } else {
+ Log.d(TAG, "Media seemed to have been mounted read only");
+ }
+ }
+ }
+
+ };
}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
index 78cc15b2c..0c6ae2645 100644
--- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java
@@ -6,8 +6,8 @@ import android.graphics.drawable.ColorDrawable;
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 androidx.core.view.WindowCompat;
+import androidx.appcompat.app.ActionBar;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -151,10 +151,7 @@ public class VideoplayerActivity extends MediaplayerActivity {
videoview.getHolder().addCallback(surfaceHolderCallback);
videoframe.setOnTouchListener(onVideoviewTouched);
videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider
-
- if (Build.VERSION.SDK_INT >= 16) {
- videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
- }
+ videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
videoOverlay.setFitsSystemWindows(true);
setupVideoControlsToggler();
@@ -351,9 +348,9 @@ public class VideoplayerActivity extends MediaplayerActivity {
controls.startAnimation(animation);
}
}
- int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0;
- getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN
- | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag);
+ getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
+ | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
videoOverlay.setFitsSystemWindows(true);
videoOverlay.setVisibility(View.GONE);
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
new file mode 100644
index 000000000..474b96c38
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/activity/WidgetConfigActivity.java
@@ -0,0 +1,107 @@
+package de.danoeh.antennapod.activity;
+
+import android.Manifest;
+import android.app.WallpaperManager;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.widget.ImageView;
+import android.widget.RemoteViews;
+import androidx.appcompat.app.AppCompatActivity;
+
+import android.appwidget.AppWidgetManager;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.RelativeLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import androidx.core.content.ContextCompat;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.receiver.PlayerWidget;
+import de.danoeh.antennapod.core.service.PlayerWidgetJobService;
+
+public class WidgetConfigActivity extends AppCompatActivity {
+ private int appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
+
+ private SeekBar opacitySeekBar;
+ private TextView opacityTextView;
+ private RelativeLayout widgetPreview;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getTheme());
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_widget_config);
+
+ Intent configIntent = getIntent();
+ Bundle extras = configIntent.getExtras();
+ if (extras != null) {
+ appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID,
+ AppWidgetManager.INVALID_APPWIDGET_ID);
+ }
+
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ setResult(RESULT_CANCELED, resultValue);
+ if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
+ finish();
+ }
+
+ displayDeviceBackground();
+ opacityTextView = findViewById(R.id.widget_opacity_textView);
+ opacitySeekBar = findViewById(R.id.widget_opacity_seekBar);
+ widgetPreview = findViewById(R.id.widgetLayout);
+ findViewById(R.id.butConfirm).setOnClickListener(this::confirmCreateWidget);
+ opacitySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
+ opacityTextView.setText(seekBar.getProgress() + "%");
+ int color = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress());
+ widgetPreview.setBackgroundColor(color);
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ });
+ }
+
+ private void displayDeviceBackground() {
+ int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);
+ if (Build.VERSION.SDK_INT < 27 || permission == PackageManager.PERMISSION_GRANTED) {
+ final WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
+ final Drawable wallpaperDrawable = wallpaperManager.getDrawable();
+ ImageView background = findViewById(R.id.widget_config_background);
+ background.setImageDrawable(wallpaperDrawable);
+ }
+ }
+
+ private void confirmCreateWidget(View v) {
+ int backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.getProgress());
+
+ SharedPreferences prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor);
+ editor.apply();
+
+ Intent resultValue = new Intent();
+ resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
+ setResult(RESULT_OK, resultValue);
+ finish();
+ PlayerWidgetJobService.updateWidget(this);
+ }
+
+ private int getColorWithAlpha(int color, int opacity) {
+ return (int) Math.round(0xFF * (0.01 * opacity)) * 0x1000000 + color;
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java
index 2d7898d5b..c79c611ce 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
@@ -2,11 +2,10 @@ package de.danoeh.antennapod.activity.gpoddernet;
import android.content.Context;
import android.content.Intent;
-import android.content.res.Configuration;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
-import android.support.v7.app.AppCompatActivity;
+import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
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 2eff33339..9cd5cc3ab 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java
@@ -1,10 +1,11 @@
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;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.ItemTouchHelper;
import android.text.Layout;
import android.util.Log;
import android.view.ContextMenu;
@@ -23,19 +24,23 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
import java.lang.ref.WeakReference;
+import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
import de.danoeh.antennapod.fragment.ItemFragment;
+import de.danoeh.antennapod.fragment.ItemPagerFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@@ -165,7 +170,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.progress.setVisibility(View.INVISIBLE);
}
- if(media.isCurrentlyPlaying()) {
+ if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
@@ -189,7 +194,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
holder.butSecondary.setTag(item);
new CoverLoader(mainActivityRef.get())
- .withUri(item.getImageLocation())
+ .withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(holder.placeholder)
.withCoverView(holder.cover)
@@ -241,7 +246,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
MainActivity mainActivity = mainActivityRef.get();
if (mainActivity != null) {
long[] ids = itemAccess.getItemsIds().toArray();
- mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition()));
+ mainActivity.loadChildFragment(ItemPagerFragment.newInstance(ids, getAdapterPosition()));
}
}
@@ -262,7 +267,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivityRef.get().getMenuInflater();
- inflater.inflate(R.menu.allepisodes_context, menu);
+ inflater.inflate(R.menu.feeditemlist_context, menu);
if (item != null) {
menu.setHeaderTitle(item.getTitle());
@@ -277,9 +282,15 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
+ }
+
+ public boolean isCurrentlyPlayingItem() {
+ return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
+ }
- contextMenuInterface.setItemVisibility(R.id.remove_new_flag_item, item.isNew());
+ public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
+ progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
}
}
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 c3fac7e18..f6e6da8b4 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.v4.content.ContextCompat;
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
import android.text.Layout;
import android.text.Selection;
import android.text.Spannable;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
index 33f925e3f..098e9a616 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java
@@ -1,9 +1,8 @@
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 androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
index 74bc84878..e3ca5b5a5 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java
@@ -2,9 +2,10 @@ package de.danoeh.antennapod.adapter;
import android.app.Dialog;
import android.content.Context;
-import android.support.annotation.NonNull;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.RecyclerView;
+import android.widget.ProgressBar;
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -20,7 +21,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.dialog.ChooseDataFolderDialog;
-import me.zhanghai.android.materialprogressbar.MaterialProgressBar;
public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.ViewHolder> {
@@ -105,7 +105,7 @@ public class DataFolderAdapter extends RecyclerView.Adapter<DataFolderAdapter.Vi
private TextView path;
private TextView size;
private RadioButton radioButton;
- private MaterialProgressBar progressBar;
+ private ProgressBar progressBar;
ViewHolder(View itemView) {
super(itemView);
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
index 789c01a26..4d66fd486 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.os.Build;
-import android.support.v4.content.ContextCompat;
import android.text.Layout;
import android.text.format.DateUtils;
import android.util.Log;
@@ -13,6 +12,8 @@ import android.widget.BaseAdapter;
import android.widget.TextView;
import android.widget.Toast;
+import androidx.core.content.ContextCompat;
+
import com.joanzapata.iconify.widget.IconButton;
import com.joanzapata.iconify.widget.IconTextView;
@@ -24,6 +25,7 @@ import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
/** Displays a list of DownloadStatus entries. */
public class DownloadLogAdapter extends BaseAdapter {
@@ -132,7 +134,7 @@ public class DownloadLogAdapter extends BaseAdapter {
FeedMedia media = DBReader.getFeedMedia(holder.id);
if (media != null) {
try {
- DBTasks.downloadFeedItems(context, media.getItem());
+ DownloadRequester.getInstance().downloadMedia(context, media.getItem());
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
index 98d55dd97..b083908a8 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java
@@ -17,9 +17,9 @@ import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
/**
* Shows a list of downloaded episodes
@@ -79,7 +79,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
}
Glide.with(context)
- .load(item.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(item))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
index df7ec46e0..66fa79a4e 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedDiscoverAdapter.java
@@ -59,6 +59,8 @@ public class FeedDiscoverAdapter extends BaseAdapter {
final PodcastSearchResult podcast = getItem(position);
+ holder.imageView.setContentDescription(podcast.title);
+
Glide.with(mainActivityRef.get())
.load(podcast.imageUrl)
.apply(new RequestOptions()
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
index d090bc4b1..aec0f0c91 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.adapter;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
-import android.support.v4.content.ContextCompat;
+import androidx.core.content.ContextCompat;
import android.text.Layout;
import android.view.LayoutInflater;
import android.view.View;
@@ -13,15 +13,18 @@ import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
+import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
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.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.ThemeUtils;
@@ -39,6 +42,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
private final int playingBackGroundColor;
private final int normalBackGroundColor;
+ private int currentlyPlayingItem = -1;
+
public FeedItemlistAdapter(Context context,
ItemAccess itemAccess,
boolean showFeedtitle,
@@ -176,8 +181,9 @@ public class FeedItemlistAdapter extends BaseAdapter {
}
typeDrawables.recycle();
- if(media.isCurrentlyPlaying()) {
+ if (media.isCurrentlyPlaying()) {
holder.container.setBackgroundColor(playingBackGroundColor);
+ currentlyPlayingItem = position;
} else {
holder.container.setBackgroundColor(normalBackGroundColor);
}
@@ -195,6 +201,20 @@ public class FeedItemlistAdapter extends BaseAdapter {
return convertView;
}
+ public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event, ListView listView) {
+ if (currentlyPlayingItem != -1 && currentlyPlayingItem < getCount()) {
+ View view = listView.getChildAt(currentlyPlayingItem
+ - listView.getFirstVisiblePosition() + listView.getHeaderViewsCount());
+ if (view == null) {
+ return;
+ }
+ Holder holder = (Holder) view.getTag();
+ holder.episodeProgress.setVisibility(View.VISIBLE);
+ holder.episodeProgress.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ holder.lenSize.setText(Converter.getDurationStringLong(event.getDuration() - event.getPosition()));
+ }
+ }
+
static class Holder {
LinearLayout container;
TextView title;
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 c10bb7638..8d469c7a6 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java
@@ -1,6 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import android.os.Build;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -51,6 +52,17 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
.replaceAll("\\s+", " ")
.trim();
holder.description.setText(description);
+
+ final int MAX_LINES_COLLAPSED = 3;
+ holder.description.setMaxLines(MAX_LINES_COLLAPSED);
+ holder.description.setOnClickListener(v -> {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
+ && holder.description.getMaxLines() > MAX_LINES_COLLAPSED) {
+ holder.description.setMaxLines(MAX_LINES_COLLAPSED);
+ } else {
+ holder.description.setMaxLines(2000);
+ }
+ });
}
return convertView;
}
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 be8e52cfc..50b11a15b 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java
@@ -7,7 +7,7 @@ import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.preference.PreferenceManager;
-import android.support.v7.app.AlertDialog;
+import androidx.appcompat.app.AlertDialog;
import android.util.TypedValue;
import android.view.LayoutInflater;
import android.view.View;
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 382abfb32..5ccec0ade 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java
@@ -1,12 +1,11 @@
package de.danoeh.antennapod.adapter;
-import android.content.Context;
import android.os.Build;
-import android.support.annotation.Nullable;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.MotionEventCompat;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
+import androidx.annotation.Nullable;
+import androidx.core.content.ContextCompat;
+import androidx.core.view.MotionEventCompat;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.ItemTouchHelper;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
@@ -25,6 +24,8 @@ import android.widget.TextView;
import com.joanzapata.iconify.Iconify;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.fragment.ItemPagerFragment;
import org.apache.commons.lang3.ArrayUtils;
import java.lang.ref.WeakReference;
@@ -38,10 +39,10 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ThemeUtils;
-import de.danoeh.antennapod.fragment.ItemFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
@@ -80,11 +81,13 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
notifyDataSetChanged();
}
+ @Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.queue_listitem, parent, false);
return new ViewHolder(view);
}
+ @Override
public void onBindViewHolder(ViewHolder holder, int pos) {
FeedItem item = itemAccess.getItem(pos);
holder.bind(item);
@@ -160,7 +163,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
if (activity != null) {
long[] ids = itemAccess.getQueueIds().toArray();
int position = ArrayUtils.indexOf(ids, item.getId());
- activity.loadChildFragment(ItemFragment.newInstance(ids, position));
+ activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
}
@@ -169,7 +172,8 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
FeedItem item = itemAccess.getItem(getAdapterPosition());
MenuInflater inflater = mainActivity.get().getMenuInflater();
- inflater.inflate(R.menu.queue_context, menu);
+ inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items
+ inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds
if (item != null) {
menu.setHeaderTitle(item.getTitle());
@@ -184,7 +188,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
item1.setVisible(visible);
}
};
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, itemAccess.getQueueIds());
+
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item,
+ R.id.skip_episode_item); // Skip Episode is not useful in Queue, so hide it.
+ // Queue-specific menu preparation
+ final boolean keepSorted = UserPreferences.isQueueKeepSorted();
+ final LongList queueAccess = itemAccess.getQueueIds();
+ if (queueAccess.size() == 0 || queueAccess.get(0) == item.getId() || keepSorted) {
+ contextMenuInterface.setItemVisibility(R.id.move_to_top_item, false);
+ }
+ if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == item.getId() || keepSorted) {
+ contextMenuInterface.setItemVisibility(R.id.move_to_bottom_item, false);
+ }
}
@Override
@@ -288,13 +303,22 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap
butSecondary.setTag(item);
new CoverLoader(mainActivity.get())
- .withUri(item.getImageLocation())
+ .withUri(ImageResourceUtils.getImageLocation(item))
.withFallbackUri(item.getFeed().getImageLocation())
.withPlaceholderView(placeholder)
.withCoverView(cover)
.load();
}
+ public boolean isCurrentlyPlayingItem() {
+ return item.getMedia() != null && item.getMedia().isCurrentlyPlaying();
+ }
+
+ public void notifyPlaybackPositionUpdated(PlaybackPositionEvent event) {
+ progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration()));
+ progressLeft.setText(Converter.getDurationStringLong(event.getPosition()));
+ progressRight.setText(Converter.getDurationStringLong(event.getDuration()));
+ }
}
public interface ItemAccess {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java
new file mode 100644
index 000000000..10bda4efa
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SimpleIconListAdapter.java
@@ -0,0 +1,59 @@
+package de.danoeh.antennapod.adapter;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.bumptech.glide.Glide;
+import com.bumptech.glide.load.engine.DiskCacheStrategy;
+import com.bumptech.glide.request.RequestOptions;
+import de.danoeh.antennapod.R;
+
+import java.util.List;
+
+/**
+ * Displays a list of items that have a subtitle and an icon.
+ */
+public class SimpleIconListAdapter<T extends SimpleIconListAdapter.ListItem> extends ArrayAdapter<T> {
+ private final Context context;
+ private final List<T> listItems;
+
+ public SimpleIconListAdapter(Context context, List<T> listItems) {
+ super(context, R.layout.simple_icon_list_item, listItems);
+ this.context = context;
+ this.listItems = listItems;
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent) {
+ if (view == null) {
+ view = View.inflate(context, R.layout.simple_icon_list_item, null);
+ }
+
+ ListItem item = listItems.get(position);
+ ((TextView) view.findViewById(R.id.title)).setText(item.title);
+ ((TextView) view.findViewById(R.id.subtitle)).setText(item.subtitle);
+ Glide.with(context)
+ .load(item.imageUrl)
+ .apply(new RequestOptions()
+ .diskCacheStrategy(DiskCacheStrategy.NONE)
+ .fitCenter()
+ .dontAnimate())
+ .into(((ImageView) view.findViewById(R.id.icon)));
+ return view;
+ }
+
+ public static class ListItem {
+ public final String title;
+ public final String subtitle;
+ public final String imageUrl;
+
+ public ListItem(String title, String subtitle, String imageUrl) {
+ this.title = title;
+ this.subtitle = subtitle;
+ this.imageUrl = imageUrl;
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
index 31e82dbe0..f013f2a49 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java
@@ -1,31 +1,30 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
-import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
-
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;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.view.PieChartView;
/**
* Adapter for the statistics list
*/
-public class StatisticsListAdapter extends BaseAdapter {
+public class StatisticsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
+ private static final int TYPE_HEADER = 0;
+ private static final int TYPE_FEED = 1;
private final Context context;
- private List<DBReader.StatisticsItem> feedTime = new ArrayList<>();
+ private DBReader.StatisticsData statisticsData;
private boolean countAll = true;
public StatisticsListAdapter(Context context) {
@@ -37,66 +36,102 @@ public class StatisticsListAdapter extends BaseAdapter {
}
@Override
- public int getCount() {
- return feedTime.size();
+ public int getItemCount() {
+ return statisticsData.feedTime.size() + 1;
}
- @Override
public DBReader.StatisticsItem getItem(int position) {
- return feedTime.get(position);
+ if (position == 0) {
+ return null;
+ }
+ return statisticsData.feedTime.get(position - 1);
}
@Override
- public long getItemId(int position) {
- return feedTime.get(position).feed.getId();
+ public int getItemViewType(int position) {
+ return position == 0 ? TYPE_HEADER : TYPE_FEED;
}
+ @NonNull
@Override
- public View getView(int position, View convertView, ViewGroup parent) {
- StatisticsHolder holder;
- Feed feed = feedTime.get(position).feed;
-
- if (convertView == null) {
- holder = new StatisticsHolder();
- LayoutInflater inflater = (LayoutInflater) context
- .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- convertView = inflater.inflate(R.layout.statistics_listitem, parent, false);
+ public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ if (viewType == TYPE_HEADER) {
+ return new HeaderHolder(inflater.inflate(R.layout.statistics_listitem_total_time, parent, false));
+ }
+ return new StatisticsHolder(inflater.inflate(R.layout.statistics_listitem, parent, false));
+ }
- holder.image = convertView.findViewById(R.id.imgvCover);
- holder.title = convertView.findViewById(R.id.txtvTitle);
- holder.time = convertView.findViewById(R.id.txtvTime);
- convertView.setTag(holder);
+ @Override
+ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder h, int position) {
+ if (getItemViewType(position) == TYPE_HEADER) {
+ HeaderHolder holder = (HeaderHolder) h;
+ long time = countAll ? statisticsData.totalTimeCountAll : statisticsData.totalTime;
+ holder.totalTime.setText(Converter.shortLocalizedDuration(context, time));
+ float[] dataValues = new float[statisticsData.feedTime.size()];
+ for (int i = 0; i < statisticsData.feedTime.size(); i++) {
+ DBReader.StatisticsItem item = statisticsData.feedTime.get(i);
+ dataValues[i] = countAll ? item.timePlayedCountAll : item.timePlayed;
+ }
+ holder.pieChart.setData(dataValues);
} else {
- holder = (StatisticsHolder) convertView.getTag();
+ StatisticsHolder holder = (StatisticsHolder) h;
+ DBReader.StatisticsItem statsItem = statisticsData.feedTime.get(position - 1);
+ Glide.with(context)
+ .load(statsItem.feed.getImageLocation())
+ .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(statsItem.feed.getTitle());
+ long time = countAll ? statsItem.timePlayedCountAll : statsItem.timePlayed;
+ holder.time.setText(Converter.shortLocalizedDuration(context, time));
+
+ holder.itemView.setOnClickListener(v -> {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(context);
+ dialog.setTitle(statsItem.feed.getTitle());
+ dialog.setMessage(context.getString(R.string.statistics_details_dialog,
+ countAll ? statsItem.episodesStartedIncludingMarked : statsItem.episodesStarted,
+ statsItem.episodes, Converter.shortLocalizedDuration(context,
+ countAll ? statsItem.timePlayedCountAll : statsItem.timePlayed),
+ Converter.shortLocalizedDuration(context, statsItem.time)));
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ });
}
-
- Glide.with(context)
- .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(holder.image);
-
- holder.title.setText(feed.getTitle());
- holder.time.setText(Converter.shortLocalizedDuration(context,
- countAll ? feedTime.get(position).timePlayedCountAll
- : feedTime.get(position).timePlayed));
- return convertView;
}
- public void update(List<DBReader.StatisticsItem> feedTime) {
- this.feedTime = feedTime;
+ public void update(DBReader.StatisticsData statistics) {
+ this.statisticsData = statistics;
notifyDataSetChanged();
}
- static class StatisticsHolder {
+ static class HeaderHolder extends RecyclerView.ViewHolder {
+ TextView totalTime;
+ PieChartView pieChart;
+
+ HeaderHolder(View itemView) {
+ super(itemView);
+ totalTime = itemView.findViewById(R.id.total_time);
+ pieChart = itemView.findViewById(R.id.pie_chart);
+ }
+ }
+
+ static class StatisticsHolder extends RecyclerView.ViewHolder {
ImageView image;
TextView title;
TextView time;
+
+ StatisticsHolder(View itemView) {
+ super(itemView);
+ image = itemView.findViewById(R.id.imgvCover);
+ title = itemView.findViewById(R.id.txtvTitle);
+ time = itemView.findViewById(R.id.txtvTime);
+ }
}
}
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 e0fb65c61..3141c6046 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.adapter;
import android.content.Context;
-import android.support.v4.app.Fragment;
+import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,7 +30,6 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
public static final Object ADD_ITEM_OBJ = new Object();
/** the position in the view that holds the add item; 0 is the first, -1 is the last position */
- private static final int ADD_POSITION = -1;
private static final String TAG = "SubscriptionsAdapter";
private final WeakReference<MainActivity> mainActivityRef;
@@ -41,28 +40,14 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
this.itemAccess = itemAccess;
}
- private int getAddTilePosition() {
- if(ADD_POSITION < 0) {
- return ADD_POSITION + getCount();
- }
- return ADD_POSITION;
- }
-
- private int getAdjustedPosition(int origPosition) {
- return origPosition < getAddTilePosition() ? origPosition : origPosition - 1;
- }
-
@Override
public int getCount() {
- return 1 + itemAccess.getCount();
+ return itemAccess.getCount();
}
@Override
public Object getItem(int position) {
- if (position == getAddTilePosition()) {
- return ADD_ITEM_OBJ;
- }
- return itemAccess.getItem(getAdjustedPosition(position));
+ return itemAccess.getItem(position);
}
@Override
@@ -72,10 +57,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
@Override
public long getItemId(int position) {
- if (position == getAddTilePosition()) {
- return 0;
- }
- return itemAccess.getItem(getAdjustedPosition(position)).getId();
+ return itemAccess.getItem(position).getId();
}
@Override
@@ -98,24 +80,11 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
holder = (Holder) convertView.getTag();
}
- if (position == getAddTilePosition()) {
- holder.feedTitle.setText("{md-add 500%}\n\n" + mainActivityRef.get().getString(R.string.add_feed_label));
- holder.feedTitle.setVisibility(View.VISIBLE);
- // prevent any accidental re-use of old values (not sure how that would happen...)
- holder.count.setPrimaryText("");
- // make it go away, we don't need it for add feed
- holder.count.setVisibility(View.INVISIBLE);
-
- // when this holder is reused, we could else end up with a cover image
- Glide.with(mainActivityRef.get()).clear(holder.imageView);
-
- return convertView;
- }
-
final Feed feed = (Feed) getItem(position);
if (feed == null) return null;
holder.feedTitle.setText(feed.getTitle());
+ holder.imageView.setContentDescription(feed.getTitle());
holder.feedTitle.setVisibility(View.VISIBLE);
int count = itemAccess.getFeedCounter(feed.getId());
if(count > 0) {
@@ -137,12 +106,8 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- if (position == getAddTilePosition()) {
- mainActivityRef.get().loadChildFragment(new AddFeedFragment());
- } else {
- Fragment fragment = FeedItemlistFragment.newInstance(getItemId(position));
- mainActivityRef.get().loadChildFragment(fragment);
- }
+ Fragment fragment = FeedItemlistFragment.newInstance(getItemId(position));
+ mainActivityRef.get().loadChildFragment(fragment);
}
static class Holder {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
index 3299db3ab..a8001eeb1 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import android.support.annotation.AttrRes;
-import android.support.annotation.StringRes;
+import androidx.annotation.AttrRes;
+import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
index 1275a799b..10458ed46 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import android.support.annotation.AttrRes;
-import android.support.annotation.StringRes;
+import androidx.annotation.AttrRes;
+import androidx.annotation.StringRes;
import android.widget.Toast;
import de.danoeh.antennapod.R;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
index c1559528e..026386cf9 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java
@@ -1,16 +1,16 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import android.support.annotation.AttrRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.StringRes;
import android.widget.Toast;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -64,7 +64,7 @@ class DownloadActionButton extends ItemActionButton {
private void downloadEpisode(Context context) {
try {
- DBTasks.downloadFeedItems(context, item);
+ DownloadRequester.getInstance().downloadMedia(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
index da5ebf6e1..861c6a4be 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java
@@ -2,14 +2,15 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.content.res.TypedArray;
-import android.support.annotation.AttrRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.StringRes;
+import androidx.annotation.AttrRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
import android.view.View;
import android.widget.ImageButton;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DownloadRequester;
public abstract class ItemActionButton {
@@ -20,12 +21,12 @@ public abstract class ItemActionButton {
}
@StringRes
- abstract public int getLabel();
+ public abstract int getLabel();
@AttrRes
- abstract public int getDrawable();
+ public abstract int getDrawable();
- abstract public void onClick(Context context);
+ public abstract void onClick(Context context);
public int getVisibility() {
return View.VISIBLE;
@@ -43,6 +44,8 @@ public abstract class ItemActionButton {
return new PlayActionButton(item);
} else if (isDownloadingMedia) {
return new CancelDownloadActionButton(item);
+ } else if (UserPreferences.streamOverDownload()) {
+ return new StreamActionButton(item);
} else if (MobileDownloadHelper.userAllowedMobileDownloads() || !MobileDownloadHelper.userChoseAddToQueue() || isInQueue) {
return new DownloadActionButton(item, isInQueue);
} else {
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
index 4d906cee5..354ded73d 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import android.support.annotation.AttrRes;
-import android.support.annotation.StringRes;
+import androidx.annotation.AttrRes;
+import androidx.annotation.StringRes;
import android.view.View;
import de.danoeh.antennapod.R;
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
index f8d2a139e..77efd9023 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MobileDownloadHelper.java
@@ -3,15 +3,14 @@ package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
import android.widget.Toast;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
class MobileDownloadHelper {
private static long addToQueueTimestamp;
@@ -27,16 +26,15 @@ class MobileDownloadHelper {
}
static void confirmMobileDownload(final Context context, final FeedItem item) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context)
- .title(R.string.confirm_mobile_download_dialog_title)
- .content(R.string.confirm_mobile_download_dialog_message)
- .positiveText(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily))
- .onPositive((dialog, which) -> downloadFeedItems(context, item));
+ AlertDialog.Builder builder = new AlertDialog.Builder(context)
+ .setTitle(R.string.confirm_mobile_download_dialog_title)
+ .setMessage(R.string.confirm_mobile_download_dialog_message)
+ .setPositiveButton(context.getText(R.string.confirm_mobile_download_dialog_enable_temporarily),
+ (dialog, which) -> downloadFeedItems(context, item));
if (!DBReader.getQueueIDList().contains(item.getId())) {
- builder
- .content(R.string.confirm_mobile_download_dialog_message_not_in_queue)
- .neutralText(R.string.confirm_mobile_download_dialog_only_add_to_queue)
- .onNeutral((dialog, which) -> addToQueue(context, item));
+ builder.setMessage(R.string.confirm_mobile_download_dialog_message_not_in_queue)
+ .setNeutralButton(R.string.confirm_mobile_download_dialog_only_add_to_queue,
+ (dialog, which) -> addToQueue(context, item));
}
builder.show();
}
@@ -50,7 +48,7 @@ class MobileDownloadHelper {
private static void downloadFeedItems(Context context, FeedItem item) {
allowMobileDownloadTimestamp = System.currentTimeMillis();
try {
- DBTasks.downloadFeedItems(context, item);
+ DownloadRequester.getInstance().downloadMedia(context, item);
Toast.makeText(context, R.string.status_downloading_label, Toast.LENGTH_SHORT).show();
} catch (DownloadRequestException e) {
e.printStackTrace();
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
index 3992c7240..0d314b5eb 100644
--- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.adapter.actionbutton;
import android.content.Context;
-import android.support.annotation.AttrRes;
-import android.support.annotation.StringRes;
+import androidx.annotation.AttrRes;
+import androidx.annotation.StringRes;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -12,7 +12,6 @@ import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
-import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE;
class PlayActionButton extends ItemActionButton {
@@ -52,12 +51,14 @@ class PlayActionButton extends ItemActionButton {
}
private void togglePlayPause(Context context, FeedMedia media) {
- new PlaybackServiceStarter(context, media)
- .startWhenPrepared(true)
- .shouldStream(false)
- .start();
-
- String pauseOrResume = media.isCurrentlyPlaying() ? ACTION_PAUSE_PLAY_CURRENT_EPISODE : ACTION_RESUME_PLAY_CURRENT_EPISODE;
- IntentUtils.sendLocalBroadcast(context, pauseOrResume);
+ if (media.isCurrentlyPlaying()) {
+ IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ } else {
+ new PlaybackServiceStarter(context, media)
+ .callEvenIfRunning(true)
+ .startWhenPrepared(true)
+ .shouldStream(false)
+ .start();
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
new file mode 100644
index 000000000..c1e619fdf
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/StreamActionButton.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.adapter.actionbutton;
+
+import android.content.Context;
+
+import androidx.annotation.AttrRes;
+import androidx.annotation.StringRes;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
+
+import static de.danoeh.antennapod.core.service.playback.PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE;
+
+public class StreamActionButton extends ItemActionButton {
+
+ StreamActionButton(FeedItem item) {
+ super(item);
+ }
+
+ @Override
+ @StringRes
+ public int getLabel() {
+ return R.string.stream_label;
+ }
+
+ @Override
+ @AttrRes
+ public int getDrawable() {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isCurrentlyPlaying()) {
+ return R.attr.av_pause;
+ }
+ return R.attr.action_stream;
+ }
+
+ @Override
+ public void onClick(Context context) {
+ final FeedMedia media = item.getMedia();
+ if (media == null) {
+ return;
+ }
+
+ if (media.isPlaying()) {
+ togglePlayPause(context, media);
+ } else {
+ DBTasks.playMedia(context, media, false, true, true);
+ }
+ }
+
+ private void togglePlayPause(Context context, FeedMedia media) {
+ if (media.isCurrentlyPlaying()) {
+ IntentUtils.sendLocalBroadcast(context, ACTION_PAUSE_PLAY_CURRENT_EPISODE);
+ } else {
+ new PlaybackServiceStarter(context, media)
+ .callEvenIfRunning(true)
+ .startWhenPrepared(true)
+ .shouldStream(true)
+ .start();
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java
index f5213e4ab..cc3b6fba0 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
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.adapter.itunes;
import android.content.Context;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
new file mode 100644
index 000000000..339a98dfa
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java
@@ -0,0 +1,72 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.content.Context;
+import android.net.Uri;
+import androidx.annotation.NonNull;
+import androidx.documentfile.provider.DocumentFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+
+import de.danoeh.antennapod.core.export.ExportWriter;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.LangUtils;
+import io.reactivex.Observable;
+
+/**
+ * Writes an OPML file into the user selected export directory in the background.
+ */
+public class DocumentFileExportWorker {
+
+ private final @NonNull ExportWriter exportWriter;
+ private @NonNull Context context;
+ private @NonNull Uri outputFileUri;
+
+ public DocumentFileExportWorker(@NonNull ExportWriter exportWriter, @NonNull Context context, @NonNull Uri outputFileUri) {
+ this.exportWriter = exportWriter;
+ this.context = context;
+ this.outputFileUri = outputFileUri;
+ }
+
+ public Observable<DocumentFile> exportObservable() {
+ DocumentFile output = DocumentFile.fromSingleUri(context, outputFileUri);
+ return Observable.create(subscriber -> {
+ OutputStream outputStream = null;
+ OutputStreamWriter writer = null;
+ try {
+ Uri uri = output.getUri();
+ if (uri == null) {
+ throw new FileNotFoundException("Export file not found.");
+ }
+ outputStream = context.getContentResolver().openOutputStream(uri);
+ if (outputStream == null) {
+ throw new IOException();
+ }
+ writer = new OutputStreamWriter(outputStream, LangUtils.UTF_8);
+ exportWriter.writeDocument(DBReader.getFeedList(), writer);
+ subscriber.onNext(output);
+ } catch (IOException e) {
+ subscriber.onError(e);
+ } finally {
+ if (writer != null) {
+ try {
+ writer.close();
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ }
+ if (outputStream != null) {
+ try {
+ outputStream.close();
+ } catch (IOException e) {
+ subscriber.onError(e);
+ }
+ }
+ subscriber.onComplete();
+ }
+ });
+ }
+
+}
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 219725b01..40b101ddf 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.asynctask;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
import android.util.Log;
import java.io.File;
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 13b95907f..b88b58537 100644
--- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
+++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.asynctask;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.AsyncTask;
-import android.support.v7.app.AlertDialog;
+import androidx.appcompat.app.AlertDialog;
import android.util.Log;
import org.xmlpull.v1.XmlPullParserException;
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 eb70d8e0b..c3f5d898c 100644
--- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
+++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java
@@ -35,6 +35,6 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
@Override
public int getNotificationIconResource(Context context) {
- return R.drawable.ic_stat_antenna_default;
+ return R.drawable.ic_antenna;
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
index c185a5557..ec285a8f6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java
@@ -2,14 +2,16 @@ package de.danoeh.antennapod.dialog;
import android.content.Context;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import android.view.View;
+import androidx.appcompat.app.AlertDialog;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DataFolderAdapter;
public class ChooseDataFolderDialog {
- public static abstract class RunnableWithString implements Runnable {
+ public abstract static class RunnableWithString implements Runnable {
public RunnableWithString() {
super();
}
@@ -25,21 +27,23 @@ public class ChooseDataFolderDialog {
DataFolderAdapter adapter = new DataFolderAdapter(context, handlerFunc);
if (adapter.getItemCount() == 0) {
- new MaterialDialog.Builder(context)
- .title(R.string.error_label)
- .content(R.string.external_storage_error_msg)
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(context)
+ .setTitle(R.string.error_label)
+ .setMessage(R.string.external_storage_error_msg)
+ .setPositiveButton(android.R.string.ok, null)
.show();
return;
}
- MaterialDialog dialog = new MaterialDialog.Builder(context)
- .title(R.string.choose_data_directory)
- .content(R.string.choose_data_directory_message)
- .adapter(adapter, null)
- .negativeText(R.string.cancel_label)
- .cancelable(true)
- .build();
+ View content = View.inflate(context, R.layout.choose_data_folder_dialog, null);
+ AlertDialog dialog = new AlertDialog.Builder(context)
+ .setView(content)
+ .setTitle(R.string.choose_data_directory)
+ .setMessage(R.string.choose_data_directory_message)
+ .setNegativeButton(R.string.cancel_label, null)
+ .create();
+ ((RecyclerView) content.findViewById(R.id.recyclerView)).setLayoutManager(new LinearLayoutManager(context));
+ ((RecyclerView) content.findViewById(R.id.recyclerView)).setAdapter(adapter);
adapter.setDialog(dialog);
dialog.show();
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
index ed35495fa..ff131aeba 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java
@@ -4,16 +4,6 @@ import android.app.AlertDialog;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
-import android.support.annotation.IdRes;
-import android.support.annotation.NonNull;
-import android.support.annotation.PluralsRes;
-import android.support.annotation.StringRes;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.app.Fragment;
-import android.support.v4.util.ArrayMap;
-import android.support.v7.app.ActionBar;
-import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -24,6 +14,17 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
+import androidx.annotation.IdRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.PluralsRes;
+import androidx.annotation.StringRes;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.collection.ArrayMap;
+import androidx.core.app.ActivityCompat;
+import androidx.fragment.app.Fragment;
+
+import com.google.android.material.snackbar.Snackbar;
import com.leinardi.android.speeddial.SpeedDialView;
import java.util.ArrayList;
@@ -35,10 +36,12 @@ import java.util.Map;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.LongList;
+import de.danoeh.antennapod.core.util.SortOrder;
public class EpisodesApplyActionFragment extends Fragment {
@@ -263,6 +266,18 @@ public class EpisodesApplyActionFragment extends Fragment {
mSelectToggle.setTitle(titleResId);
}
+ private static final Map<Integer, SortOrder> menuItemIdToSortOrder;
+ static {
+ Map<Integer, SortOrder> map = new ArrayMap<>();
+ map.put(R.id.sort_title_a_z, SortOrder.EPISODE_TITLE_A_Z);
+ map.put(R.id.sort_title_z_a, SortOrder.EPISODE_TITLE_Z_A);
+ map.put(R.id.sort_date_new_old, SortOrder.DATE_NEW_OLD);
+ map.put(R.id.sort_date_old_new, SortOrder.DATE_OLD_NEW);
+ map.put(R.id.sort_duration_long_short, SortOrder.DURATION_LONG_SHORT);
+ map.put(R.id.sort_duration_short_long, SortOrder.DURATION_SHORT_LONG);
+ menuItemIdToSortOrder = Collections.unmodifiableMap(map);
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
@StringRes int resId = 0;
@@ -305,24 +320,12 @@ public class EpisodesApplyActionFragment extends Fragment {
checkWithMedia();
resId = R.string.selected_has_media_label;
break;
- case R.id.sort_title_a_z:
- sortByTitle(false);
- return true;
- case R.id.sort_title_z_a:
- sortByTitle(true);
- return true;
- case R.id.sort_date_new_old:
- sortByDate(true);
- return true;
- case R.id.sort_date_old_new:
- sortByDate(false);
- return true;
- case R.id.sort_duration_long_short:
- sortByDuration(true);
- return true;
- case R.id.sort_duration_short_long:
- sortByDuration(false);
- return true;
+ default: // handle various sort options
+ SortOrder sortOrder = menuItemIdToSortOrder.get(item.getItemId());
+ if (sortOrder != null) {
+ sort(sortOrder);
+ return true;
+ }
}
if(resId != 0) {
Snackbar.make(getActivity().findViewById(R.id.content), resId, Snackbar.LENGTH_SHORT)
@@ -333,52 +336,9 @@ public class EpisodesApplyActionFragment extends Fragment {
}
}
- private void sortByTitle(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- if (reverse) {
- return -1 * lhs.getTitle().compareTo(rhs.getTitle());
- } else {
- return lhs.getTitle().compareTo(rhs.getTitle());
- }
- });
- refreshTitles();
- refreshCheckboxes();
- }
-
- private void sortByDate(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- if (lhs.getPubDate() == null) {
- return -1;
- } else if (rhs.getPubDate() == null) {
- return 1;
- }
- int code = lhs.getPubDate().compareTo(rhs.getPubDate());
- if (reverse) {
- return -1 * code;
- } else {
- return code;
- }
- });
- refreshTitles();
- refreshCheckboxes();
- }
-
- private void sortByDuration(final boolean reverse) {
- Collections.sort(episodes, (lhs, rhs) -> {
- int ordering;
- if (!lhs.hasMedia()) {
- ordering = 1;
- } else if (!rhs.hasMedia()) {
- ordering = -1;
- } else {
- ordering = lhs.getMedia().getDuration() - rhs.getMedia().getDuration();
- }
- if(reverse) {
- return -1 * ordering;
- } else {
- return ordering;
- }
- });
+ private void sort(@NonNull SortOrder sortOrder) {
+ FeedItemPermutors.getPermutor(sortOrder)
+ .reorder(episodes);
refreshTitles();
refreshCheckboxes();
}
@@ -525,7 +485,7 @@ public class EpisodesApplyActionFragment extends Fragment {
}
}
try {
- DBTasks.downloadFeedItems(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()]));
+ DownloadRequester.getInstance().downloadMedia(getActivity(), toDownload.toArray(new FeedItem[toDownload.size()]));
} catch (DownloadRequestException e) {
e.printStackTrace();
DownloadRequestErrorDialogCreator.newRequestErrorDialog(getActivity(), e.getMessage());
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
index 607084c42..d2912f90f 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.dialog;
import android.content.Context;
-import android.support.v7.app.AlertDialog;
+import androidx.appcompat.app.AlertDialog;
import android.text.TextUtils;
import java.util.Arrays;
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 933ced0f9..17668586b 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.dialog;
import android.content.Context;
-import android.support.v7.app.AlertDialog;
+import androidx.appcompat.app.AlertDialog;
import android.text.Editable;
import android.text.InputType;
import android.view.View;
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
new file mode 100644
index 000000000..2ee716c7c
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/IntraFeedSortDialog.java
@@ -0,0 +1,51 @@
+package de.danoeh.antennapod.dialog;
+
+import android.content.Context;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.util.SortOrder;
+
+public abstract class IntraFeedSortDialog {
+
+ @Nullable
+ protected SortOrder currentSortOrder;
+ @NonNull
+ protected Context context;
+
+ public IntraFeedSortDialog(@NonNull Context context, @Nullable SortOrder sortOrder) {
+ this.context = context;
+ this.currentSortOrder = sortOrder;
+ }
+
+ public void openDialog() {
+ final String[] items = context.getResources().getStringArray(R.array.feed_episodes_sort_options);
+ final String[] valueStrs = context.getResources().getStringArray(R.array.feed_episodes_sort_values);
+ final SortOrder[] values = new SortOrder[valueStrs.length];
+ for (int i = 0; i < valueStrs.length; i++) {
+ values[i] = SortOrder.valueOf(valueStrs[i]);
+ }
+
+ int idxCurrentSort = -1;
+ for (int i = 0; i < values.length; i++) {
+ if (currentSortOrder == values[i]) {
+ idxCurrentSort = i;
+ break;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.sort);
+ builder.setSingleChoiceItems(items, idxCurrentSort, (dialog, idxNewSort) -> {
+ updateSort(values[idxNewSort]);
+ dialog.dismiss();
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+
+ protected abstract void updateSort(@NonNull SortOrder sortOrder);
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
index e8c7520b7..3e4e40a5b 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java
@@ -2,20 +2,22 @@ package de.danoeh.antennapod.dialog;
import android.app.Dialog;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.DialogFragment;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.DialogFragment;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.SeekBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import java.util.Locale;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
-import java.util.Locale;
-
public class PlaybackControlsDialog extends DialogFragment {
private static final float PLAYBACK_SPEED_STEP = 0.05f;
private static final float DEFAULT_MIN_PLAYBACK_SPEED = 0.5f;
@@ -23,7 +25,7 @@ public class PlaybackControlsDialog extends DialogFragment {
private static final String ARGUMENT_IS_PLAYING_VIDEO = "isPlayingVideo";
private PlaybackController controller;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private boolean isPlayingVideo;
public static PlaybackControlsDialog newInstance(boolean isPlayingVideo) {
@@ -41,7 +43,12 @@ public class PlaybackControlsDialog extends DialogFragment {
@Override
public void onStart() {
super.onStart();
- controller = new PlaybackController(getActivity(), false);
+ controller = new PlaybackController(getActivity(), false) {
+ @Override
+ public void setupGUI() {
+ setupUi();
+ }
+ };
controller.init();
setupUi();
}
@@ -58,15 +65,14 @@ public class PlaybackControlsDialog extends DialogFragment {
public Dialog onCreateDialog(Bundle savedInstanceState) {
isPlayingVideo = getArguments() != null && getArguments().getBoolean(ARGUMENT_IS_PLAYING_VIDEO);
- dialog = new MaterialDialog.Builder(getContext())
- .title(R.string.audio_controls)
- .customView(R.layout.audio_controls, true)
- .neutralText(R.string.close_label)
- .onNeutral((dialog1, which) -> {
- final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left);
- final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right);
+ dialog = new AlertDialog.Builder(getContext())
+ .setTitle(R.string.audio_controls)
+ .setView(R.layout.audio_controls)
+ .setPositiveButton(R.string.close_label, (dialog1, which) -> {
+ final SeekBar left = dialog.findViewById(R.id.volume_left);
+ final SeekBar right = dialog.findViewById(R.id.volume_right);
UserPreferences.setVolume(left.getProgress(), right.getProgress());
- }).build();
+ }).create();
return dialog;
}
@@ -109,6 +115,7 @@ public class PlaybackControlsDialog extends DialogFragment {
controller.setPlaybackSpeed(playbackSpeed);
String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
+ PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(playbackSpeed);
if (isPlayingVideo) {
UserPreferences.setVideoPlaybackSpeed(speedPref);
} else {
@@ -135,7 +142,7 @@ public class PlaybackControlsDialog extends DialogFragment {
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
- barPlaybackSpeed.setProgress((int) ((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
+ barPlaybackSpeed.setProgress(Math.round((currentSpeed - minPlaybackSpeed) / PLAYBACK_SPEED_STEP));
final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
@@ -206,9 +213,11 @@ public class PlaybackControlsDialog extends DialogFragment {
}
private float getCurrentSpeed() {
- if (isPlayingVideo) {
- return UserPreferences.getVideoPlaybackSpeed();
+ Playable media = null;
+ if (controller != null) {
+ media = controller.getMedia();
}
- return UserPreferences.getPlaybackSpeed();
+
+ return PlaybackSpeedUtils.getCurrentPlaybackSpeed(media);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
index c1008a380..11256f2de 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java
@@ -4,7 +4,8 @@ import android.app.Dialog;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
-import android.support.v4.content.ContextCompat;
+import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.ContextCompat;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
@@ -16,10 +17,6 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.TextView;
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
-import com.afollestad.materialdialogs.internal.MDButton;
-
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
@@ -48,7 +45,7 @@ public class ProxyDialog {
private final Context context;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private Spinner spType;
private EditText etHost;
@@ -64,54 +61,53 @@ public class ProxyDialog {
this.context = context;
}
- public Dialog createDialog() {
- dialog = new MaterialDialog.Builder(context)
- .title(R.string.pref_proxy_title)
- .customView(R.layout.proxy_settings, true)
- .positiveText(R.string.proxy_test_label)
- .negativeText(R.string.cancel_label)
- .onPositive((dialog1, which) -> {
- if(!testSuccessful) {
- dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
- test();
- return;
- }
- String type = (String) ((Spinner) dialog1.findViewById(R.id.spType)).getSelectedItem();
- ProxyConfig proxy;
- if(Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) {
- proxy = ProxyConfig.direct();
- } else {
- String host = etHost.getText().toString();
- String port = etPort.getText().toString();
- String username = etUsername.getText().toString();
- if(TextUtils.isEmpty(username)) {
- username = null;
- }
- String password = etPassword.getText().toString();
- if(TextUtils.isEmpty(password)) {
- password = null;
- }
- int portValue = 0;
- if(!TextUtils.isEmpty(port)) {
- portValue = Integer.valueOf(port);
- }
- if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) {
- proxy = ProxyConfig.socks(host, portValue, username, password);
- } else {
- proxy = ProxyConfig.http(host, portValue, username, password);
- }
- }
- UserPreferences.setProxyConfig(proxy);
- AntennapodHttpClient.reinit();
- dialog.dismiss();
- })
- .onNegative((dialog1, which) -> dialog1.dismiss())
- .autoDismiss(false)
- .build();
- View view = dialog.getCustomView();
- spType = view.findViewById(R.id.spType);
+ public Dialog show() {
+ View content = View.inflate(context, R.layout.proxy_settings, null);
+ dialog = new AlertDialog.Builder(context)
+ .setTitle(R.string.pref_proxy_title)
+ .setView(content)
+ .setNegativeButton(R.string.cancel_label, null)
+ .setPositiveButton(R.string.proxy_test_label, null)
+ .show();
+ // To prevent cancelling the dialog on button click
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener((view) -> {
+ if (!testSuccessful) {
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(false);
+ test();
+ return;
+ }
+ String type = (String) ((Spinner) content.findViewById(R.id.spType)).getSelectedItem();
+ ProxyConfig proxy;
+ if (Proxy.Type.valueOf(type) == Proxy.Type.DIRECT) {
+ proxy = ProxyConfig.direct();
+ } else {
+ String host = etHost.getText().toString();
+ String port = etPort.getText().toString();
+ String username = etUsername.getText().toString();
+ if(TextUtils.isEmpty(username)) {
+ username = null;
+ }
+ String password = etPassword.getText().toString();
+ if(TextUtils.isEmpty(password)) {
+ password = null;
+ }
+ int portValue = 0;
+ if(!TextUtils.isEmpty(port)) {
+ portValue = Integer.valueOf(port);
+ }
+ if (Proxy.Type.valueOf(type) == Proxy.Type.SOCKS) {
+ proxy = ProxyConfig.socks(host, portValue, username, password);
+ } else {
+ proxy = ProxyConfig.http(host, portValue, username, password);
+ }
+ }
+ UserPreferences.setProxyConfig(proxy);
+ AntennapodHttpClient.reinit();
+ dialog.dismiss();
+ });
- List<String> types= new ArrayList<>();
+ spType = content.findViewById(R.id.spType);
+ List<String> types = new ArrayList<>();
types.add(Proxy.Type.DIRECT.name());
types.add(Proxy.Type.HTTP.name());
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -123,22 +119,22 @@ public class ProxyDialog {
spType.setAdapter(adapter);
ProxyConfig proxyConfig = UserPreferences.getProxyConfig();
spType.setSelection(adapter.getPosition(proxyConfig.type.name()));
- etHost = view.findViewById(R.id.etHost);
+ etHost = content.findViewById(R.id.etHost);
if(!TextUtils.isEmpty(proxyConfig.host)) {
etHost.setText(proxyConfig.host);
}
etHost.addTextChangedListener(requireTestOnChange);
- etPort = view.findViewById(R.id.etPort);
+ etPort = content.findViewById(R.id.etPort);
if(proxyConfig.port > 0) {
etPort.setText(String.valueOf(proxyConfig.port));
}
etPort.addTextChangedListener(requireTestOnChange);
- etUsername = view.findViewById(R.id.etUsername);
+ etUsername = content.findViewById(R.id.etUsername);
if(!TextUtils.isEmpty(proxyConfig.username)) {
etUsername.setText(proxyConfig.username);
}
etUsername.addTextChangedListener(requireTestOnChange);
- etPassword = view.findViewById(R.id.etPassword);
+ etPassword = content.findViewById(R.id.etPassword);
if(!TextUtils.isEmpty(proxyConfig.password)) {
etPassword.setText(proxyConfig.username);
}
@@ -159,7 +155,7 @@ public class ProxyDialog {
enableSettings(false);
}
});
- txtvMessage = view.findViewById(R.id.txtvMessage);
+ txtvMessage = content.findViewById(R.id.txtvMessage);
checkValidity();
return dialog;
}
@@ -230,14 +226,12 @@ public class ProxyDialog {
private void setTestRequired(boolean required) {
if(required) {
testSuccessful = false;
- MDButton button = dialog.getActionButton(DialogAction.POSITIVE);
- button.setText(context.getText(R.string.proxy_test_label));
- button.setEnabled(true);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(R.string.proxy_test_label);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
} else {
testSuccessful = true;
- MDButton button = dialog.getActionButton(DialogAction.POSITIVE);
- button.setText(context.getText(android.R.string.ok));
- button.setEnabled(true);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(android.R.string.ok);
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(true);
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
index 24656ed29..7cb274708 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java
@@ -2,19 +2,17 @@ package de.danoeh.antennapod.dialog;
import android.app.Dialog;
import android.content.Context;
-import android.content.Intent;
import android.content.SharedPreferences;
-import android.net.Uri;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
import android.util.Log;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.util.IntentUtils;
public class RatingDialog {
@@ -59,14 +57,10 @@ public class RatingDialog {
private static void rateNow() {
Context context = mContext.get();
- if(context == null) {
+ if (context == null) {
return;
}
- final String appPackage = "de.danoeh.antennapod";
- final Uri uri = Uri.parse("https://play.google.com/store/apps/details?id=" + appPackage);
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- context.startActivity(intent);
+ IntentUtils.openInBrowser(context, "https://play.google.com/store/apps/details?id=de.danoeh.antennapod");
saveRated();
}
@@ -102,21 +96,18 @@ public class RatingDialog {
}
@Nullable
- private static MaterialDialog createDialog() {
+ private static AlertDialog createDialog() {
Context context = mContext.get();
- if(context == null) {
+ if (context == null) {
return null;
}
- return new MaterialDialog.Builder(context)
- .title(R.string.rating_title)
- .content(R.string.rating_message)
- .positiveText(R.string.rating_now_label)
- .negativeText(R.string.rating_never_label)
- .neutralText(R.string.rating_later_label)
- .onPositive((dialog, which) -> rateNow())
- .onNegative((dialog, which) -> saveRated())
- .onNeutral((dialog, which) -> resetStartDate())
- .cancelListener(dialog1 -> resetStartDate())
- .build();
+ return new AlertDialog.Builder(context)
+ .setTitle(R.string.rating_title)
+ .setMessage(R.string.rating_message)
+ .setPositiveButton(R.string.rating_now_label, (dialog, which) -> rateNow())
+ .setNegativeButton(R.string.rating_never_label, (dialog, which) -> saveRated())
+ .setNeutralButton(R.string.rating_later_label, (dialog, which) -> resetStartDate())
+ .setOnCancelListener(dialog1 -> resetStartDate())
+ .create();
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
index 31a544582..699c6f492 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/RenameFeedDialog.java
@@ -3,10 +3,12 @@ package de.danoeh.antennapod.dialog;
import android.app.Activity;
import android.text.InputType;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import java.lang.ref.WeakReference;
+import android.view.View;
+import android.widget.EditText;
+import androidx.appcompat.app.AlertDialog;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -25,20 +27,24 @@ public class RenameFeedDialog {
if(activity == null) {
return;
}
- new MaterialDialog.Builder(activity)
- .title(de.danoeh.antennapod.core.R.string.rename_feed_label)
- .inputType(InputType.TYPE_CLASS_TEXT)
- .input(feed.getTitle(), feed.getTitle(), true, (dialog, input) -> {
- feed.setCustomTitle(input.toString());
+
+ View content = View.inflate(activity, R.layout.edit_text_dialog, null);
+ EditText editText = content.findViewById(R.id.text);
+ editText.setText(feed.getTitle());
+ AlertDialog dialog = new AlertDialog.Builder(activity)
+ .setView(content)
+ .setTitle(de.danoeh.antennapod.core.R.string.rename_feed_label)
+ .setPositiveButton(android.R.string.ok, (d, input) -> {
+ feed.setCustomTitle(editText.getText().toString());
DBWriter.setFeedCustomTitle(feed);
- dialog.dismiss();
})
- .neutralText(de.danoeh.antennapod.core.R.string.reset)
- .onNeutral((dialog, which) -> dialog.getInputEditText().setText(feed.getFeedTitle()))
- .negativeText(de.danoeh.antennapod.core.R.string.cancel_label)
- .onNegative((dialog, which) -> dialog.dismiss())
- .autoDismiss(false)
+ .setNeutralButton(de.danoeh.antennapod.core.R.string.reset, null)
+ .setNegativeButton(de.danoeh.antennapod.core.R.string.cancel_label, null)
.show();
+
+ // To prevent cancelling the dialog on button click
+ dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(
+ (view) -> editText.setText(feed.getFeedTitle()));
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
index dc056a3f9..8d176c708 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java
@@ -3,7 +3,6 @@ package de.danoeh.antennapod.dialog;
import android.content.Context;
import android.text.Editable;
import android.text.TextWatcher;
-import android.util.Log;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
@@ -12,9 +11,7 @@ import android.widget.EditText;
import android.widget.Spinner;
import android.widget.Toast;
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
-
+import androidx.appcompat.app.AlertDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@@ -26,7 +23,7 @@ public abstract class SleepTimerDialog {
private final Context context;
- private MaterialDialog dialog;
+ private AlertDialog dialog;
private EditText etxtTime;
private Spinner spTimeUnit;
private CheckBox cbShakeToReset;
@@ -38,40 +35,38 @@ public abstract class SleepTimerDialog {
this.context = context;
}
- public MaterialDialog createNewDialog() {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.set_sleeptimer_label);
- builder.customView(R.layout.time_dialog, false);
- builder.positiveText(R.string.set_sleeptimer_label);
- builder.negativeText(R.string.cancel_label);
- builder.onNegative((dialog, which) -> dialog.dismiss());
- builder.onPositive((dialog, which) -> {
- try {
- savePreferences();
- long input = SleepTimerPreferences.timerMillis();
- onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
- dialog.dismiss();
- } catch (NumberFormatException e) {
- e.printStackTrace();
- Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
- Toast.LENGTH_LONG);
- toast.show();
- }
- });
- dialog = builder.build();
-
- View view = dialog.getView();
- etxtTime = view.findViewById(R.id.etxtTime);
- spTimeUnit = view.findViewById(R.id.spTimeUnit);
- cbShakeToReset = view.findViewById(R.id.cbShakeToReset);
- cbVibrate = view.findViewById(R.id.cbVibrate);
- chAutoEnable = view.findViewById(R.id.chAutoEnable);
+ public AlertDialog createNewDialog() {
+ View content = View.inflate(context, R.layout.time_dialog, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.set_sleeptimer_label);
+ builder.setView(content);
+ builder.setNegativeButton(R.string.cancel_label, (dialog, which) -> dialog.dismiss());
+ builder.setPositiveButton(R.string.set_sleeptimer_label, (dialog, which) -> {
+ try {
+ savePreferences();
+ long input = SleepTimerPreferences.timerMillis();
+ onTimerSet(input, cbShakeToReset.isChecked(), cbVibrate.isChecked());
+ dialog.dismiss();
+ } catch (NumberFormatException e) {
+ e.printStackTrace();
+ Toast toast = Toast.makeText(context, R.string.time_dialog_invalid_input,
+ Toast.LENGTH_LONG);
+ toast.show();
+ }
+ });
+ dialog = builder.create();
+
+ etxtTime = content.findViewById(R.id.etxtTime);
+ spTimeUnit = content.findViewById(R.id.spTimeUnit);
+ cbShakeToReset = content.findViewById(R.id.cbShakeToReset);
+ cbVibrate = content.findViewById(R.id.cbVibrate);
+ chAutoEnable = content.findViewById(R.id.chAutoEnable);
etxtTime.setText(SleepTimerPreferences.lastTimerValue());
etxtTime.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
- checkInputLength(s.length());
+ dialog.getButton(AlertDialog.BUTTON_POSITIVE).setEnabled(s.length() > 0);
}
@Override
@@ -109,16 +104,6 @@ public abstract class SleepTimerDialog {
return dialog;
}
- private void checkInputLength(int length) {
- if (length > 0) {
- Log.d(TAG, "Length is larger than 0, enabling confirm button");
- dialog.getActionButton(DialogAction.POSITIVE).setEnabled(true);
- } else {
- Log.d(TAG, "Length is smaller than 0, disabling confirm button");
- dialog.getActionButton(DialogAction.POSITIVE).setEnabled(false);
- }
- }
-
public abstract void onTimerSet(long millis, boolean shakeToReset, boolean vibrate);
private void savePreferences() {
diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
index bf3faf89a..ef624ebe6 100644
--- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
+++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java
@@ -1,37 +1,21 @@
package de.danoeh.antennapod.dialog;
-import android.content.ActivityNotFoundException;
import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
import android.os.Build;
-import android.support.v7.app.AlertDialog;
-import android.util.Log;
-import android.view.View;
-
-import com.afollestad.materialdialogs.DialogAction;
-import com.afollestad.materialdialogs.MaterialDialog;
+import androidx.appcompat.app.AlertDialog;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.IntentUtils;
-
public class VariableSpeedDialog {
- private static final String TAG = VariableSpeedDialog.class.getSimpleName();
-
- private static final Intent playStoreIntent = new Intent(Intent.ACTION_VIEW,
- Uri.parse("market://details?id=com.falconware.prestissimo"));
-
private VariableSpeedDialog() {
}
public static void showDialog(final Context context) {
- if (org.antennapod.audio.MediaPlayer.isPrestoLibraryInstalled(context)
- || UserPreferences.useSonic()
+ if (UserPreferences.useSonic()
|| UserPreferences.useExoplayer()
|| Build.VERSION.SDK_INT >= 23) {
showSpeedSelectorDialog(context);
@@ -45,38 +29,17 @@ public class VariableSpeedDialog {
}
private static void showGetPluginDialog(final Context context, boolean showSpeedSelector) {
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.no_playback_plugin_title);
- builder.content(R.string.no_playback_plugin_or_sonic_msg);
- builder.positiveText(R.string.enable_sonic);
- builder.negativeText(R.string.download_plugin_label);
- builder.neutralText(R.string.close_label);
- builder.onPositive((dialog, which) -> {
- if (Build.VERSION.SDK_INT >= 16) { // just to be safe
- UserPreferences.enableSonic();
- if(showSpeedSelector) {
- showSpeedSelectorDialog(context);
- }
- }
- });
- builder.onNegative((dialog, which) -> {
- try {
- context.startActivity(playStoreIntent);
- } catch (ActivityNotFoundException e) {
- // this is usually thrown on an emulator if the Android market is not installed
- Log.e(TAG, Log.getStackTraceString(e));
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.no_playback_plugin_title);
+ builder.setMessage(R.string.no_playback_plugin_or_sonic_msg);
+ builder.setPositiveButton(R.string.enable_sonic, (dialog, which) -> {
+ UserPreferences.enableSonic();
+ if (showSpeedSelector) {
+ showSpeedSelectorDialog(context);
}
});
- builder.forceStacking(true);
- MaterialDialog dialog = builder.show();
- if (Build.VERSION.SDK_INT < 16) {
- View pos = dialog.getActionButton(DialogAction.POSITIVE);
- pos.setEnabled(false);
- }
- if(!IntentUtils.isCallable(context.getApplicationContext(), playStoreIntent)) {
- View pos = dialog.getActionButton(DialogAction.NEGATIVE);
- pos.setEnabled(false);
- }
+ builder.setNeutralButton(R.string.close_label, null);
+ builder.show();
}
private static void showSpeedSelectorDialog(final Context context) {
diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
index ca9ed83d7..6535df5ef 100644
--- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
+++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.discovery;
-import android.support.annotation.Nullable;
+import androidx.annotation.Nullable;
import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast;
import de.mfietz.fyydlin.SearchHit;
import org.json.JSONArray;
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 3ef010f88..2cfe7c1e8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java
@@ -2,20 +2,15 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
-import android.provider.MediaStore;
-import android.support.v4.app.Fragment;
+import androidx.fragment.app.Fragment;
import android.view.ContextMenu;
-import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@@ -51,7 +46,7 @@ public class AddFeedFragment extends Fragment {
View butOpmlImport = root.findViewById(R.id.btn_opml_import);
butOpmlImport.setOnClickListener(v -> startActivity(new Intent(getActivity(),
OpmlImportFromPathActivity.class)));
-
+ root.findViewById(R.id.search_icon).setOnClickListener(view -> performSearch());
return root;
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
index bb52b26b7..3949a03a9 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
index bb8f4df9a..2df28b262 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.support.v4.app.ListFragment;
+import androidx.fragment.app.ListFragment;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
index 1d9020f0d..47d7a0b86 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java
@@ -2,9 +2,9 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
+import androidx.fragment.app.Fragment;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.widget.SearchView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -101,7 +101,6 @@ public class CombinedSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java
index 705151062..7f70daaec 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,8 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.ListFragment;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.ListFragment;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -16,7 +16,8 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
@@ -27,6 +28,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_ADD_TO_QUEUE;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
@@ -38,10 +41,6 @@ public class CompletedDownloadsFragment extends ListFragment {
private static final String TAG = CompletedDownloadsFragment.class.getSimpleName();
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.DOWNLOADLOG_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE;
-
private List<FeedItem> items = new ArrayList<>();
private DownloadedEpisodesListAdapter listAdapter;
private Disposable disposable;
@@ -56,19 +55,24 @@ public class CompletedDownloadsFragment extends ListFragment {
listAdapter = new DownloadedEpisodesListAdapter(getActivity(), itemAccess);
setListAdapter(listAdapter);
setListShown(false);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroyView();
}
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -79,7 +83,7 @@ public class CompletedDownloadsFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(items);
- ((MainActivity) requireActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
+ ((MainActivity) requireActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override
@@ -135,14 +139,15 @@ public class CompletedDownloadsFragment extends ListFragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- }
- }
- };
+ @Subscribe
+ public void onDownloadLogChanged(DownloadLogEvent event) {
+ loadItems();
+ }
+
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadItems();
+ }
private void loadItems() {
if (disposable != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
index db9dd9530..5467d71a8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.Fragment;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -15,6 +15,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import io.reactivex.Maybe;
@@ -70,7 +71,7 @@ public class CoverFragment extends Fragment {
txtvPodcastTitle.setText(media.getFeedTitle());
txtvEpisodeTitle.setText(media.getEpisodeTitle());
Glide.with(this)
- .load(media.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(media))
.apply(new RequestOptions()
.diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
.dontAnimate()
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
index 26b115b4b..16337b00d 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java
@@ -4,8 +4,8 @@ import android.app.AlertDialog;
import android.app.Dialog;
import android.content.res.TypedArray;
import android.os.Bundle;
-import android.support.v4.app.ListFragment;
-import android.support.v4.view.MenuItemCompat;
+import androidx.fragment.app.ListFragment;
+import androidx.core.view.MenuItemCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -19,7 +19,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.DownloadLogAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadLogEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -30,6 +30,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
/**
* Shows the download log
@@ -45,14 +47,12 @@ public class DownloadLogFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
loadItems();
}
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -77,6 +77,13 @@ public class DownloadLogFragment extends ListFragment {
adapter = new DownloadLogAdapter(getActivity(), itemAccess);
setListAdapter(adapter);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ EventBus.getDefault().unregister(this);
+ super.onDestroyView();
}
private void onFragmentLoaded() {
@@ -133,15 +140,10 @@ public class DownloadLogFragment extends ListFragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
-
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EventDistributor.DOWNLOADLOG_UPDATE) != 0) {
- loadItems();
- }
- }
- };
+ @Subscribe
+ public void onDownloadLogChanged(DownloadLogEvent event) {
+ loadItems();
+ }
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
index aa6029c84..b1bcdf404 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java
@@ -4,11 +4,11 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
+import com.google.android.material.tabs.TabLayout;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.viewpager.widget.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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 ca21df661..8cdec9f38 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java
@@ -4,11 +4,11 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
+import com.google.android.material.tabs.TabLayout;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.viewpager.widget.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
index 1cedb5a91..5dbb703b7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java
@@ -4,15 +4,13 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.os.Handler;
-import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.SearchView;
-import android.support.v7.widget.SimpleItemAnimator;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
+import androidx.core.view.MenuItemCompat;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.appcompat.widget.SearchView;
+import androidx.recyclerview.widget.SimpleItemAnimator;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -23,8 +21,20 @@ import android.view.ViewGroup;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-import com.joanzapata.iconify.Iconify;
+
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.ArrayList;
+import java.util.List;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
@@ -32,22 +42,16 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
-import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedItemFilter;
import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.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.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.dialog.FilterDialog;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
import de.danoeh.antennapod.view.EmptyViewHandler;
@@ -55,13 +59,6 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Set;
/**
* Shows unread or recently published episodes
@@ -69,11 +66,6 @@ import java.util.Set;
public abstract class EpisodesListFragment extends Fragment {
public static final String TAG = "EpisodesListFragment";
-
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE |
- EventDistributor.UNREAD_ITEMS_UPDATE |
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private static final String DEFAULT_PREF_NAME = "PrefAllEpisodesFragment";
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
@@ -107,7 +99,6 @@ public abstract class EpisodesListFragment extends Fragment {
public void onStart() {
super.onStart();
setHasOptionsMenu(true);
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -129,7 +120,6 @@ public abstract class EpisodesListFragment extends Fragment {
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -149,7 +139,7 @@ public abstract class EpisodesListFragment extends Fragment {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(PREF_SCROLL_POSITION, firstItem);
editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
- editor.commit();
+ editor.apply();
}
private void restoreScrollPosition() {
@@ -162,7 +152,7 @@ public abstract class EpisodesListFragment extends Fragment {
SharedPreferences.Editor editor = prefs.edit();
editor.putInt(PREF_SCROLL_POSITION, 0);
editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
- editor.commit();
+ editor.apply();
}
}
@@ -179,7 +169,6 @@ public abstract class EpisodesListFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -202,10 +191,7 @@ public abstract class EpisodesListFragment extends Fragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.refresh_item:
- List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
- if (feeds != null) {
- DBTasks.refreshAllFeeds(getActivity(), feeds);
- }
+ AutoUpdateManager.runImmediate(requireContext());
return true;
case R.id.mark_all_read_item:
ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(),
@@ -262,17 +248,7 @@ public abstract class EpisodesListFragment extends Fragment {
}
FeedItem selectedItem = listAdapter.getSelectedItem();
- // Remove new flag 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 Remove new flag, given there is no UI to undo it otherwise,
- // i.e., there is context menu item for Mark as new
- if (R.id.remove_new_flag_item == item.getItemId()) {
- removeNewFlagWithUndo(selectedItem);
- return true;
- }
-
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
@NonNull
@@ -403,6 +379,20 @@ public abstract class EpisodesListFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (listAdapter != null) {
+ for (int i = 0; i < listAdapter.getItemCount(); i++) {
+ AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)
+ recyclerView.findViewHolderForAdapterPosition(i);
+ if (holder != null && holder.isCurrentlyPlayingItem()) {
+ holder.notifyPlaybackPositionUpdated(event);
+ break;
+ }
+ }
+ }
+ }
+
protected boolean shouldUpdatedItemRemainInList(FeedItem item) {
return true;
}
@@ -412,7 +402,7 @@ public abstract class EpisodesListFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) {
+ if (isMenuInvalidationAllowed && event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
requireActivity().invalidateOptionsMenu();
}
if (update.mediaIds.length > 0) {
@@ -425,17 +415,27 @@ public abstract class EpisodesListFragment extends Fragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- requireActivity().invalidateOptionsMenu();
- }
- }
+ private void updateUi() {
+ loadItems();
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ requireActivity().invalidateOptionsMenu();
}
- };
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ updateUi();
+ }
void loadItems() {
if (disposable != null) {
@@ -453,36 +453,4 @@ public abstract class EpisodesListFragment extends Fragment {
@NonNull
protected abstract List<FeedItem> loadData();
-
- void removeNewFlagWithUndo(FeedItem item) {
- if (item == null) {
- return;
- }
-
- Log.d(TAG, "removeNewFlagWithUndo(" + 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.removed_new_flag_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/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
index 348c73b92..ce2232a55 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java
@@ -3,8 +3,8 @@ 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 androidx.core.app.ActivityOptionsCompat;
+import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -18,16 +18,20 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.event.ServiceEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackController;
import io.reactivex.Maybe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Fragment which is supposed to be displayed outside of the MediaplayerActivity
@@ -67,9 +71,9 @@ public class ExternalPlayerFragment extends Fragment {
if (controller != null && controller.getMedia() != null) {
Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia());
- if (Build.VERSION.SDK_INT >= 16 && controller.getMedia().getMediaType() == MediaType.AUDIO) {
- ActivityOptionsCompat options = ActivityOptionsCompat.
- makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
+ if (controller.getMedia().getMediaType() == MediaType.AUDIO) {
+ ActivityOptionsCompat options = ActivityOptionsCompat
+ .makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition");
startActivity(intent, options.toBundle());
} else {
startActivity(intent);
@@ -138,6 +142,7 @@ public class ExternalPlayerFragment extends Fragment {
controller = setupPlaybackController();
controller.init();
loadMediaInfo();
+ EventBus.getDefault().register(this);
}
@Override
@@ -147,6 +152,12 @@ public class ExternalPlayerFragment extends Fragment {
controller.release();
controller = null;
}
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ onPositionObserverUpdate();
}
@Override
@@ -217,7 +228,7 @@ public class ExternalPlayerFragment extends Fragment {
onPositionObserverUpdate();
Glide.with(getActivity())
- .load(media.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(media))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
index 536ebd468..f73735658 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java
@@ -1,13 +1,12 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.design.widget.Snackbar;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
+import androidx.annotation.NonNull;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
-import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
@@ -55,7 +54,8 @@ public class FavoriteEpisodesFragment extends EpisodesListFragment {
emptyView.setTitle(R.string.no_fav_episodes_head_label);
emptyView.setMessage(R.string.no_fav_episodes_label);
- ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
+ ItemTouchHelper.LEFT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
new file mode 100644
index 000000000..3b843e150
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java
@@ -0,0 +1,226 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.LightingColorFilter;
+import android.net.Uri;
+import android.os.Bundle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.Fragment;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+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 de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
+import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.glide.FastBlurTransformation;
+import de.danoeh.antennapod.core.storage.DBReader;
+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 io.reactivex.Maybe;
+import io.reactivex.MaybeOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.StringUtils;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+
+/**
+ * Displays information about a feed.
+ */
+public class FeedInfoFragment extends Fragment {
+
+ private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
+ private static final String TAG = "FeedInfoActivity";
+
+ private Feed feed;
+ private Disposable disposable;
+ private ImageView imgvCover;
+ private TextView txtvTitle;
+ private TextView txtvDescription;
+ private TextView lblLanguage;
+ private TextView txtvLanguage;
+ private TextView lblAuthor;
+ private TextView txtvAuthor;
+ private TextView txtvUrl;
+ private TextView txtvAuthorHeader;
+ private ImageView imgvBackground;
+
+ public static FeedInfoFragment newInstance(Feed feed) {
+ FeedInfoFragment fragment = new FeedInfoFragment();
+ Bundle arguments = new Bundle();
+ arguments.putLong(EXTRA_FEED_ID, feed.getId());
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if(feed != null && feed.getDownload_url() != null) {
+ String url = feed.getDownload_url();
+ ClipData clipData = ClipData.newPlainText(url, url);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getContext()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
+ Toast t = Toast.makeText(getContext(), R.string.copied_url_msg, Toast.LENGTH_SHORT);
+ t.show();
+ }
+ }
+ };
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ ((MainActivity)getActivity()).getSupportActionBar().setTitle(R.string.feed_info_label);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.feedinfo, null);
+ setHasOptionsMenu(true);
+
+ imgvCover = root.findViewById(R.id.imgvCover);
+ txtvTitle = root.findViewById(R.id.txtvTitle);
+ txtvAuthorHeader = root.findViewById(R.id.txtvAuthor);
+ imgvBackground = root.findViewById(R.id.imgvBackground);
+ root.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE);
+ root.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE);
+ // https://github.com/bumptech/glide/issues/529
+ imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000));
+
+
+ txtvDescription = root.findViewById(R.id.txtvDescription);
+ lblLanguage = root.findViewById(R.id.lblLanguage);
+ txtvLanguage = root.findViewById(R.id.txtvLanguage);
+ lblAuthor = root.findViewById(R.id.lblAuthor);
+ txtvAuthor = root.findViewById(R.id.txtvDetailsAuthor);
+ txtvUrl = root.findViewById(R.id.txtvUrl);
+
+ txtvUrl.setOnClickListener(copyUrlToClipboard);
+ postponeEnterTransition();
+ return root;
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ long feedId = getArguments().getLong(EXTRA_FEED_ID);
+ 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;
+ showFeed();
+ }, error -> Log.d(TAG, Log.getStackTraceString(error)),
+ this::startPostponedEnterTransition);
+ }
+
+ private void showFeed() {
+ Log.d(TAG, "Language is " + feed.getLanguage());
+ Log.d(TAG, "Author is " + feed.getAuthor());
+ Log.d(TAG, "URL is " + feed.getDownload_url());
+ Glide.with(getContext())
+ .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(getContext())
+ .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());
+
+ String description = HtmlToPlainText.getPlainText(feed.getDescription());
+
+ txtvDescription.setText(description);
+
+ if (!TextUtils.isEmpty(feed.getAuthor())) {
+ txtvAuthor.setText(feed.getAuthor());
+ txtvAuthorHeader.setText(feed.getAuthor());
+ } else {
+ lblAuthor.setVisibility(View.GONE);
+ txtvAuthor.setVisibility(View.GONE);
+ }
+ if (!TextUtils.isEmpty(feed.getLanguage())) {
+ txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage()));
+ } else {
+ lblLanguage.setVisibility(View.GONE);
+ txtvLanguage.setVisibility(View.GONE);
+ }
+ txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}");
+ Iconify.addIcons(txtvUrl);
+
+ getActivity().invalidateOptionsMenu();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ inflater.inflate(R.menu.feedinfo, menu);
+ }
+
+ @Override
+ public void onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ 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(getContext(), new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink()))));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ boolean handled = false;
+ try {
+ handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ DownloadRequestErrorDialogCreator.newRequestErrorDialog(getContext(), e.getMessage());
+ }
+ return handled || super.onOptionsItemSelected(item);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
index 4e4b40096..f03aef207 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java
@@ -3,13 +3,8 @@ package de.danoeh.antennapod.fragment;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
import android.graphics.LightingColorFilter;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.ListFragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -24,6 +19,11 @@ import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+import androidx.fragment.app.ListFragment;
+
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
@@ -37,8 +37,6 @@ import org.greenrobot.eventbus.ThreadMode;
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.FeedItemlistAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
@@ -47,7 +45,12 @@ import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
+
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -61,6 +64,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.Optional;
@@ -81,12 +85,6 @@ import io.reactivex.schedulers.Schedulers;
@SuppressLint("ValidFragment")
public class FeedItemlistFragment extends ListFragment {
private static final String TAG = "ItemlistFragment";
-
- private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE
- | EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.PLAYER_STATUS_UPDATE;
-
- public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem";
private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id";
private FeedItemlistAdapter adapter;
@@ -140,37 +138,31 @@ public class FeedItemlistFragment extends ListFragment {
}
@Override
- public void onStart() {
- super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
- EventBus.getDefault().register(this);
- loadItems();
+ public void onHiddenChanged(boolean hidden) {
+ super.onHiddenChanged(hidden);
+ if (!hidden && getActivity() != null) {
+ ((MainActivity) getActivity()).getSupportActionBar().setTitle("");
+ }
}
@Override
- public void onResume() {
- super.onResume();
- ((MainActivity)getActivity()).getSupportActionBar().setTitle("");
- updateProgressBarVisibility();
- }
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
- @Override
- public void onStop() {
- super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
- EventBus.getDefault().unregister(this);
- if(disposable != null) {
- disposable.dispose();
- }
+ registerForContextMenu(getListView());
+
+ EventBus.getDefault().register(this);
+ loadItems();
}
@Override
public void onDestroyView() {
super.onDestroyView();
- resetViewState();
- }
- private void resetViewState() {
+ EventBus.getDefault().unregister(this);
+ if (disposable != null) {
+ disposable.dispose();
+ }
adapter = null;
listFooter = null;
}
@@ -193,11 +185,11 @@ public class FeedItemlistFragment extends ListFragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
+ menu.findItem(R.id.sort_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.filter_items).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.episode_actions).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
menu.findItem(R.id.refresh_item).setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
@@ -323,7 +315,7 @@ public class FeedItemlistFragment extends ListFragment {
contextMenu = menu;
lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo;
- FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null);
+ FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item);
}
@Override
@@ -340,14 +332,7 @@ public class FeedItemlistFragment extends ListFragment {
return super.onContextItemSelected(item);
}
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- registerForContextMenu(getListView());
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
@Override
@@ -358,7 +343,7 @@ public class FeedItemlistFragment extends ListFragment {
position -= l.getHeaderViewsCount();
MainActivity activity = (MainActivity) getActivity();
long[] ids = FeedItemUtil.getIds(feed.getItems());
- activity.loadChildFragment(ItemFragment.newInstance(ids, position));
+ activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position));
activity.getSupportActionBar().setTitle(feed.getTitle());
}
@@ -390,26 +375,43 @@ public class FeedItemlistFragment extends ListFragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isUpdatingFeed != event.update.feedIds.length > 0) {
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeed)) {
updateProgressBarVisibility();
}
- if(adapter != null && update.mediaIds.length > 0) {
+ if (adapter != null && update.mediaIds.length > 0) {
adapter.notifyDataSetChanged();
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (adapter != null) {
+ adapter.notifyCurrentlyPlayingItemChanged(event, getListView());
+ }
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent. arg " + arg);
- refreshHeaderView();
- loadItems();
- updateProgressBarVisibility();
- }
+ private void updateUi() {
+ refreshHeaderView();
+ loadItems();
+ updateProgressBarVisibility();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ updateUi();
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ if (event.contains(feed)) {
+ updateUi();
}
- };
+ }
private void updateProgressBarVisibility() {
if (isUpdatingFeed != updateRefreshMenuItemChecker.isRefreshing()) {
@@ -421,8 +423,9 @@ public class FeedItemlistFragment extends ListFragment {
}
- private void onFragmentLoaded() {
- if(!isVisible()) {
+ private void displayList() {
+ if (getView() == null) {
+ Log.e(TAG, "Required root view is not yet created. Stop binding data to UI.");
return;
}
if (adapter == null) {
@@ -506,10 +509,8 @@ public class FeedItemlistFragment extends ListFragment {
imgvCover.setOnClickListener(v -> showFeedInfo());
butShowSettings.setOnClickListener(v -> {
if (feed != null) {
- Intent startIntent = new Intent(getActivity(), FeedSettingsActivity.class);
- startIntent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID,
- feed.getId());
- startActivity(startIntent);
+ FeedSettingsFragment fragment = FeedSettingsFragment.newInstance(feed);
+ ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.FLIP);
}
});
headerCreated = true;
@@ -517,10 +518,8 @@ public class FeedItemlistFragment extends ListFragment {
private void showFeedInfo() {
if (feed != null) {
- Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class);
- startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID,
- feed.getId());
- startActivity(startIntent);
+ FeedInfoFragment fragment = FeedInfoFragment.newInstance(feed);
+ ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.FLIP);
}
}
@@ -626,7 +625,7 @@ public class FeedItemlistFragment extends ListFragment {
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
feed = result.orElse(null);
- onFragmentLoaded();
+ displayList();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@@ -638,6 +637,11 @@ public class FeedItemlistFragment extends ListFragment {
FeedItemFilter filter = feed.getItemFilter();
feed.setItems(filter.filter(feed.getItems()));
}
+ if (feed != null && feed.getSortOrder() != null) {
+ List<FeedItem> feedItems = feed.getItems();
+ FeedItemPermutors.getPermutor(feed.getSortOrder()).reorder(feedItems);
+ feed.setItems(feedItems);
+ }
return Optional.ofNullable(feed);
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
index 4fb3d90f5..b745313aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java
@@ -1,49 +1,134 @@
package de.danoeh.antennapod.fragment;
-import android.arch.lifecycle.ViewModelProviders;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
-import android.support.v14.preference.SwitchPreference;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.PreferenceFragmentCompat;
+import androidx.preference.SwitchPreference;
+import androidx.preference.ListPreference;
+import androidx.preference.PreferenceFragmentCompat;
+import android.util.Log;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
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.preferences.UserPreferences;
+import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
import de.danoeh.antennapod.dialog.EpisodeFilterDialog;
-import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel;
+import io.reactivex.Maybe;
+import io.reactivex.MaybeOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Locale;
-import static de.danoeh.antennapod.activity.FeedSettingsActivity.EXTRA_FEED_ID;
+import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
public class FeedSettingsFragment extends PreferenceFragmentCompat {
private static final CharSequence PREF_EPISODE_FILTER = "episodeFilter";
+ private static final String PREF_FEED_PLAYBACK_SPEED = "feedPlaybackSpeed";
+ private static final DecimalFormat decimalFormat = new DecimalFormat("0.00", DecimalFormatSymbols.getInstance(Locale.US));
+ private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId";
+ private static final String TAG = "FeedSettingsFragment";
+
private Feed feed;
+ private Disposable disposable;
private FeedPreferences feedPreferences;
+ public static FeedSettingsFragment newInstance(Feed feed) {
+ FeedSettingsFragment fragment = new FeedSettingsFragment();
+ Bundle arguments = new Bundle();
+ arguments.putLong(EXTRA_FEED_ID, feed.getId());
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.feed_settings);
+ postponeEnterTransition();
long feedId = getArguments().getLong(EXTRA_FEED_ID);
- ViewModelProviders.of(getActivity()).get(FeedSettingsViewModel.class).getFeed(feedId)
+ 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 = feed.getPreferences();
+ ((MainActivity) getActivity()).getSupportActionBar().setSubtitle(feed.getTitle());
setupAutoDownloadPreference();
setupKeepUpdatedPreference();
setupAutoDeletePreference();
setupAuthentificationPreference();
setupEpisodeFilterPreference();
+ setupPlaybackSpeedPreference();
updateAutoDeleteSummary();
updateAutoDownloadEnabled();
- }).dispose();
+ updatePlaybackSpeedPreference();
+ }, error -> Log.d(TAG, Log.getStackTraceString(error)),
+ this::startPostponedEnterTransition);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.feed_settings_label);
+ if (feed != null) {
+ ((MainActivity) getActivity()).getSupportActionBar().setSubtitle(feed.getTitle());
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ ((MainActivity) getActivity()).getSupportActionBar().setSubtitle(null);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void setupPlaybackSpeedPreference() {
+ ListPreference feedPlaybackSpeedPreference = findPreference(PREF_FEED_PLAYBACK_SPEED);
+
+ String[] speeds = UserPreferences.getPlaybackSpeedArray();
+
+ String[] values = new String[speeds.length + 1];
+ values[0] = decimalFormat.format(SPEED_USE_GLOBAL);
+
+ String[] entries = new String[speeds.length + 1];
+ entries[0] = getString(R.string.feed_auto_download_global);
+
+ System.arraycopy(speeds, 0, values, 1, speeds.length);
+ System.arraycopy(speeds, 0, entries, 1, speeds.length);
+
+ feedPlaybackSpeedPreference.setEntryValues(values);
+ feedPlaybackSpeedPreference.setEntries(entries);
+
+ feedPlaybackSpeedPreference.setOnPreferenceChangeListener((preference, newValue) -> {
+ feedPreferences.setFeedPlaybackSpeed(Float.parseFloat((String) newValue));
+ feed.savePreferences();
+ updatePlaybackSpeedPreference();
+ return false;
+ });
}
private void setupEpisodeFilterPreference() {
@@ -95,8 +180,15 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
});
}
+ private void updatePlaybackSpeedPreference() {
+ ListPreference feedPlaybackSpeedPreference = findPreference(PREF_FEED_PLAYBACK_SPEED);
+
+ float speedValue = feedPreferences.getFeedPlaybackSpeed();
+ feedPlaybackSpeedPreference.setValue(decimalFormat.format(speedValue));
+ }
+
private void updateAutoDeleteSummary() {
- ListPreference autoDeletePreference = (ListPreference) findPreference("autoDelete");
+ ListPreference autoDeletePreference = findPreference("autoDelete");
switch (feedPreferences.getAutoDeleteAction()) {
case GLOBAL:
@@ -170,7 +262,7 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat {
}
@Override
- public void onConfirmButtonPressed(DialogInterface dialog) {
+ public void onConfirmButtonPressed(DialogInterface dialog) {
DBWriter.setFeedsItemsAutoDownload(feed, autoDownload);
}
}
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 9c16cfe56..aa26610aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java
@@ -2,9 +2,9 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
+import androidx.fragment.app.Fragment;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.widget.SearchView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -100,7 +100,6 @@ public class FyydSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_fyyd_label));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index 5cf2c5eeb..85978b761 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -2,7 +2,6 @@ package de.danoeh.antennapod.fragment;
import android.annotation.SuppressLint;
import android.app.Activity;
-import android.content.ActivityNotFoundException;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
@@ -12,8 +11,8 @@ import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.Fragment;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
@@ -96,13 +95,7 @@ public class ItemDescriptionFragment extends Fragment {
if (Timeline.isTimecodeLink(url)) {
onTimecodeLinkSelected(url);
} else {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- try {
- startActivity(intent);
- } catch (ActivityNotFoundException e) {
- e.printStackTrace();
- return true;
- }
+ IntentUtils.openInBrowser(getContext(), url);
}
return true;
}
@@ -151,7 +144,6 @@ public class ItemDescriptionFragment extends Fragment {
}
};
- @SuppressWarnings("deprecation")
@SuppressLint("NewApi")
@Override
public boolean onContextItemSelected(MenuItem item) {
@@ -159,11 +151,7 @@ public class ItemDescriptionFragment extends Fragment {
if (selectedURL != null) {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- getActivity().startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), selectedURL);
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
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 3a48c5431..9a88441e0 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -3,122 +3,97 @@ package de.danoeh.antennapod.fragment;
import android.content.ClipData;
import android.content.Context;
import android.content.Intent;
+import android.content.res.TypedArray;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.support.annotation.Nullable;
-import android.support.v4.app.Fragment;
-import android.support.v4.content.ContextCompat;
-import android.support.v4.view.GestureDetectorCompat;
import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
+import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuInflater;
import android.view.MenuItem;
-import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
+import android.widget.Button;
import android.widget.ImageView;
-import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
-
+import androidx.annotation.AttrRes;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.StringRes;
+import androidx.core.content.ContextCompat;
+import androidx.fragment.app.Fragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import com.joanzapata.iconify.Iconify;
import com.joanzapata.iconify.widget.IconButton;
-
-import org.apache.commons.lang3.ArrayUtils;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
-
-import java.util.List;
-
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.CastEnabledActivity;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.Downloader;
-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.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.DateUtils;
-import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.playback.Timeline;
-import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
-import de.danoeh.antennapod.view.OnSwipeGesture;
-import de.danoeh.antennapod.view.SwipeGestureDetector;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.apache.commons.lang3.ArrayUtils;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
+import java.util.List;
/**
* Displays information about a FeedItem and actions.
*/
-public class ItemFragment extends Fragment implements OnSwipeGesture {
+public class ItemFragment extends Fragment {
private static final String TAG = "ItemFragment";
-
- private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE;
-
- private static final String ARG_FEEDITEMS = "feeditems";
- private static final String ARG_FEEDITEM_POS = "feeditem_pos";
-
- private GestureDetectorCompat headerGestureDetector;
- private GestureDetectorCompat webviewGestureDetector;
+ private static final String ARG_FEEDITEM = "feeditem";
/**
* Creates a new instance of an ItemFragment
*
- * @param feeditem The ID of the FeedItem that should be displayed.
+ * @param feeditem The ID of the FeedItem to show
* @return The ItemFragment instance
*/
public static ItemFragment newInstance(long feeditem) {
- return newInstance(new long[] { feeditem }, 0);
- }
-
- /**
- * Creates a new instance of an ItemFragment
- *
- * @param feeditems The IDs of the FeedItems that belong to the same list
- * @param feedItemPos The position of the FeedItem that is currently shown
- * @return The ItemFragment instance
- */
- public static ItemFragment newInstance(long[] feeditems, int feedItemPos) {
ItemFragment fragment = new ItemFragment();
Bundle args = new Bundle();
- args.putLongArray(ARG_FEEDITEMS, feeditems);
- args.putInt(ARG_FEEDITEM_POS, feedItemPos);
+ args.putLong(ARG_FEEDITEM, feeditem);
fragment.setArguments(args);
return fragment;
}
private boolean itemsLoaded = false;
- private long[] feedItems;
- private int feedItemPos;
+ private long itemId;
private FeedItem item;
private String webviewData;
private List<Downloader> downloaderList;
@@ -132,9 +107,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
private ImageView imgvCover;
private ProgressBar progbarDownload;
private ProgressBar progbarLoading;
- private IconButton butAction1;
- private IconButton butAction2;
- private Menu popupMenu;
+ private Button butAction1;
+ private Button butAction2;
private Disposable disposable;
@@ -146,20 +120,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- setRetainInstance(true);
- setHasOptionsMenu(true);
- feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
- feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
-
- headerGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this));
- webviewGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this) {
- // necessary for the longclick context menu to work properly
- @Override
- public boolean onDown(MotionEvent e) {
- return false;
- }
- });
+ itemId = getArguments().getLong(ARG_FEEDITEM);
}
@Override
@@ -169,11 +131,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
root = layout.findViewById(R.id.content_root);
- LinearLayout header = root.findViewById(R.id.header);
- if(feedItems.length > 0) {
- header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
- }
-
txtvPodcast = layout.findViewById(R.id.txtvPodcast);
txtvPodcast.setOnClickListener(v -> openPodcast());
txtvTitle = layout.findViewById(R.id.txtvTitle);
@@ -204,17 +161,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
webvDescription.getSettings().setLayoutAlgorithm(
WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
webvDescription.getSettings().setLoadWithOverviewMode(true);
- if(feedItems.length > 0) {
webvDescription.setOnLongClickListener(webViewLongClickListener);
- }
- webvDescription.setOnTouchListener((v, event) -> webviewGestureDetector.onTouchEvent(event));
+
webvDescription.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- if(IntentUtils.isCallable(getActivity(), intent)) {
- startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), url);
return true;
}
});
@@ -264,6 +216,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
@Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ load();
+ }
+
+ @Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@@ -271,9 +229,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
- load();
}
@Override
@@ -288,7 +244,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
}
@@ -304,68 +259,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
- @Override
- public boolean onSwipeLeftToRight() {
- return swipeFeedItem(-1);
- }
-
- @Override
- public boolean onSwipeRightToLeft() {
- return swipeFeedItem(+1);
- }
-
- private boolean swipeFeedItem(int position) {
- Log.d(TAG, String.format("onSwipe() shift: %s", position));
- feedItemPos = (feedItemPos + position) % feedItems.length;
- if (feedItemPos < 0) {
- feedItemPos = feedItems.length - 1;
- }
- load();
- return true;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
- if(!isAdded() || item == null) {
- return;
- }
- super.onCreateOptionsMenu(menu, inflater);
- if (Flavors.FLAVOR == Flavors.PLAY) {
- ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
- }
- inflater.inflate(R.menu.feeditem_options, menu);
- popupMenu = menu;
- if (item.hasMedia()) {
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null);
- } else {
- // these are already available via button1 and button2
- FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null,
- R.id.mark_read_item, R.id.visit_website_item);
- }
- }
-
- @Override
- public boolean onOptionsItemSelected(MenuItem menuItem) {
- switch(menuItem.getItemId()) {
- case R.id.open_podcast:
- openPodcast();
- return true;
- default:
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
- }
- }
-
- private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
- @Override
- public void setItemVisibility(int id, boolean visible) {
- MenuItem item = popupMenu.findItem(id);
- if (item != null) {
- item.setVisible(visible);
- }
- }
- };
-
-
private void onFragmentLoaded() {
if (webviewData != null) {
webvDescription.loadDataWithBaseURL("https://127.0.0.1", webviewData, "text/html", "utf-8", "about:blank");
@@ -378,7 +271,6 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
Log.d(TAG, "updateAppearance item is null");
return;
}
- getActivity().supportInvalidateOptionsMenu();
txtvPodcast.setText(item.getFeed().getTitle());
txtvTitle.setText(item.getTitle());
@@ -388,7 +280,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
Glide.with(getActivity())
- .load(item.getImageLocation())
+ .load(ImageResourceUtils.getImageLocation(item))
.apply(new RequestOptions()
.placeholder(R.color.light_gray)
.error(R.color.light_gray)
@@ -409,53 +301,59 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
FeedMedia media = item.getMedia();
- String butAction1Icon = null;
- int butAction1Text = 0;
- String butAction2Icon = null;
- int butAction2Text = 0;
+ @AttrRes int butAction1Icon = 0;
+ @StringRes int butAction1Text = 0;
+ @AttrRes int butAction2Icon = 0;
+ @StringRes int butAction2Text = 0;
if (media == null) {
if (!item.isPlayed()) {
- butAction1Icon = "{fa-check 24sp}";
+ butAction1Icon = R.attr.navigation_accept;
butAction1Text = R.string.mark_read_label;
}
if (item.getLink() != null) {
- butAction2Icon = "{md-web 24sp}";
+ butAction2Icon = R.attr.location_web_site;
butAction2Text = R.string.visit_website_label;
}
} else {
- if(media.getDuration() > 0) {
+ if (media.getDuration() > 0) {
txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
}
boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
if (!media.isDownloaded()) {
- butAction2Icon = "{md-settings-input-antenna 24sp}";
+ butAction2Icon = R.attr.action_stream;
butAction2Text = R.string.stream_label;
} else {
- butAction2Icon = "{md-delete 24sp}";
+ butAction2Icon = R.attr.content_discard;
butAction2Text = R.string.delete_label;
}
if (isDownloading) {
- butAction1Icon = "{md-cancel 24sp}";
+ butAction1Icon = R.attr.navigation_cancel;
butAction1Text = R.string.cancel_label;
} else if (media.isDownloaded()) {
- butAction1Icon = "{md-play-arrow 24sp}";
+ butAction1Icon = R.attr.av_play;
butAction1Text = R.string.play_label;
} else {
- butAction1Icon = "{md-file-download 24sp}";
+ butAction1Icon = R.attr.av_download;
butAction1Text = R.string.download_label;
}
}
- if(butAction1Icon != null && butAction1Text != 0) {
- butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
- Iconify.addIcons(butAction1);
+ if (butAction1Icon != 0 && butAction1Text != 0) {
+ butAction1.setText(butAction1Text);
+ butAction1.setTransformationMethod(null);
+ TypedValue typedValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(butAction1Icon, typedValue, true);
+ butAction1.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
butAction1.setVisibility(View.VISIBLE);
} else {
butAction1.setVisibility(View.INVISIBLE);
}
- if(butAction2Icon != null && butAction2Text != 0) {
- butAction2.setText(butAction2Icon +"\u0020\u0020" + getActivity().getString(butAction2Text));
- Iconify.addIcons(butAction2);
+ if (butAction2Icon != 0 && butAction2Text != 0) {
+ butAction2.setText(butAction2Text);
+ butAction2.setTransformationMethod(null);
+ TypedValue typedValue = new TypedValue();
+ getContext().getTheme().resolveAttribute(butAction2Icon, typedValue, true);
+ butAction2.setCompoundDrawablesWithIntrinsicBounds(typedValue.resourceId, 0, 0, 0);
butAction2.setVisibility(View.VISIBLE);
} else {
butAction2.setVisibility(View.INVISIBLE);
@@ -485,11 +383,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if (selectedURL != null) {
switch (item.getItemId()) {
case R.id.open_in_browser_item:
- Uri uri = Uri.parse(selectedURL);
- final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(getActivity(), intent)) {
- getActivity().startActivity(intent);
- }
+ IntentUtils.openInBrowser(getContext(), selectedURL);
break;
case R.id.share_url_item:
ShareUtils.shareLink(getActivity(), selectedURL);
@@ -541,8 +435,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(FeedItemEvent event) {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
- for(FeedItem item : event.items) {
- if(feedItems[feedItemPos] == item.getId()) {
+ for (FeedItem item : event.items) {
+ if (this.item.getId() == item.getId()) {
load();
return;
}
@@ -565,18 +459,13 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
}
}
-
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- load();
- }
- }
- };
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ load();
+ }
private void load() {
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
progbarLoading.setVisibility(View.VISIBLE);
@@ -593,7 +482,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
@Nullable
private FeedItem loadInBackground() {
- FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
+ FeedItem feedItem = DBReader.getFeedItem(itemId);
Context context = getContext();
if (feedItem != null && context != null) {
Timeline t = new Timeline(context, feedItem);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
new file mode 100644
index 000000000..74530e424
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemPagerFragment.java
@@ -0,0 +1,193 @@
+package de.danoeh.antennapod.fragment;
+
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.core.view.ViewCompat;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentStatePagerAdapter;
+import androidx.viewpager.widget.ViewPager;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.CastEnabledActivity;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.util.Flavors;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Displays information about a list of FeedItems.
+ */
+public class ItemPagerFragment extends Fragment {
+ private static final String ARG_FEEDITEMS = "feeditems";
+ private static final String ARG_FEEDITEM_POS = "feeditem_pos";
+
+ /**
+ * Creates a new instance of an ItemPagerFragment.
+ *
+ * @param feeditem The ID of the FeedItem that should be displayed.
+ * @return The ItemFragment instance
+ */
+ public static ItemPagerFragment newInstance(long feeditem) {
+ return newInstance(new long[] { feeditem }, 0);
+ }
+
+ /**
+ * Creates a new instance of an ItemPagerFragment.
+ *
+ * @param feeditems The IDs of the FeedItems that belong to the same list
+ * @param feedItemPos The position of the FeedItem that is currently shown
+ * @return The ItemFragment instance
+ */
+ public static ItemPagerFragment newInstance(long[] feeditems, int feedItemPos) {
+ ItemPagerFragment fragment = new ItemPagerFragment();
+ Bundle args = new Bundle();
+ args.putLongArray(ARG_FEEDITEMS, feeditems);
+ args.putInt(ARG_FEEDITEM_POS, feedItemPos);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ private long[] feedItems;
+ private int feedItemPos;
+ private FeedItem item;
+ private Disposable disposable;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+
+ feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
+ feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
+ }
+
+ @Override
+ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
+ @Nullable Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View layout = inflater.inflate(R.layout.feeditem_pager_fragment, container, false);
+
+ ViewPager pager = layout.findViewById(R.id.pager);
+ // FragmentStatePagerAdapter documentation:
+ // > When using FragmentStatePagerAdapter the host ViewPager must have a valid ID set.
+ // When opening multiple ItemPagerFragments by clicking "item" -> "visit podcast" -> "item" -> etc,
+ // the ID is no longer unique and FragmentStatePagerAdapter does not display any pages.
+ int newId = ViewCompat.generateViewId();
+ pager.setId(newId);
+ pager.setAdapter(new ItemPagerAdapter());
+ pager.setCurrentItem(feedItemPos);
+ loadItem(feedItems[feedItemPos]);
+ pager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
+ @Override
+ public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
+
+ }
+
+ @Override
+ public void onPageSelected(int position) {
+ loadItem(feedItems[position]);
+ }
+
+ @Override
+ public void onPageScrollStateChanged(int state) {
+
+ }
+ });
+
+ return layout;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ private void loadItem(long itemId) {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+
+ disposable = Observable.fromCallable(() -> DBReader.getFeedItem(itemId))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ item = result;
+ getActivity().invalidateOptionsMenu();
+ }, Throwable::printStackTrace);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (!isAdded() || item == null) {
+ return;
+ }
+ super.onCreateOptionsMenu(menu, inflater);
+ if (Flavors.FLAVOR == Flavors.PLAY) {
+ ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
+ inflater.inflate(R.menu.feeditem_options, menu);
+
+ FeedItemMenuHandler.MenuInterface popupMenuInterface = (id, visible) -> {
+ MenuItem item = menu.findItem(id);
+ if (item != null) {
+ item.setVisible(visible);
+ }
+ };
+
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch (menuItem.getItemId()) {
+ case R.id.open_podcast:
+ openPodcast();
+ return true;
+ default:
+ return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item);
+ }
+ }
+
+ private void openPodcast() {
+ Fragment fragment = FeedItemlistFragment.newInstance(item.getFeedId());
+ ((MainActivity) getActivity()).loadChildFragment(fragment);
+ }
+
+ private class ItemPagerAdapter extends FragmentStatePagerAdapter {
+
+ ItemPagerAdapter() {
+ super(getFragmentManager(), BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
+ }
+
+ @NonNull
+ @Override
+ public Fragment getItem(int position) {
+ return ItemFragment.newInstance(feedItems[position]);
+ }
+
+ @Override
+ public int getCount() {
+ return feedItems.length;
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
index 80767bef2..4b1544e47 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java
@@ -2,10 +2,11 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
-import android.support.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.widget.SearchView;
+import androidx.annotation.NonNull;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -18,34 +19,18 @@ import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
-
import de.danoeh.antennapod.discovery.ItunesPodcastSearcher;
import de.danoeh.antennapod.discovery.ItunesTopListLoader;
import de.danoeh.antennapod.discovery.PodcastSearchResult;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import java.util.Locale;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
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;
//Searches iTunes store for given string and displays results in a list
public class ItunesSearchFragment extends Fragment {
@@ -134,9 +119,9 @@ public class ItunesSearchFragment extends Fragment {
progressBar.setVisibility(View.GONE);
gridView.setVisibility(View.VISIBLE);
String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(getActivity())
+ .setMessage(prefix + " " + error.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
});
});
@@ -165,9 +150,8 @@ public class ItunesSearchFragment extends Fragment {
inflater.inflate(R.menu.itunes_search, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_itunes_label));
- sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() {
+ sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
sv.clearFocus();
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 adae4f2a5..2bfdd040b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java
@@ -1,12 +1,11 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.helper.ItemTouchHelper;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.ItemTouchHelper;
import android.view.LayoutInflater;
import android.view.Menu;
-import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
@@ -16,6 +15,7 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
/**
* Like 'EpisodesFragment' except that it only shows new episodes and
@@ -54,7 +54,8 @@ public class NewEpisodesFragment extends EpisodesListFragment {
emptyView.setTitle(R.string.no_new_episodes_head_label);
emptyView.setMessage(R.string.no_new_episodes_label);
- ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
+ ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0,
+ ItemTouchHelper.RIGHT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
@@ -63,7 +64,7 @@ public class NewEpisodesFragment extends EpisodesListFragment {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder;
- removeNewFlagWithUndo(holder.getFeedItem());
+ FeedItemMenuHandler.removeNewFlagWithUndo(NewEpisodesFragment.this, 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 e2060481f..a97e3dae8 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java
@@ -2,9 +2,9 @@ package de.danoeh.antennapod.fragment;
import android.content.res.TypedArray;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.ListFragment;
-import android.support.v4.view.MenuItemCompat;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.ListFragment;
+import androidx.core.view.MenuItemCompat;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -12,6 +12,8 @@ import android.view.MenuItem;
import android.view.View;
import android.widget.ListView;
+import de.danoeh.antennapod.core.event.PlaybackHistoryEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -24,7 +26,6 @@ import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.Downloader;
@@ -39,12 +40,8 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class PlaybackHistoryFragment extends ListFragment {
-
public static final String TAG = "PlaybackHistoryFragment";
- private static final int EVENTS = EventDistributor.PLAYBACK_HISTORY_UPDATE |
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private List<FeedItem> playbackHistory;
private FeedItemlistAdapter adapter;
private List<Downloader> downloaderList;
@@ -83,7 +80,6 @@ public class PlaybackHistoryFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
loadItems();
}
@@ -92,7 +88,6 @@ public class PlaybackHistoryFragment extends ListFragment {
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
- EventDistributor.getInstance().unregister(contentUpdate);
if (disposable != null) {
disposable.dispose();
}
@@ -111,7 +106,7 @@ public class PlaybackHistoryFragment extends ListFragment {
super.onListItemClick(l, v, position, id);
position -= l.getHeaderViewsCount();
long[] ids = FeedItemUtil.getIds(playbackHistory);
- ((MainActivity) getActivity()).loadChildFragment(ItemFragment.newInstance(ids, position));
+ ((MainActivity) getActivity()).loadChildFragment(ItemPagerFragment.newInstance(ids, position));
}
@Override
@@ -166,16 +161,17 @@ public class PlaybackHistoryFragment extends ListFragment {
}
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onHistoryUpdated(PlaybackHistoryEvent event) {
+ loadItems();
+ getActivity().supportInvalidateOptionsMenu();
+ }
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- loadItems();
- getActivity().supportInvalidateOptionsMenu();
- }
- }
- };
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ loadItems();
+ getActivity().supportInvalidateOptionsMenu();
+ }
private void onFragmentLoaded() {
adapter.notifyDataSetChanged();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
index 9763f8f2d..b36ce6145 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java
@@ -4,14 +4,6 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.LinearLayoutManager;
-import android.support.v7.widget.RecyclerView;
-import android.support.v7.widget.SearchView;
-import android.support.v7.widget.SimpleItemAnimator;
-import android.support.v7.widget.helper.ItemTouchHelper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -19,11 +11,26 @@ import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.ItemTouchHelper;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+import androidx.recyclerview.widget.SimpleItemAnimator;
+
+import com.google.android.material.snackbar.Snackbar;
import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+
import java.util.List;
import de.danoeh.antennapod.R;
@@ -33,35 +40,32 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.DownloaderUpdate;
import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.event.PlaybackPositionEvent;
+import de.danoeh.antennapod.core.event.PlayerStatusEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.EventDistributor;
-import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadService;
import de.danoeh.antennapod.core.service.download.Downloader;
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.DownloadRequester;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
-import de.danoeh.antennapod.core.util.QueueSorter;
import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment;
import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
import de.danoeh.antennapod.menuhandler.MenuItemUtils;
-
import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
-import org.greenrobot.eventbus.EventBus;
-import org.greenrobot.eventbus.Subscribe;
-import org.greenrobot.eventbus.ThreadMode;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE;
import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REMOVE_FROM_QUEUE;
@@ -70,13 +74,8 @@ import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REM
* Shows all items in the queue
*/
public class QueueFragment extends Fragment {
-
public static final String TAG = "QueueFragment";
- private static final int EVENTS = EventDistributor.DOWNLOAD_HANDLED |
- EventDistributor.UNREAD_ITEMS_UPDATE | // sent when playback position is reset
- EventDistributor.PLAYER_STATUS_UPDATE;
-
private TextView infoBar;
private RecyclerView recyclerView;
private QueueRecyclerAdapter recyclerAdapter;
@@ -91,10 +90,12 @@ public class QueueFragment extends Fragment {
private static final String PREFS = "QueueFragment";
private static final String PREF_SCROLL_POSITION = "scroll_position";
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
+ private static final String PREF_SHOW_LOCK_WARNING = "show_lock_warning";
private Disposable disposable;
private LinearLayoutManager layoutManager;
private ItemTouchHelper itemTouchHelper;
+ private SharedPreferences prefs;
@Override
@@ -102,6 +103,7 @@ public class QueueFragment extends Fragment {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
+ prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
}
@Override
@@ -111,7 +113,6 @@ public class QueueFragment extends Fragment {
onFragmentLoaded(true);
}
loadItems(true);
- EventDistributor.getInstance().register(contentUpdate);
EventBus.getDefault().register(this);
}
@@ -124,9 +125,8 @@ public class QueueFragment extends Fragment {
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
EventBus.getDefault().unregister(this);
- if(disposable != null) {
+ if (disposable != null) {
disposable.dispose();
}
}
@@ -196,8 +196,8 @@ public class QueueFragment extends Fragment {
Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
downloaderList = update.downloaders;
- if (isUpdatingFeeds != update.feedIds.length > 0) {
- getActivity().supportInvalidateOptionsMenu();
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
+ getActivity().invalidateOptionsMenu();
}
if (recyclerAdapter != null && update.mediaIds.length > 0) {
for (long mediaId : update.mediaIds) {
@@ -209,6 +209,37 @@ public class QueueFragment extends Fragment {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(PlaybackPositionEvent event) {
+ if (recyclerAdapter != null) {
+ for (int i = 0; i < recyclerAdapter.getItemCount(); i++) {
+ QueueRecyclerAdapter.ViewHolder holder = (QueueRecyclerAdapter.ViewHolder)
+ recyclerView.findViewHolderForAdapterPosition(i);
+ if (holder != null && holder.isCurrentlyPlayingItem()) {
+ holder.notifyPlaybackPositionUpdated(event);
+ break;
+ }
+ }
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onPlayerStatusChanged(PlayerStatusEvent event) {
+ loadItems(false);
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
+ }
+ }
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ // Sent when playback position is reset
+ loadItems(false);
+ if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
+ getActivity().supportInvalidateOptionsMenu();
+ }
+ }
+
private void saveScrollPosition() {
int firstItem = layoutManager.findFirstVisibleItemPosition();
View firstItemView = layoutManager.findViewByPosition(firstItem);
@@ -219,15 +250,13 @@ public class QueueFragment extends Fragment {
topOffset = firstItemView.getTop();
}
- SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- editor.putInt(PREF_SCROLL_POSITION, firstItem);
- editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
- editor.commit();
+ prefs.edit()
+ .putInt(PREF_SCROLL_POSITION, firstItem)
+ .putFloat(PREF_SCROLL_OFFSET, topOffset)
+ .apply();
}
private void restoreScrollPosition() {
- SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
if (position > 0 || offset > 0) {
@@ -259,7 +288,6 @@ public class QueueFragment extends Fragment {
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
@@ -299,25 +327,10 @@ public class QueueFragment extends Fragment {
if (!super.onOptionsItemSelected(item)) {
switch (item.getItemId()) {
case R.id.queue_lock:
- boolean newLockState = !UserPreferences.isQueueLocked();
- UserPreferences.setQueueLocked(newLockState);
- getActivity().supportInvalidateOptionsMenu();
- if (recyclerAdapter != null) {
- recyclerAdapter.setLocked(newLockState);
- }
- if (newLockState) {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_locked, Snackbar.LENGTH_SHORT).show();
- } else {
- Snackbar.make(getActivity().findViewById(R.id.content), R.string
- .queue_unlocked, Snackbar.LENGTH_SHORT).show();
- }
+ toggleQueueLock();
return true;
case R.id.refresh_item:
- List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
- if (feeds != null) {
- DBTasks.refreshAllFeeds(getActivity(), feeds);
- }
+ AutoUpdateManager.runImmediate(requireContext());
return true;
case R.id.clear_queue:
// make sure the user really wants to clear the queue
@@ -335,7 +348,7 @@ public class QueueFragment extends Fragment {
conDialog.createNewDialog().show();
return true;
case R.id.episode_actions:
- ((MainActivity) requireActivity()) .loadChildFragment(
+ ((MainActivity) requireActivity()).loadChildFragment(
EpisodesApplyActionFragment.newInstance(queue, ACTION_DELETE | ACTION_REMOVE_FROM_QUEUE));
return true;
case R.id.queue_sort_episode_title_asc:
@@ -377,7 +390,7 @@ public class QueueFragment extends Fragment {
UserPreferences.setQueueKeepSorted(keepSortedNew);
if (keepSortedNew) {
SortOrder sortOrder = UserPreferences.getQueueKeepSortedOrder();
- QueueSorter.sort(sortOrder, true);
+ DBWriter.reorderQueue(sortOrder, true);
if (recyclerAdapter != null) {
recyclerAdapter.setLocked(true);
}
@@ -394,6 +407,48 @@ public class QueueFragment extends Fragment {
}
}
+ private void toggleQueueLock() {
+ boolean isLocked = UserPreferences.isQueueLocked();
+ if (isLocked) {
+ setQueueLocked(false);
+ } else {
+ boolean shouldShowLockWarning = prefs.getBoolean(PREF_SHOW_LOCK_WARNING, true);
+ if (!shouldShowLockWarning) {
+ setQueueLocked(true);
+ } else {
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setTitle(R.string.lock_queue);
+ builder.setMessage(R.string.queue_lock_warning);
+
+ View view = View.inflate(getContext(), R.layout.checkbox_do_not_show_again, null);
+ CheckBox checkDoNotShowAgain = view.findViewById(R.id.checkbox_do_not_show_again);
+ builder.setView(view);
+
+ builder.setPositiveButton(R.string.lock_queue, (dialog, which) -> {
+ prefs.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked()).apply();
+ setQueueLocked(true);
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.show();
+ }
+ }
+ }
+
+ private void setQueueLocked(boolean locked) {
+ UserPreferences.setQueueLocked(locked);
+ getActivity().supportInvalidateOptionsMenu();
+ if (recyclerAdapter != null) {
+ recyclerAdapter.setLocked(locked);
+ }
+ if (locked) {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_locked, Snackbar.LENGTH_SHORT).show();
+ } else {
+ Snackbar.make(getActivity().findViewById(R.id.content), R.string
+ .queue_unlocked, Snackbar.LENGTH_SHORT).show();
+ }
+ }
+
/**
* This method is called if the user clicks on a sort order menu item.
*
@@ -401,7 +456,7 @@ public class QueueFragment extends Fragment {
*/
private void setSortOrder(SortOrder sortOrder) {
UserPreferences.setQueueKeepSortedOrder(sortOrder);
- QueueSorter.sort(sortOrder, true);
+ DBWriter.reorderQueue(sortOrder, true);
}
@Override
@@ -430,7 +485,7 @@ public class QueueFragment extends Fragment {
DBWriter.moveQueueItemToBottom(selectedItem.getId(), true);
return true;
default:
- return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem);
+ return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem);
}
}
@@ -454,7 +509,8 @@ public class QueueFragment extends Fragment {
registerForContextMenu(recyclerView);
itemTouchHelper = new ItemTouchHelper(
- new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) {
+ new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN,
+ ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
// Position tracking whilst dragging
int dragFrom = -1;
@@ -596,17 +652,19 @@ public class QueueFragment extends Fragment {
private void refreshInfoBar() {
String info = queue.size() + getString(R.string.episodes_suffix);
- if(queue.size() > 0) {
+ if (queue.size() > 0) {
long timeLeft = 0;
- float playbackSpeed = UserPreferences.getPlaybackSpeed();
- for(FeedItem item : queue) {
- if(item.getMedia() != null) {
- timeLeft +=
- (long) ((item.getMedia().getDuration() - item.getMedia().getPosition())
- / playbackSpeed);
+ for (FeedItem item : queue) {
+ float playbackSpeed = 1;
+ if (UserPreferences.timeRespectsSpeed()) {
+ playbackSpeed = PlaybackSpeedUtils.getCurrentPlaybackSpeed(item.getMedia());
+ }
+ if (item.getMedia() != null) {
+ long itemTimeLeft = item.getMedia().getDuration() - item.getMedia().getPosition();
+ timeLeft += itemTimeLeft / playbackSpeed;
}
}
- info += " \u2022 ";
+ info += " • ";
info += getString(R.string.time_left_label);
info += Converter.getDurationStringLocalized(getActivity(), timeLeft);
}
@@ -673,19 +731,6 @@ public class QueueFragment extends Fragment {
}
};
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & EVENTS) != 0) {
- Log.d(TAG, "arg: " + arg);
- loadItems(false);
- if (isUpdatingFeeds != updateRefreshMenuItemChecker.isRefreshing()) {
- getActivity().supportInvalidateOptionsMenu();
- }
- }
- }
- };
-
private void loadItems(final boolean restoreScrollPosition) {
Log.d(TAG, "loadItems()");
if(disposable != null) {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
index e4213cc6b..7e217cde4 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java
@@ -2,7 +2,8 @@ package de.danoeh.antennapod.fragment;
import android.content.Intent;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -11,7 +12,6 @@ import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ProgressBar;
import android.widget.TextView;
-import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.activity.OnlineFeedViewActivity;
@@ -110,9 +110,9 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView.
Log.e(TAG, Log.getStackTraceString(error));
view.setAlpha(1f);
String prefix = getString(R.string.error_msg_prefix);
- new MaterialDialog.Builder(getActivity())
- .content(prefix + " " + error.getMessage())
- .neutralText(android.R.string.ok)
+ new AlertDialog.Builder(getActivity())
+ .setMessage(prefix + " " + error.getMessage())
+ .setPositiveButton(android.R.string.ok, null)
.show();
});
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
index 2a7f7d12b..7e8823c27 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.fragment;
import android.os.Bundle;
-import android.support.v4.app.ListFragment;
+import androidx.fragment.app.ListFragment;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
@@ -25,6 +25,7 @@ import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.DownloadRequester;
import de.danoeh.antennapod.view.EmptyViewHandler;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Displays all running downloads and provides actions to cancel them
@@ -75,7 +76,7 @@ public class RunningDownloadsFragment extends ListFragment {
setListAdapter(null);
}
- @Subscribe(sticky = true)
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(DownloadEvent event) {
Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]");
DownloaderUpdate update = event.update;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
index 0892bce0a..d124d6aa2 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java
@@ -2,11 +2,11 @@ package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.os.Bundle;
-import android.support.annotation.NonNull;
-import android.support.v4.app.ListFragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.widget.SearchView;
+import androidx.annotation.NonNull;
+import androidx.fragment.app.ListFragment;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.SearchView;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
@@ -20,7 +20,7 @@ import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SearchlistAdapter;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedComponent;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -30,6 +30,8 @@ import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
/**
* Performs a search operation on all feeds or one specific feed and displays the search result.
@@ -76,7 +78,6 @@ public class SearchFragment extends ListFragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
search();
}
@@ -86,7 +87,6 @@ public class SearchFragment extends ListFragment {
if(disposable != null) {
disposable.dispose();
}
- EventDistributor.getInstance().unregister(contentUpdate);
}
@Override
@@ -103,6 +103,13 @@ public class SearchFragment extends ListFragment {
searchAdapter = new SearchlistAdapter(getActivity(), itemAccess);
setListAdapter(searchAdapter);
+ EventBus.getDefault().register(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ EventBus.getDefault().unregister(this);
}
@Override
@@ -145,15 +152,10 @@ public class SearchFragment extends ListFragment {
MenuItemCompat.setActionView(item, sv);
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((arg & (EventDistributor.UNREAD_ITEMS_UPDATE
- | EventDistributor.DOWNLOAD_HANDLED)) != 0) {
- search();
- }
- }
- };
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ search();
+ }
private void onSearchResults(List<SearchResult> results) {
searchResults = results;
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
index 15c6052a9..aa6d80bb0 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java
@@ -5,8 +5,8 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.support.annotation.StringRes;
-import android.support.v4.app.Fragment;
+import androidx.annotation.StringRes;
+import androidx.fragment.app.Fragment;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
@@ -18,6 +18,8 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
+import com.google.android.material.floatingactionbutton.FloatingActionButton;
+
import java.util.concurrent.Callable;
import de.danoeh.antennapod.R;
@@ -25,19 +27,29 @@ import de.danoeh.antennapod.activity.MainActivity;
import de.danoeh.antennapod.adapter.SubscriptionsAdapter;
import de.danoeh.antennapod.core.asynctask.FeedRemover;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
-import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
+import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.service.download.DownloadService;
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.storage.DownloadRequester;
import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.dialog.RenameFeedDialog;
+import de.danoeh.antennapod.menuhandler.MenuItemUtils;
+import de.danoeh.antennapod.view.EmptyViewHandler;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* Fragment for displaying feed subscriptions
@@ -45,17 +57,17 @@ import io.reactivex.schedulers.Schedulers;
public class SubscriptionFragment extends Fragment {
public static final String TAG = "SubscriptionFragment";
-
- private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
- | EventDistributor.UNREAD_ITEMS_UPDATE;
private static final String PREFS = "SubscriptionFragment";
private static final String PREF_NUM_COLUMNS = "columns";
private GridView subscriptionGridLayout;
private DBReader.NavDrawerData navDrawerData;
private SubscriptionsAdapter subscriptionAdapter;
+ private FloatingActionButton subscriptionAddButton;
+ private EmptyViewHandler emptyView;
private int mPosition = -1;
+ private boolean isUpdatingFeeds = false;
private Disposable disposable;
private SharedPreferences prefs;
@@ -76,6 +88,8 @@ public class SubscriptionFragment extends Fragment {
subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid);
subscriptionGridLayout.setNumColumns(prefs.getInt(PREF_NUM_COLUMNS, 3));
registerForContextMenu(subscriptionGridLayout);
+ subscriptionAddButton = root.findViewById(R.id.subscriptions_add);
+ setupEmptyView();
return root;
}
@@ -89,6 +103,8 @@ public class SubscriptionFragment extends Fragment {
menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3);
menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4);
menu.findItem(R.id.subscription_num_columns_5).setChecked(columns == 5);
+
+ isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker);
}
@Override
@@ -97,6 +113,9 @@ public class SubscriptionFragment extends Fragment {
return true;
}
switch (item.getItemId()) {
+ case R.id.refresh_item:
+ AutoUpdateManager.runImmediate(requireContext());
+ return true;
case R.id.subscription_num_columns_2:
setColumnNumber(2);
return true;
@@ -120,13 +139,28 @@ public class SubscriptionFragment extends Fragment {
getActivity().invalidateOptionsMenu();
}
+ private void setupEmptyView() {
+ emptyView = new EmptyViewHandler(getContext());
+ emptyView.setIcon(R.attr.ic_folder);
+ emptyView.setTitle(R.string.no_subscriptions_head_label);
+ emptyView.setMessage(R.string.no_subscriptions_label);
+ emptyView.attachToListView(subscriptionGridLayout);
+ }
+
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
- subscriptionAdapter = new SubscriptionsAdapter((MainActivity)getActivity(), itemAccess);
+
+ subscriptionAdapter = new SubscriptionsAdapter((MainActivity) getActivity(), itemAccess);
subscriptionGridLayout.setAdapter(subscriptionAdapter);
subscriptionGridLayout.setOnItemClickListener(subscriptionAdapter);
+ subscriptionAddButton.setOnClickListener(view -> {
+ if (getActivity() instanceof MainActivity) {
+ ((MainActivity) getActivity()).loadChildFragment(new AddFeedFragment());
+ }
+ });
+
if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.subscriptions_label);
}
@@ -135,14 +169,14 @@ public class SubscriptionFragment extends Fragment {
@Override
public void onStart() {
super.onStart();
- EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
loadSubscriptions();
}
@Override
public void onStop() {
super.onStop();
- EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
if(disposable != null) {
disposable.dispose();
}
@@ -152,12 +186,14 @@ public class SubscriptionFragment extends Fragment {
if(disposable != null) {
disposable.dispose();
}
+ emptyView.hide();
disposable = Observable.fromCallable(DBReader::getNavDrawerData)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
navDrawerData = result;
subscriptionAdapter.notifyDataSetChanged();
+ emptyView.updateVisibility();
}, error -> Log.e(TAG, Log.getStackTraceString(error)));
}
@@ -268,15 +304,26 @@ public class SubscriptionFragment extends Fragment {
dialog.createNewDialog().show();
}
- private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
- @Override
- public void update(EventDistributor eventDistributor, Integer arg) {
- if ((EVENTS & arg) != 0) {
- Log.d(TAG, "Received contentUpdate Intent.");
- loadSubscriptions();
- }
+ @Subscribe
+ public void onFeedListChanged(FeedListUpdateEvent event) {
+ loadSubscriptions();
+ }
+
+ @Subscribe
+ public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) {
+ loadSubscriptions();
+ }
+
+ @Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) {
+ getActivity().invalidateOptionsMenu();
}
- };
+ }
+
+ private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker =
+ () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds();
private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/TransitionEffect.java b/app/src/main/java/de/danoeh/antennapod/fragment/TransitionEffect.java
new file mode 100644
index 000000000..461fa9da3
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/TransitionEffect.java
@@ -0,0 +1,5 @@
+package de.danoeh.antennapod.fragment;
+
+public enum TransitionEffect {
+ NONE, FLIP, FADE
+}
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 4dc114f9b..380f6741a 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
@@ -4,11 +4,11 @@ import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Bundle;
-import android.support.design.widget.TabLayout;
-import android.support.v4.app.Fragment;
-import android.support.v4.app.FragmentManager;
-import android.support.v4.app.FragmentPagerAdapter;
-import android.support.v4.view.ViewPager;
+import com.google.android.material.tabs.TabLayout;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentManager;
+import androidx.fragment.app.FragmentPagerAdapter;
+import androidx.viewpager.widget.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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 49851ebb4..4baa74df6 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java
@@ -4,9 +4,9 @@ import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.v4.app.Fragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
+import androidx.fragment.app.Fragment;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.widget.SearchView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -54,9 +54,8 @@ public abstract class PodcastListFragment extends Fragment {
inflater.inflate(R.menu.gpodder_podcasts, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
- sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() {
+ sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String s) {
sv.clearFocus();
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 10bd636dd..ffe69aa9a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.fragment.gpodnet;
import android.os.Bundle;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.widget.SearchView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -50,7 +50,6 @@ public class SearchListFragment extends PodcastListFragment {
// parent already inflated menu
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setQuery(query, false);
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
index d39829260..92cd4ca84 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.fragment.gpodnet;
import android.os.Bundle;
-import android.support.annotation.Nullable;
+import androidx.annotation.Nullable;
import org.apache.commons.lang3.Validate;
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 1e46b1ac5..cde8fb3df 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java
@@ -4,9 +4,9 @@ import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Bundle;
-import android.support.v4.app.ListFragment;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.widget.SearchView;
+import androidx.fragment.app.ListFragment;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.widget.SearchView;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -40,7 +40,6 @@ public class TagListFragment extends ListFragment {
inflater.inflate(R.menu.gpodder_podcasts, menu);
MenuItem searchItem = menu.findItem(R.id.action_search);
final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem);
- MenuItemUtils.adjustTextColor(getActivity(), sv);
sv.setQueryHint(getString(R.string.gpodnet_search_hint));
sv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java
new file mode 100644
index 000000000..62a5eb306
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutDevelopersFragment.java
@@ -0,0 +1,65 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutDevelopersFragment extends ListFragment {
+ private Disposable developersLoader;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ getListView().setSelector(android.R.color.transparent);
+
+ developersLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
+ ArrayList<SimpleIconListAdapter.ListItem> developers = new ArrayList<>();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open("developers.csv")));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] info = line.split(";");
+ developers.add(new SimpleIconListAdapter.ListItem(info[0], info[2],
+ "https://avatars2.githubusercontent.com/u/" + info[1] + "?s=60&v=4"));
+ }
+ emitter.onSuccess(developers);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ developers -> setListAdapter(new SimpleIconListAdapter<>(getContext(), developers)),
+ error -> Toast.makeText(getContext(), "Error while loading developers", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (developersLoader != null) {
+ developersLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.developers);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java
new file mode 100644
index 000000000..0fa7bd4bb
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutFragment.java
@@ -0,0 +1,56 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Context;
+import android.os.Bundle;
+import androidx.preference.PreferenceFragmentCompat;
+import com.google.android.material.snackbar.Snackbar;
+import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.util.IntentUtils;
+
+public class AboutFragment extends PreferenceFragmentCompat {
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ addPreferencesFromResource(R.xml.preferences_about);
+
+ findPreference("about_version").setSummary(String.format(
+ "%s (%s)", BuildConfig.VERSION_NAME, BuildConfig.COMMIT_HASH));
+ findPreference("about_version").setOnPreferenceClickListener((preference) -> {
+ ClipboardManager clipboard = (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
+ ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title),
+ findPreference("about_version").getSummary());
+ clipboard.setPrimaryClip(clip);
+ Snackbar.make(getView(), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show();
+ return true;
+ });
+ findPreference("about_developers").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutDevelopersFragment())
+ .addToBackStack(getString(R.string.developers)).commit();
+ return true;
+ });
+ findPreference("about_translators").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutTranslatorsFragment())
+ .addToBackStack(getString(R.string.translators)).commit();
+ return true;
+ });
+ findPreference("about_privacy_policy").setOnPreferenceClickListener((preference) -> {
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/privacy.html");
+ return true;
+ });
+ findPreference("about_licenses").setOnPreferenceClickListener((preference) -> {
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutLicensesFragment())
+ .addToBackStack(getString(R.string.translators)).commit();
+ return true;
+ });
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.about_pref);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java
new file mode 100644
index 000000000..536d11e01
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutLicensesFragment.java
@@ -0,0 +1,126 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.ListView;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.NodeList;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutLicensesFragment extends ListFragment {
+ private Disposable licensesLoader;
+ private final ArrayList<LicenseItem> licenses = new ArrayList<>();
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+
+ licensesLoader = Single.create((SingleOnSubscribe<ArrayList<LicenseItem>>) emitter -> {
+ licenses.clear();
+ InputStream stream = getContext().getAssets().open("licenses.xml");
+ DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ NodeList libraryList = docBuilder.parse(stream).getElementsByTagName("library");
+ for (int i = 0; i < libraryList.getLength(); i++) {
+ NamedNodeMap lib = libraryList.item(i).getAttributes();
+ licenses.add(new LicenseItem(
+ lib.getNamedItem("name").getTextContent(),
+ String.format("By %s, %s license",
+ lib.getNamedItem("author").getTextContent(),
+ lib.getNamedItem("license").getTextContent()),
+ null,
+ lib.getNamedItem("website").getTextContent(),
+ lib.getNamedItem("licenseText").getTextContent()));
+ }
+ emitter.onSuccess(licenses);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ developers -> setListAdapter(new SimpleIconListAdapter<LicenseItem>(getContext(), developers)),
+ error -> Toast.makeText(getContext(), "Error while loading licenses", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ private static class LicenseItem extends SimpleIconListAdapter.ListItem {
+ final String licenseUrl;
+ final String licenseTextFile;
+
+ LicenseItem(String title, String subtitle, String imageUrl, String licenseUrl, String licenseTextFile) {
+ super(title, subtitle, imageUrl);
+ this.licenseUrl = licenseUrl;
+ this.licenseTextFile = licenseTextFile;
+ }
+ }
+
+ @Override
+ public void onListItemClick(@NonNull ListView l, @NonNull View v, int position, long id) {
+ super.onListItemClick(l, v, position, id);
+
+ LicenseItem item = licenses.get(position);
+ CharSequence[] items = {"View website", "View license"};
+ new AlertDialog.Builder(getContext())
+ .setTitle(item.title)
+ .setItems(items, (dialog, which) -> {
+ if (which == 0) {
+ IntentUtils.openInBrowser(getContext(), item.licenseUrl);
+ } else if (which == 1) {
+ showLicenseText(item.licenseTextFile);
+ }
+ }).show();
+ }
+
+ private void showLicenseText(String licenseTextFile) {
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open(licenseTextFile)));
+ StringBuilder licenseText = new StringBuilder();
+ String line;
+ while ((line = reader.readLine()) != null) {
+ licenseText.append(line).append("\n");
+ }
+
+ new AlertDialog.Builder(getContext())
+ .setMessage(licenseText)
+ .show();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (licensesLoader != null) {
+ licensesLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.licenses);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java
new file mode 100644
index 000000000..914dbb9a2
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AboutTranslatorsFragment.java
@@ -0,0 +1,64 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Toast;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.fragment.app.ListFragment;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.SimpleIconListAdapter;
+import io.reactivex.Single;
+import io.reactivex.SingleOnSubscribe;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+
+public class AboutTranslatorsFragment extends ListFragment {
+ private Disposable translatorsLoader;
+
+ @Override
+ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ getListView().setDivider(null);
+ getListView().setSelector(android.R.color.transparent);
+
+ translatorsLoader = Single.create((SingleOnSubscribe<ArrayList<SimpleIconListAdapter.ListItem>>) emitter -> {
+ ArrayList<SimpleIconListAdapter.ListItem> translators = new ArrayList<>();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ getContext().getAssets().open("translators.csv")));
+ String line;
+ while ((line = reader.readLine()) != null) {
+ String[] info = line.split(";");
+ translators.add(new SimpleIconListAdapter.ListItem(info[0], info[1], null));
+ }
+ emitter.onSuccess(translators);
+ })
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ translators -> setListAdapter(new SimpleIconListAdapter<>(getContext(), translators)),
+ error -> Toast.makeText(getContext(), "Error while loading translators", Toast.LENGTH_LONG).show()
+ );
+
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (translatorsLoader != null) {
+ translatorsLoader.dispose();
+ }
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.translators);
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
index a04615a00..fa17fed0a 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java
@@ -1,29 +1,42 @@
package de.danoeh.antennapod.fragment.preferences;
+import android.Manifest;
import android.app.Activity;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;
+import android.os.Build;
import android.os.Bundle;
-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.PreferenceScreen;
+import androidx.annotation.NonNull;
+import androidx.core.content.ContextCompat;
+import androidx.preference.CheckBoxPreference;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceScreen;
import android.util.Log;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "AutoDnldPrefFragment";
+
+ private static final int LOCATION_PERMISSION_REQUEST_CODE = 1;
+ private static final String PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT = "prefAutoDownloadWifiFilterAndroid10PermissionPrompt";
+
private CheckBoxPreference[] selectedNetworks;
+ private Preference prefPermissionRequestPromptOnAndroid10 = null;
+
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
addPreferencesFromResource(R.xml.preferences_autodownload);
@@ -35,6 +48,12 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.pref_automatic_download_title);
+ }
+
+ @Override
public void onResume() {
super.onResume();
checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload());
@@ -175,10 +194,65 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat {
}
private void setSelectedNetworksEnabled(boolean b) {
+ if (showPermissionRequestPromptOnAndroid10IfNeeded(b)) {
+ return;
+ }
+
if (selectedNetworks != null) {
for (Preference p : selectedNetworks) {
p.setEnabled(b);
}
}
}
+
+ @Override
+ public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
+ if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) {
+ return;
+ }
+ if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) &&
+ grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+ buildAutodownloadSelectedNetworksPreference();
+ }
+ }
+
+ private boolean showPermissionRequestPromptOnAndroid10IfNeeded(boolean wifiFilterEnabled) {
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) {
+ return false;
+ }
+
+ // Cases Android 10(Q) or later
+ if (prefPermissionRequestPromptOnAndroid10 != null) {
+ getPreferenceScreen().removePreference(prefPermissionRequestPromptOnAndroid10);
+ prefPermissionRequestPromptOnAndroid10 = null;
+ }
+
+ if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION)
+ == PackageManager.PERMISSION_GRANTED) {
+ return false;
+ }
+
+ // Case location permission not yet granted, permission-specific UI is needed
+ if (!wifiFilterEnabled) {
+ // Don't show the UI when WiFi filter disabled.
+ // it still return true, so that the caller knows
+ // it does not have required permission, and will not invoke codes that require so.
+ return true;
+ }
+
+ Preference pref = new Preference(requireActivity());
+ pref.setKey(PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT);
+ pref.setTitle(R.string.autodl_wifi_filter_permission_title);
+ pref.setSummary(R.string.autodl_wifi_filter_permission_message);
+ pref.setIcon(R.drawable.ic_warning_red);
+ pref.setOnPreferenceClickListener(preference -> {
+ requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE);
+ return true;
+ });
+ pref.setPersistent(false);
+ getPreferenceScreen().addPreference(pref);
+ prefPermissionRequestPromptOnAndroid10 = pref;
+ return true;
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
index 491922056..c6ae8e20c 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java
@@ -3,12 +3,13 @@ package de.danoeh.antennapod.fragment.preferences;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceFragmentCompat;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
import android.text.Html;
import android.text.format.DateUtils;
import android.widget.Toast;
import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.service.GpodnetSyncService;
import de.danoeh.antennapod.dialog.AuthenticationDialog;
@@ -30,6 +31,12 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.gpodnet_main_label);
+ }
+
+ @Override
public void onResume() {
super.onResume();
GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
index 229274b76..51f31eb92 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.fragment.preferences;
import android.os.Bundle;
-import android.support.v7.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceFragmentCompat;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
@@ -14,6 +14,12 @@ public class IntegrationsPreferencesFragment extends PreferenceFragmentCompat {
setupIntegrationsScreen();
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.integrations_label);
+ }
+
private void setupIntegrationsScreen() {
findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> {
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder);
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
index dcba5fe89..5fd38d663 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java
@@ -1,27 +1,15 @@
package de.danoeh.antennapod.fragment.preferences;
-import android.content.ActivityNotFoundException;
-import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.net.Uri;
-import android.os.Build;
import android.os.Bundle;
-import android.support.v4.content.FileProvider;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.preference.PreferenceFragmentCompat;
-import android.util.Log;
-import android.widget.Toast;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.preference.PreferenceFragmentCompat;
import com.bytehamster.lib.preferencesearch.SearchConfiguration;
import com.bytehamster.lib.preferencesearch.SearchPreference;
-import de.danoeh.antennapod.CrashReportWriter;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.activity.AboutActivity;
+import de.danoeh.antennapod.activity.BugReportActivity;
import de.danoeh.antennapod.activity.PreferenceActivity;
-import de.danoeh.antennapod.activity.StatisticsActivity;
-
-import java.util.List;
+import de.danoeh.antennapod.core.util.IntentUtils;
public class MainPreferencesFragment extends PreferenceFragmentCompat {
private static final String TAG = "MainPreferencesFragment";
@@ -31,9 +19,9 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
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_KNOWN_ISSUES = "prefKnownIssues";
private static final String PREF_FAQ = "prefFaq";
- private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
+ private static final String PREF_VIEW_MAILING_LIST = "prefViewMailingList";
+ private static final String PREF_SEND_BUG_REPORT = "prefSendBugReport";
private static final String STATISTICS = "statistics";
private static final String PREF_ABOUT = "prefAbout";
@@ -44,6 +32,12 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
setupSearch();
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label);
+ }
+
private void setupMainScreen() {
findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> {
((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_user_interface);
@@ -68,59 +62,32 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat {
findPreference(PREF_ABOUT).setOnPreferenceClickListener(
preference -> {
- startActivity(new Intent(getActivity(), AboutActivity.class));
+ getFragmentManager().beginTransaction().replace(R.id.content, new AboutFragment())
+ .addToBackStack(getString(R.string.about_pref)).commit();
return true;
}
);
findPreference(STATISTICS).setOnPreferenceClickListener(
preference -> {
- startActivity(new Intent(getActivity(), StatisticsActivity.class));
+ getFragmentManager().beginTransaction().replace(R.id.content, new StatisticsFragment())
+ .addToBackStack(getString(R.string.statistics_label)).commit();
return true;
}
);
- findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
+ findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
+ IntentUtils.openInBrowser(getContext(), "https://antennapod.org/faq.html");
return true;
});
- findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
- openInBrowser("https://antennapod.org/faq.html");
+ findPreference(PREF_VIEW_MAILING_LIST).setOnPreferenceClickListener(preference -> {
+ IntentUtils.openInBrowser(getContext(), "https://groups.google.com/forum/#!forum/antennapod");
return true;
});
- findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> {
- Context context = getActivity().getApplicationContext();
- Intent emailIntent = new Intent(Intent.ACTION_SEND);
- emailIntent.setType("text/plain");
- emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
- emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
- emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
- // the attachment
- Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority),
- CrashReportWriter.getFile());
- emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- String intentTitle = getActivity().getString(R.string.send_email);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
+ findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> {
+ startActivity(new Intent(getActivity(), BugReportActivity.class));
return true;
});
}
- private void openInBrowser(String url) {
- try {
- Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(myIntent);
- } catch (ActivityNotFoundException e) {
- Toast.makeText(getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
- Log.e(TAG, Log.getStackTraceString(e));
- }
- }
-
private void setupSearch() {
SearchPreference searchPreference = (SearchPreference) findPreference("searchPreference");
SearchConfiguration config = searchPreference.getSearchConfiguration();
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
index ac2436e25..1ca8f63aa 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java
@@ -4,12 +4,9 @@ import android.app.TimePickerDialog;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.app.AppCompatActivity;
-import android.support.v7.preference.Preference;
-import android.support.v7.preference.PreferenceFragmentCompat;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.PreferenceFragmentCompat;
import android.text.format.DateFormat;
-import com.afollestad.materialdialogs.MaterialDialog;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -31,6 +28,12 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.network_pref);
+ }
+
+ @Override
public void onResume() {
super.onResume();
setUpdateIntervalText();
@@ -59,7 +62,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
// validate and set correct value: number of downloads between 1 and 50 (inclusive)
findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
ProxyDialog dialog = new ProxyDialog(getActivity());
- dialog.createDialog().show();
+ dialog.show();
return true;
});
}
@@ -101,13 +104,10 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
private void showUpdateIntervalTimePreferencesDialog() {
final Context context = getActivity();
- MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
- builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
- builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
- builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
- builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
- builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
- builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.setMessage(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.setPositiveButton(R.string.pref_autoUpdateIntervallOrTime_Interval, (dialog, which) -> {
AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
@@ -127,8 +127,9 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
builder1.show();
});
- builder.onNegative((dialog, which) -> {
- int hourOfDay = 7, minute = 0;
+ builder.setNegativeButton(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay, (dialog, which) -> {
+ int hourOfDay = 7;
+ int minute = 0;
int[] updateTime = UserPreferences.getUpdateTimeOfDay();
if (updateTime.length == 2) {
hourOfDay = updateTime[0];
@@ -145,7 +146,7 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat {
timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
timePickerDialog.show();
});
- builder.onNeutral((dialog, which) -> {
+ builder.setNeutralButton(R.string.pref_autoUpdateIntervallOrTime_Disable, (dialog, which) -> {
UserPreferences.disableAutoUpdate();
setUpdateIntervalText();
});
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
index e1714d4bd..b82bba89b 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java
@@ -4,10 +4,18 @@ import android.app.Activity;
import android.content.res.Resources;
import android.os.Build;
import android.os.Bundle;
-import android.support.v7.preference.ListPreference;
-import android.support.v7.preference.PreferenceFragmentCompat;
+
+import androidx.annotation.NonNull;
+import androidx.collection.ArrayMap;
+import androidx.preference.ListPreference;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceFragmentCompat;
+
+import java.util.Map;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MediaplayerActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
@@ -28,9 +36,9 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
}
@Override
- public void onResume() {
- super.onResume();
- checkSonicItemVisibility();
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.playback_pref);
}
private void setupPlaybackScreen() {
@@ -56,6 +64,43 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
behaviour.setEntries(R.array.video_background_behavior_options_without_pip);
behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip);
}
+
+ buildEnqueueLocationPreference();
+ }
+
+ private void buildEnqueueLocationPreference() {
+ final Resources res = requireActivity().getResources();
+ final Map<String, String> options = new ArrayMap<>();
+ {
+ String[] keys = res.getStringArray(R.array.enqueue_location_values);
+ String[] values = res.getStringArray(R.array.enqueue_location_options);
+ for (int i = 0; i < keys.length; i++) {
+ options.put(keys[i], values[i]);
+ }
+ }
+
+ ListPreference pref = requirePreference(UserPreferences.PREF_ENQUEUE_LOCATION);
+ pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(pref.getValue())));
+
+ pref.setOnPreferenceChangeListener((preference, newValue) -> {
+ if (!(newValue instanceof String)) {
+ return false;
+ }
+ String newValStr = (String)newValue;
+ pref.setSummary(res.getString(R.string.pref_enqueue_location_sum, options.get(newValStr)));
+ return true;
+ });
+ }
+
+ @NonNull
+ private <T extends Preference> T requirePreference(@NonNull CharSequence key) {
+ // Possibly put it to a common method in abstract base class
+ T result = findPreference(key);
+ if (result == null) {
+ throw new IllegalArgumentException("Preference with key '" + key + "' is not found");
+
+ }
+ return result;
}
private void buildSmartMarkAsPlayedPreference() {
@@ -79,14 +124,4 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat {
}
pref.setEntries(entries);
}
-
-
-
- private void checkSonicItemVisibility() {
- if (Build.VERSION.SDK_INT < 16) {
- ListPreference p = (ListPreference) findPreference(UserPreferences.PREF_MEDIA_PLAYER);
- p.setEntries(R.array.media_player_options_no_sonic);
- p.setEntryValues(R.array.media_player_values_no_sonic);
- }
- }
}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java
new file mode 100644
index 000000000..7e1fdf2b9
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java
@@ -0,0 +1,180 @@
+package de.danoeh.antennapod.fragment.preferences;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ProgressBar;
+import android.widget.RadioButton;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AlertDialog;
+import androidx.fragment.app.Fragment;
+import androidx.recyclerview.widget.LinearLayoutManager;
+import androidx.recyclerview.widget.RecyclerView;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.adapter.StatisticsListAdapter;
+import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import io.reactivex.Completable;
+import io.reactivex.Observable;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.Disposable;
+import io.reactivex.schedulers.Schedulers;
+
+/**
+ * Displays the 'statistics' screen
+ */
+public class StatisticsFragment extends Fragment {
+ private static final String TAG = StatisticsFragment.class.getSimpleName();
+ private static final String PREF_NAME = "StatisticsActivityPrefs";
+ private static final String PREF_COUNT_ALL = "countAll";
+
+ private Disposable disposable;
+ private RecyclerView feedStatisticsList;
+ private ProgressBar progressBar;
+ private StatisticsListAdapter listAdapter;
+ private boolean countAll = false;
+ private SharedPreferences prefs;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ prefs = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE);
+ countAll = prefs.getBoolean(PREF_COUNT_ALL, false);
+ setHasOptionsMenu(true);
+ }
+
+ @Nullable
+ @Override
+ public View onCreateView(
+ @NonNull LayoutInflater inflater,
+ @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ View root = inflater.inflate(R.layout.statistics_activity, container, false);
+ feedStatisticsList = root.findViewById(R.id.statistics_list);
+ progressBar = root.findViewById(R.id.progressBar);
+ listAdapter = new StatisticsListAdapter(getContext());
+ listAdapter.setCountAll(countAll);
+ feedStatisticsList.setLayoutManager(new LinearLayoutManager(getContext()));
+ feedStatisticsList.setAdapter(listAdapter);
+ return root;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.statistics_label);
+ refreshStatistics();
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ inflater.inflate(R.menu.statistics, menu);
+ menu.findItem(R.id.statistics_reset).setEnabled(!countAll);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == R.id.statistics_mode) {
+ selectStatisticsMode();
+ return true;
+ }
+ if (item.getItemId() == R.id.statistics_reset) {
+ confirmResetStatistics();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void selectStatisticsMode() {
+ View contentView = View.inflate(getContext(), R.layout.statistics_mode_select_dialog, null);
+ AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
+ builder.setView(contentView);
+ builder.setTitle(R.string.statistics_mode);
+
+ if (countAll) {
+ ((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).setChecked(true);
+ } else {
+ ((RadioButton) contentView.findViewById(R.id.statistics_mode_normal)).setChecked(true);
+ }
+
+ builder.setPositiveButton(android.R.string.ok, (dialog, which) -> {
+ countAll = ((RadioButton) contentView.findViewById(R.id.statistics_mode_count_all)).isChecked();
+ listAdapter.setCountAll(countAll);
+ prefs.edit().putBoolean(PREF_COUNT_ALL, countAll).apply();
+ refreshStatistics();
+ getActivity().invalidateOptionsMenu();
+ });
+
+ builder.show();
+ }
+
+ private void confirmResetStatistics() {
+ if (!countAll) {
+ ConfirmationDialog conDialog = new ConfirmationDialog(
+ getActivity(),
+ R.string.statistics_reset_data,
+ R.string.statistics_reset_data_msg) {
+
+ @Override
+ public void onConfirmButtonPressed(DialogInterface dialog) {
+ dialog.dismiss();
+ doResetStatistics();
+ }
+ };
+ conDialog.createNewDialog().show();
+ }
+ }
+
+ private void doResetStatistics() {
+ progressBar.setVisibility(View.VISIBLE);
+ feedStatisticsList.setVisibility(View.GONE);
+ if (disposable != null) {
+ disposable.dispose();
+ }
+
+ disposable = Completable.fromFuture(DBWriter.resetStatistics())
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(this::refreshStatistics, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+
+ private void refreshStatistics() {
+ progressBar.setVisibility(View.VISIBLE);
+ feedStatisticsList.setVisibility(View.GONE);
+ loadStatistics();
+ }
+
+ private void loadStatistics() {
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ disposable = Observable.fromCallable(() -> DBReader.getStatistics(countAll))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ listAdapter.update(result);
+ progressBar.setVisibility(View.GONE);
+ feedStatisticsList.setVisibility(View.VISIBLE);
+ }, error -> Log.e(TAG, Log.getStackTraceString(error)));
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
index b4226b546..2c1590c47 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java
@@ -4,6 +4,7 @@ import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.ProgressDialog;
+import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -11,15 +12,19 @@ import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
-import android.support.v4.app.ActivityCompat;
-import android.support.v4.content.FileProvider;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.preference.PreferenceFragmentCompat;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.FileProvider;
+import androidx.documentfile.provider.DocumentFile;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.PreferenceFragmentCompat;
import android.util.Log;
+
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
import de.danoeh.antennapod.activity.ImportExportActivity;
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
import de.danoeh.antennapod.asynctask.ExportWorker;
import de.danoeh.antennapod.core.export.ExportWriter;
import de.danoeh.antennapod.core.export.html.HtmlWriter;
@@ -45,6 +50,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE };
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
+ private static final int CHOOSE_OPML_EXPORT_PATH = 1;
+ private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds.opml";
+ private static final String CONTENT_TYPE_OPML = "text/x-opml";
+ private static final int CHOOSE_HTML_EXPORT_PATH = 2;
+ private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds.html";
+ private static final String CONTENT_TYPE_HTML = "text/html";
private Disposable disposable;
@Override
@@ -54,11 +65,25 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
@Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.storage_pref);
+ }
+
+ @Override
public void onResume() {
super.onResume();
setDataFolderText();
}
+ @Override
+ public void onStop() {
+ super.onStop();
+ if (disposable != null) {
+ disposable.dispose();
+ }
+ }
+
private void setupStorageScreen() {
final Activity activity = getActivity();
@@ -69,9 +94,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
);
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new OpmlWriter()));
+ preference -> {
+ openOpmlExportPathPicker();
+ return true;
+ }
+ );
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
- preference -> export(new HtmlWriter()));
+ preference -> {
+ openHtmlExportPathPicker();
+ return true;
+ });
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
preference -> {
activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
@@ -129,52 +161,65 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
}
private boolean export(ExportWriter exportWriter) {
+ return export(exportWriter, null);
+ }
+
+ private boolean export(ExportWriter exportWriter, final Uri uri) {
Context context = getActivity();
final ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.setMessage(context.getString(R.string.exporting_label));
progressDialog.setIndeterminate(true);
progressDialog.show();
- final AlertDialog.Builder alert = new AlertDialog.Builder(context)
- .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
- Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
- disposable = observable.subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(output -> {
- 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) -> {
+ if (uri == null) {
+ Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
context.getString(R.string.provider_authority), output);
- Intent sendIntent = new Intent(Intent.ACTION_SEND);
- sendIntent.putExtra(Intent.EXTRA_SUBJECT,
- context.getResources().getText(R.string.opml_export_label));
- sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
- sendIntent.setType("text/plain");
- sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
- List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
- for (ResolveInfo resolveInfo : resInfoList) {
- String packageName = resolveInfo.activityInfo.packageName;
- context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
- }
- }
- context.startActivity(Intent.createChooser(sendIntent,
- context.getResources().getText(R.string.send_label)));
- });
- alert.create().show();
- }, error -> {
- alert.setTitle(R.string.export_error_label);
- alert.setMessage(error.getMessage());
- alert.show();
- }, progressDialog::dismiss);
+ showExportSuccessDialog(context.getString(R.string.export_success_sum, output.toString()), fileUri);
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ } else {
+ Observable<DocumentFile> observable = new DocumentFileExportWorker(exportWriter, context, uri).exportObservable();
+ disposable = observable.subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(output -> {
+ showExportSuccessDialog(context.getString(R.string.export_success_sum, output.getUri()), output.getUri());
+ }, this::showExportErrorDialog, progressDialog::dismiss);
+ }
return true;
}
- public void unsubscribeExportSubscription() {
- if (disposable != null) {
- disposable.dispose();
- }
+ private void showExportSuccessDialog(final String message, final Uri streamUri) {
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_success_title);
+ alert.setMessage(message);
+ alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
+ Intent sendIntent = new Intent(Intent.ACTION_SEND);
+ sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.opml_export_label));
+ sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
+ sendIntent.setType("text/plain");
+ sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
+ List<ResolveInfo> resInfoList = getContext().getPackageManager()
+ .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
+ for (ResolveInfo resolveInfo : resInfoList) {
+ String packageName = resolveInfo.activityInfo.packageName;
+ getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ }
+ }
+ getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label)));
+ });
+ alert.create().show();
+ }
+
+ private void showExportErrorDialog(final Throwable error) {
+ final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
+ .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
+ alert.setTitle(R.string.export_error_label);
+ alert.setMessage(error.getMessage());
+ alert.show();
}
@SuppressLint("NewApi")
@@ -184,22 +229,22 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
File path;
- if(dir != null) {
+ if (dir != null) {
path = new File(dir);
} else {
path = getActivity().getExternalFilesDir(null);
}
String message = null;
- final Context context= getActivity().getApplicationContext();
- if(!path.exists()) {
+ final Context context = getActivity().getApplicationContext();
+ if (!path.exists()) {
message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
- } else if(!path.canRead()) {
+ } else if (!path.canRead()) {
message = String.format(context.getString(R.string.folder_not_readable_error), dir);
- } else if(!path.canWrite()) {
+ } else if (!path.canWrite()) {
message = String.format(context.getString(R.string.folder_not_writable_error), dir);
}
- if(message == null) {
+ if (message == null) {
Log.d(TAG, "Setting data folder: " + dir);
UserPreferences.setDataFolder(dir);
setDataFolderText();
@@ -210,6 +255,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
ab.show();
}
}
+
+ if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_OPML_EXPORT_PATH) {
+ Uri uri = data.getData();
+ export(new OpmlWriter(), uri);
+ }
+
+ if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_HTML_EXPORT_PATH) {
+ Uri uri = data.getData();
+ export(new HtmlWriter(), uri);
+ }
}
private void setDataFolderText() {
@@ -231,6 +286,50 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
}
+ private void openOpmlExportPathPicker() {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(CONTENT_TYPE_OPML)
+ .putExtra(Intent.EXTRA_TITLE, DEFAULT_OPML_OUTPUT_NAME);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ startActivityForResult(intentPickAction, CHOOSE_OPML_EXPORT_PATH);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ }
+
+ // If we are using a SDK lower than API 21 or the implicit intent failed
+ // fallback to the legacy export process
+ export(new OpmlWriter());
+ }
+
+ private void openHtmlExportPathPicker() {
+ if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
+ Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
+ .addCategory(Intent.CATEGORY_OPENABLE)
+ .setType(CONTENT_TYPE_HTML)
+ .putExtra(Intent.EXTRA_TITLE, DEFAULT_HTML_OUTPUT_NAME);
+
+ // Creates an implicit intent to launch a file manager which lets
+ // the user choose a specific directory to export to.
+ try {
+ startActivityForResult(intentPickAction, CHOOSE_HTML_EXPORT_PATH);
+ return;
+ } catch (ActivityNotFoundException e) {
+ Log.e(TAG, "No activity found. Should never happen...");
+ }
+ }
+
+ // If we are using a SDK lower than API 21 or the implicit intent failed
+ // fallback to the legacy export process
+ export(new HtmlWriter());
+ }
+
private void showChooseDataFolderDialog() {
ChooseDataFolderDialog.showDialog(
getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
index e1d44f7d3..191999cf7 100644
--- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java
@@ -4,13 +4,14 @@ import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
-import android.support.design.widget.Snackbar;
-import android.support.v7.app.AlertDialog;
-import android.support.v7.preference.PreferenceFragmentCompat;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.appcompat.app.AlertDialog;
+import androidx.preference.PreferenceFragmentCompat;
import android.widget.ListView;
import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import org.apache.commons.lang3.ArrayUtils;
@@ -25,6 +26,12 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat {
setupInterfaceScreen();
}
+ @Override
+ public void onStart() {
+ super.onStart();
+ ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.user_interface_label);
+ }
+
private void setupInterfaceScreen() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
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 156657a00..60eaf14a5 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java
@@ -1,11 +1,11 @@
package de.danoeh.antennapod.menuhandler;
import android.content.Context;
-import android.content.Intent;
-import android.net.Uri;
-import android.support.annotation.Nullable;
+import android.os.Handler;
+import androidx.annotation.NonNull;
+import com.google.android.material.snackbar.Snackbar;
+import androidx.fragment.app.Fragment;
import android.util.Log;
-import android.widget.Toast;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.feed.FeedItem;
@@ -18,7 +18,6 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
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.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
/**
@@ -51,35 +50,21 @@ public class FeedItemMenuHandler {
* @param mi An instance of MenuInterface that the method uses to change a
* MenuItem's visibility
* @param selectedItem The FeedItem for which the menu is supposed to be prepared
- * @param showExtendedMenu True if MenuItems that let the user share information about
- * the FeedItem and visit its website should be set visible. This
- * parameter should be set to false if the menu space is limited.
- * @param queueAccess Used for testing if the queue contains the selected item; only used for
- * move to top/bottom in the queue
* @return Returns true if selectedItem is not null.
*/
public static boolean onPrepareMenu(MenuInterface mi,
- FeedItem selectedItem,
- boolean showExtendedMenu,
- @Nullable LongList queueAccess) {
+ FeedItem selectedItem) {
if (selectedItem == null) {
return false;
}
boolean hasMedia = selectedItem.getMedia() != null;
boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING;
- boolean keepSorted = UserPreferences.isQueueKeepSorted();
if (!isPlaying) {
mi.setItemVisibility(R.id.skip_episode_item, false);
}
boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE);
- if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) {
- mi.setItemVisibility(R.id.move_to_top_item, false);
- }
- if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId() || keepSorted) {
- mi.setItemVisibility(R.id.move_to_bottom_item, false);
- }
if (!isInQueue) {
mi.setItemVisibility(R.id.remove_from_queue_item, false);
}
@@ -87,12 +72,12 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.add_to_queue_item, false);
}
- if (!showExtendedMenu || !ShareUtils.hasLinkToShare(selectedItem)) {
+ if (!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);
}
- if (!showExtendedMenu || !hasMedia || selectedItem.getMedia().getDownload_url() == null) {
+ if (!hasMedia || selectedItem.getMedia().getDownload_url() == null) {
mi.setItemVisibility(R.id.share_download_url_item, false);
mi.setItemVisibility(R.id.share_download_url_with_position_item, false);
}
@@ -104,6 +89,7 @@ public class FeedItemMenuHandler {
boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists();
mi.setItemVisibility(R.id.share_file, fileDownloaded);
+ mi.setItemVisibility(R.id.remove_new_flag_item, selectedItem.isNew());
if (selectedItem.isPlayed()) {
mi.setItemVisibility(R.id.mark_read_item, false);
} else {
@@ -114,7 +100,7 @@ public class FeedItemMenuHandler {
mi.setItemVisibility(R.id.reset_position, false);
}
- if(!UserPreferences.isEnableAutodownload()) {
+ if(!UserPreferences.isEnableAutodownload() || fileDownloaded) {
mi.setItemVisibility(R.id.activate_auto_download, false);
mi.setItemVisibility(R.id.deactivate_auto_download, false);
} else if(selectedItem.getAutoDownload()) {
@@ -141,10 +127,8 @@ public class FeedItemMenuHandler {
*/
public static boolean onPrepareMenu(MenuInterface mi,
FeedItem selectedItem,
- boolean showExtendedMenu,
- LongList queueAccess,
int... excludeIds) {
- boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess);
+ boolean rc = onPrepareMenu(mi, selectedItem);
if (rc && excludeIds != null) {
for (int id : excludeIds) {
mi.setItemVisibility(id, false);
@@ -153,8 +137,16 @@ public class FeedItemMenuHandler {
return rc;
}
- public static boolean onMenuItemClicked(Context context, int menuItemId,
- FeedItem selectedItem) {
+ /**
+ * Default menu handling for the given FeedItem.
+ *
+ * A Fragment instance, (rather than the more generic Context), is needed as a parameter
+ * to support some UI operations, e.g., creating a Snackbar.
+ */
+ public static boolean onMenuItemClicked(@NonNull Fragment fragment, int menuItemId,
+ @NonNull FeedItem selectedItem) {
+
+ @NonNull Context context = fragment.requireContext();
switch (menuItemId) {
case R.id.skip_episode_item:
IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE);
@@ -162,9 +154,12 @@ public class FeedItemMenuHandler {
case R.id.remove_item:
DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId());
break;
+ case R.id.remove_new_flag_item:
+ removeNewFlagWithUndo(fragment, selectedItem);
+ break;
case R.id.mark_read_item:
selectedItem.setPlayed(true);
- DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, false);
+ DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, true);
if(GpodnetPreferences.loggedIn()) {
FeedMedia media = selectedItem.getMedia();
// not all items have media, Gpodder only cares about those that do
@@ -216,14 +211,7 @@ public class FeedItemMenuHandler {
DBWriter.setFeedItemAutoDownload(selectedItem, false);
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(FeedItemUtil.getLinkWithFallback(selectedItem));
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(context, intent)) {
- context.startActivity(intent);
- } else {
- Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
- Toast.LENGTH_SHORT).show();
- }
+ IntentUtils.openInBrowser(context, FeedItemUtil.getLinkWithFallback(selectedItem));
break;
case R.id.share_link_item:
ShareUtils.shareFeedItemLink(context, selectedItem);
@@ -249,4 +237,39 @@ public class FeedItemMenuHandler {
return true;
}
+ /**
+ * Remove new flag with additional UI logic to allow undo with Snackbar.
+ *
+ * Undo is useful for Remove new flag, given there is no UI to undo it otherwise
+ * ,i.e., there is (context) menu item for add new flag
+ */
+ public static void removeNewFlagWithUndo(@NonNull Fragment fragment, FeedItem item) {
+ if (item == null) {
+ return;
+ }
+
+ Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")");
+ // 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(fragment.requireContext().getMainLooper());
+ final Runnable r = () -> {
+ FeedMedia media = item.getMedia();
+ if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) {
+ DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), media.getId());
+ }
+ };
+
+ Snackbar snackbar = Snackbar.make(fragment.getView(), fragment.getString(R.string.removed_new_flag_label),
+ Snackbar.LENGTH_LONG);
+ snackbar.setAction(fragment.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/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
index 3949119fc..e32deba27 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java
@@ -2,31 +2,28 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.Intent;
-import android.net.Uri;
-import android.support.v7.app.AlertDialog;
-import android.text.TextUtils;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.widget.Toast;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Iterator;
+import androidx.annotation.NonNull;
+
+import org.apache.commons.lang3.StringUtils;
+
import java.util.Set;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.dialog.ConfirmationDialog;
import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItemFilter;
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.IntentUtils;
import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.SortOrder;
import de.danoeh.antennapod.dialog.FilterDialog;
+import de.danoeh.antennapod.dialog.IntraFeedSortDialog;
/**
* Handles interactions with the FeedItemMenu.
@@ -50,6 +47,10 @@ public class FeedMenuHandler {
Log.d(TAG, "Preparing options menu");
menu.findItem(R.id.refresh_complete_item).setVisible(selectedFeed.isPaged());
+ if (StringUtils.isBlank(selectedFeed.getLink())) {
+ menu.findItem(R.id.visit_website_item).setVisible(false);
+ menu.findItem(R.id.share_link_item).setVisible(false);
+ }
return true;
}
@@ -68,6 +69,9 @@ public class FeedMenuHandler {
case R.id.refresh_complete_item:
DBTasks.forceRefreshCompleteFeed(context, selectedFeed);
break;
+ case R.id.sort_items:
+ showSortDialog(context, selectedFeed);
+ break;
case R.id.filter_items:
showFilterDialog(context, selectedFeed);
break;
@@ -86,14 +90,7 @@ public class FeedMenuHandler {
conDialog.createNewDialog().show();
break;
case R.id.visit_website_item:
- Uri uri = Uri.parse(selectedFeed.getLink());
- Intent intent = new Intent(Intent.ACTION_VIEW, uri);
- if(IntentUtils.isCallable(context, intent)) {
- context.startActivity(intent);
- } else {
- Toast.makeText(context, context.getString(R.string.download_error_malformed_url),
- Toast.LENGTH_SHORT).show();
- }
+ IntentUtils.openInBrowser(context, selectedFeed.getLink());
break;
case R.id.share_link_item:
ShareUtils.shareFeedlink(context, selectedFeed);
@@ -118,4 +115,17 @@ public class FeedMenuHandler {
filterDialog.openDialog();
}
+
+
+ private static void showSortDialog(Context context, Feed selectedFeed) {
+ IntraFeedSortDialog sortDialog = new IntraFeedSortDialog(context, selectedFeed.getSortOrder()) {
+ @Override
+ protected void updateSort(@NonNull SortOrder sortOrder) {
+ selectedFeed.setSortOrder(sortOrder);
+ DBWriter.setFeedItemSortOrder(selectedFeed.getId(), sortOrder);
+ }
+ };
+ sortDialog.openDialog();
+ }
+
}
diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
index 7b9fcad9b..64eb72ee3 100644
--- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
+++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java
@@ -2,14 +2,8 @@ package de.danoeh.antennapod.menuhandler;
import android.content.Context;
import android.content.res.TypedArray;
-import android.graphics.Color;
-import android.os.Build;
-import android.support.v7.widget.SearchView;
import android.view.Menu;
import android.view.MenuItem;
-import android.widget.EditText;
-
-import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
@@ -17,18 +11,6 @@ import de.danoeh.antennapod.core.preferences.UserPreferences;
*/
public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuItemUtils {
- public static void adjustTextColor(Context context, SearchView sv) {
- if(Build.VERSION.SDK_INT < 14) {
- EditText searchEditText = sv.findViewById(R.id.search_src_text);
- if (UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark
- || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) {
- searchEditText.setTextColor(Color.WHITE);
- } else {
- searchEditText.setTextColor(Color.BLACK);
- }
- }
- }
-
@SuppressWarnings("ResourceType")
public static void refreshLockItem(Context context, Menu menu) {
final MenuItem queueLock = menu.findItem(de.danoeh.antennapod.R.id.queue_lock);
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java
index b810cbfa6..007457c24 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java
@@ -4,8 +4,8 @@ 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 androidx.preference.SwitchPreference;
+import androidx.preference.PreferenceViewHolder;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.widget.TextView;
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java
index 50e76838c..a58986241 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.preferences;
import android.app.AlertDialog;
import android.content.Context;
-import android.support.v7.preference.Preference;
+import androidx.preference.Preference;
import android.text.InputFilter;
import android.util.AttributeSet;
import android.view.View;
diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
index 1879dcbf8..d0321ddd2 100644
--- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java
@@ -3,8 +3,12 @@ package de.danoeh.antennapod.preferences;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
+
import de.danoeh.antennapod.BuildConfig;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
+import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
public class PreferenceUpgrader {
@@ -21,7 +25,7 @@ public class PreferenceUpgrader {
if (oldVersion != newVersion) {
NotificationUtils.createChannels(context);
- UserPreferences.restartUpdateAlarm();
+ AutoUpdateManager.restartUpdateAlarm();
upgrade(oldVersion);
upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply();
@@ -29,6 +33,9 @@ public class PreferenceUpgrader {
}
private static void upgrade(int oldVersion) {
+ if (oldVersion == -1) {
+ return;
+ }
if (oldVersion < 1070196) {
// migrate episode cleanup value (unit changed from days to hours)
int oldValueInDays = UserPreferences.getEpisodeCleanupValue();
@@ -62,5 +69,21 @@ public class PreferenceUpgrader {
break;
}
}
+ if (oldVersion < 1070400) {
+ int theme = UserPreferences.getTheme();
+ if (theme == R.style.Theme_AntennaPod_Light) {
+ prefs.edit().putString(UserPreferences.PREF_THEME, "system").apply();
+ }
+
+ UserPreferences.setQueueLocked(false);
+ prefs.edit().putBoolean(UserPreferences.PREF_STREAM_OVER_DOWNLOAD, false).apply();
+
+ if (!prefs.contains(UserPreferences.PREF_ENQUEUE_LOCATION)) {
+ final String keyOldPrefEnqueueFront = "prefQueueAddToFront";
+ boolean enqueueAtFront = prefs.getBoolean(keyOldPrefEnqueueFront, false);
+ EnqueueLocation enqueueLocation = enqueueAtFront ? EnqueueLocation.FRONT : EnqueueLocation.BACK;
+ UserPreferences.setEnqueueLocation(enqueueLocation);
+ }
+ }
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
index 03958508d..5fa6588d9 100644
--- a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
+++ b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java
@@ -46,7 +46,7 @@ public class SPAUtil {
SharedPreferences.Editor editor = prefs.edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true);
- editor.commit();
+ editor.apply();
return true;
} else {
@@ -63,7 +63,7 @@ public class SPAUtil {
SharedPreferences.Editor editor = PreferenceManager
.getDefaultSharedPreferences(c.getApplicationContext()).edit();
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false);
- editor.commit();
+ editor.apply();
}
}
}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
index e79389fb3..4c116fa2d 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java
@@ -88,7 +88,7 @@ public class AspectRatioVideoView extends VideoView {
mVideoWidth = videoWidth;
mVideoHeight = videoHeight;
- /**
+ /*
* If this isn't set the video is stretched across the
* SurfaceHolders display surface (i.e. the SurfaceHolder
* as the same size and the video is drawn to fit this
diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
index 8b886e699..0bfd0247f 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java
@@ -2,14 +2,14 @@ package de.danoeh.antennapod.view;
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.support.annotation.AttrRes;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.RecyclerView;
+import android.widget.AbsListView;
+import androidx.annotation.AttrRes;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
-import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -54,28 +54,31 @@ public class EmptyViewHandler {
emptyView.setVisibility(View.GONE);
}
- public void attachToListView(ListView listView) {
+ public void attachToListView(AbsListView listView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(listView);
layoutAdded = true;
- ((ViewGroup) listView.getParent()).addView(emptyView);
listView.setEmptyView(emptyView);
}
public void attachToRecyclerView(RecyclerView recyclerView) {
if (layoutAdded) {
- throw new IllegalStateException("Can not attach to ListView multiple times");
+ throw new IllegalStateException("Can not attach EmptyView multiple times");
}
+ addToParentView(recyclerView);
layoutAdded = true;
this.recyclerView = recyclerView;
- ViewGroup parent = ((ViewGroup) recyclerView.getParent());
- parent.addView(emptyView);
updateAdapter(recyclerView.getAdapter());
+ }
+ private void addToParentView(View view) {
+ ViewGroup parent = ((ViewGroup) view.getParent());
+ parent.addView(emptyView);
if (parent instanceof RelativeLayout) {
RelativeLayout.LayoutParams layoutParams =
- (RelativeLayout.LayoutParams)emptyView.getLayoutParams();
+ (RelativeLayout.LayoutParams) emptyView.getLayoutParams();
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT, RelativeLayout.TRUE);
emptyView.setLayoutParams(layoutParams);
}
@@ -99,7 +102,7 @@ public class EmptyViewHandler {
}
};
- private void updateVisibility() {
+ public void updateVisibility() {
boolean empty;
if (adapter == null) {
empty = true;
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java b/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java
new file mode 100644
index 000000000..d1b2abf23
--- /dev/null
+++ b/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java
@@ -0,0 +1,121 @@
+package de.danoeh.antennapod.view;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import androidx.annotation.NonNull;
+import androidx.appcompat.widget.AppCompatImageView;
+import io.reactivex.annotations.Nullable;
+
+public class PieChartView extends AppCompatImageView {
+ private PieChartDrawable drawable;
+
+ public PieChartView(Context context) {
+ super(context);
+ setup();
+ }
+
+ public PieChartView(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ setup();
+ }
+
+ public PieChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setup();
+ }
+
+ @SuppressLint("ClickableViewAccessibility")
+ private void setup() {
+ drawable = new PieChartDrawable();
+ setImageDrawable(drawable);
+ }
+
+ /**
+ * Set array od names, array of values and array of colors.
+ */
+ public void setData(float[] dataValues) {
+ drawable.dataValues = dataValues;
+ drawable.valueSum = 0;
+ for (float datum : dataValues) {
+ drawable.valueSum += datum;
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ int width = getMeasuredWidth();
+ setMeasuredDimension(width, width / 2);
+ }
+
+ private static class PieChartDrawable extends Drawable {
+ private static final float MIN_DEGREES = 10f;
+ private static final float PADDING_DEGREES = 3f;
+ private static final float STROKE_SIZE = 15f;
+ private static final int[] COLOR_VALUES = new int[]{0xFF3775E6, 0xffe51c23, 0xffff9800, 0xff259b24, 0xff9c27b0,
+ 0xff0099c6, 0xffdd4477, 0xff66aa00, 0xffb82e2e, 0xff316395,
+ 0xff994499, 0xff22aa99, 0xffaaaa11, 0xff6633cc, 0xff0073e6};
+ private float[] dataValues;
+ private float valueSum;
+ private final Paint paint;
+
+ private PieChartDrawable() {
+ paint = new Paint();
+ paint.setFlags(Paint.ANTI_ALIAS_FLAG);
+ paint.setStyle(Paint.Style.STROKE);
+ paint.setStrokeJoin(Paint.Join.ROUND);
+ paint.setStrokeCap(Paint.Cap.ROUND);
+ paint.setStrokeWidth(STROKE_SIZE);
+ }
+
+ @Override
+ public void draw(@NonNull Canvas canvas) {
+ if (valueSum == 0) {
+ return;
+ }
+ float radius = getBounds().height() - STROKE_SIZE;
+ float center = getBounds().width() / 2.f;
+ RectF arcBounds = new RectF(center - radius, STROKE_SIZE, center + radius, STROKE_SIZE + radius * 2);
+
+ float startAngle = 180;
+ for (int i = 0; i < dataValues.length; i++) {
+ float datum = dataValues[i];
+ float sweepAngle = (180f - PADDING_DEGREES) * (datum / valueSum);
+ if (sweepAngle < MIN_DEGREES) {
+ break;
+ }
+ paint.setColor(COLOR_VALUES[i % COLOR_VALUES.length]);
+ float padding = i == 0 ? PADDING_DEGREES / 2 : PADDING_DEGREES;
+ canvas.drawArc(arcBounds, startAngle + padding, sweepAngle - padding, false, paint);
+ startAngle = startAngle + sweepAngle;
+ }
+
+ paint.setColor(Color.GRAY);
+ float sweepAngle = 360 - startAngle - PADDING_DEGREES / 2;
+ if (sweepAngle > PADDING_DEGREES) {
+ canvas.drawArc(arcBounds, startAngle + PADDING_DEGREES, sweepAngle - PADDING_DEGREES, false, paint);
+ }
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.TRANSLUCENT;
+ }
+
+ @Override
+ public void setAlpha(int alpha) {
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter cf) {
+ }
+ }
+}
diff --git a/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java
index 45c3a35bd..5bd335532 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.view;
-import android.support.annotation.Nullable;
-import android.support.v7.widget.RecyclerView;
+import androidx.annotation.Nullable;
+import androidx.recyclerview.widget.RecyclerView;
/**
* AdapterDataObserver that relays all events to the method anythingChanged().
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 7ce33e11f..f82309c4a 100644
--- a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java
+++ b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.view;
import android.content.Context;
-import android.support.v7.widget.AppCompatImageView;
+import androidx.appcompat.widget.AppCompatImageView;
import android.util.AttributeSet;
/**
diff --git a/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java b/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java
deleted file mode 100644
index fe11a645c..000000000
--- a/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package de.danoeh.antennapod.viewmodel;
-
-import android.arch.lifecycle.ViewModel;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.storage.DBReader;
-import io.reactivex.Maybe;
-
-public class FeedSettingsViewModel extends ViewModel {
- private Feed feed;
-
- public Maybe<Feed> getFeed(long feedId) {
- if (feed == null) {
- return loadFeed(feedId);
- } else {
- return Maybe.just(feed);
- }
- }
-
- private Maybe<Feed> loadFeed(long feedId) {
- return Maybe.create(emitter -> {
- Feed feed = DBReader.getFeed(feedId);
- if (feed != null) {
- this.feed = feed;
- emitter.onSuccess(feed);
- } else {
- emitter.onComplete();
- }
- });
- }
-}
diff --git a/app/src/main/play/listings/de-DE/full-description.txt b/app/src/main/play/listings/de-DE/full-description.txt
index c49417c3e..5365983bb 100644
--- a/app/src/main/play/listings/de-DE/full-description.txt
+++ b/app/src/main/play/listings/de-DE/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht, angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs. Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch die Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
-Aber am wichtigsten: Downloade, streame oder füge Episoden zur Abspielliste hinzu und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
-
-AntennaPod ist, von Podcast-Enthusiasten gemacht, frei im Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
-
-<b>Alle Funktionen:</b><br>
-IMPORTIERE, ORGANISIERE UND HÖRE<br>
-&#8226; Importiere oder füge Feeds über das iTunes und gPodder.net Verzeichnis, OMPL Dateien und RSS oder Atom Links hinzu.
-&#8226; Bediene die Wiedergabe von überall: Startbildschirm-Widget, Benachrichtigung und Kopfhörer- und Bluetooth-Bedienelemente<br>
-&#8226; Genieße das Zuhören auf deine Art mit einstellbarer Abspielgeschwindigkeit, der Unterstützung von Kapiteln (MP3, OGG, Podlove) und ausgereifter Schlummerfunktion (durch Schütteln zurücksetzen, Lautstärke verringern und Geschwindigkeit verlangsamen)
-&#8226; Greife auf Passwort-geschützte Feeds und Episoden zu<br>
-&#8226; Nutze den Vorteil von Paged Feeds (http://www.podlove.org/paged-feeds)
-
-ORDNE, TEILE & GENIEßE
-&#8226; Bleib an den Besten der Besten dran, indem Du Episoden als Favoriten markierst<br>
-&#8226; Finde Episoden durch die Liste zuletzt gespielter Episoden oder durch Suche in Titel und Shownotes
-&#8226; Teile Episoden and Feeds über soziale Medien, E-Mail, den gPodder.net-Dienst oder als OPML-Export
-
-STEUERE DAS SYSTEM<br>
-&#8226; Kontrolliere automatisches Herunterladen: Wähle Feeds aus, schließe mobile Netze aus, suche bestimmte WiFi-Netze aus, setze voraus, dass das Smartphone geladen wird und lege Zeitpunkte oder Intervalle fest<br>
-&#8226; Verwalte deinen Speicherplatz durch das Festlegen der Anzahl gespeicherter Episoden, schlaues Löschen und durch Auswahl des Speicherortes<br>
-&#8226; Benutze AntennaPod in deiner Sprache (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Passe das Aussehen mit dem hellen oder dunklen Theme an<br>
-&#8226; Sichere deine Abonnements mit gPodder.net oder über den OPML-Export
-
-<b>Tritt der AntennaPod-Community bei!</b><br>
+AntennaPod ist ein Podcast-Manager und -Player, der dir unmittelbar Zugriff auf Millionen von freien und bezahlten Podcasts ermöglicht. Angefangen von unabhängigen Podcastern zu großen Rundfunkanstalten oder Hörfunksendern wie BBC, NPR und CNN. Abonniere, importiere und exportiere deine Feeds mühelos mit Hilfe des iTunes-Verzeichnisses, OPML-Dateien oder einfachen RSS-URLs.
+Downloade, streame oder sortiere Episoden in der Abspielliste und genieße sie mit einstellbarer Abspielgeschwindigkeit, Unterstützung von Kapiteln und Schlummerfunktion.
+Reduziere Aufwand, Stromverbrauch und Datenverbrauch durch leistungsfähige Kontrolle der Downloads (bestimmte Uhrzeiten, Intervalle, WiFi-Netze) und des Löschens (basierend auf deinen Favoriten und weiteren Einstellungen).
+
+AntennaPod ist von Podcast-Enthusiasten gemacht und frei im wahrsten Sinne des Wortes: Open Source, keine Kosten, keine Werbung.
+
+<b>Importiere, organisiere und spiele ab</b>
+• Bediene die Wiedergabe von überall: Startbildschirm-Widget, Benachrichtigung und Kopfhörer- und Bluetooth-Bedienelemente
+• Füge Feeds über iTunes und gPodder.net oder über OMPL Dateien und RSS oder Atom Links hinzu.
+• Genieße das Zuhören genau wie du willst - mit einstellbarer Abspielgeschwindigkeit, der Unterstützung von Kapiteln und ausgereifter Schlummerfunktion (durch Schütteln zurücksetzen, Lautstärke verringern)
+• Greife auf Passwort-geschützte Feeds und Episoden zu
+
+<b>Behalte den Ãœberblick</b>
+• Behalte den Überblick über die Besten der Besten, indem du Episoden als Favoriten markierst
+• Finde Episoden durch die Liste zuletzt gespielter Episoden oder durch Suche in Titel und Shownotes
+• Teile Episoden and Feeds über soziale Medien, E-Mail, den gPodder.net-Dienst oder als OPML-Export
+
+<b>Behalte die Kontrolle</b>
+• Kontrolliere automatisches Herunterladen: Wähle Feeds aus, schließe mobile Netze aus, suche bestimmte WiFi-Netze aus, setze voraus, dass das Smartphone geladen wird und lege Zeitpunkte oder Intervalle fest
+• Verwalte deinen Speicherplatz durch das Festlegen der Anzahl gespeicherter Episoden, schlaues Löschen und durch Auswahl des Speicherortes
+• Passe das Aussehen mit dem hellen oder dunklen Theme an deine Wünsche an
+• Sichere deine Abonnements mit gPodder.net oder über den OPML-Export
+
+<b>Tritt der AntennaPod-Community bei!</b>
AntennaPod wird aktiv von Freiwilligen weiterentwickelt. Auch du kannst bei der Entwicklung mit Quellcode oder Kommentaren mitwirken!
-Wir verwenden GitHub für Funktionswünsche (Feature Requests), Fehlerberichte (Bug Reports) und zum Beisteuern von Code (Code Contributions).
+Wir verwenden GitHub für Funktionswünsche, Fehlerberichte und zur Beteiligung an der Entwicklung:
https://www.github.com/AntennaPod/AntennaPod
-Teile deine Idee und Lieblingspodcastmomente und äußere Deine Dankbarkeit gegenüber allen Freiwilligen in unserer Google Group:<br>
-https://groups.google.com/forum/#!forum/antennapod (Englisch)
-
-Du hast eine Frage oder willst uns Feedback geben?
-https://twitter.com/@AntennaPod
-
-Mit Transifex kannst du uns beim Ãœbersetzen helfen:<br>
-https://www.transifex.com/antennapod/antennapod
-
-Probiere unser Beta-Testing-Programm aus, um die neusten Funktionen als Erster zu erhalten:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Mit Transifex kannst du uns beim Ãœbersetzen helfen:
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/1.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/1.png
new file mode 100644
index 000000000..f5f7a463b
--- /dev/null
+++ b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/1.png
Binary files differ
diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/2.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/2.png
new file mode 100644
index 000000000..eac002341
--- /dev/null
+++ b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/2.png
Binary files differ
diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/3.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/3.png
new file mode 100644
index 000000000..f189d11f2
--- /dev/null
+++ b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/3.png
Binary files differ
diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/4.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/4.png
new file mode 100644
index 000000000..dcd040008
--- /dev/null
+++ b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/4.png
Binary files differ
diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/5.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/5.png
new file mode 100644
index 000000000..03467f768
--- /dev/null
+++ b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/5.png
Binary files differ
diff --git a/app/src/main/play/listings/de-DE/graphics/phone-screenshots/6.png b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/6.png
new file mode 100644
index 000000000..4d687d88b
--- /dev/null
+++ b/app/src/main/play/listings/de-DE/graphics/phone-screenshots/6.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/full-description.txt b/app/src/main/play/listings/en-US/full-description.txt
index c562387af..e0042e07f 100644
--- a/app/src/main/play/listings/en-US/full-description.txt
+++ b/app/src/main/play/listings/en-US/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs. Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based on your favourites and delay settings).<br>
-But most importantly: Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer.
-
-Made by podcast-enthousiast, AntennaPod is free in all senses of the word: open source, no costs, no ads.
-
-<b>All features:</b><br>
-IMPORT, ORGANIZE AND PLAY<br>
-&#8226; Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links<br>
-&#8226; Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls<br>
-&#8226; Enjoy listening your way with adjustable playback speed, chapter support (MP3, VorbisComment and Podlove), remembered playback position and an advanced sleep timer (shake to reset, lower volume and slow down playback)<br>
-&#8226; Access password-protected feeds and episodes<br>
-&#8226; Take advantage of paged feeds (www.podlove.org/paged-feeds)
-
-KEEP TRACK, SHARE & APPRECIATE<br>
-&#8226; Keep track of the best of the best by marking episodes as favourites<br>
-&#8226; Find that one episode through the playback history or by searching (titles and shownotes)<br>
-&#8226; Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export<br>
-
-CONTROL THE SYSTEM<br>
-&#8226; Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals<br>
-&#8226; Manage storage by setting the amount of cached episodes, smart deletion (based on your favourites and play status) and selecting your preferred location<br>
-&#8226; Use AntennaPod in your language (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Adapt to your environment using the light and dark theme<br>
-&#8226; Back-up your subscriptions with the gPodder.net integration and OPML export
-
-<b>Join the AntennaPod community!</b><br>
+AntennaPod is a podcast manager and player that gives you instant access to millions of free and paid podcasts, from independent podcasters to large publishing houses such as the BBC, NPR and CNN. Add, import and export their feeds hassle-free using the iTunes podcast database, OPML files or simple RSS URLs.
+Download, stream or queue episodes and enjoy them the way you like with adjustable playback speeds, chapter support and a sleep timer.
+Save effort, battery power and mobile data usage with powerful automation controls for downloading episodes (specify times, intervals and WiFi networks) and deleting episodes (based on your favourites and delay settings).
+
+Made by podcast-enthusiasts, AntennaPod is free in all senses of the word: open source, no costs, no ads.
+
+<b>Import, organize and play</b>
+• Manage playback from anywhere: homescreen widget, system notification and earplug and bluetooth controls
+• Add and import feeds via the iTunes and gPodder.net directories, OPML files and RSS or Atom links
+• Enjoy listening your way with adjustable playback speed, chapter support, remembered playback position and an advanced sleep timer (shake to reset, lower volume)
+• Access password-protected feeds and episodes
+
+<b>Keep track, share & appreciate</b>
+• Keep track of the best of the best by marking episodes as favourites
+• Find that one episode through the playback history or by searching titles and shownotes
+• Share episodes and feeds through advanced social media and email options, the gPodder.net services and via OPML export
+
+<b>Control the system</b>
+• Take control over automated downloading: choose feeds, exclude mobile networks, select specific WiFi networks, require the phone to be charging and set times or intervals
+• Manage storage by setting the amount of cached episodes, smart deletion and selecting your preferred location
+• Adapt to your environment using the light and dark theme
+• Back-up your subscriptions with the gPodder.net integration and OPML export
+
+<b>Join the AntennaPod community!</b>
AntennaPod is under active development by volunteers. You can contribute too, with code or with comment!
-GitHub is the place to go for feature requests, bug reports and code contributions:<br>
+GitHub is the place to go for feature requests, bug reports and code contributions:
https://www.github.com/AntennaPod/AntennaPod
-Our Google Group is the place to share your ideas, favourite podcasting moments and gratitude to all the volunteers:<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-Have a question or want to give us feedback?
-https://twitter.com/@AntennaPod
-
-Transifex is the place to help with translations:<br>
+Transifex is the place to help with translations:
https://www.transifex.com/antennapod/antennapod
-
-Check out our Beta Testing programme to get the latest features first:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod
diff --git a/app/src/main/play/listings/en-US/graphics/icon/icon_play.png b/app/src/main/play/listings/en-US/graphics/icon/icon.png
index 824346d21..824346d21 100644
--- a/app/src/main/play/listings/en-US/graphics/icon/icon_play.png
+++ b/app/src/main/play/listings/en-US/graphics/icon/icon.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/00.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/00.png
deleted file mode 100644
index 9cc754f22..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/00.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/01.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/01.png
deleted file mode 100644
index 3ec9ceadf..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/01.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/02.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/02.png
deleted file mode 100644
index 020fa9a01..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/02.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/03.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/03.png
deleted file mode 100644
index f860032c3..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/03.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/04.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/04.png
deleted file mode 100644
index be012eead..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/04.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/05.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/05.png
deleted file mode 100644
index 379ad27eb..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/05.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/06.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/06.png
deleted file mode 100644
index b73d58d40..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/06.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/07.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/07.png
deleted file mode 100644
index 61246306f..000000000
--- a/app/src/main/play/listings/en-US/graphics/phone-screenshots/07.png
+++ /dev/null
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/1.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/1.png
new file mode 100644
index 000000000..f201567a1
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/phone-screenshots/1.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/2.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/2.png
new file mode 100644
index 000000000..1c0cd9e8d
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/phone-screenshots/2.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/3.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/3.png
new file mode 100644
index 000000000..c7812c099
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/phone-screenshots/3.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/4.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/4.png
new file mode 100644
index 000000000..8eb6c2bad
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/phone-screenshots/4.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/5.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/5.png
new file mode 100644
index 000000000..65fe711a6
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/phone-screenshots/5.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/phone-screenshots/6.png b/app/src/main/play/listings/en-US/graphics/phone-screenshots/6.png
new file mode 100644
index 000000000..35a1d3b08
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/phone-screenshots/6.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/promo-graphic/promo-graphic.png b/app/src/main/play/listings/en-US/graphics/promo-graphic/promo-graphic.png
new file mode 100644
index 000000000..a12aab438
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/promo-graphic/promo-graphic.png
Binary files differ
diff --git a/app/src/main/play/listings/en-US/graphics/tv-banner/tv-banner.png b/app/src/main/play/listings/en-US/graphics/tv-banner/tv-banner.png
new file mode 100644
index 000000000..b278213d5
--- /dev/null
+++ b/app/src/main/play/listings/en-US/graphics/tv-banner/tv-banner.png
Binary files differ
diff --git a/app/src/main/play/listings/es-ES/full-description.txt b/app/src/main/play/listings/es-ES/full-description.txt
index 8c5cba745..b98757cee 100644
--- a/app/src/main/play/listings/es-ES/full-description.txt
+++ b/app/src/main/play/listings/es-ES/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod es un reproductor y administrador de pódcast que te da acceso instantáneo a millones de pódcast gratuitos y de pago, desde editores independientes a grandes editoriales como la BBC, NPR y CNN. Añade, importa y exporta tus fuentes sin complicaciones usando la base de datos de pódcast de iTunes, archivos OPML o las URL de tipo RSS. Ahorra esfuerzo, energía de la batería y uso de datos móviles con potentes controles de automatización para descargar episodios (especifica horarios, intervalos y redes wifi) y elimina episodios (según tus favoritos y la configuración de retardo).<br>
-Y lo que es más importante: Descarga, escucha en stream, o añade la cola episodios y disfrútalos como quieras con velocidad de reproducción ajustable, soporte para capítulos y temporizador de sueño.
+AntennaPod is un gestor y reproductor de podcast que te da acceso instantáneo a millones de podcast gratuitos y de pago, desde podcasters independientes a grandes estaciones como la BBC, NPR y CNN. Agrega, importa y exporta las fuentes de manera sencilla usando el listado de iTunes, archivos OPML o las URL de tipo RSS. Ahorra esfuerzo, batería y datos con los controles de descarga y de borrado de episodios (basado en favoritos y ajustes de tiempo).
+Descarga, escucha en stream, o añade la cola episodios y disfrútalos como quieras con velocidad de reproducción ajustable, soporte para capítulos y temporizador de sueño.
+Ahorra esuferzo, batería y consumo de datos con los potentes controles de automatización para descarga (especifica a qué horas y desde qué redes Wi-Fi) y borrado de episodios antiguos (basado en tus preferencias).
Creado por entusiastas del pódcast, AntennaPod es libre en todos los sentidos: código abierto, gratuito y sin publicidad.
-<b>Todas las características:</b><br>
-IMPORTAR, ORGANIZAR Y REPRODUCIR<br>
-&#8226; Añade e importa fuentes mediante los directorios de iTunes y gPodder.net, archivos OPML y enlaces RSS o Atom<br>
-&#8226; Administra la reproducción desde cualquier parte: control en pantalla de inicio, notificación del sistema y controles de auricular y bluetooth<br>
-&#8226; Disfruta escuchando a tu manera con velocidad de reproducción ajustable, soporte de capítulos (MP3, VorbisComment y Podlove), recordatorio del punto de reproducción y el temporizador de sueño avanzado (agita para restablecer, bajar el volumen y disminuir la velocidad de reproducción)<br>
-&#8226; Accede a fuentes y episodios protegidos con contraseña<br>
-&#8226; Aprovecha las fuentes paginadas (www.podlove.org/paged-feeds)
+<b>Importar, organizar y reproducir</b>
+• Administra la reproducción desde cualquier parte: control en pantalla de inicio, notificación del sistema y controles de auricular y bluetooth
+• Añade e importa fuentes mediante los directorios de iTunes y gPodder.net, archivos OPML y enlaces RSS o Atom
+• Disfruta escuchando a tu manera con velocidad de reproducción ajustable, soporte de capítulos (MP3, VorbisComment y Podlove), recordatorio del punto de reproducción y el temporizador de sueño avanzado (agita para restablecer, bajar el volumen y disminuir la velocidad de reproducción)
+• Accede a fuentes y episodios protegidos con contraseña
-MANTÉN UN SEGUIMIENTO, COMPARTE Y APRECIA
-&#8226; Haz un seguimiento de lo mejor de lo mejor marcando episodios como favoritos<br>
-&#8226; Encuentra ese episodio a través del historial de reproducción o por búsqueda (títulos y notas de episodios)<br>
-&#8226; Comparte episodios y fuentes a través de las avanzadas redes sociales y opciones de correo electrónico, los servicios de gPodder.net y la exportación OPML<br>
+<b>Seguir, valorar y compartir </b>
+• Haz un seguimiento de lo mejor de lo mejor marcando episodios como favoritos
+• Busca ese episodio en el historial o busca por título y notas
+• Comparte episodios y fuentes a través de las avanzadas redes sociales y opciones de correo electrónico, los servicios de gPodder.net y la exportación OPML
-CONTROLA EL SISTEMA<br>
-&#8226; Controla las descargas automáticas: elige las fuentes, excluye las redes móviles, selecciona redes wifi específicas, o solo cuando el teléfono se esté cargando y establece horarios o intervalos<br>
-&#8226; Administra el almacenamiento configurando la cantidad de episodios almacenados, el borrado inteligente (según tus favoritos y el estado de reproducción) y selecciona tu ubicación preferida<br>
-&#8226; Usa AntennaPod en tu idioma (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Adáptate a tu entorno usando el tema claro u oscuro<br>
-&#8226; Haz una copia de seguridad de tus suscripciones con la integración de gPodder.net y la exportación OPML
+<b>Control del sistema</b>
+• Controla las descargas automáticas: elige las fuentes, excluye las redes móviles, selecciona redes wifi específicas, o solo cuando el teléfono se esté cargando y establece horarios o intervalos
+• Gestiona el almacenamiento configurando la cantidad de episodios en caché, configura borrado inteligente y eligiendo tu ubicación favorita
+• Adáptate a tu entorno usando el tema claro u oscuro
+• Haz una copia de seguridad de tus suscripciones con la integración de gPodder.net y la exportación OPML
-<b>¡Únete a la comunidad AntennaPod!</b><br>
+<b>¡Únete a la comunidad AntennaPod!</b>
AntennaPod está en continuo desarrollo por voluntarios. ¡Tú también puedes contribuir, con tu código o con tus comentarios!
-GitHub es el sitio que debes visitar para solicitar características nuevas, reportar fallos y contribuir con código<br>
+GitHub es el sitio que debes visitar para solicitar características nuevas, reportar fallos y contribuir con código:
https://www.github.com/AntennaPod/AntennaPod
-Nuestro Grupo de Google es el sitio para compartir tus ideas, momentos favoritos de tus pódcast y tu gratitud a los voluntarios:<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-¿Tienes una pregunta o quieres darnos tu opinión?
-https://twitter.com/@AntennaPod
-
-Transifex es el sitio para ayudar con las traducciones:<br>
-https://www.transifex.com/antennapod/antennapod
-
-Echa un vistazo a nuestro programa de pruebas Beta y ser el primero en usar las nuevas características:<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Para ayudar con las traducciones en Transifex:
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/listings/fr-FR/full-description.txt b/app/src/main/play/listings/fr-FR/full-description.txt
index 5c69f3892..fe8734be4 100644
--- a/app/src/main/play/listings/fr-FR/full-description.txt
+++ b/app/src/main/play/listings/fr-FR/full-description.txt
@@ -1,42 +1,31 @@
-AntennaPod est un lecteur de podcast permettant l'accès à des millions de podcast gratuits ou payants produit aussi bien par des podcasters indépendants que des éditeurs professionnels comme la BBC, NPR ou CNN. Ajoutez, importez et exportez leurs flux facilement à partir d'ITunes, de fichiers OPML ou simplement à partir de liens RSS. Economisez votre temps, votre batterie et votre consommation de données grâce à l'automatisation des téléchargements (date, fréquence, choix du réseau WiFi, etc...) et de la suppression des épisodes (selon vos critères)<br>
-Avant tout : téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous voulez grâce au réglage de vitesse de lecture, au support des chapitres et au minuteur d'arrêt.
-
-Conçu par des fans de podcast, AntennaPod est gratuit dans tous les sens du terme : open source, gratuit et sans publicité.
-
-<b>Caractéristiques complètes :</b><br>
-IMPORTER, GÉRER ET ÉCOUTER<br>
-&#8226; Ajouter et importer à partir d'iTunes, gPodder.net, fichiers OPML, liens RSS ou Atom<br>
-&#8226; Gérez la lecture de n'importe où : widget sur l'écran d'accueil, notification système, commande casque ou Bluetooth<br>
-&#8226; Écoutez à votre façon grâce à une vitesse de lecture réglable, au support des chapitres (MP3, VorbisComment et Podlove), à l'enregistrement de la position de lecture et à une mise en veille automatique puissante (secouez pour prolonger le minuteur, baisser le volume et ralentir la lecture)<br>
-&#8226; Accès aux flux et épisodes protégés par mot de passe<br>
-&#8226; Tirez profit des flux paginés (www.podlove.org/paged-feeds)
-
-SUIVEZ, PARTAGEZ & PROFITEZ<br>
-&#8226; Marquer les meilleurs épisodes en tant que favoris<br>
-&#8226; Retrouvez un épisode à partir de l'historique de lecture ou en recherchant parmi les titres et commentaires des épisodes précédents<br>
-&#8226; Partagez vos épisodes et flux sur les réseaux sociaux, par email, sur gPodder.net ou en les exportant au format OPML<br>
-
-CONTRÔLER LE SYSTÈME<br>
-&#8226; Prenez le contrôle en automatisant vos téléchargements : choix des flux, restriction de la connexion mobile, sélection du réseau WIFI à utiliser, uniquement durant la recharge et spécifiez la fréquence de mise à jour vous-même<br>
-&#8226; Gérez l'espace de stockage en paramétrant le nombre d'épisodes à garder, leur suppression automatique (en fonction de vos favoris et de leur statut) et leur emplacement sur le disque<br>
-&#8226; Utilisez AntennaPod dans votre langue (EN, DE, CS, NL, NB, JA, PT, ES, SV, CA, UK, FR, KO, TR, ZH)<br>
-&#8226; Choix d'un thème clair ou sombre selon vos préférences<br>
-&#8226; Sauvegardez vos abonnements avec l’intégration à gPodder.net et les exports OPML
-
-<b>Rejoignez la communauté d'AntennaPod !</b><br>
+AntennaPod est un lecteur de podcast permettant l'accès à des millions de podcast gratuits ou payants produit par des podcasters indépendants ou de gros éditeurs comme la BBC, NPR, CNN. Ajoutez, importez et exportez facilement des podcasts à partir d'ITunes, de fichiers OPML ou de liens RSS.
+Téléchargez, streamez ou ajoutez à la liste de lecture vos épisodes et écoutez les comme vous aimez avec une vitesse de lecture réglable, le support des chapitres et un minuteur d'arrêt automatique.
+Economisez votre temps, votre batterie et votre consommation de données grâce à l'automatisation des téléchargements (date, fréquence, choix du réseau WiFi) et la suppression des épisodes (selon vos critères)
+
+Conçu par des fans de podcast, AntennaPod est gratuit, open source et sans publicité.
+
+<b>Importez, organisez et écoutez</b>
+• Gérez la lecture à l'aide : de widget, de notification système, de commande casque ou bluetooth
+• Ajouter et importer à partir d'iTunes, gPodder.net, de fichiers OPML, de liens RSS ou Atom
+• Ecoutez à votre façon grâce à une vitesse de lecture réglable, au support des chapitres, à l'enregistrement de la position de lecture et à un minuteur d'arrêt automatique puissant (secouez pour réinitialiser, baisse du volume)
+• Accès possible aux flux et épisodes nécessitant un mot de passe
+
+<b>Suivez, partagez et appréciez</b>
+• Enregistrez les meilleurs épisodes en tant que favoris
+• Retrouvez un épisode à partir de l'historique de lecture ou en recherchant les noms et commentaires des épisodes
+• Partagez vos épisodes et flux sur les réseaux sociaux, par email, sur gPodder.net ou en les exportant au format OPML
+
+<b>Contrôlez</b>
+• Prenez le contrôle en automatisant vos téléchargements : flux spécifiques, restriction de la connexion mobile, réseaux WIFI autorisés, seulement pendant la recharge, fréquence de mise à jour
+• Gérez l'espace de stockage en réglant le nombre d'épisodes à garder, quand les supprimer et où les enregistrer
+• Choisissez le thème de l'interface
+• Sauvegardez vos abonnements avec gPodder.net ou des exports OPML
+
+<b>Rejoignez la communauté d'AntennaPod !</b>
AntennaPod est développé activement par des volontaires. Vous pouvez aussi contribuer avec du code, des traductions ou des commentaires !
-GitHub est l'endroit où aller pour demander de nouvelles options, faire part de bug ou pour contribuer au code :<br>
+Allez sur GitHub pour demander de nouvelles options, signaler des bugs ou pour contribuer au développement :
https://www.github.com/AntennaPod/AntennaPod
-Rejoignez notre Google Group pour partager vos idées, podcast préférés et vos remerciements à tous les bénévoles :<br>
-https://groups.google.com/forum/#!forum/antennapod
-
-Vous avez une question ou des suggestions ?
-https://twitter.com/@AntennaPod
-
-Retrouvez nous sur Transifex pour contribuer à la traduction de cette app :<br>
-https://www.transifex.com/antennapod/antennapod
-
-Jetez un coup d’œil à notre programme de version Beta pour bénéficier des dernières options :<br>
-https://www.github.com/AntennaPod/AntennaPod/wiki/Help-test-AntennaPod \ No newline at end of file
+Allez sur Transifex pour aider la traduction :
+https://www.transifex.com/antennapod/antennapod \ No newline at end of file
diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/1.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/1.png
new file mode 100644
index 000000000..e32664902
--- /dev/null
+++ b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/1.png
Binary files differ
diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/2.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/2.png
new file mode 100644
index 000000000..f1119dd11
--- /dev/null
+++ b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/2.png
Binary files differ
diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/3.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/3.png
new file mode 100644
index 000000000..17cae455a
--- /dev/null
+++ b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/3.png
Binary files differ
diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/4.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/4.png
new file mode 100644
index 000000000..5fa129b40
--- /dev/null
+++ b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/4.png
Binary files differ
diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/5.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/5.png
new file mode 100644
index 000000000..2d7572b65
--- /dev/null
+++ b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/5.png
Binary files differ
diff --git a/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/6.png b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/6.png
new file mode 100644
index 000000000..cdf2cac38
--- /dev/null
+++ b/app/src/main/play/listings/fr-FR/graphics/phone-screenshots/6.png
Binary files differ
diff --git a/app/src/main/play/release-notes/en-US/default.txt b/app/src/main/play/release-notes/en-US/default.txt
index 11371852d..0c260fbe4 100644
--- a/app/src/main/play/release-notes/en-US/default.txt
+++ b/app/src/main/play/release-notes/en-US/default.txt
@@ -1,8 +1,7 @@
-- Display episode image on widget (by @brad)
-- Added checkbox to keep queue sorted (by @damoasda)
-- New UI for "Add podcast" screen (by @ByteHamster)
-- Added batch editing to the queue (by @ByteHamster)
-- Added option to adapt remaining time to playback speed (by @CedricCabessa)
-- Removed broken Flattr integration (by @ByteHamster)
-- Added filter to "All episodes" list (by @jhunnius)
-- Tons of bug fixes and performance improvements
+- Added per-feed playback speed setting (by @spacecowboy)
+- Support sorting in Podcast screen (by orionlee)
+- Option to show stream button rather than download in lists (by dsmith47)
+- Option to replace Episode cover with Podcast cover (by xgouchet)
+- Transparent widget (by M-arcel)
+- User interface tweaks (by ByteHamster)
+- Tons of bug fixes and improvements
diff --git a/app/src/main/res/anim/card_flip_left_in.xml b/app/src/main/res/anim/card_flip_left_in.xml
new file mode 100644
index 000000000..0ffc85aec
--- /dev/null
+++ b/app/src/main/res/anim/card_flip_left_in.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Before rotating, immediately set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="0" />
+
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="-180"
+ android:valueTo="0"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
+ <objectAnimator
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set> \ No newline at end of file
diff --git a/app/src/main/res/anim/card_flip_left_out.xml b/app/src/main/res/anim/card_flip_left_out.xml
new file mode 100644
index 000000000..817f0d3fc
--- /dev/null
+++ b/app/src/main/res/anim/card_flip_left_out.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="0"
+ android:valueTo="180"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set> \ No newline at end of file
diff --git a/app/src/main/res/anim/card_flip_right_in.xml b/app/src/main/res/anim/card_flip_right_in.xml
new file mode 100644
index 000000000..31ff1dde5
--- /dev/null
+++ b/app/src/main/res/anim/card_flip_right_in.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Before rotating, immediately set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:duration="0" />
+
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="180"
+ android:valueTo="0"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
+ <objectAnimator
+ android:valueFrom="0.0"
+ android:valueTo="1.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set> \ No newline at end of file
diff --git a/app/src/main/res/anim/card_flip_right_out.xml b/app/src/main/res/anim/card_flip_right_out.xml
new file mode 100644
index 000000000..b57113fea
--- /dev/null
+++ b/app/src/main/res/anim/card_flip_right_out.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Rotate. -->
+ <objectAnimator
+ android:valueFrom="0"
+ android:valueTo="-180"
+ android:propertyName="rotationY"
+ android:interpolator="@android:interpolator/accelerate_decelerate"
+ android:duration="@integer/card_flip_time_full" />
+
+ <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
+ <objectAnimator
+ android:valueFrom="1.0"
+ android:valueTo="0.0"
+ android:propertyName="alpha"
+ android:startOffset="@integer/card_flip_time_half"
+ android:duration="1" />
+</set> \ No newline at end of file
diff --git a/app/src/main/res/layout/about_teaser.xml b/app/src/main/res/layout/about_teaser.xml
new file mode 100644
index 000000000..a9e50f6da
--- /dev/null
+++ b/app/src/main/res/layout/about_teaser.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<ImageView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:adjustViewBounds="true"
+ android:layout_height="wrap_content" app:srcCompat="@drawable/teaser" /> \ No newline at end of file
diff --git a/app/src/main/res/layout/activity_widget_config.xml b/app/src/main/res/layout/activity_widget_config.xml
new file mode 100644
index 000000000..ca8aba52d
--- /dev/null
+++ b/app/src/main/res/layout/activity_widget_config.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ tools:context="de.danoeh.antennapod.activity.WidgetConfigActivity">
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="200dp"
+ android:layout_gravity="center">
+
+ <ImageView
+ android:id="@+id/widget_config_background"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:src="@drawable/teaser"
+ android:scaleType="centerCrop" />
+
+ <include
+ android:id="@+id/widget_config_preview"
+ layout="@layout/player_widget"
+ android:layout_width="match_parent"
+ android:layout_height="80dp"
+ android:layout_gravity="center"
+ android:layout_margin="16dp" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_vertical"
+ android:orientation="horizontal">
+
+ <TextView
+ android:id="@+id/textView"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/widget_opacity"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorPrimary" />
+
+ <TextView
+ android:id="@+id/widget_opacity_textView"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:gravity="end"
+ android:text="100%"
+ android:textSize="16sp"
+ android:textColor="?android:attr/textColorSecondary" />
+
+ </LinearLayout>
+
+ <SeekBar
+ android:id="@+id/widget_opacity_seekBar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="16dp"
+ android:layout_marginBottom="16dp"
+ android:max="100"
+ android:progress="100" />
+
+ <Button
+ android:id="@+id/butConfirm"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:text="@string/widget_create_button" />
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml
index a7f7d9f12..9e57e0743 100644
--- a/app/src/main/res/layout/addfeed.xml
+++ b/app/src/main/res/layout/addfeed.xml
@@ -13,7 +13,7 @@
android:focusableInTouchMode="true"
android:padding="8dp">
- <android.support.v7.widget.CardView
+ <androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp"
@@ -32,6 +32,7 @@
android:layout_marginRight="8dp"
android:contentDescription="@string/search_podcast_hint"
app:srcCompat="?attr/action_search"
+ android:id="@+id/search_icon"
android:scaleType="center"/>
<EditText
@@ -53,7 +54,7 @@
</LinearLayout>
- </android.support.v7.widget.CardView>
+ </androidx.cardview.widget.CardView>
<fragment
android:id="@+id/quickFeedDiscovery"
@@ -62,7 +63,7 @@
android:layout_height="wrap_content"
android:layout_margin="8dp"/>
- <android.support.v7.widget.CardView
+ <androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp"
@@ -72,7 +73,7 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dp"
+ android:padding="16dp"
android:orientation="vertical">
<TextView
@@ -82,6 +83,8 @@
android:text="@string/txtvfeedurl_label"
android:textSize="18sp"
android:layout_marginBottom="8dp"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
android:textColor="?android:attr/textColorPrimary"/>
<EditText
@@ -96,15 +99,17 @@
<Button
android:id="@+id/butConfirm"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_gravity="end"
+ style="?android:attr/buttonBarButtonStyle"
android:text="@string/confirm_label"/>
</LinearLayout>
- </android.support.v7.widget.CardView>
+ </androidx.cardview.widget.CardView>
- <android.support.v7.widget.CardView
+ <androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp"
@@ -170,7 +175,7 @@
</LinearLayout>
- </android.support.v7.widget.CardView>
+ </androidx.cardview.widget.CardView>
</LinearLayout>
diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml
index 53636c2b6..9160998ac 100644
--- a/app/src/main/res/layout/all_episodes_fragment.xml
+++ b/app/src/main/res/layout/all_episodes_fragment.xml
@@ -17,7 +17,7 @@
android:visibility="gone"
tools:text="(i) Information" />
- <android.support.v7.widget.RecyclerView
+ <androidx.recyclerview.widget.RecyclerView
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml
index 090aec47f..5049db215 100644
--- a/app/src/main/res/layout/audio_controls.xml
+++ b/app/src/main/res/layout/audio_controls.xml
@@ -3,7 +3,8 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="wrap_content">
+ android:layout_height="wrap_content"
+ android:padding="16dp">
<LinearLayout
android:layout_width="wrap_content"
diff --git a/app/src/main/res/layout/bug_report.xml b/app/src/main/res/layout/bug_report.xml
new file mode 100644
index 000000000..e97e85265
--- /dev/null
+++ b/app/src/main/res/layout/bug_report.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+ <Button
+ android:id="@+id/btn_open_bug_tracker"
+ android:text="@string/open_bug_tracker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <Button
+ android:id="@+id/btn_copy_log"
+ android:text="@string/copy_to_clipboard"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:layout_marginTop="8dp"
+ android:id="@+id/crash_report_logs"
+ android:textIsSelectable="true"
+ android:textSize="12sp"
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="1"/>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/checkbox_do_not_show_again.xml b/app/src/main/res/layout/checkbox_do_not_show_again.xml
new file mode 100644
index 000000000..15f26e8b4
--- /dev/null
+++ b/app/src/main/res/layout/checkbox_do_not_show_again.xml
@@ -0,0 +1,17 @@
+<?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="wrap_content"
+ android:orientation="vertical"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp">
+
+ <CheckBox
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/checkbox_do_not_show_again"
+ android:text="@string/checkbox_do_not_show_again"/>
+
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/choose_data_folder_dialog.xml b/app/src/main/res/layout/choose_data_folder_dialog.xml
new file mode 100644
index 000000000..bac14a108
--- /dev/null
+++ b/app/src/main/res/layout/choose_data_folder_dialog.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
+
+ <androidx.recyclerview.widget.RecyclerView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/recyclerView" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/choose_data_folder_dialog_entry.xml b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
index f4a2ff703..ae59c0614 100644
--- a/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
+++ b/app/src/main/res/layout/choose_data_folder_dialog_entry.xml
@@ -37,12 +37,11 @@
android:layout_height="wrap_content"
tools:text="2 GB" />
- <me.zhanghai.android.materialprogressbar.MaterialProgressBar
+ <ProgressBar
android:id="@+id/used_space"
- style="@style/Widget.MaterialProgressBar.ProgressBar.Horizontal"
+ style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- app:mpb_progressStyle="horizontal" />
+ android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
diff --git a/app/src/main/res/layout/downloaded_episodeslist_item.xml b/app/src/main/res/layout/downloaded_episodeslist_item.xml
index 65a08251f..3f8065466 100644
--- a/app/src/main/res/layout/downloaded_episodeslist_item.xml
+++ b/app/src/main/res/layout/downloaded_episodeslist_item.xml
@@ -17,7 +17,7 @@
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
diff --git a/app/src/main/res/layout/edit_text_dialog.xml b/app/src/main/res/layout/edit_text_dialog.xml
new file mode 100644
index 000000000..6bf0bc6cb
--- /dev/null
+++ b/app/src/main/res/layout/edit_text_dialog.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
+
+ <EditText
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:inputType="text"
+ android:ems="10"
+ android:id="@+id/text" />
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml
index 50061c4d8..416fc3aec 100644
--- a/app/src/main/res/layout/feedinfo.xml
+++ b/app/src/main/res/layout/feedinfo.xml
@@ -24,7 +24,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
- <android.support.v7.widget.GridLayout
+ <androidx.gridlayout.widget.GridLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
@@ -107,7 +107,7 @@
tools:text="http://www.example.com/feed"
tools:background="@android:color/holo_green_dark"/>
- </android.support.v7.widget.GridLayout>
+ </androidx.gridlayout.widget.GridLayout>
<TextView
style="@style/AntennaPod.TextView.Heading"
diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml
index 000539e60..3352fdf19 100644
--- a/app/src/main/res/layout/feeditem_fragment.xml
+++ b/app/src/main/res/layout/feeditem_fragment.xml
@@ -37,7 +37,7 @@
android:contentDescription="@string/cover_label"
android:gravity="center_vertical"
android:foreground="?attr/selectableItemBackground"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark" />
<TextView
@@ -112,38 +112,43 @@
android:orientation="horizontal"
tools:background="@android:color/holo_blue_bright">
- <com.joanzapata.iconify.widget.IconButton
- android:id="@+id/butAction1"
+ <View
android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/butAction1"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginRight="8dp"
- android:layout_marginEnd="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
android:ellipsize="end"
- android:gravity="center"
+ android:drawablePadding="8dp"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
tools:text="Button 1"
tools:background="@android:color/holo_red_light" />
- <com.joanzapata.iconify.widget.IconButton
- android:id="@+id/butAction2"
+ <View
android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
+ <Button
+ android:id="@+id/butAction2"
+ style="?attr/buttonBarButtonStyle"
+ android:layout_width="wrap_content"
android:layout_height="48dp"
- android:layout_gravity="center_vertical"
- android:layout_marginLeft="8dp"
- android:layout_marginStart="8dp"
- android:layout_weight="1"
- android:background="?attr/selectableItemBackground"
+ android:drawablePadding="8dp"
android:ellipsize="end"
- android:gravity="center"
android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_small"
tools:text="Button 2"
tools:background="@android:color/holo_orange_dark" />
+ <View
+ android:layout_width="0dp"
+ android:layout_height="1dp"
+ android:layout_weight="1" />
+
</LinearLayout>
<View
diff --git a/app/src/main/res/layout/feeditem_pager_fragment.xml b/app/src/main/res/layout/feeditem_pager_fragment.xml
new file mode 100644
index 000000000..8ea5bf4f9
--- /dev/null
+++ b/app/src/main/res/layout/feeditem_pager_fragment.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.viewpager.widget.ViewPager
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent" />
diff --git a/app/src/main/res/layout/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml
index e1f451e9e..596135d88 100644
--- a/app/src/main/res/layout/feeditemlist_header.xml
+++ b/app/src/main/res/layout/feeditemlist_header.xml
@@ -27,7 +27,7 @@
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:contentDescription="@string/cover_label"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<ImageButton
diff --git a/app/src/main/res/layout/fragment_itunes_search.xml b/app/src/main/res/layout/fragment_itunes_search.xml
index 0cc13f74c..211f6c9ef 100644
--- a/app/src/main/res/layout/fragment_itunes_search.xml
+++ b/app/src/main/res/layout/fragment_itunes_search.xml
@@ -20,7 +20,7 @@
tools:listitem="@layout/gpodnet_podcast_listitem" />
<TextView
- android:id="@id/android:empty"
+ android:id="@android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
diff --git a/app/src/main/res/layout/fragment_subscriptions.xml b/app/src/main/res/layout/fragment_subscriptions.xml
index a716cecb6..69eee04ce 100644
--- a/app/src/main/res/layout/fragment_subscriptions.xml
+++ b/app/src/main/res/layout/fragment_subscriptions.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -11,6 +11,18 @@
android:horizontalSpacing="2dp"
android:verticalSpacing="2dp"
android:layout_height="match_parent"
- android:layout_gravity="center_horizontal">
+ android:layout_gravity="center_horizontal"
+ android:paddingBottom="88dp"
+ android:clipToPadding="false">
</GridView>
-</LinearLayout>
+
+ <com.google.android.material.floatingactionbutton.FloatingActionButton
+ android:id="@+id/subscriptions_add"
+ android:layout_width="56dp"
+ android:layout_height="56dp"
+ android:layout_margin="16dp"
+ android:layout_gravity="bottom|end"
+ android:contentDescription="@string/add_feed_label"
+ android:src="@drawable/ic_add_white_24dp"
+ />
+</FrameLayout>
diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
index 27a8bbdca..6e02fa090 100644
--- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml
+++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml
@@ -23,7 +23,7 @@
android:contentDescription="@string/cover_label"
android:cropToPadding="true"
android:scaleType="fitXY"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark" />
<LinearLayout
diff --git a/app/src/main/res/layout/itunes_podcast_listitem.xml b/app/src/main/res/layout/itunes_podcast_listitem.xml
index 4848563b1..b2411c5df 100644
--- a/app/src/main/res/layout/itunes_podcast_listitem.xml
+++ b/app/src/main/res/layout/itunes_podcast_listitem.xml
@@ -25,7 +25,7 @@
android:cropToPadding="true"
android:scaleType="fitXY"
tools:background="@android:color/holo_green_dark"
- tools:src="@drawable/ic_stat_antenna_default" />
+ tools:src="@drawable/ic_antenna" />
<LinearLayout
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml
index 6cabcdff2..a226e482f 100644
--- a/app/src/main/res/layout/main.xml
+++ b/app/src/main/res/layout/main.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.DrawerLayout
+<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
@@ -19,7 +19,7 @@
tools:layout_height="64dp"
tools:background="@android:color/holo_green_light" />
- <android.support.v7.widget.Toolbar
+ <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -48,4 +48,4 @@
<include layout="@layout/nav_list" />
-</android.support.v4.widget.DrawerLayout> \ No newline at end of file
+</androidx.drawerlayout.widget.DrawerLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml
index a6427e985..b759b0092 100644
--- a/app/src/main/res/layout/mediaplayerinfo_activity.xml
+++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<android.support.v4.widget.DrawerLayout
+<androidx.drawerlayout.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -12,12 +12,12 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
- <android.support.design.widget.AppBarLayout
+ <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
- <android.support.v7.widget.Toolbar
+ <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -37,7 +37,7 @@
app:strokeColor="?android:attr/textColorSecondary"
app:radius="4dp" />
- </android.support.design.widget.AppBarLayout>
+ </com.google.android.material.appbar.AppBarLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
@@ -155,11 +155,11 @@
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="10sp"
+ android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
- <Button
+ <ImageButton
android:id="@+id/butPlaybackSpeed"
android:layout_width="@dimen/audioplayer_playercontrols_length"
android:layout_height="@dimen/audioplayer_playercontrols_length"
@@ -167,13 +167,28 @@
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"
+ android:src="?attr/av_speed"
+ android:scaleType="fitCenter"
+ tools:src="@drawable/ic_playback_speed_white"
tools:visibility="gone"
tools:background="@android:color/holo_green_dark" />
+ <TextView
+ android:id="@+id/txtvPlaybackSpeed"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/butPlaybackSpeed"
+ android:layout_alignLeft="@id/butPlaybackSpeed"
+ android:layout_alignStart="@id/butPlaybackSpeed"
+ android:layout_alignRight="@id/butPlaybackSpeed"
+ android:layout_alignEnd="@id/butPlaybackSpeed"
+ android:layout_marginTop="-8dp"
+ android:gravity="center"
+ android:text="1.00"
+ android:textSize="12sp"
+ android:textColor="?android:attr/textColorSecondary"
+ android:clickable="false"/>
+
<ImageButton
android:id="@+id/butCastDisconnect"
android:layout_width="@dimen/audioplayer_playercontrols_length"
@@ -216,7 +231,7 @@
android:layout_marginTop="-8dp"
android:gravity="center"
android:text="30"
- android:textSize="10sp"
+ android:textSize="12sp"
android:textColor="?android:attr/textColorSecondary"
android:clickable="false"/>
@@ -236,7 +251,7 @@
</LinearLayout>
- <android.support.v4.view.ViewPager
+ <androidx.viewpager.widget.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
@@ -257,4 +272,4 @@
<include layout="@layout/nav_list" />
-</android.support.v4.widget.DrawerLayout> \ No newline at end of file
+</androidx.drawerlayout.widget.DrawerLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/nav_feedlistitem.xml b/app/src/main/res/layout/nav_feedlistitem.xml
index 816870d1c..52833b3cd 100644
--- a/app/src/main/res/layout/nav_feedlistitem.xml
+++ b/app/src/main/res/layout/nav_feedlistitem.xml
@@ -25,7 +25,7 @@
android:scaleType="centerCrop"
android:layout_marginTop="4dp"
android:layout_marginBottom="4dp"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<TextView
diff --git a/app/src/main/res/layout/numberpicker.xml b/app/src/main/res/layout/numberpicker.xml
index 813326bd6..d493f2e6c 100644
--- a/app/src/main/res/layout/numberpicker.xml
+++ b/app/src/main/res/layout/numberpicker.xml
@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
- android:padding="32dp">
+ android:padding="16dp">
<EditText
android:layout_width="match_parent"
diff --git a/app/src/main/res/layout/onlinefeedview_activity.xml b/app/src/main/res/layout/onlinefeedview_activity.xml
new file mode 100644
index 000000000..4ed8a0341
--- /dev/null
+++ b/app/src/main/res/layout/onlinefeedview_activity.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <androidx.cardview.widget.CardView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ app:cardCornerRadius="4dp"
+ android:layout_margin="32dp"
+ android:elevation="16dp">
+
+ <FrameLayout
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <ProgressBar
+ style="?android:attr/progressBarStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_gravity="center"/>
+
+ <LinearLayout
+ android:id="@+id/feedDisplay"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <include layout="@layout/feeditemlist_header"/>
+
+ <Spinner
+ android:id="@+id/spinnerAlternateUrls"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_marginTop="8dp"
+ android:padding="8dp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="@dimen/text_size_micro"/>
+
+ <Button
+ android:id="@+id/butSubscribe"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="16dp"
+ android:focusable="false"
+ android:text="@string/subscribe_label"/>
+
+ <ListView
+ android:id="@+id/listview"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"/>
+
+ </LinearLayout>
+
+ </FrameLayout>
+ </androidx.cardview.widget.CardView>
+</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/onlinefeedview_header.xml b/app/src/main/res/layout/onlinefeedview_header.xml
index 057bfb379..aa690e142 100644
--- a/app/src/main/res/layout/onlinefeedview_header.xml
+++ b/app/src/main/res/layout/onlinefeedview_header.xml
@@ -6,27 +6,6 @@
android:layout_height="wrap_content"
android:layout_width="match_parent">
- <include layout="@layout/feeditemlist_header"/>
-
- <Spinner
- android:id="@+id/spinnerAlternateUrls"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginLeft="8dp"
- android:layout_marginRight="8dp"
- android:layout_marginTop="8dp"
- android:padding="8dp"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_micro"/>
-
- <Button
- android:id="@+id/butSubscribe"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_margin="16dp"
- android:focusable="false"
- android:text="@string/subscribe_label"/>
-
<TextView
android:id="@+id/txtvDescription"
android:layout_width="wrap_content"
@@ -50,8 +29,7 @@
android:layout_marginStart="16dp"
android:layout_marginRight="16dp"
android:layout_marginEnd="16dp"
- android:textColor="?attr/colorAccent"
- android:textSize="@dimen/text_size_small"
+ android:textSize="@dimen/text_size_medium"
android:text="@string/episodes_label"/>
</LinearLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/pager_fragment.xml b/app/src/main/res/layout/pager_fragment.xml
index 54b711b1c..492743239 100644
--- a/app/src/main/res/layout/pager_fragment.xml
+++ b/app/src/main/res/layout/pager_fragment.xml
@@ -7,14 +7,14 @@
android:orientation="vertical">
- <android.support.design.widget.TabLayout
+ <com.google.android.material.tabs.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="fill"
app:tabMode="fixed" />
- <android.support.v4.view.ViewPager
+ <androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
diff --git a/app/src/main/res/layout/proxy_settings.xml b/app/src/main/res/layout/proxy_settings.xml
index 983325030..e4e57cc92 100644
--- a/app/src/main/res/layout/proxy_settings.xml
+++ b/app/src/main/res/layout/proxy_settings.xml
@@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
-
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:orientation="vertical">
+ android:layout_width="match_parent"
+ android:orientation="vertical"
+ android:padding="16dp">
<TextView
android:id="@+id/txtvType"
diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml
index 85b0df58c..63da6a315 100644
--- a/app/src/main/res/layout/queue_fragment.xml
+++ b/app/src/main/res/layout/queue_fragment.xml
@@ -20,7 +20,7 @@
android:layout_below="@id/info_bar"
android:background="?android:attr/listDivider"/>
- <android.support.v7.widget.RecyclerView
+ <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml
index 6b41b68d5..1dcc34bce 100644
--- a/app/src/main/res/layout/queue_listitem.xml
+++ b/app/src/main/res/layout/queue_listitem.xml
@@ -51,7 +51,7 @@
android:layout_height="@dimen/thumbnail_length_queue_item"
android:layout_centerVertical="true"
android:contentDescription="@string/cover_label"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/quick_feed_discovery.xml b/app/src/main/res/layout/quick_feed_discovery.xml
index ce5cfa65b..75a35a130 100644
--- a/app/src/main/res/layout/quick_feed_discovery.xml
+++ b/app/src/main/res/layout/quick_feed_discovery.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<android.support.v7.widget.CardView
+<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
@@ -10,35 +10,32 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:padding="8dp"
+ android:padding="16dp"
android:orientation="vertical">
- <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
- android:orientation="horizontal">
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
<TextView
- android:layout_width="wrap_content"
+ android:layout_width="0dip"
android:layout_height="wrap_content"
android:text="@string/discover"
android:textSize="18sp"
android:layout_marginBottom="8dp"
+ android:layout_weight="1"
+ android:layout_marginLeft="4dp"
+ android:layout_marginStart="4dp"
android:textColor="?android:attr/textColorPrimary"/>
- <TextView
- android:layout_width="0dp"
+ <Button
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:minHeight="0dp"
+ android:minWidth="0dp"
android:text="@string/discover_more"
- android:gravity="end"
- android:textAlignment="viewEnd"
- android:textSize="18sp"
- android:background="?android:attr/selectableItemBackground"
- android:layout_marginBottom="8dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingLeft="8dp"
- android:paddingRight="8dp"
- android:layout_weight="1"
android:id="@+id/discover_more"
- android:textColor="@color/antennapod_blue"/>
+ style="?android:attr/buttonBarButtonStyle"/>
</LinearLayout>
<RelativeLayout
@@ -52,8 +49,6 @@
android:numColumns="4"
app:layout_columnWeight="1"
app:layout_rowWeight="1"
- android:horizontalSpacing="4dp"
- android:verticalSpacing="4dp"
android:scrollbars="none"
android:layout_marginTop="8dp"
android:layout_centerInParent="true"
@@ -70,7 +65,7 @@
<TextView
android:id="@+id/discover_error"
- android:textColor="@color/md_edittext_error"
+ android:textColor="@color/download_failed_red"
android:layout_width="match_parent"
android:layout_centerInParent="true"
android:layout_height="wrap_content" />
@@ -79,5 +74,5 @@
</LinearLayout>
-</android.support.v7.widget.CardView>
+</androidx.cardview.widget.CardView>
diff --git a/app/src/main/res/layout/quick_feed_discovery_item.xml b/app/src/main/res/layout/quick_feed_discovery_item.xml
index 6b0c98013..3a0fe0b4b 100644
--- a/app/src/main/res/layout/quick_feed_discovery_item.xml
+++ b/app/src/main/res/layout/quick_feed_discovery_item.xml
@@ -2,12 +2,16 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="match_parent">
+ android:layout_height="match_parent"
+ android:padding="4dp"
+ android:clipToPadding="false">
<de.danoeh.antennapod.view.SquareImageView
android:id="@+id/discovery_cover"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:elevation="4dp"
+ android:outlineProvider="bounds"
android:foreground="?android:attr/selectableItemBackground"/>
</LinearLayout>
diff --git a/app/src/main/res/layout/searchlist_item.xml b/app/src/main/res/layout/searchlist_item.xml
index 50374c737..4a055fea9 100644
--- a/app/src/main/res/layout/searchlist_item.xml
+++ b/app/src/main/res/layout/searchlist_item.xml
@@ -18,7 +18,7 @@
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
android:contentDescription="@string/cover_label"
android:scaleType="centerCrop"
- tools:src="@drawable/ic_stat_antenna_default"
+ tools:src="@drawable/ic_antenna"
tools:background="@android:color/holo_green_dark"/>
<LinearLayout
diff --git a/app/src/main/res/layout/simple_icon_list_item.xml b/app/src/main/res/layout/simple_icon_list_item.xml
new file mode 100644
index 000000000..7ed129204
--- /dev/null
+++ b/app/src/main/res/layout/simple_icon_list_item.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal"
+ android:padding="16dp">
+
+ <ImageView
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:id="@+id/icon"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"
+ android:layout_marginLeft="16dp"
+ android:layout_gravity="center_vertical"
+ android:layout_marginStart="16dp">
+
+ <TextView
+ tools:text="Title"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/title"/>
+
+ <TextView
+ tools:text="Subtitle"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="14sp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/subtitle"/>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/app/src/main/res/layout/statistics_activity.xml b/app/src/main/res/layout/statistics_activity.xml
index 4a72dc7de..9d9cad438 100644
--- a/app/src/main/res/layout/statistics_activity.xml
+++ b/app/src/main/res/layout/statistics_activity.xml
@@ -1,41 +1,24 @@
<?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:paddingTop="8dp"
- android:paddingBottom="8dp">
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="@string/total_time_listened_to_podcasts"
- android:gravity="center_horizontal"/>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical">
<ProgressBar
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/progressBar"
- android:layout_gravity="center_horizontal"
- style="?android:attr/progressBarStyleLarge"/>
-
- <TextView
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:id="@+id/total_time"
- android:gravity="center_horizontal"
- android:textSize="45sp"/>
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/progressBar"
+ android:layout_gravity="center"/>
- <ListView
- android:id="@+id/statistics_list"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:choiceMode="singleChoice"
- android:clipToPadding="false"
- android:divider="@android:color/transparent"
- android:dividerHeight="0dp"
- android:paddingBottom="@dimen/list_vertical_padding"
- android:paddingTop="@dimen/list_vertical_padding"
- android:scrollbarStyle="outsideOverlay" />
+ <androidx.recyclerview.widget.RecyclerView
+ android:id="@+id/statistics_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:paddingBottom="@dimen/list_vertical_padding"
+ android:paddingTop="@dimen/list_vertical_padding"
+ android:scrollbarStyle="outsideOverlay"
+ tools:listitem="@layout/statistics_listitem"/>
-</LinearLayout>
+</FrameLayout>
diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml
index b186add9e..6bd2a907e 100644
--- a/app/src/main/res/layout/statistics_listitem.xml
+++ b/app/src/main/res/layout/statistics_listitem.xml
@@ -3,63 +3,55 @@
xmlns:tools="http://schemas.android.com/tools"
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">
+ android:layout_height="wrap_content"
+ android:paddingLeft="16dp"
+ android:paddingRight="16dp"
+ android:paddingTop="8dp"
+ android:paddingBottom="8dp"
+ android:background="?android:attr/selectableItemBackground">
<ImageView
- android:id="@+id/imgvCover"
- android:contentDescription="@string/cover_label"
- 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"
- tools:src="@drawable/ic_stat_antenna_default"
- tools:background="@android:color/holo_green_dark"/>
+ android:id="@+id/imgvCover"
+ android:contentDescription="@string/cover_label"
+ android:layout_width="40dp"
+ android:layout_height="40dp"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_centerVertical="true"
+ android:adjustViewBounds="true"
+ android:cropToPadding="true"
+ android:scaleType="centerCrop"
+ tools:src="@drawable/ic_antenna"
+ tools:background="@android:color/holo_green_dark"/>
<TextView
- android:id="@+id/txtvTime"
- 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"/>
+ android:id="@+id/txtvTitle"
+ android:lines="1"
+ android:ellipsize="end"
+ android:singleLine="true"
+ android:textColor="?android:attr/textColorPrimary"
+ android:textSize="16sp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_toRightOf="@id/imgvCover"
+ android:layout_alignTop="@id/imgvCover"
+ android:layout_alignWithParentIfMissing="true"
+ tools:text="Feed title"/>
<TextView
- android:id="@+id/txtvTitle"
- android:lines="1"
- android:ellipsize="end"
- android:singleLine="true"
- android:layout_centerVertical="true"
- android:textColor="?android:attr/textColorPrimary"
- android:textSize="@dimen/text_size_navdrawer"
- 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"/>
-
+ android:id="@+id/txtvTime"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:lines="1"
+ android:textColor="?android:attr/textColorTertiary"
+ android:textSize="14sp"
+ android:layout_toEndOf="@+id/imgvCover"
+ android:layout_toRightOf="@+id/imgvCover"
+ android:layout_marginLeft="16dp"
+ android:layout_marginStart="16dp"
+ android:layout_below="@+id/txtvTitle"
+ tools:text="23 hours"/>
</RelativeLayout>
diff --git a/app/src/main/res/layout/statistics_listitem_total_time.xml b/app/src/main/res/layout/statistics_listitem_total_time.xml
new file mode 100644
index 000000000..2e0ae54d6
--- /dev/null
+++ b/app/src/main/res/layout/statistics_listitem_total_time.xml
@@ -0,0 +1,42 @@
+<?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="wrap_content"
+ android:padding="16dp">
+
+ <de.danoeh.antennapod.view.PieChartView
+ android:id="@+id/pie_chart"
+ android:layout_marginLeft="8dp"
+ android:layout_marginRight="8dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/total_time_description"
+ android:textSize="14sp"
+ android:text="@string/total_time_listened_to_podcasts"
+ android:gravity="center_horizontal"
+ android:layout_above="@+id/total_time"/>
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:id="@+id/total_time"
+ android:textColor="?android:attr/textColorPrimary"
+ android:gravity="center_horizontal"
+ android:textSize="28sp"
+ android:layout_marginBottom="16dp"
+ android:layout_alignBottom="@id/pie_chart"
+ tools:text="10.0 hours"/>
+
+ <View
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:layout_marginTop="16dp"
+ android:background="?android:attr/dividerVertical"
+ android:layout_below="@+id/pie_chart"/>
+
+</RelativeLayout> \ No newline at end of file
diff --git a/app/src/main/res/layout/time_dialog.xml b/app/src/main/res/layout/time_dialog.xml
index ba4249268..b3742c20c 100644
--- a/app/src/main/res/layout/time_dialog.xml
+++ b/app/src/main/res/layout/time_dialog.xml
@@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
- android:gravity="center">
+ android:gravity="center"
+ android:padding="16dp">
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
@@ -33,7 +34,7 @@
</LinearLayout>
<LinearLayout
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
diff --git a/app/src/main/res/layout/videoplayer_activity.xml b/app/src/main/res/layout/videoplayer_activity.xml
index 4de46810e..b26ef304c 100644
--- a/app/src/main/res/layout/videoplayer_activity.xml
+++ b/app/src/main/res/layout/videoplayer_activity.xml
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/black"
@@ -33,27 +34,27 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/rewind_label"
- android:src="@drawable/ic_av_fast_rewind_white_80dp" />
+ app:srcCompat="@drawable/ic_av_fast_rewind_white_80dp" />
<ImageButton
android:id="@+id/butPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/pause_label"
- android:src="@drawable/ic_av_pause_white_80dp" />
+ app:srcCompat="@drawable/ic_av_pause_white_80dp" />
<ImageButton
android:id="@+id/butFF"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
- android:background="@drawable/md_transparent"
+ android:background="@android:color/transparent"
android:contentDescription="@string/fast_forward_label"
- android:src="@drawable/ic_av_fast_forward_white_80dp" />
+ app:srcCompat="@drawable/ic_av_fast_forward_white_80dp" />
</LinearLayout>
diff --git a/app/src/main/res/menu/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml
deleted file mode 100644
index 907bc9334..000000000
--- a/app/src/main/res/menu/allepisodes_context.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
- <item
- android:id="@id/skip_episode_item"
- android:menuCategory="container"
- android:title="@string/skip_episode_label" />
-
-
- <item
- android:id="@+id/remove_new_flag_item"
- android:menuCategory="container"
- android:title="@string/remove_new_flag_label" />
-
- <item
- android:id="@+id/mark_read_item"
- android:menuCategory="container"
- android:title="@string/mark_read_label" />
- <item
- android:id="@+id/mark_unread_item"
- android:menuCategory="container"
- android:title="@string/mark_unread_label" />
-
- <item
- android:id="@+id/add_to_queue_item"
- android:menuCategory="container"
- android:title="@string/add_to_queue_label" />
- <item
- android:id="@+id/remove_from_queue_item"
- android:menuCategory="container"
- android:title="@string/remove_from_queue_label" />
- <item
- android:id="@+id/add_to_favorites_item"
- android:menuCategory="container"
- android:title="@string/add_to_favorite_label" />
- <item
- android:id="@+id/remove_from_favorites_item"
- android:menuCategory="container"
- android:title="@string/remove_from_favorite_label" />
- <item
- android:id="@+id/reset_position"
- android:menuCategory="container"
- android:title="@string/reset_position" />
-
- <item
- android:id="@+id/activate_auto_download"
- android:menuCategory="container"
- android:title="@string/activate_auto_download" />
- <item
- android:id="@+id/deactivate_auto_download"
- android:menuCategory="container"
- android:title="@string/deactivate_auto_download" />
-
- <item
- android:id="@+id/visit_website_item"
- android:menuCategory="container"
- android:title="@string/visit_website_label" />
- <item
- android:id="@+id/share_item"
- android:menuCategory="container"
- android:title="@string/share_label">
- <menu>
- <item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
- android:id="@+id/share_link_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_link_with_position_label" />
- <item
- android:id="@+id/share_download_url_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_label" />
- <item
- android:id="@+id/share_download_url_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_with_position_label" />
- <item
- android:id="@+id/share_file"
- android:menuCategory="container"
- android:title="@string/share_file_label" />
- </menu>
- </item>
-</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml
index 1e1aa8f56..2fac77375 100644
--- a/app/src/main/res/menu/episodes.xml
+++ b/app/src/main/res/menu/episodes.xml
@@ -6,15 +6,15 @@
<item
android:id="@+id/action_search"
android:icon="?attr/action_search"
- custom:showAsAction="collapseActionView|ifRoom"
- custom:actionViewClass="android.support.v7.widget.SearchView"
+ custom:showAsAction="collapseActionView|always"
+ custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
<item
android:id="@+id/refresh_item"
android:title="@string/refresh_label"
android:menuCategory="container"
- custom:showAsAction="ifRoom"
+ custom:showAsAction="always"
android:icon="?attr/navigation_refresh"/>
<item
diff --git a/app/src/main/res/menu/feedinfo.xml b/app/src/main/res/menu/feedinfo.xml
index 300068007..f20d679a5 100644
--- a/app/src/main/res/menu/feedinfo.xml
+++ b/app/src/main/res/menu/feedinfo.xml
@@ -11,7 +11,7 @@
<item
android:id="@+id/share_link_item"
custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:title="@string/share_website_url_label">
</item>
<item
android:id="@+id/share_download_url_item"
diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml
index 0801b79a1..e415ff85a 100644
--- a/app/src/main/res/menu/feeditem_options.xml
+++ b/app/src/main/res/menu/feeditem_options.xml
@@ -9,6 +9,11 @@
</item>
<item
+ android:id="@+id/remove_new_flag_item"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/remove_new_flag_label" />
+
+ <item
android:id="@+id/mark_read_item"
custom:showAsAction="collapseActionView"
android:title="@string/mark_read_label">
diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml
index df13cb027..6e4966206 100644
--- a/app/src/main/res/menu/feeditemlist_context.xml
+++ b/app/src/main/res/menu/feeditemlist_context.xml
@@ -8,6 +8,11 @@
android:title="@string/skip_episode_label" />
<item
+ android:id="@+id/remove_new_flag_item"
+ android:menuCategory="container"
+ android:title="@string/remove_new_flag_label" />
+
+ <item
android:id="@+id/mark_read_item"
android:menuCategory="container"
android:title="@string/mark_read_label" />
diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml
index 0cc8addfe..13c019b65 100644
--- a/app/src/main/res/menu/feedlist.xml
+++ b/app/src/main/res/menu/feedlist.xml
@@ -3,17 +3,17 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
- android:id="@+id/filter_items"
- android:icon="?attr/ic_filter"
+ android:id="@+id/sort_items"
+ android:icon="?attr/ic_sort"
android:menuCategory="container"
- android:title="@string/filter"
+ android:title="@string/sort"
custom:showAsAction="always">
</item>
<item
- android:id="@+id/episode_actions"
+ android:id="@+id/filter_items"
+ android:icon="?attr/ic_filter"
android:menuCategory="container"
- android:icon="?attr/checkbox_multiple"
- android:title="@string/batch_edit"
+ android:title="@string/filter"
custom:showAsAction="always">
</item>
<item
@@ -34,10 +34,17 @@
android:id="@+id/action_search"
android:icon="?attr/action_search"
custom:showAsAction="always|collapseActionView"
- custom:actionViewClass="android.support.v7.widget.SearchView"
+ custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
<item
+ android:id="@+id/episode_actions"
+ android:menuCategory="container"
+ android:icon="?attr/checkbox_multiple"
+ android:title="@string/batch_edit"
+ custom:showAsAction="collapseActionView">
+ </item>
+ <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
android:menuCategory="container"
@@ -49,7 +56,7 @@
android:id="@+id/share_link_item"
android:menuCategory="container"
custom:showAsAction="collapseActionView"
- android:title="@string/share_link_label">
+ android:title="@string/share_website_url_label">
</item>
<item
android:id="@+id/share_download_url_item"
diff --git a/app/src/main/res/menu/gpodder_podcasts.xml b/app/src/main/res/menu/gpodder_podcasts.xml
index 88fa36a4a..93d93157a 100644
--- a/app/src/main/res/menu/gpodder_podcasts.xml
+++ b/app/src/main/res/menu/gpodder_podcasts.xml
@@ -7,7 +7,7 @@
android:id="@+id/action_search"
android:icon="?attr/action_search"
custom:showAsAction="collapseActionView|ifRoom"
- custom:actionViewClass="android.support.v7.widget.SearchView"
+ custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
</menu>
diff --git a/app/src/main/res/menu/itunes_search.xml b/app/src/main/res/menu/itunes_search.xml
index 88fa36a4a..93d93157a 100644
--- a/app/src/main/res/menu/itunes_search.xml
+++ b/app/src/main/res/menu/itunes_search.xml
@@ -7,7 +7,7 @@
android:id="@+id/action_search"
android:icon="?attr/action_search"
custom:showAsAction="collapseActionView|ifRoom"
- custom:actionViewClass="android.support.v7.widget.SearchView"
+ custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
</menu>
diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml
index 44d511ee4..055951760 100644
--- a/app/src/main/res/menu/mediaplayer.xml
+++ b/app/src/main/res/menu/mediaplayer.xml
@@ -35,6 +35,14 @@
</item>
<item
+ android:id="@+id/open_feed_item"
+ android:icon="?attr/feed"
+ custom:showAsAction="collapseActionView"
+ android:title="@string/open_podcast"
+ android:visible="false">
+ </item>
+
+ <item
android:id="@+id/visit_website_item"
android:icon="?attr/location_web_site"
custom:showAsAction="collapseActionView"
diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml
index a4e511eb8..b86ad5d63 100644
--- a/app/src/main/res/menu/queue.xml
+++ b/app/src/main/res/menu/queue.xml
@@ -20,7 +20,7 @@
android:id="@+id/action_search"
android:icon="?attr/action_search"
custom:showAsAction="collapseActionView|ifRoom"
- custom:actionViewClass="android.support.v7.widget.SearchView"
+ custom:actionViewClass="androidx.appcompat.widget.SearchView"
android:title="@string/search_label"/>
<item
diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml
index e1c3e6216..522e712e2 100644
--- a/app/src/main/res/menu/queue_context.xml
+++ b/app/src/main/res/menu/queue_context.xml
@@ -11,77 +11,6 @@
android:id="@+id/move_to_bottom_item"
android:menuCategory="container"
android:title="@string/move_to_bottom_label" />
+ <!-- rest of the menu items can be found in the generic feeditemlist_context.xml -->
- <item
- android:id="@+id/mark_read_item"
- android:menuCategory="container"
- android:title="@string/mark_read_label" />
-
- <item
- android:id="@+id/mark_unread_item"
- android:menuCategory="container"
- android:title="@string/mark_unread_label" />
-
- <item
- android:id="@+id/remove_from_queue_item"
- android:menuCategory="container"
- android:title="@string/remove_from_queue_label" />
- <item
- android:id="@+id/remove_item"
- android:menuCategory="container"
- android:title="@string/delete_label" />
-
- <item
- android:id="@+id/add_to_favorites_item"
- android:menuCategory="container"
- android:title="@string/add_to_favorite_label" />
- <item
- android:id="@+id/remove_from_favorites_item"
- android:menuCategory="container"
- android:title="@string/remove_from_favorite_label" />
- <item
- android:id="@+id/reset_position"
- android:menuCategory="container"
- android:title="@string/reset_position" />
-
- <item
- android:id="@+id/activate_auto_download"
- android:menuCategory="container"
- android:title="@string/activate_auto_download" />
- <item
- android:id="@+id/deactivate_auto_download"
- android:menuCategory="container"
- android:title="@string/deactivate_auto_download" />
-
- <item
- android:id="@+id/visit_website_item"
- android:menuCategory="container"
- android:title="@string/visit_website_label" />
- <item
- android:id="@+id/share_item"
- android:menuCategory="container"
- android:title="@string/share_label">
- <menu>
- <item
- android:id="@+id/share_link_item"
- android:menuCategory="container"
- android:title="@string/share_link_label" />
- <item
- android:id="@+id/share_link_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_link_with_position_label" />
- <item
- android:id="@+id/share_download_url_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_label" />
- <item
- android:id="@+id/share_download_url_with_position_item"
- android:menuCategory="container"
- android:title="@string/share_item_url_with_position_label" />
- <item
- android:id="@+id/share_file"
- android:menuCategory="container"
- android:title="@string/share_file_label" />
- </menu>
- </item>
</menu> \ No newline at end of file
diff --git a/app/src/main/res/menu/statistics.xml b/app/src/main/res/menu/statistics.xml
index 6ecc70707..eb2a51550 100644
--- a/app/src/main/res/menu/statistics.xml
+++ b/app/src/main/res/menu/statistics.xml
@@ -3,6 +3,12 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
+ android:id="@+id/statistics_reset"
+ android:title="@string/statistics_reset_data"
+ custom:showAsAction="never"
+ />
+
+ <item
android:id="@+id/statistics_mode"
android:icon="?attr/ic_filter"
android:title="@string/statistics_mode"
diff --git a/app/src/main/res/menu/subscriptions.xml b/app/src/main/res/menu/subscriptions.xml
index f39e0ac97..1780592d5 100644
--- a/app/src/main/res/menu/subscriptions.xml
+++ b/app/src/main/res/menu/subscriptions.xml
@@ -3,6 +3,13 @@
xmlns:custom="http://schemas.android.com/apk/res-auto">
<item
+ android:id="@+id/refresh_item"
+ android:title="@string/refresh_label"
+ android:menuCategory="container"
+ custom:showAsAction="always"
+ android:icon="?attr/navigation_refresh"/>
+
+ <item
android:id="@+id/subscription_num_columns"
android:title="@string/subscription_num_columns"
custom:showAsAction="never">
diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml
new file mode 100644
index 000000000..8c444ee8b
--- /dev/null
+++ b/app/src/main/res/values/integers.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <integer name="card_flip_time_full">400</integer>
+ <integer name="card_flip_time_half">200</integer>
+</resources> \ No newline at end of file
diff --git a/app/src/main/res/xml/feed_settings.xml b/app/src/main/res/xml/feed_settings.xml
index 5fd6b2038..edc608d60 100644
--- a/app/src/main/res/xml/feed_settings.xml
+++ b/app/src/main/res/xml/feed_settings.xml
@@ -4,17 +4,26 @@
<SwitchPreference
android:key="keepUpdated"
+ android:icon="?attr/navigation_refresh"
android:title="@string/keep_updated"
android:summary="@string/keep_updated_summary"/>
<Preference
android:key="authentication"
+ android:icon="?attr/ic_key"
android:title="@string/authentication_label"
android:summary="@string/authentication_descr"/>
<ListPreference
+ android:key="feedPlaybackSpeed"
+ android:icon="?attr/av_speed"
+ android:title="@string/playback_speed"
+ android:summary="@string/pref_feed_playback_speed_sum"/>
+
+ <ListPreference
android:entries="@array/spnAutoDeleteItems"
android:entryValues="@array/spnAutoDeleteValues"
+ android:icon="?attr/content_discard"
android:title="@string/auto_delete_label"
android:key="autoDelete"/>
diff --git a/app/src/main/res/xml/player_widget_info.xml b/app/src/main/res/xml/player_widget_info.xml
index 1dbfabcd6..79cdd4a69 100644
--- a/app/src/main/res/xml/player_widget_info.xml
+++ b/app/src/main/res/xml/player_widget_info.xml
@@ -6,6 +6,7 @@
android:previewImage="@drawable/ic_widget_preview"
android:minHeight="40dp"
android:minWidth="250dp"
- android:minResizeWidth="40dp">
+ android:minResizeWidth="40dp"
+ android:configure="de.danoeh.antennapod.activity.WidgetConfigActivity">
</appwidget-provider> \ No newline at end of file
diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml
index 6c1e470c0..37707ead6 100644
--- a/app/src/main/res/xml/preferences.xml
+++ b/app/src/main/res/xml/preferences.xml
@@ -43,16 +43,14 @@
<Preference
android:key="prefFaq"
android:title="@string/pref_faq"
- android:icon="?attr/ic_question_answer" />
-
+ android:icon="?attr/ic_questionmark" />
<Preference
- android:key="prefKnownIssues"
- android:title="@string/pref_known_issues"
- android:icon="?attr/ic_known_issues" />
+ android:key="prefViewMailingList"
+ android:title="@string/view_mailing_list"
+ android:icon="?attr/ic_chat" />
<Preference
- android:key="prefSendCrashReport"
- android:title="@string/crash_report_title"
- android:summary="@string/crash_report_sum"
+ android:key="prefSendBugReport"
+ android:title="@string/bug_report_title"
android:icon="?attr/ic_bug" />
<Preference
android:key="prefAbout"
diff --git a/app/src/main/res/xml/preferences_about.xml b/app/src/main/res/xml/preferences_about.xml
new file mode 100644
index 000000000..9b8d744e1
--- /dev/null
+++ b/app/src/main/res/xml/preferences_about.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<PreferenceScreen
+ xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <Preference
+ android:layout="@layout/about_teaser"/>
+ <Preference
+ android:key="about_version"
+ android:title="@string/antennapod_version"
+ android:icon="?attr/ic_unfav"
+ android:summary="1.7.2 (asd8qs)"/>
+ <Preference
+ android:key="about_developers"
+ android:icon="?attr/ic_settings"
+ android:summary="@string/developers_summary"
+ android:title="@string/developers"/>
+ <Preference
+ android:key="about_translators"
+ android:icon="?attr/ic_chat"
+ android:summary="@string/translators_summary"
+ android:title="@string/translators"/>
+ <Preference
+ android:key="about_privacy_policy"
+ android:icon="?attr/ic_questionmark"
+ android:summary="https://antennapod.org/privacy.html"
+ android:title="@string/privacy_policy"/>
+ <Preference
+ android:key="about_licenses"
+ android:icon="?attr/action_about"
+ android:summary="@string/licenses_summary"
+ android:title="@string/licenses"/>
+
+</PreferenceScreen>
diff --git a/app/src/main/res/xml/preferences_autodownload.xml b/app/src/main/res/xml/preferences_autodownload.xml
index a4967c839..333224aa0 100644
--- a/app/src/main/res/xml/preferences_autodownload.xml
+++ b/app/src/main/res/xml/preferences_autodownload.xml
@@ -15,16 +15,14 @@
android:key="prefEpisodeCacheSize"
android:title="@string/pref_episode_cache_title"
android:summary="@string/pref_episode_cache_summary"
- android:entryValues="@array/episode_cache_size_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/episode_cache_size_values"/>
<ListPreference
android:defaultValue="-1"
android:entries="@array/episode_cleanup_entries"
android:key="prefEpisodeCleanup"
android:title="@string/pref_episode_cleanup_title"
android:summary="@string/pref_episode_cleanup_summary"
- android:entryValues="@array/episode_cleanup_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/episode_cleanup_values"/>
<SwitchPreference
android:key="prefEnableAutoDownloadOnBattery"
android:title="@string/pref_automatic_download_on_battery_title"
diff --git a/app/src/main/res/xml/preferences_playback.xml b/app/src/main/res/xml/preferences_playback.xml
index d609d3daa..7a7535dda 100644
--- a/app/src/main/res/xml/preferences_playback.xml
+++ b/app/src/main/res/xml/preferences_playback.xml
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
-<PreferenceScreen
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto">
+<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/interruptions">
<SwitchPreference
@@ -42,8 +40,7 @@
android:entryValues="@array/video_background_behavior_values"
android:key="prefVideoBehavior"
android:summary="@string/pref_videoBehavior_sum"
- android:title="@string/pref_videoBehavior_title"
- app:useStockLayout="true"/>
+ android:title="@string/pref_videoBehavior_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/playback_control">
@@ -76,6 +73,11 @@
android:key="prefPlaybackTimeRespectsSpeed"
android:summary="@string/pref_playback_time_respects_speed_sum"
android:title="@string/pref_playback_time_respects_speed_title"/>
+ <SwitchPreference
+ android:defaultValue="false"
+ android:key="prefStreamOverDownload"
+ android:summary="@string/pref_stream_over_download_sum"
+ android:title="@string/pref_stream_over_download_title"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/queue_label">
@@ -85,12 +87,12 @@
android:key="prefEnqueueDownloaded"
android:summary="@string/pref_enqueue_downloaded_summary"
android:title="@string/pref_enqueue_downloaded_title" />
- <SwitchPreference
- android:defaultValue="false"
- android:enabled="true"
- android:key="prefQueueAddToFront"
- android:summary="@string/pref_queueAddToFront_sum"
- android:title="@string/pref_queueAddToFront_title"/>
+ <ListPreference
+ android:defaultValue="BACK"
+ android:entries="@array/enqueue_location_options"
+ android:entryValues="@array/enqueue_location_values"
+ android:key="prefEnqueueLocation"
+ android:title="@string/pref_enqueue_location_title"/>
<SwitchPreference
android:defaultValue="true"
android:enabled="true"
@@ -103,8 +105,7 @@
android:entryValues="@array/smart_mark_as_played_values"
android:key="prefSmartMarkAsPlayedSecs"
android:summary="@string/pref_smart_mark_as_played_sum"
- android:title="@string/pref_smart_mark_as_played_title"
- app:useStockLayout="true"/>
+ android:title="@string/pref_smart_mark_as_played_title"/>
<SwitchPreference
android:defaultValue="true"
android:enabled="true"
@@ -120,8 +121,7 @@
android:key="prefMediaPlayer"
android:title="@string/media_player"
android:summary="@string/pref_media_player_message"
- android:entryValues="@array/media_player_values"
- app:useStockLayout="true"/>
+ android:entryValues="@array/media_player_values"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/experimental_pref">
diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml
index 1d970a5f7..20a06c4c4 100644
--- a/app/src/main/res/xml/preferences_user_interface.xml
+++ b/app/src/main/res/xml/preferences_user_interface.xml
@@ -10,8 +10,7 @@
android:title="@string/pref_set_theme_title"
android:key="prefTheme"
android:summary="@string/pref_set_theme_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
+ android:defaultValue="system"/>
<Preference
android:key="prefHiddenDrawerItems"
android:summary="@string/pref_nav_drawer_items_sum"
@@ -22,16 +21,20 @@
android:title="@string/pref_nav_drawer_feed_order_title"
android:key="prefDrawerFeedOrder"
android:summary="@string/pref_nav_drawer_feed_order_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
+ android:defaultValue="0"/>
<ListPreference
android:entryValues="@array/nav_drawer_feed_counter_values"
android:entries="@array/nav_drawer_feed_counter_options"
android:title="@string/pref_nav_drawer_feed_counter_title"
android:key="prefDrawerFeedIndicator"
android:summary="@string/pref_nav_drawer_feed_counter_sum"
- android:defaultValue="0"
- app:useStockLayout="true"/>
+ android:defaultValue="0"/>
+ <SwitchPreference
+ android:title="@string/pref_episode_cover_title"
+ android:key="prefEpisodeCover"
+ android:summary="@string/pref_episode_cover_summary"
+ android:defaultValue="true"
+ android:enabled="true"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/external_elements">
<SwitchPreference
@@ -64,7 +67,6 @@
android:key="prefBackButtonBehavior"
android:title="@string/pref_back_button_behavior_title"
android:summary="@string/pref_back_button_behavior_sum"
- android:defaultValue="default"
- app:useStockLayout="true"/>
+ android:defaultValue="default"/>
</PreferenceCategory>
</PreferenceScreen>
diff --git a/app/src/main/templates/about.html b/app/src/main/templates/about.html
deleted file mode 100644
index fd70ab549..000000000
--- a/app/src/main/templates/about.html
+++ /dev/null
@@ -1,183 +0,0 @@
-<!DOCTYPE html>
-<html>
-<head>
- <meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
- <style type="text/css">
-
- @font-face {
- font-family: 'Roboto-Light';
- src: url('file:///android_asset/Roboto-Light.ttf');
- }
-
- html, body {
- background: @background@;
- margin: 0;
- padding: 0;
- }
-
- * {
- color: @fontcolor@;
- font-family: roboto-Light;
- font-size: 12pt;
- }
-
- img#logo {
- display: block;
- margin-left: auto;
- margin-right: auto;
- max-height: 200px;
- max-height: 50vh;
- max-width: 100%;
- height: auto;
- width: auto;
- }
-
- div#logobackground{
- width: 100%;
- background: #42a5f5;
- }
-
- .card {
- background: @card_background@;
- margin: 10px;
- padding: 10px;
- border: 1px solid @card_border@;
- border-top-width: 0;
- border-bottom-width: 2px;
- }
-
- h1 {
- font-size: 15pt;
- margin-left: 20px;
- }
-
- h2 {
- font-size: 13pt;
- margin-top: 0px;
- }
-
- a {
- font-size: 14px;
- color: #00A8DF;
- text-decoration: none;
- }
-
- </style>
- <title>About AntennaPod</title>
-</head>
-<body>
-<div id="logobackground">
-<img id="logo" src="file:///android_asset/logo.png" alt="Logo"/>
-</div>
-
-<h1>AntennaPod</h1>
-
-<div class="card">
-<table>
-<tr><td>Version:</td><td><b>@versionname@</b></td></tr>
-<tr><td>Commit:</td><td><b>@commit@</b></td></tr>
-</table>
-</div>
-
-<div class="card">
-Created by Daniel Oeh<br />
-Copyright &copy; 2012-@year@<br />
-AntennaPod Contributors <a href="CONTRIBUTORS.txt">(View)</a><br />
-Licensed under the MIT License <a href="LICENSE.txt">(View)</a><br />
-Privacy Policy <a href="https://antennapod.org/privacy.html">(View)</a>
-</div>
-
-<h1>Used libraries</h1>
-
-<div class="card">
-<h2>Apache Commons <a href="http://commons.apache.org/">(Link)</a></h2>
-by The Apache Software Foundation, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>EventBus <a href="https://github.com/greenrobot/EventBus">(Link)</a></h2>
-by greenrobot, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>ExoPlayer <a href="https://github.com/google/ExoPlayer">(Link)</a></h2>
-by Google, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
- <h2>Floating Action Button Speed Dial <a href="https://github.com/leinardi/FloatingActionButtonSpeedDial">(Link)</a></h2>
- by Roberto Leinardi, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Glide <a href="https://github.com/bumptech/glide/">(Link)</a></h2>
-licensed under the Simplified BSD license <a href="LICENSE_GLIDE.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Iconify <a href="https://github.com/JoanZapata/android-iconify">(Link)</a></h2>
-by Joan Zapata, licensed under the Apache 2.0 license <a href="LICENSE_ANDROID_ICONIFY.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>jsoup <a href="http://jsoup.org/">(Link)</a></h2>
-licensed under the MIT license <a href="LICENSE_JSOUP.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Material Design Icons <a href="https://github.com/google/material-design-icons">(Link)</a></h2>
-by Google, licensed under an Attribution-ShareAlike 4.0 International license <a href="LICENSE_MATERIAL_DESIGN_ICONS.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Material Design Icons <a href="https://github.com/Templarian/MaterialDesign">(Link)</a></h2>
-by Templarian, licensed under the SIL Open Font License, Version 1.1 <a href="LICENSE_SIL.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Material Dialogs <a href="https://github.com/afollestad/material-dialogs">(Link)</a></h2>
-by Aidan Michael Follestad, licensed under the MIT License <a href="LICENSE_MATERIAL_DIALOGS.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>OkHttp <a href="https://github.com/square/okhttp">(Link)</a></h2>
-by Square, licensed under the Apache 2.0 license <a href="LICENSE_OKHTTP.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Okio <a href="https://github.com/square/okio">(Link)</a></h2>
-by Square, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Presto Client <a href="http://www.aocate.com/presto/">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_PRESTO.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>RecyclerView-FlexibleDivider <a href="https://github.com/yqritc/RecyclerView-FlexibleDivider">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>RxAndroid <a href="https://github.com/ReactiveX/RxAndroid">(Link)</a></h2>
-licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>StackBlur <a href="https://github.com/kikoso/android-stackblur">(Link)</a></h2>
-by Enrique L&oacute;pez Ma&ntilde;as, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>Triangle Label View <a href="https://github.com/shts/TriangleLabelView">(Link)</a></h2>
-by Shota Saito, licensed under the Apache 2.0 license <a href="LICENSE_TRIANGLE_LABEL_VIEW.txt">(View)</a>
-</div>
-
-<div class="card">
-<h2>AntennaPod-AudioPlayer <a href="https://github.com/AntennaPod/AntennaPod-AudioPlayer/">(Link)</a></h2>
-by the AntennaPod team, licensed under the Apache 2.0 license <a href="LICENSE_APACHE-2.0.txt">(View)</a>
-</div>
-
-</body>
-</html>
diff --git a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
index 87304b3d6..912571a7e 100644
--- a/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/play/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -4,9 +4,9 @@ import android.content.SharedPreferences;
import android.media.AudioManager;
import android.os.Bundle;
import android.preference.PreferenceManager;
-import android.support.annotation.CallSuper;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.AppCompatActivity;
+import androidx.annotation.CallSuper;
+import androidx.core.view.MenuItemCompat;
+import androidx.appcompat.app.AppCompatActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
@@ -29,17 +29,34 @@ public abstract class CastEnabledActivity extends AppCompatActivity
implements SharedPreferences.OnSharedPreferenceChangeListener {
public static final String TAG = "CastEnabledActivity";
- protected CastManager castManager;
- protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
+ private CastConsumer castConsumer;
+ private CastManager castManager;
+
+ private SwitchableMediaRouteActionProvider mediaRouteActionProvider;
private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ if (!CastManager.isInitialized()) {
+ return;
+ }
+
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
registerOnSharedPreferenceChangeListener(this);
+ castConsumer = new DefaultCastConsumer() {
+ @Override
+ public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
+ onCastConnectionChanged(true);
+ }
+
+ @Override
+ public void onDisconnected() {
+ onCastConnectionChanged(false);
+ }
+ };
castManager = CastManager.getInstance();
castManager.addCastConsumer(castConsumer);
castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
@@ -48,6 +65,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onDestroy() {
+ if (!CastManager.isInitialized()) {
+ super.onDestroy();
+ return;
+ }
PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
.unregisterOnSharedPreferenceChangeListener(this);
castManager.removeCastConsumer(castConsumer);
@@ -58,6 +79,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
getMenuInflater().inflate(R.menu.cast_enabled, menu);
castButtonVisibilityManager.setMenu(menu);
return true;
@@ -67,6 +91,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@CallSuper
public boolean onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
+ if (!CastManager.isInitialized()) {
+ return true;
+ }
+
MenuItem mediaRouteButton = menu.findItem(R.id.media_route_menu_item);
if (mediaRouteButton == null) {
Log.wtf(TAG, "MediaRoute item could not be found on the menu!", new Exception());
@@ -83,15 +111,22 @@ public abstract class CastEnabledActivity extends AppCompatActivity
@Override
protected void onResume() {
super.onResume();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(true);
}
@Override
protected void onPause() {
super.onPause();
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.setResumed(false);
}
+
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
@@ -105,18 +140,6 @@ public abstract class CastEnabledActivity extends AppCompatActivity
}
}
- CastConsumer castConsumer = new DefaultCastConsumer() {
- @Override
- public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
- onCastConnectionChanged(true);
- }
-
- @Override
- public void onDisconnected() {
- onCastConnectionChanged(false);
- }
- };
-
private void onCastConnectionChanged(boolean connected) {
if (connected) {
castButtonVisibilityManager.onConnected();
@@ -133,6 +156,9 @@ public abstract class CastEnabledActivity extends AppCompatActivity
* @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
*/
public final void requestCastButton(int showAsAction) {
+ if (!CastManager.isInitialized()) {
+ return;
+ }
castButtonVisibilityManager.requestCastButton(showAsAction);
}
diff --git a/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java b/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
index 916b13a38..2a879c62d 100644
--- a/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
+++ b/app/src/play/java/de/danoeh/antennapod/config/CastCallbackImpl.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.config;
-import android.support.annotation.NonNull;
-import android.support.v7.app.MediaRouteControllerDialogFragment;
-import android.support.v7.app.MediaRouteDialogFactory;
+import androidx.annotation.NonNull;
+import androidx.mediarouter.app.MediaRouteControllerDialogFragment;
+import androidx.mediarouter.app.MediaRouteDialogFactory;
import de.danoeh.antennapod.core.CastCallbacks;
import de.danoeh.antennapod.fragment.CustomMRControllerDialogFragment;
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 ae8e20575..bf5b85c82 100644
--- a/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
+++ b/app/src/play/java/de/danoeh/antennapod/dialog/CustomMRControllerDialog.java
@@ -8,19 +8,19 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
-import android.support.annotation.NonNull;
+import androidx.annotation.NonNull;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
-import android.support.v4.util.Pair;
-import android.support.v4.view.MarginLayoutParamsCompat;
-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 androidx.core.util.Pair;
+import androidx.core.view.MarginLayoutParamsCompat;
+import androidx.core.view.accessibility.AccessibilityEventCompat;
+import androidx.mediarouter.app.MediaRouteControllerDialog;
+import androidx.palette.graphics.Palette;
+import androidx.mediarouter.media.MediaRouter;
+import androidx.appcompat.widget.AppCompatImageView;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
@@ -311,9 +311,7 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
AccessibilityEventCompat.TYPE_ANNOUNCEMENT);
event.setPackageName(getContext().getPackageName());
event.setClassName(getClass().getName());
- int resId = isPlaying ?
- android.support.v7.mediarouter.R.string.mr_controller_pause :
- android.support.v7.mediarouter.R.string.mr_controller_play;
+ int resId = isPlaying ? R.string.mr_controller_pause : R.string.mr_controller_play;
event.getText().add(getContext().getString(resId));
accessibilityManager.sendAccessibilityEvent(event);
}
@@ -359,17 +357,17 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
if (route.getPresentationDisplay() != null &&
route.getPresentationDisplay().getDisplayId() != MediaRouter.RouteInfo.PRESENTATION_DISPLAY_ID_NONE) {
// The user is currently casting screen.
- titleView.setText(android.support.v7.mediarouter.R.string.mr_controller_casting_screen);
+ titleView.setText(R.string.mr_controller_casting_screen);
showTitle = true;
} else if (state == null || state.getState() == PlaybackStateCompat.STATE_NONE) {
// Show "No media selected" as we don't yet know the playback state.
// (Only exception is bluetooth where we don't show anything.)
if (!route.isBluetooth()) {
- titleView.setText(android.support.v7.mediarouter.R.string.mr_controller_no_media_selected);
+ titleView.setText(R.string.mr_controller_no_media_selected);
showTitle = true;
}
} else if (!hasTitle && !hasSubtitle) {
- titleView.setText(android.support.v7.mediarouter.R.string.mr_controller_no_info_available);
+ titleView.setText(R.string.mr_controller_no_info_available);
showTitle = true;
} else {
if (hasTitle) {
@@ -435,16 +433,12 @@ public class CustomMRControllerDialog extends MediaRouteControllerDialog {
| PlaybackStateCompat.ACTION_PLAY_PAUSE)) != 0;
if (isPlaying && supportsPause) {
playPauseButton.setVisibility(View.VISIBLE);
- playPauseButton.setImageResource(getThemeResource(getContext(),
- android.support.v7.mediarouter.R.attr.mediaRoutePauseDrawable));
- playPauseButton.setContentDescription(getContext().getResources()
- .getText(android.support.v7.mediarouter.R.string.mr_controller_pause));
+ playPauseButton.setImageResource(getThemeResource(getContext(), R.attr.mediaRoutePauseDrawable));
+ playPauseButton.setContentDescription(getContext().getResources().getText(R.string.mr_controller_pause));
} else if (!isPlaying && supportsPlay) {
playPauseButton.setVisibility(View.VISIBLE);
- playPauseButton.setImageResource(getThemeResource(getContext(),
- android.support.v7.mediarouter.R.attr.mediaRoutePlayDrawable));
- playPauseButton.setContentDescription(getContext().getResources()
- .getText(android.support.v7.mediarouter.R.string.mr_controller_play));
+ playPauseButton.setImageResource(getThemeResource(getContext(), R.attr.mediaRoutePlayDrawable));
+ playPauseButton.setContentDescription(getContext().getResources().getText(R.string.mr_controller_play));
} else {
playPauseButton.setVisibility(View.GONE);
}
diff --git a/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java b/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java
index a960ec998..dad7b0bfd 100644
--- a/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java
+++ b/app/src/play/java/de/danoeh/antennapod/fragment/CustomMRControllerDialogFragment.java
@@ -2,8 +2,8 @@ package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.os.Bundle;
-import android.support.v7.app.MediaRouteControllerDialog;
-import android.support.v7.app.MediaRouteControllerDialogFragment;
+import androidx.mediarouter.app.MediaRouteControllerDialog;
+import androidx.mediarouter.app.MediaRouteControllerDialogFragment;
import de.danoeh.antennapod.dialog.CustomMRControllerDialog;
diff --git a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
index c9d52df0c..810074bbe 100644
--- a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
+++ b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
@@ -1,8 +1,13 @@
package de.danoeh.antennapod.preferences;
+import android.content.Context;
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
+import de.danoeh.antennapod.R;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.fragment.preferences.PlaybackPreferencesFragment;
@@ -18,6 +23,7 @@ public class PreferenceControllerFlavorHelper {
final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
.isGooglePlayServicesAvailable(ui.getActivity());
if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
+ displayRestartRequiredDialog(ui.requireContext());
return true;
} else {
GoogleApiAvailability.getInstance()
@@ -29,4 +35,13 @@ public class PreferenceControllerFlavorHelper {
return true;
});
}
+
+ private static void displayRestartRequiredDialog(@NonNull Context context) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(context);
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.setCancelable(false);
+ dialog.show();
+ }
}