summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
Diffstat (limited to 'core')
-rw-r--r--core/build.gradle87
-rw-r--r--core/lint.xml11
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java11
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java11
-rw-r--r--core/src/main/assets/shownotes-style.css7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/ChapterMerger.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java500
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java65
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java107
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java111
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java490
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java115
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java640
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java254
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java23
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/MediaType.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java17
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/glide/MetadataRetrieverLoader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java50
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java41
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java69
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java105
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java60
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java60
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java100
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java10
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java122
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/NavDrawerData.java100
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java51
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java (renamed from core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java)85
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapper.java8
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java46
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemFilterQuery.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedMediaCursorMapper.java67
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java72
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java46
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java826
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceAuthenticationException.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceBadStatusCodeException.java12
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceException.java15
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetDevice.java73
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetEpisodeActionPostResponse.java49
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetPodcast.java72
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetTag.java66
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetUploadChangesResponse.java53
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeAction.java260
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeActionChanges.java34
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/model/ISyncService.java20
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/model/SubscriptionChanges.java39
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/model/SyncServiceException.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/sync/model/UploadChangesResponse.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java14
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java11
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java32
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java7
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java38
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java19
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java34
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java26
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Optional.java213
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java6
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/ShownotesProvider.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java71
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java287
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java182
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableException.java13
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java40
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java133
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java1
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/RemoteMedia.java344
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java30
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java5
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java9
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java2
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java16
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java2
-rw-r--r--core/src/main/res/drawable/filter_dialog_background_light.xml2
-rw-r--r--core/src/main/res/drawable/ic_add.xml (renamed from core/src/main/res/drawable/ic_add_black.xml)2
-rw-r--r--core/src/main/res/drawable/ic_appearance.xml5
-rw-r--r--core/src/main/res/drawable/ic_appearance_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_appearance_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_pause_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_play_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_replay_white_48dp.xml9
-rw-r--r--core/src/main/res/drawable/ic_av_skip_black_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_skip_black_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_skip_white_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_av_skip_white_48dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_bug.xml5
-rw-r--r--core/src/main/res/drawable/ic_bug_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_bug_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_cancel.xml5
-rw-r--r--core/src/main/res/drawable/ic_cancel_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_cancel_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_chapter_next.xml9
-rw-r--r--core/src/main/res/drawable/ic_chapter_prev.xml9
-rw-r--r--core/src/main/res/drawable/ic_chat.xml5
-rw-r--r--core/src/main/res/drawable/ic_chat_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_chat_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_check.xml (renamed from core/src/main/res/drawable/ic_check_white.xml)2
-rw-r--r--core/src/main/res/drawable/ic_check_multiple.xml5
-rw-r--r--core/src/main/res/drawable/ic_check_multiple_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_check_multiple_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_contribute.xml9
-rw-r--r--core/src/main/res/drawable/ic_delete.xml5
-rw-r--r--core/src/main/res/drawable/ic_delete_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_delete_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_download.xml (renamed from core/src/main/res/drawable/ic_add_white.xml)2
-rw-r--r--core/src/main/res/drawable/ic_download_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_download_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_drag_darktheme.xml2
-rw-r--r--core/src/main/res/drawable/ic_drag_lighttheme.xml2
-rw-r--r--core/src/main/res/drawable/ic_fab_edit.xml5
-rw-r--r--core/src/main/res/drawable/ic_fab_edit_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_fab_edit_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_fast_forward.xml (renamed from core/src/main/res/drawable/ic_av_fast_rewind_black_48dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_fast_forward_video_white.xml (renamed from core/src/main/res/drawable/ic_av_fast_forward_white_80dp.xml)0
-rw-r--r--core/src/main/res/drawable/ic_fast_rewind.xml (renamed from core/src/main/res/drawable/ic_av_fast_forward_black_48dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_fast_rewind_video_white.xml (renamed from core/src/main/res/drawable/ic_av_fast_rewind_white_80dp.xml)0
-rw-r--r--core/src/main/res/drawable/ic_feed.xml6
-rw-r--r--core/src/main/res/drawable/ic_feed_black.xml4
-rw-r--r--core/src/main/res/drawable/ic_feed_white.xml6
-rw-r--r--core/src/main/res/drawable/ic_filter.xml7
-rw-r--r--core/src/main/res/drawable/ic_filter_black_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_filter_white_24dp.xml7
-rw-r--r--core/src/main/res/drawable/ic_folder.xml5
-rw-r--r--core/src/main/res/drawable/ic_folder_black.xml2
-rw-r--r--core/src/main/res/drawable/ic_folder_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_history.xml5
-rw-r--r--core/src/main/res/drawable/ic_history_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_history_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_info.xml5
-rw-r--r--core/src/main/res/drawable/ic_info_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_key.xml (renamed from core/src/main/res/drawable/ic_key_black.xml)2
-rw-r--r--core/src/main/res/drawable/ic_key_white.xml6
-rw-r--r--core/src/main/res/drawable/ic_load_more.xml (renamed from core/src/main/res/drawable/ic_check_black.xml)2
-rw-r--r--core/src/main/res/drawable/ic_load_more_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_load_more_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_lock_closed.xml5
-rw-r--r--core/src/main/res/drawable/ic_lock_closed_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_lock_closed_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_lock_open.xml5
-rw-r--r--core/src/main/res/drawable/ic_lock_open_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_lock_open_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_network.xml (renamed from core/src/main/res/drawable/ic_bookmark_white_24dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_network_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_network_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_cast_off.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_forward.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_fast_rewind.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_pause.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_play.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_skip.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_sync.xml5
-rw-r--r--core/src/main/res/drawable/ic_notification_sync_error.xml5
-rw-r--r--core/src/main/res/drawable/ic_notifications.xml5
-rw-r--r--core/src/main/res/drawable/ic_notifications_black.xml9
-rw-r--r--core/src/main/res/drawable/ic_notifications_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_pause.xml (renamed from core/src/main/res/drawable/ic_av_fast_rewind_white_48dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_pause_video_white.xml (renamed from core/src/main/res/drawable/ic_av_pause_white_80dp.xml)0
-rw-r--r--core/src/main/res/drawable/ic_play_24dp.xml (renamed from core/src/main/res/drawable/ic_av_play_white_24dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_play_48dp.xml (renamed from core/src/main/res/drawable/ic_av_play_black_48dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_play_video_white.xml (renamed from core/src/main/res/drawable/ic_av_play_white_80dp.xml)0
-rw-r--r--core/src/main/res/drawable/ic_playback_speed.xml8
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_black.xml8
-rw-r--r--core/src/main/res/drawable/ic_playback_speed_white.xml8
-rw-r--r--core/src/main/res/drawable/ic_playlist.xml5
-rw-r--r--core/src/main/res/drawable/ic_playlist_black.xml2
-rw-r--r--core/src/main/res/drawable/ic_playlist_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_questionmark.xml5
-rw-r--r--core/src/main/res/drawable/ic_questionmark_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_questionmark_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_refresh.xml5
-rw-r--r--core/src/main/res/drawable/ic_refresh_black.xml2
-rw-r--r--core/src/main/res/drawable/ic_refresh_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_remove.xml (renamed from core/src/main/res/drawable/ic_remove_black.xml)2
-rw-r--r--core/src/main/res/drawable/ic_remove_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_replay.xml (renamed from core/src/main/res/drawable/ic_av_replay_black_48dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_search.xml5
-rw-r--r--core/src/main/res/drawable/ic_search_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_search_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_select_all.xml (renamed from core/src/main/res/drawable/ic_select_all_black.xml)2
-rw-r--r--core/src/main/res/drawable/ic_select_all_white.xml9
-rw-r--r--core/src/main/res/drawable/ic_select_none.xml (renamed from core/src/main/res/drawable/ic_select_none_white.xml)4
-rw-r--r--core/src/main/res/drawable/ic_select_none_black.xml11
-rw-r--r--core/src/main/res/drawable/ic_settings.xml5
-rw-r--r--core/src/main/res/drawable/ic_settings_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_share.xml (renamed from core/src/main/res/drawable/ic_share_white.xml)2
-rw-r--r--core/src/main/res/drawable/ic_share_black.xml7
-rw-r--r--core/src/main/res/drawable/ic_skip_24dp.xml (renamed from core/src/main/res/drawable/ic_av_play_black_24dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_skip_48dp.xml (renamed from core/src/main/res/drawable/ic_av_pause_black_48dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_sleep.xml5
-rw-r--r--core/src/main/res/drawable/ic_sleep_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_sleep_off.xml5
-rw-r--r--core/src/main/res/drawable/ic_sleep_off_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_sleep_off_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_sleep_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_sliders.xml5
-rw-r--r--core/src/main/res/drawable/ic_sliders_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_sliders_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_sort.xml (renamed from core/src/main/res/drawable/ic_bookmark_black_24dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_sort_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_sort_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_star.xml5
-rw-r--r--core/src/main/res/drawable/ic_star_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_star_border.xml5
-rw-r--r--core/src/main/res/drawable/ic_star_border_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_star_border_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_star_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_statistics.xml5
-rw-r--r--core/src/main/res/drawable/ic_statistics_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_statistics_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_storage.xml5
-rw-r--r--core/src/main/res/drawable/ic_storage_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_storage_white.xml5
-rw-r--r--core/src/main/res/drawable/ic_stream.xml (renamed from core/src/main/res/drawable/ic_stream_black.xml)4
-rw-r--r--core/src/main/res/drawable/ic_stream_white.xml14
-rw-r--r--core/src/main/res/drawable/ic_videocam.xml (renamed from core/src/main/res/drawable/ic_videocam_black_24dp.xml)2
-rw-r--r--core/src/main/res/drawable/ic_videocam_white_24dp.xml8
-rw-r--r--core/src/main/res/drawable/ic_volume_adaption.xml (renamed from core/src/main/res/drawable/ic_volume_adaption_white.xml)2
-rw-r--r--core/src/main/res/drawable/ic_volume_adaption_black.xml9
-rw-r--r--core/src/main/res/drawable/ic_web.xml5
-rw-r--r--core/src/main/res/drawable/ic_web_black.xml5
-rw-r--r--core/src/main/res/drawable/ic_web_white.xml5
-rw-r--r--core/src/main/res/layout/more_content_list_footer.xml2
-rw-r--r--core/src/main/res/layout/player_widget.xml13
-rw-r--r--core/src/main/res/values-large/dimens.xml5
-rw-r--r--core/src/main/res/values/arrays.xml10
-rw-r--r--core/src/main/res/values/attrs.xml52
-rw-r--r--core/src/main/res/values/dimens.xml7
-rw-r--r--core/src/main/res/values/strings.xml109
-rw-r--r--core/src/main/res/values/styles.xml106
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java18
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java682
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java59
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/cast/MediaInfoCreator.java5
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java13
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java2
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java36
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java21
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaMother.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaTest.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java3
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java1
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java33
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java12
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbCleanupTests.java6
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbNullCleanupAlgorithmTest.java6
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbQueueCleanupAlgorithmTest.java4
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java14
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java8
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbTestUtils.java8
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java34
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java4
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java15
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java4
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java11
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/syndication/handler/FeedParserTestHelper.java2
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java29
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java7
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java63
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java25
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/playback/ExternalMediaTest.java56
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java85
-rw-r--r--core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java2
-rw-r--r--core/src/test/resources/feed-rss-testMultipleFundingTags.xml9
-rw-r--r--core/src/test/resources/media-parser/hindenburg-journalist-pro.m4abin0 -> 23315 bytes
-rw-r--r--core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3bin0 -> 206098 bytes
341 files changed, 1511 insertions, 7880 deletions
diff --git a/core/build.gradle b/core/build.gradle
index e68fa9f97..e78b70881 100644
--- a/core/build.gradle
+++ b/core/build.gradle
@@ -1,78 +1,32 @@
apply plugin: "com.android.library"
+apply from: "../common.gradle"
+apply from: "../playFlavor.gradle"
android {
- compileSdkVersion rootProject.ext.compileSdkVersion
+ lintOptions {
+ disable "InvalidPeriodicWorkRequestInterval", "ObsoleteLintCustomCheck", "DefaultLocale", "UnusedAttribute",
+ "ParcelClassLoader", "Typos", "ExtraTranslation", "ImpliedQuantity", "CheckResult",
+ "PluralsCandidate", "UnusedQuantity", "StringFormatCount", "TrustAllX509TrustManager",
+ "StaticFieldLeak", "TypographyEllipsis", "IconDensities", "IconDuplicates"
+ }
defaultConfig {
- minSdkVersion rootProject.ext.minSdkVersion
- targetSdkVersion rootProject.ext.targetSdkVersion
-
- multiDexEnabled false
-
- testApplicationId "de.danoeh.antennapod.core.tests"
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'de.danoeh.antennapod.core.ApCoreEventBusIndex']
}
}
}
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
- }
- debug {
- // debug build has method count over 64k single-dex threshold.
- // For building debug build to use on Android < 21 (pre-Android 5) devices,
- // you need to manually change class
- // de.danoeh.antennapod.PodcastApp to extend MultiDexApplication .
- // See Issue #2813
- multiDexEnabled true
- }
- }
-
- packagingOptions {
- exclude "META-INF/LICENSE.txt"
- exclude "META-INF/NOTICE.txt"
- }
-
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
-
- flavorDimensions "market"
- productFlavors {
- free {
- dimension "market"
- }
- play {
- dimension "market"
- }
- }
-
- testOptions {
- unitTests {
- includeAndroidResources = true
- }
- }
-
- lintOptions {
- disable "InvalidPeriodicWorkRequestInterval", "ObsoleteLintCustomCheck", "DefaultLocale", "UnusedAttribute",
- "GradleDependency", "ParcelClassLoader", "Typos", "ExtraTranslation", "ImpliedQuantity",
- "PluralsCandidate", "UnusedQuantity", "StringFormatCount", "TrustAllX509TrustManager",
- "StaticFieldLeak", "TypographyEllipsis", "IconDensities", "IconDuplicates", "CheckResult"
-
- warningsAsErrors true
- abortOnError true
- }
}
dependencies {
+ implementation project(':model')
+ implementation project(':net:ssl')
+ implementation project(':net:sync:gpoddernet')
+ implementation project(':net:sync:model')
implementation project(':ui:app-start-intent')
implementation project(':ui:common')
+ implementation project(':ui:png-icons')
annotationProcessor "androidx.annotation:annotation:$annotationVersion"
implementation "androidx.appcompat:appcompat:$appcompatVersion"
@@ -102,13 +56,11 @@ dependencies {
// Non-free dependencies:
playApi 'com.google.android.libraries.cast.companionlibrary:ccl:2.9.1'
playApi 'androidx.mediarouter:mediarouter:1.0.0'
- playApi 'com.google.android.gms:play-services-cast:8.4.0'
+ playApi "com.google.android.gms:play-services-cast:$playServicesVersion"
playApi "com.google.android.support:wearable:$wearableSupportVersion"
compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion"
- // bundle conscrypt with free builds
- freeImplementation "org.conscrypt:conscrypt-android:$conscryptVersion"
-
+ testImplementation 'androidx.test:core:1.2.0'
testImplementation "org.awaitility:awaitility:$awaitilityVersion"
testImplementation 'junit:junit:4.13'
testImplementation 'org.mockito:mockito-inline:3.5.13'
@@ -119,12 +71,3 @@ dependencies {
androidTestImplementation "androidx.test:runner:$runnerVersion"
androidTestImplementation "androidx.test:rules:$rulesVersion"
}
-
-tasks.withType(Test) {
- testLogging {
- exceptionFormat "full"
- events "skipped", "passed", "failed"
- showStandardStreams true
- displayGranularity 2
- }
-}
diff --git a/core/lint.xml b/core/lint.xml
new file mode 100644
index 000000000..fd9f5eb99
--- /dev/null
+++ b/core/lint.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<lint>
+ <issue id="VectorPath">
+ <ignore path="res/drawable/ic_settings.xml" />
+ <ignore path="res/drawable/ic_settings_white.xml" />
+ </issue>
+
+ <issue id="MissingDefaultResource">
+ <ignore path="**/values-**/strings.xml" />
+ </issue>
+</lint>
diff --git a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
index 0193bf8ce..755bec14e 100644
--- a/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/free/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -1,8 +1,8 @@
package de.danoeh.antennapod.core;
import android.content.Context;
-import java.security.Security;
-import org.conscrypt.Conscrypt;
+
+import de.danoeh.antennapod.net.ssl.SslProviderInstaller;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@@ -42,16 +42,11 @@ public class ClientConfig {
UserPreferences.init(context);
UsageStatistics.init(context);
PlaybackPreferences.init(context);
- installSslProvider(context);
+ SslProviderInstaller.install(context);
NetworkUtils.init(context);
AntennapodHttpClient.setCacheDirectory(new File(context.getCacheDir(), "okhttp"));
SleepTimerPreferences.init(context);
NotificationUtils.createChannels(context);
initialized = true;
}
-
- private static void installSslProvider(Context context) {
- // Insert bundled conscrypt as highest security provider (overrides OS version).
- Security.insertProviderAt(Conscrypt.newProvider(), 1);
- }
}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java b/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
deleted file mode 100644
index 9833e2cc9..000000000
--- a/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-/**
- * Implements methods for FeedMedia that are flavor dependent.
- */
-class FeedMediaFlavorHelper {
- private FeedMediaFlavorHelper(){}
- static boolean instanceOfRemoteMedia(Object o) {
- return false;
- }
-}
diff --git a/core/src/main/assets/shownotes-style.css b/core/src/main/assets/shownotes-style.css
index c21a77eae..01d13bc16 100644
--- a/core/src/main/assets/shownotes-style.css
+++ b/core/src/main/assets/shownotes-style.css
@@ -1,11 +1,5 @@
-@font-face {
- font-family: 'Roboto-Light';
- src: url('file:///android_asset/Roboto-Light.ttf');
-}
* {
color: %s;
- font-family: roboto-Light;
- font-size: 13pt;
overflow-wrap: break-word;
}
a {
@@ -33,5 +27,4 @@ p#apNoShownotes {
transform: translate(-50%%, -50%%);
text-align: center;
-webkit-text-size-adjust: none;
- font-size: 80%%;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
index c05e2e9f1..fca83351c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/backup/OpmlBackupAgent.java
@@ -31,7 +31,7 @@ import java.util.Arrays;
import de.danoeh.antennapod.core.export.opml.OpmlElement;
import de.danoeh.antennapod.core.export.opml.OpmlReader;
import de.danoeh.antennapod.core.export.opml.OpmlWriter;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DownloadRequestException;
import de.danoeh.antennapod.core.storage.DownloadRequester;
@@ -94,7 +94,7 @@ public class OpmlBackupAgent extends BackupAgentHelper {
if (len != -1) {
byte[] oldChecksum = new byte[len];
- inState.read(oldChecksum);
+ IOUtils.read(inState, oldChecksum, 0, len);
Log.d(TAG, "Old checksum: " + new BigInteger(1, oldChecksum).toString(16));
if (Arrays.equals(oldChecksum, newChecksum)) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
index 10992408d..1cab7e0f0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/DownloaderUpdate.java
@@ -5,8 +5,8 @@ import androidx.annotation.NonNull;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.Downloader;
import de.danoeh.antennapod.core.util.LongList;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
index d3be8fac0..cbfcc37e6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FavoritesEvent.java
@@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
public class FavoritesEvent {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
index 02559b2f5..99cb01714 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedItemEvent.java
@@ -9,7 +9,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
public class FeedItemEvent {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java
index ca8db3cc9..4ed8e33ec 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/FeedListUpdateEvent.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.core.event;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import java.util.ArrayList;
import java.util.List;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java
index f9a8c63b6..c866939bd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/QueueEvent.java
@@ -7,7 +7,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import java.util.List;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
public class QueueEvent {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java
index b3241a8b6..2230ee84f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/ServiceEvent.java
@@ -2,7 +2,8 @@ package de.danoeh.antennapod.core.event;
public class ServiceEvent {
public enum Action {
- SERVICE_STARTED
+ SERVICE_STARTED,
+ SERVICE_SHUT_DOWN
}
public final Action action;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java b/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
index 3ed84f6a8..3905ce68f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/event/settings/VolumeAdaptionChangedEvent.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.core.event.settings;
-import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
public class VolumeAdaptionChangedEvent {
private final VolumeAdaptionSetting volumeAdaptionSetting;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
index e0f0d4626..e60609569 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/ExportWriter.java
@@ -19,7 +19,7 @@ import java.io.IOException;
import java.io.Writer;
import java.util.List;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
public interface ExportWriter {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java
index 60c38a391..9bc273c9e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/favorites/FavoritesWriter.java
@@ -15,8 +15,8 @@ import java.util.Map;
import java.util.TreeMap;
import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.storage.DBReader;
/** Writes saved favorites to file. */
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java
index 3f34343ee..8a660600f 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/html/HtmlWriter.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.export.html;
import android.content.Context;
import android.util.Log;
import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import java.io.IOException;
import java.io.InputStream;
import java.io.Writer;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
index c93d4e8e0..e2205471c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/export/opml/OpmlWriter.java
@@ -12,7 +12,7 @@ import java.util.Date;
import java.util.List;
import de.danoeh.antennapod.core.export.ExportWriter;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.util.DateUtils;
/** Writes OPML documents. */
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/ChapterMerger.java b/core/src/main/java/de/danoeh/antennapod/core/feed/ChapterMerger.java
index f33fa7511..80e669939 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/ChapterMerger.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/ChapterMerger.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.feed;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
+import de.danoeh.antennapod.model.feed.Chapter;
import java.util.List;
@@ -34,19 +35,19 @@ public class ChapterMerger {
Chapter chapterTarget = chapters2.get(i);
Chapter chapterOther = chapters1.get(i);
- if (Math.abs(chapterTarget.start - chapterOther.start) > 1000) {
+ if (Math.abs(chapterTarget.getStart() - chapterOther.getStart()) > 1000) {
Log.e(TAG, "Chapter lists are too different. Cancelling merge.");
return chapters1;
}
- if (TextUtils.isEmpty(chapterTarget.imageUrl)) {
- chapterTarget.imageUrl = chapterOther.imageUrl;
+ if (TextUtils.isEmpty(chapterTarget.getImageUrl())) {
+ chapterTarget.setImageUrl(chapterOther.getImageUrl());
}
- if (TextUtils.isEmpty(chapterTarget.link)) {
- chapterTarget.link = chapterOther.link;
+ if (TextUtils.isEmpty(chapterTarget.getLink())) {
+ chapterTarget.setLink(chapterOther.getLink());
}
- if (TextUtils.isEmpty(chapterTarget.title)) {
- chapterTarget.title = chapterOther.title;
+ if (TextUtils.isEmpty(chapterTarget.getTitle())) {
+ chapterTarget.setTitle(chapterOther.getTitle());
}
}
return chapters2;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java b/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
deleted file mode 100644
index dd8a466eb..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Feed.java
+++ /dev/null
@@ -1,500 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.text.TextUtils;
-
-import androidx.annotation.Nullable;
-
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-
-import de.danoeh.antennapod.core.util.SortOrder;
-
-/**
- * Data Object for a whole feed.
- *
- * @author daniel
- */
-public class Feed extends FeedFile {
-
- public static final int FEEDFILETYPE_FEED = 0;
- public static final String TYPE_RSS2 = "rss";
- public static final String TYPE_ATOM1 = "atom";
- public static final String PREFIX_LOCAL_FOLDER = "antennapod_local:";
-
- /**
- * title as defined by the feed.
- */
- private String feedTitle;
-
- /**
- * custom title set by the user.
- */
- private String customTitle;
-
- /**
- * Contains 'id'-element in Atom feed.
- */
- private String feedIdentifier;
- /**
- * Link to the website.
- */
- private String link;
- private String description;
- private String language;
- /**
- * Name of the author.
- */
- private String author;
- private String imageUrl;
- private List<FeedItem> items;
-
- /**
- * String that identifies the last update (adopted from Last-Modified or ETag header).
- */
- private String lastUpdate;
-
- private String paymentLink;
- /**
- * Feed type, for example RSS 2 or Atom.
- */
- private String type;
-
- /**
- * Feed preferences.
- */
- private FeedPreferences preferences;
-
- /**
- * The page number that this feed is on. Only feeds with page number "0" should be stored in the
- * database, feed objects with a higher page number only exist temporarily and should be merged
- * into feeds with page number "0".
- * <p/>
- * This attribute's value is not saved in the database
- */
- private int pageNr;
-
- /**
- * True if this is a "paged feed", i.e. there exist other feed files that belong to the same
- * logical feed.
- */
- private boolean paged;
-
- /**
- * Link to the next page of this feed. If this feed object represents a logical feed (i.e. a feed
- * that is saved in the database) this might be null while still being a paged feed.
- */
- private String nextPageLink;
-
- private boolean lastUpdateFailed;
-
- /**
- * Contains property strings. If such a property applies to a feed item, it is not shown in the feed list
- */
- private FeedItemFilter itemfilter;
-
- /**
- * User-preferred sortOrder for display.
- * Only those of scope {@link SortOrder.Scope#INTRA_FEED} is allowed.
- */
- @Nullable
- private SortOrder sortOrder;
-
- /**
- * This constructor is used for restoring a feed from the database.
- */
- public Feed(long id, String lastUpdate, String title, String customTitle, String link, String description, String paymentLink,
- String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl,
- String downloadUrl, boolean downloaded, boolean paged, String nextPageLink,
- String filter, @Nullable SortOrder sortOrder, boolean lastUpdateFailed) {
- super(fileUrl, downloadUrl, downloaded);
- this.id = id;
- this.feedTitle = title;
- this.customTitle = customTitle;
- this.lastUpdate = lastUpdate;
- this.link = link;
- this.description = description;
- this.paymentLink = paymentLink;
- this.author = author;
- this.language = language;
- this.type = type;
- this.feedIdentifier = feedIdentifier;
- this.imageUrl = imageUrl;
- this.paged = paged;
- this.nextPageLink = nextPageLink;
- this.items = new ArrayList<>();
- if (filter != null) {
- this.itemfilter = new FeedItemFilter(filter);
- } else {
- this.itemfilter = new FeedItemFilter(new String[0]);
- }
- setSortOrder(sortOrder);
- this.lastUpdateFailed = lastUpdateFailed;
- }
-
- /**
- * This constructor is used for test purposes.
- */
- public Feed(long id, String lastUpdate, String title, String link, String description, String paymentLink,
- String author, String language, String type, String feedIdentifier, String imageUrl, String fileUrl,
- String downloadUrl, boolean downloaded) {
- this(id, lastUpdate, title, null, link, description, paymentLink, author, language, type, feedIdentifier, imageUrl,
- fileUrl, downloadUrl, downloaded, false, null, null, null, false);
- }
-
- /**
- * This constructor can be used when parsing feed data. Only the 'lastUpdate' and 'items' field are initialized.
- */
- public Feed() {
- super();
- }
-
- /**
- * This constructor is used for requesting a feed download (it must not be used for anything else!). It should NOT be
- * used if the title of the feed is already known.
- */
- public Feed(String url, String lastUpdate) {
- super(null, url, false);
- this.lastUpdate = lastUpdate;
- }
-
- /**
- * This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
- * used if the title of the feed is already known.
- */
- public Feed(String url, String lastUpdate, String title) {
- this(url, lastUpdate);
- this.feedTitle = title;
- }
-
- /**
- * This constructor is used for requesting a feed download (it must not be used for anything else!). It should be
- * used if the title of the feed is already known.
- */
- public Feed(String url, String lastUpdate, String title, String username, String password) {
- this(url, lastUpdate, title);
- preferences = new FeedPreferences(0, true, FeedPreferences.AutoDeleteAction.GLOBAL, VolumeAdaptionSetting.OFF, username, password);
- }
-
- /**
- * Returns the item at the specified index.
- *
- */
- public FeedItem getItemAtIndex(int position) {
- return items.get(position);
- }
-
- /**
- * Returns the value that uniquely identifies this Feed. If the
- * feedIdentifier attribute is not null, it will be returned. Else it will
- * try to return the title. If the title is not given, it will use the link
- * of the feed.
- */
- public String getIdentifyingValue() {
- if (feedIdentifier != null && !feedIdentifier.isEmpty()) {
- return feedIdentifier;
- } else if (download_url != null && !download_url.isEmpty()) {
- return download_url;
- } else if (feedTitle != null && !feedTitle.isEmpty()) {
- return feedTitle;
- } else {
- return link;
- }
- }
-
- @Override
- public String getHumanReadableIdentifier() {
- if (!TextUtils.isEmpty(customTitle)) {
- return customTitle;
- } else if (!TextUtils.isEmpty(feedTitle)) {
- return feedTitle;
- } else {
- return download_url;
- }
- }
-
- public void updateFromOther(Feed other) {
- // don't update feed's download_url, we do that manually if redirected
- // see AntennapodHttpClient
- if (other.imageUrl != null) {
- this.imageUrl = other.imageUrl;
- }
- if (other.feedTitle != null) {
- feedTitle = other.feedTitle;
- }
- if (other.feedIdentifier != null) {
- feedIdentifier = other.feedIdentifier;
- }
- if (other.link != null) {
- link = other.link;
- }
- if (other.description != null) {
- description = other.description;
- }
- if (other.language != null) {
- language = other.language;
- }
- if (other.author != null) {
- author = other.author;
- }
- if (other.paymentLink != null) {
- paymentLink = other.paymentLink;
- }
- // this feed's nextPage might already point to a higher page, so we only update the nextPage value
- // if this feed is not paged and the other feed is.
- if (!this.paged && other.paged) {
- this.paged = other.paged;
- this.nextPageLink = other.nextPageLink;
- }
- }
-
- public boolean compareWithOther(Feed other) {
- if (super.compareWithOther(other)) {
- return true;
- }
- if (other.imageUrl != null) {
- if (imageUrl == null || !TextUtils.equals(imageUrl, other.imageUrl)) {
- return true;
- }
- }
- if (!TextUtils.equals(feedTitle, other.feedTitle)) {
- return true;
- }
- if (other.feedIdentifier != null) {
- if (feedIdentifier == null || !feedIdentifier.equals(other.feedIdentifier)) {
- return true;
- }
- }
- if (other.link != null) {
- if (link == null || !link.equals(other.link)) {
- return true;
- }
- }
- if (other.description != null) {
- if (description == null || !description.equals(other.description)) {
- return true;
- }
- }
- if (other.language != null) {
- if (language == null || !language.equals(other.language)) {
- return true;
- }
- }
- if (other.author != null) {
- if (author == null || !author.equals(other.author)) {
- return true;
- }
- }
- if (other.paymentLink != null) {
- if (paymentLink == null || !paymentLink.equals(other.paymentLink)) {
- return true;
- }
- }
- if (other.isPaged() && !this.isPaged()) {
- return true;
- }
- if (!TextUtils.equals(other.getNextPageLink(), this.getNextPageLink())) {
- return true;
- }
- return false;
- }
-
- public FeedItem getMostRecentItem() {
- // we could sort, but we don't need to, a simple search is fine...
- Date mostRecentDate = new Date(0);
- FeedItem mostRecentItem = null;
- for (FeedItem item : items) {
- if (item.getPubDate() != null && item.getPubDate().after(mostRecentDate)) {
- mostRecentDate = item.getPubDate();
- mostRecentItem = item;
- }
- }
- return mostRecentItem;
- }
-
- @Override
- public int getTypeAsInt() {
- return FEEDFILETYPE_FEED;
- }
-
- public String getTitle() {
- return !TextUtils.isEmpty(customTitle) ? customTitle : feedTitle;
- }
-
- public void setTitle(String title) {
- this.feedTitle = title;
- }
-
- public String getFeedTitle() {
- return this.feedTitle;
- }
-
- @Nullable
- public String getCustomTitle() {
- return this.customTitle;
- }
-
- public void setCustomTitle(String customTitle) {
- if (customTitle == null || customTitle.equals(feedTitle)) {
- this.customTitle = null;
- } else {
- this.customTitle = customTitle;
- }
- }
-
- public String getLink() {
- return link;
- }
-
- public void setLink(String link) {
- this.link = link;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public String getImageUrl() {
- return imageUrl;
- }
-
- public void setImageUrl(String imageUrl) {
- this.imageUrl = imageUrl;
- }
-
- public List<FeedItem> getItems() {
- return items;
- }
-
- public void setItems(List<FeedItem> list) {
- this.items = list;
- }
-
- public String getLastUpdate() {
- return lastUpdate;
- }
-
- public void setLastUpdate(String lastModified) {
- this.lastUpdate = lastModified;
- }
-
- public String getFeedIdentifier() {
- return feedIdentifier;
- }
-
- public void setFeedIdentifier(String feedIdentifier) {
- this.feedIdentifier = feedIdentifier;
- }
-
- public String getPaymentLink() {
- return paymentLink;
- }
-
- public void setPaymentLink(String paymentLink) {
- this.paymentLink = paymentLink;
- }
-
- public String getLanguage() {
- return language;
- }
-
- public void setLanguage(String language) {
- this.language = language;
- }
-
- public String getAuthor() {
- return author;
- }
-
- public void setAuthor(String author) {
- this.author = author;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-
- public void setPreferences(FeedPreferences preferences) {
- this.preferences = preferences;
- }
-
- public FeedPreferences getPreferences() {
- return preferences;
- }
-
- @Override
- public void setId(long id) {
- super.setId(id);
- if (preferences != null) {
- preferences.setFeedID(id);
- }
- }
-
- public int getPageNr() {
- return pageNr;
- }
-
- public void setPageNr(int pageNr) {
- this.pageNr = pageNr;
- }
-
- public boolean isPaged() {
- return paged;
- }
-
- public void setPaged(boolean paged) {
- this.paged = paged;
- }
-
- public String getNextPageLink() {
- return nextPageLink;
- }
-
- public void setNextPageLink(String nextPageLink) {
- this.nextPageLink = nextPageLink;
- }
-
- @Nullable
- public FeedItemFilter getItemFilter() {
- return itemfilter;
- }
-
- public void setItemFilter(String[] properties) {
- if (properties != null) {
- this.itemfilter = new FeedItemFilter(properties);
- }
- }
-
- @Nullable
- public SortOrder getSortOrder() {
- return sortOrder;
- }
-
- public void setSortOrder(@Nullable SortOrder sortOrder) {
- if (sortOrder != null && sortOrder.scope != SortOrder.Scope.INTRA_FEED) {
- throw new IllegalArgumentException("The specified sortOrder " + sortOrder
- + " is invalid. Only those with INTRA_FEED scope are allowed.");
- }
- this.sortOrder = sortOrder;
- }
-
- public boolean hasLastUpdateFailed() {
- return this.lastUpdateFailed;
- }
-
- public void setLastUpdateFailed(boolean lastUpdateFailed) {
- this.lastUpdateFailed = lastUpdateFailed;
- }
-
- public boolean isLocalFeed() {
- return download_url.startsWith(PREFIX_LOCAL_FOLDER);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java
deleted file mode 100644
index 3edecd35c..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedComponent.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-/**
- * Represents every possible component of a feed
- *
- * @author daniel
- */
-public abstract class FeedComponent {
-
- long id;
-
- FeedComponent() {
- super();
- }
-
- public long getId() {
- return id;
- }
-
- public void setId(long id) {
- this.id = id;
- }
-
- /**
- * Update this FeedComponent's attributes with the attributes from another
- * FeedComponent. This method should only update attributes which where read from
- * the feed.
- */
- void updateFromOther(FeedComponent other) {
- }
-
- /**
- * Compare's this FeedComponent's attribute values with another FeedComponent's
- * attribute values. This method will only compare attributes which were
- * read from the feed.
- *
- * @return true if attribute values are different, false otherwise
- */
- boolean compareWithOther(FeedComponent other) {
- return false;
- }
-
-
- /**
- * Should return a non-null, human-readable String so that the item can be
- * identified by the user. Can be title, download-url, etc.
- */
- public abstract String getHumanReadableIdentifier();
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof FeedComponent)) return false;
-
- FeedComponent that = (FeedComponent) o;
-
- return id == that.id;
-
- }
-
- @Override
- public int hashCode() {
- return (int) (id ^ (id >>> 32));
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java
deleted file mode 100644
index cc4dd230f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFile.java
+++ /dev/null
@@ -1,107 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.text.TextUtils;
-
-import java.io.File;
-
-/**
- * Represents a component of a Feed that has to be downloaded
- */
-public abstract class FeedFile extends FeedComponent {
-
- String file_url;
- protected String download_url;
- boolean downloaded;
-
- /**
- * Creates a new FeedFile object.
- *
- * @param file_url The location of the FeedFile. If this is null, the downloaded-attribute
- * will automatically be set to false.
- * @param download_url The location where the FeedFile can be downloaded.
- * @param downloaded true if the FeedFile has been downloaded, false otherwise. This parameter
- * will automatically be interpreted as false if the file_url is null.
- */
- public FeedFile(String file_url, String download_url, boolean downloaded) {
- super();
- this.file_url = file_url;
- this.download_url = download_url;
- this.downloaded = (file_url != null) && downloaded;
- }
-
- public FeedFile() {
- this(null, null, false);
- }
-
- public abstract int getTypeAsInt();
-
- /**
- * Update this FeedFile's attributes with the attributes from another
- * FeedFile. This method should only update attributes which where read from
- * the feed.
- */
- void updateFromOther(FeedFile other) {
- super.updateFromOther(other);
- this.download_url = other.download_url;
- }
-
- /**
- * Compare's this FeedFile's attribute values with another FeedFile's
- * attribute values. This method will only compare attributes which were
- * read from the feed.
- *
- * @return true if attribute values are different, false otherwise
- */
- boolean compareWithOther(FeedFile other) {
- if (super.compareWithOther(other)) {
- return true;
- }
- if (!TextUtils.equals(download_url, other.download_url)) {
- return true;
- }
- return false;
- }
-
- /**
- * Returns true if the file exists at file_url.
- */
- public boolean fileExists() {
- if (file_url == null) {
- return false;
- } else {
- File f = new File(file_url);
- return f.exists();
- }
- }
-
- public String getFile_url() {
- return file_url;
- }
-
- /**
- * Changes the file_url of this FeedFile. Setting this value to
- * null will also set the downloaded-attribute to false.
- */
- public void setFile_url(String file_url) {
- this.file_url = file_url;
- if (file_url == null) {
- downloaded = false;
- }
- }
-
- public String getDownload_url() {
- return download_url;
- }
-
- public void setDownload_url(String download_url) {
- this.download_url = download_url;
- }
-
- public boolean isDownloaded() {
- return downloaded;
- }
-
- public void setDownloaded(boolean downloaded) {
- this.downloaded = downloaded;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java
deleted file mode 100644
index 28161ac9b..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedFilter.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class FeedFilter {
-
- private static final String TAG = "FeedFilter";
-
- private final String includeFilter;
- private final String excludeFilter;
-
- public FeedFilter() {
- this("", "");
- }
-
- public FeedFilter(String includeFilter, String excludeFilter) {
- // We're storing the strings and not the parsed terms because
- // 1. It's easier to show the user exactly what they typed in this way
- // (we don't have to recreate it)
- // 2. We don't know if we'll actually be asked to parse anything anyways.
- this.includeFilter = includeFilter;
- this.excludeFilter = excludeFilter;
- }
-
- /**
- * Parses the text in to a list of single words or quoted strings.
- * Example: "One "Two Three"" returns ["One", "Two Three"]
- * @param filter string to parse in to terms
- * @return list of terms
- */
- private List<String> parseTerms(String filter) {
- // from http://stackoverflow.com/questions/7804335/split-string-on-spaces-in-java-except-if-between-quotes-i-e-treat-hello-wor
- List<String> list = new ArrayList<>();
- Matcher m = Pattern.compile("([^\"]\\S*|\".+?\")\\s*").matcher(filter);
- while (m.find())
- list.add(m.group(1).replace("\"", ""));
- return list;
- }
-
- /**
- * @param item
- * @return true if the item should be downloaded
- */
- public boolean shouldAutoDownload(FeedItem item) {
-
- List<String> includeTerms = parseTerms(includeFilter);
- List<String> excludeTerms = parseTerms(excludeFilter);
-
- if (includeTerms.size() == 0 && excludeTerms.size() == 0) {
- // nothing has been specified, so include everything
- return true;
- }
-
- // check using lowercase so the users don't have to worry about case.
- String title = item.getTitle().toLowerCase();
-
- // if it's explicitly excluded, it shouldn't be autodownloaded
- // even if it has include terms
- for (String term : excludeTerms) {
- if (title.contains(term.trim().toLowerCase())) {
- return false;
- }
- }
-
- for (String term : includeTerms) {
- if (title.contains(term.trim().toLowerCase())) {
- return true;
- }
- }
-
- // now's the tricky bit
- // if they haven't set an include filter, but they have set an exclude filter
- // default to including, but if they've set both, then exclude
- if (!hasIncludeFilter() && hasExcludeFilter()) {
- return true;
- }
-
- return false;
- }
-
- public String getIncludeFilter() {
- return includeFilter;
- }
-
- public String getExcludeFilter() { return excludeFilter; }
-
- /**
- * @return true if only include is set
- */
- public boolean includeOnly() {
- return hasIncludeFilter() && !hasExcludeFilter();
- }
-
- /**
- * @return true if only exclude is set
- */
- public boolean excludeOnly() {
- return hasExcludeFilter() && !hasIncludeFilter();
- }
-
- public boolean hasIncludeFilter() {
- return includeFilter.length() > 0;
- }
-
- public boolean hasExcludeFilter() {
- return excludeFilter.length() > 0;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
deleted file mode 100644
index b2a89d452..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java
+++ /dev/null
@@ -1,490 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.database.Cursor;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import android.text.TextUtils;
-
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-
-import java.io.Serializable;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.Callable;
-import java.util.concurrent.TimeUnit;
-
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.core.util.ShownotesProvider;
-
-/**
- * Item (episode) within a feed.
- *
- * @author daniel
- */
-public class FeedItem extends FeedComponent implements ShownotesProvider, Serializable {
-
- /** tag that indicates this item is in the queue */
- public static final String TAG_QUEUE = "Queue";
- /** tag that indicates this item is in favorites */
- public static final String TAG_FAVORITE = "Favorite";
-
- /**
- * The id/guid that can be found in the rss/atom feed. Might not be set.
- */
- private String itemIdentifier;
- private String title;
- /**
- * The description of a feeditem.
- */
- private String description;
- /**
- * The content of the content-encoded tag of a feeditem.
- */
- private String contentEncoded;
-
- private String link;
- private Date pubDate;
- private FeedMedia media;
-
- private Feed feed;
- private long feedId;
-
- private int state;
- public static final int NEW = -1;
- public static final int UNPLAYED = 0;
- public static final int PLAYED = 1;
-
- private String paymentLink;
-
- /**
- * Is true if the database contains any chapters that belong to this item. This attribute is only
- * written once by DBReader on initialization.
- * The FeedItem might still have a non-null chapters value. In this case, the list of chapters
- * has not been saved in the database yet.
- * */
- private final boolean hasChapters;
-
- /**
- * The list of chapters of this item. This might be null even if there are chapters of this item
- * in the database. The 'hasChapters' attribute should be used to check if this item has any chapters.
- * */
- private List<Chapter> chapters;
- private String imageUrl;
-
- /*
- * 0: auto download disabled
- * 1: auto download enabled (default)
- * > 1: auto download enabled, (approx.) timestamp of the last failed attempt
- * where last digit denotes the number of failed attempts
- */
- private long autoDownload = 1;
-
- /**
- * Any tags assigned to this item
- */
- private final Set<String> tags = new HashSet<>();
-
- public FeedItem() {
- this.state = UNPLAYED;
- this.hasChapters = false;
- }
-
- /**
- * This constructor is used by DBReader.
- * */
- public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId,
- boolean hasChapters, String imageUrl, int state,
- String itemIdentifier, long autoDownload) {
- this.id = id;
- this.title = title;
- this.link = link;
- this.pubDate = pubDate;
- this.paymentLink = paymentLink;
- this.feedId = feedId;
- this.hasChapters = hasChapters;
- this.imageUrl = imageUrl;
- this.state = state;
- this.itemIdentifier = itemIdentifier;
- this.autoDownload = autoDownload;
- }
-
- /**
- * This constructor should be used for creating test objects.
- */
- public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed) {
- this.id = id;
- this.title = title;
- this.itemIdentifier = itemIdentifier;
- this.link = link;
- this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null;
- this.state = state;
- this.feed = feed;
- this.hasChapters = false;
- }
-
- /**
- * This constructor should be used for creating test objects involving chapter marks.
- */
- public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, int state, Feed feed, boolean hasChapters) {
- this.id = id;
- this.title = title;
- this.itemIdentifier = itemIdentifier;
- this.link = link;
- this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null;
- this.state = state;
- this.feed = feed;
- this.hasChapters = hasChapters;
- }
-
- public static FeedItem fromCursor(Cursor cursor) {
- int indexId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_ITEM_ID);
- int indexTitle = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_TITLE);
- int indexLink = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_LINK);
- int indexPubDate = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PUBDATE);
- int indexPaymentLink = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PAYMENT_LINK);
- int indexFeedId = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_FEED);
- int indexHasChapters = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_HAS_CHAPTERS);
- int indexRead = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_READ);
- int indexItemIdentifier = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_ITEM_IDENTIFIER);
- int indexAutoDownload = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_AUTO_DOWNLOAD);
- int indexImageUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_IMAGE_URL);
-
- long id = cursor.getInt(indexId);
- String title = cursor.getString(indexTitle);
- String link = cursor.getString(indexLink);
- Date pubDate = new Date(cursor.getLong(indexPubDate));
- String paymentLink = cursor.getString(indexPaymentLink);
- long feedId = cursor.getLong(indexFeedId);
- boolean hasChapters = cursor.getInt(indexHasChapters) > 0;
- int state = cursor.getInt(indexRead);
- String itemIdentifier = cursor.getString(indexItemIdentifier);
- long autoDownload = cursor.getLong(indexAutoDownload);
- String imageUrl = cursor.getString(indexImageUrl);
-
- return new FeedItem(id, title, link, pubDate, paymentLink, feedId,
- hasChapters, imageUrl, state, itemIdentifier, autoDownload);
- }
-
- public void updateFromOther(FeedItem other) {
- super.updateFromOther(other);
- if (other.imageUrl != null) {
- this.imageUrl = other.imageUrl;
- }
- if (other.title != null) {
- title = other.title;
- }
- if (other.getDescription() != null) {
- description = other.getDescription();
- }
- if (other.getContentEncoded() != null) {
- contentEncoded = other.contentEncoded;
- }
- if (other.link != null) {
- link = other.link;
- }
- if (other.pubDate != null && !other.pubDate.equals(pubDate)) {
- pubDate = other.pubDate;
- }
- if (other.media != null) {
- if (media == null) {
- setMedia(other.media);
- // reset to new if feed item did link to a file before
- setNew();
- } else if (media.compareWithOther(other.media)) {
- media.updateFromOther(other.media);
- }
- }
- if (other.paymentLink != null) {
- paymentLink = other.paymentLink;
- }
- if (other.chapters != null) {
- if (!hasChapters) {
- chapters = other.chapters;
- }
- }
- }
-
- /**
- * Returns the value that uniquely identifies this FeedItem. If the
- * itemIdentifier attribute is not null, it will be returned. Else it will
- * try to return the title. If the title is not given, it will use the link
- * of the entry.
- */
- public String getIdentifyingValue() {
- if (itemIdentifier != null && !itemIdentifier.isEmpty()) {
- return itemIdentifier;
- } else if (title != null && !title.isEmpty()) {
- return title;
- } else if (hasMedia() && media.getDownload_url() != null) {
- return media.getDownload_url();
- } else {
- return link;
- }
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getDescription() {
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public String getLink() {
- return link;
- }
-
- public void setLink(String link) {
- this.link = link;
- }
-
- public Date getPubDate() {
- if (pubDate != null) {
- return (Date) pubDate.clone();
- } else {
- return null;
- }
- }
-
- public void setPubDate(Date pubDate) {
- if (pubDate != null) {
- this.pubDate = (Date) pubDate.clone();
- } else {
- this.pubDate = null;
- }
- }
-
- @Nullable
- public FeedMedia getMedia() {
- return media;
- }
-
- /**
- * Sets the media object of this FeedItem. If the given
- * FeedMedia object is not null, it's 'item'-attribute value
- * will also be set to this item.
- */
- public void setMedia(FeedMedia media) {
- this.media = media;
- if (media != null && media.getItem() != this) {
- media.setItem(this);
- }
- }
-
- public Feed getFeed() {
- return feed;
- }
-
- public void setFeed(Feed feed) {
- this.feed = feed;
- }
-
- public boolean isNew() {
- return state == NEW;
- }
-
-
- public void setNew() {
- state = NEW;
- }
-
- public boolean isPlayed() {
- return state == PLAYED;
- }
-
- public void setPlayed(boolean played) {
- if(played) {
- state = PLAYED;
- } else {
- state = UNPLAYED;
- }
- }
-
- private boolean isInProgress() {
- return (media != null && media.isInProgress());
- }
-
- public String getContentEncoded() {
- return contentEncoded;
- }
-
- public void setContentEncoded(String contentEncoded) {
- this.contentEncoded = contentEncoded;
- }
-
- public String getPaymentLink() {
- return paymentLink;
- }
-
- public void setPaymentLink(String paymentLink) {
- this.paymentLink = paymentLink;
- }
-
- public List<Chapter> getChapters() {
- return chapters;
- }
-
- public void setChapters(List<Chapter> chapters) {
- this.chapters = chapters;
- }
-
- public String getItemIdentifier() {
- return itemIdentifier;
- }
-
- public void setItemIdentifier(String itemIdentifier) {
- this.itemIdentifier = itemIdentifier;
- }
-
- public boolean hasMedia() {
- return media != null;
- }
-
- private boolean isPlaying() {
- return media != null && media.isPlaying();
- }
-
- @Override
- public Callable<String> loadShownotes() {
- return () -> {
- if (contentEncoded == null || description == null) {
- DBReader.loadDescriptionOfFeedItem(FeedItem.this);
- }
- if (TextUtils.isEmpty(contentEncoded)) {
- return description;
- } else if (TextUtils.isEmpty(description)) {
- return contentEncoded;
- } else if (description.length() > 1.25 * contentEncoded.length()) {
- return description;
- } else {
- return contentEncoded;
- }
- };
- }
-
- public String getImageLocation() {
- if (imageUrl != null) {
- return imageUrl;
- } else if (media != null && media.hasEmbeddedPicture()) {
- return FeedMedia.FILENAME_PREFIX_EMBEDDED_COVER + media.getLocalMediaUrl();
- } else if (feed != null) {
- return feed.getImageUrl();
- } else {
- return null;
- }
- }
-
- public enum State {
- UNREAD, IN_PROGRESS, READ, PLAYING
- }
-
- public State getState() {
- if (hasMedia()) {
- if (isPlaying()) {
- return State.PLAYING;
- }
- if (isInProgress()) {
- return State.IN_PROGRESS;
- }
- }
- return (isPlayed() ? State.READ : State.UNREAD);
- }
-
- public long getFeedId() {
- return feedId;
- }
-
- public void setFeedId(long feedId) {
- this.feedId = feedId;
- }
-
- /**
- * Returns the image of this item, as specified in the feed.
- * To load the image that can be displayed to the user, use {@link #getImageLocation},
- * which also considers embedded pictures or the feed picture if no other picture is present.
- */
- public String getImageUrl() {
- return imageUrl;
- }
-
- public void setImageUrl(String imageUrl) {
- this.imageUrl = imageUrl;
- }
-
- @Override
- public String getHumanReadableIdentifier() {
- return title;
- }
-
- public boolean hasChapters() {
- return hasChapters;
- }
-
- public void setAutoDownload(boolean autoDownload) {
- this.autoDownload = autoDownload ? 1 : 0;
- }
-
- public boolean getAutoDownload() {
- return this.autoDownload > 0;
- }
-
- public int getFailedAutoDownloadAttempts() {
- if (autoDownload <= 1) {
- return 0;
- }
- int failedAttempts = (int)(autoDownload % 10);
- if (failedAttempts == 0) {
- failedAttempts = 10;
- }
- return failedAttempts;
- }
-
- public boolean isAutoDownloadable() {
- if (media == null || media.isPlaying() || media.isDownloaded() || autoDownload == 0) {
- return false;
- }
- if (autoDownload == 1) {
- return true;
- }
- int failedAttempts = getFailedAutoDownloadAttempts();
- double magicValue = 1.767; // 1.767^(10[=#maxNumAttempts]-1) = 168 hours / 7 days
- int millisecondsInHour = 3600000;
- long waitingTime = (long) (Math.pow(magicValue, failedAttempts - 1) * millisecondsInHour);
- long grace = TimeUnit.MINUTES.toMillis(5);
- return System.currentTimeMillis() > (autoDownload + waitingTime - grace);
- }
-
- /**
- * @return true if the item has this tag
- */
- public boolean isTagged(String tag) { return tags.contains(tag); }
-
- /**
- * @param tag adds this tag to the item. NOTE: does NOT persist to the database
- */
- public void addTag(String tag) { tags.add(tag); }
-
- /**
- * @param tag the to remove
- */
- public void removeTag(String tag) { tags.remove(tag); }
-
- @NonNull
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
deleted file mode 100644
index bd30a3953..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItemFilter.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.text.TextUtils;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.util.LongList;
-
-import static de.danoeh.antennapod.core.feed.FeedItem.TAG_FAVORITE;
-
-public class FeedItemFilter {
-
- private final String[] properties;
-
- public final boolean showPlayed;
- public final boolean showUnplayed;
- public final boolean showPaused;
- public final boolean showNotPaused;
- public final boolean showQueued;
- public final boolean showNotQueued;
- public final boolean showDownloaded;
- public final boolean showNotDownloaded;
- public final boolean showHasMedia;
- public final boolean showNoMedia;
- public final boolean showIsFavorite;
- public final boolean showNotFavorite;
-
- public static FeedItemFilter unfiltered() {
- return new FeedItemFilter("");
- }
-
- public FeedItemFilter(String properties) {
- this(TextUtils.split(properties, ","));
- }
-
- public FeedItemFilter(String[] properties) {
- this.properties = properties;
-
- // see R.arrays.feed_filter_values
- showUnplayed = hasProperty("unplayed");
- showPaused = hasProperty("paused");
- showNotPaused = hasProperty("not_paused");
- showPlayed = hasProperty("played");
- showQueued = hasProperty("queued");
- showNotQueued = hasProperty("not_queued");
- showDownloaded = hasProperty("downloaded");
- showNotDownloaded = hasProperty("not_downloaded");
- showHasMedia = hasProperty("has_media");
- showNoMedia = hasProperty("no_media");
- showIsFavorite = hasProperty("is_favorite");
- showNotFavorite = hasProperty("not_favorite");
- }
-
- private boolean hasProperty(String property) {
- return Arrays.asList(properties).contains(property);
- }
-
- /**
- * Run a list of feed items through the filter.
- */
- public List<FeedItem> filter(List<FeedItem> items) {
- if (properties.length == 0) {
- return items;
- }
-
- List<FeedItem> result = new ArrayList<>();
-
- // Check for filter combinations that will always return an empty list
- // (e.g. requiring played and unplayed at the same time)
- if (showPlayed && showUnplayed) return result;
- if (showQueued && showNotQueued) return result;
- if (showDownloaded && showNotDownloaded) return result;
-
- final LongList queuedIds = DBReader.getQueueIDList();
- for (FeedItem item : items) {
- // If the item does not meet a requirement, skip it.
-
- if (showPlayed && !item.isPlayed()) continue;
- if (showUnplayed && item.isPlayed()) continue;
-
- if (showPaused && item.getState() != FeedItem.State.IN_PROGRESS) continue;
- if (showNotPaused && item.getState() == FeedItem.State.IN_PROGRESS) continue;
-
- boolean queued = queuedIds.contains(item.getId());
- if (showQueued && !queued) continue;
- if (showNotQueued && queued) continue;
-
- boolean downloaded = item.getMedia() != null && item.getMedia().isDownloaded();
- if (showDownloaded && !downloaded) continue;
- if (showNotDownloaded && downloaded) continue;
-
- if (showHasMedia && !item.hasMedia()) continue;
- if (showNoMedia && item.hasMedia()) continue;
-
- if (showIsFavorite && !item.isTagged(TAG_FAVORITE)) continue;
- if (showNotFavorite && item.isTagged(TAG_FAVORITE)) continue;
-
- // If the item reaches here, it meets all criteria
- result.add(item);
- }
-
- return result;
- }
-
- public String[] getValues() {
- return properties.clone();
- }
-
- public boolean isShowDownloaded() {
- return showDownloaded;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
deleted file mode 100644
index 9049a3ba9..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
+++ /dev/null
@@ -1,640 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.database.Cursor;
-import android.media.MediaMetadataRetriever;
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-import androidx.annotation.Nullable;
-import android.support.v4.media.MediaBrowserCompat;
-import android.support.v4.media.MediaDescriptionCompat;
-
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.Callable;
-
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.service.playback.PlaybackService;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.core.util.ChapterUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.core.sync.model.EpisodeAction;
-import de.danoeh.antennapod.core.util.playback.PlayableException;
-
-public class FeedMedia extends FeedFile implements Playable {
- private static final String TAG = "FeedMedia";
-
- public static final int FEEDFILETYPE_FEEDMEDIA = 2;
- public static final int PLAYABLE_TYPE_FEEDMEDIA = 1;
- public static final String FILENAME_PREFIX_EMBEDDED_COVER = "metadata-retriever:";
-
- public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId";
- private static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
-
- /**
- * Indicates we've checked on the size of the item via the network
- * and got an invalid response. Using Integer.MIN_VALUE because
- * 1) we'll still check on it in case it gets downloaded (it's <= 0)
- * 2) By default all FeedMedia have a size of 0 if we don't know it,
- * so this won't conflict with existing practice.
- */
- private static final int CHECKED_ON_SIZE_BUT_UNKNOWN = Integer.MIN_VALUE;
-
- private int duration;
- private int position; // Current position in file
- private long lastPlayedTime; // Last time this media was played (in ms)
- private int played_duration; // How many ms of this file have been played
- private long size; // File size in Byte
- private String mime_type;
- @Nullable private volatile FeedItem item;
- private Date playbackCompletionDate;
- private int startPosition = -1;
- private int playedDurationWhenStarted;
-
- // if null: unknown, will be checked
- private Boolean hasEmbeddedPicture;
-
- /* Used for loading item when restoring from parcel. */
- private long itemID;
-
- public FeedMedia(FeedItem i, String download_url, long size,
- String mime_type) {
- super(null, download_url, false);
- this.item = i;
- this.size = size;
- this.mime_type = mime_type;
- }
-
- public FeedMedia(long id, FeedItem item, int duration, int position,
- long size, String mime_type, String file_url, String download_url,
- boolean downloaded, Date playbackCompletionDate, int played_duration,
- long lastPlayedTime) {
- super(file_url, download_url, downloaded);
- this.id = id;
- this.item = item;
- this.duration = duration;
- this.position = position;
- this.played_duration = played_duration;
- this.playedDurationWhenStarted = played_duration;
- this.size = size;
- this.mime_type = mime_type;
- this.playbackCompletionDate = playbackCompletionDate == null
- ? null : (Date) playbackCompletionDate.clone();
- this.lastPlayedTime = lastPlayedTime;
- }
-
- private FeedMedia(long id, FeedItem item, int duration, int position,
- long size, String mime_type, String file_url, String download_url,
- boolean downloaded, Date playbackCompletionDate, int played_duration,
- Boolean hasEmbeddedPicture, long lastPlayedTime) {
- this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded,
- playbackCompletionDate, played_duration, lastPlayedTime);
- this.hasEmbeddedPicture = hasEmbeddedPicture;
- }
-
- public static FeedMedia fromCursor(Cursor cursor) {
- int indexId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_MEDIA_ID);
- int indexPlaybackCompletionDate = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE);
- int indexDuration = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DURATION);
- int indexPosition = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_POSITION);
- int indexSize = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_SIZE);
- int indexMimeType = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_MIME_TYPE);
- int indexFileUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_FILE_URL);
- int indexDownloadUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DOWNLOAD_URL);
- int indexDownloaded = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DOWNLOADED);
- int indexPlayedDuration = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PLAYED_DURATION);
- int indexLastPlayedTime = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_LAST_PLAYED_TIME);
-
- long mediaId = cursor.getLong(indexId);
- Date playbackCompletionDate = null;
- long playbackCompletionTime = cursor.getLong(indexPlaybackCompletionDate);
- if (playbackCompletionTime > 0) {
- playbackCompletionDate = new Date(playbackCompletionTime);
- }
-
- Boolean hasEmbeddedPicture;
- switch(cursor.getInt(cursor.getColumnIndex(PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE))) {
- case 1:
- hasEmbeddedPicture = Boolean.TRUE;
- break;
- case 0:
- hasEmbeddedPicture = Boolean.FALSE;
- break;
- default:
- hasEmbeddedPicture = null;
- break;
- }
-
- return new FeedMedia(
- mediaId,
- null,
- cursor.getInt(indexDuration),
- cursor.getInt(indexPosition),
- cursor.getLong(indexSize),
- cursor.getString(indexMimeType),
- cursor.getString(indexFileUrl),
- cursor.getString(indexDownloadUrl),
- cursor.getInt(indexDownloaded) > 0,
- playbackCompletionDate,
- cursor.getInt(indexPlayedDuration),
- hasEmbeddedPicture,
- cursor.getLong(indexLastPlayedTime)
- );
- }
-
-
- @Override
- public String getHumanReadableIdentifier() {
- if (item != null && item.getTitle() != null) {
- return item.getTitle();
- } else {
- return download_url;
- }
- }
-
- /**
- * Returns a MediaItem representing the FeedMedia object.
- * This is used by the MediaBrowserService
- */
- public MediaBrowserCompat.MediaItem getMediaItem() {
- Playable p = this;
- MediaDescriptionCompat.Builder builder = new MediaDescriptionCompat.Builder()
- .setMediaId(String.valueOf(id))
- .setTitle(p.getEpisodeTitle())
- .setDescription(p.getFeedTitle())
- .setSubtitle(p.getFeedTitle());
- if (item != null) {
- // getImageLocation() also loads embedded images, which we can not send to external devices
- if (item.getImageUrl() != null) {
- builder.setIconUri(Uri.parse(item.getImageUrl()));
- } else if (item.getFeed() != null && item.getFeed().getImageUrl() != null) {
- builder.setIconUri(Uri.parse(item.getFeed().getImageUrl()));
- }
- }
- return new MediaBrowserCompat.MediaItem(builder.build(), MediaBrowserCompat.MediaItem.FLAG_PLAYABLE);
- }
-
- /**
- * Uses mimetype to determine the type of media.
- */
- public MediaType getMediaType() {
- return MediaType.fromMimeType(mime_type);
- }
-
- public void updateFromOther(FeedMedia other) {
- super.updateFromOther(other);
- if (other.size > 0) {
- size = other.size;
- }
- if (other.mime_type != null) {
- mime_type = other.mime_type;
- }
- }
-
- public boolean compareWithOther(FeedMedia other) {
- if (super.compareWithOther(other)) {
- return true;
- }
- if (other.mime_type != null) {
- if (mime_type == null || !mime_type.equals(other.mime_type)) {
- return true;
- }
- }
- if (other.size > 0 && other.size != size) {
- return true;
- }
- return false;
- }
-
- /**
- * Reads playback preferences to determine whether this FeedMedia object is
- * currently being played.
- */
- public boolean isPlaying() {
- return PlaybackPreferences.getCurrentlyPlayingMediaType() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA
- && PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == id;
- }
-
- /**
- * Reads playback preferences to determine whether this FeedMedia object is
- * currently being played and the current player status is playing.
- */
- public boolean isCurrentlyPlaying() {
- return isPlaying() && PlaybackService.isRunning &&
- ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING));
- }
-
- public boolean hasAlmostEnded() {
- int smartMarkAsPlayedSecs = UserPreferences.getSmartMarkAsPlayedSecs();
- return this.duration > 0 && this.position >= this.duration - smartMarkAsPlayedSecs * 1000;
- }
-
- @Override
- public int getTypeAsInt() {
- return FEEDFILETYPE_FEEDMEDIA;
- }
-
- public int getDuration() {
- return duration;
- }
-
- public void setDuration(int duration) {
- this.duration = duration;
- }
-
- @Override
- public void setLastPlayedTime(long lastPlayedTime) {
- this.lastPlayedTime = lastPlayedTime;
- }
-
- public int getPlayedDuration() {
- return played_duration;
- }
-
- public void setPlayedDuration(int played_duration) {
- this.played_duration = played_duration;
- }
-
- public int getPosition() {
- return position;
- }
-
- @Override
- public long getLastPlayedTime() {
- return lastPlayedTime;
- }
-
- public void setPosition(int position) {
- this.position = position;
- if(position > 0 && item != null && item.isNew()) {
- this.item.setPlayed(false);
- }
- }
-
- public long getSize() {
- return size;
- }
-
- public void setSize(long size) {
- this.size = size;
- }
-
- /**
- * Indicates we asked the service what the size was, but didn't
- * get a valid answer and we shoudln't check using the network again.
- */
- public void setCheckedOnSizeButUnknown() {
- this.size = CHECKED_ON_SIZE_BUT_UNKNOWN;
- }
-
- public boolean checkedOnSizeButUnknown() {
- return (CHECKED_ON_SIZE_BUT_UNKNOWN == this.size);
- }
-
- public String getMime_type() {
- return mime_type;
- }
-
- public void setMime_type(String mime_type) {
- this.mime_type = mime_type;
- }
-
- @Nullable
- public FeedItem getItem() {
- return item;
- }
-
- /**
- * Sets the item object of this FeedMedia. If the given
- * FeedItem object is not null, it's 'media'-attribute value
- * will also be set to this media object.
- */
- public void setItem(FeedItem item) {
- this.item = item;
- if (item != null && item.getMedia() != this) {
- item.setMedia(this);
- }
- }
-
- public Date getPlaybackCompletionDate() {
- return playbackCompletionDate == null
- ? null : (Date) playbackCompletionDate.clone();
- }
-
- public void setPlaybackCompletionDate(Date playbackCompletionDate) {
- this.playbackCompletionDate = playbackCompletionDate == null
- ? null : (Date) playbackCompletionDate.clone();
- }
-
- public boolean isInProgress() {
- return (this.position > 0);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public boolean hasEmbeddedPicture() {
- if(hasEmbeddedPicture == null) {
- checkEmbeddedPicture();
- }
- return hasEmbeddedPicture;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(id);
- dest.writeLong(item != null ? item.getId() : 0L);
-
- dest.writeInt(duration);
- dest.writeInt(position);
- dest.writeLong(size);
- dest.writeString(mime_type);
- dest.writeString(file_url);
- dest.writeString(download_url);
- dest.writeByte((byte) ((downloaded) ? 1 : 0));
- dest.writeLong((playbackCompletionDate != null) ? playbackCompletionDate.getTime() : 0);
- dest.writeInt(played_duration);
- dest.writeLong(lastPlayedTime);
- }
-
- @Override
- public void writeToPreferences(Editor prefEditor) {
- if(item != null && item.getFeed() != null) {
- prefEditor.putLong(PREF_FEED_ID, item.getFeed().getId());
- } else {
- prefEditor.putLong(PREF_FEED_ID, 0L);
- }
- prefEditor.putLong(PREF_MEDIA_ID, id);
- }
-
- @Override
- public void loadMetadata() throws PlayableException {
- if (item == null && itemID != 0) {
- item = DBReader.getFeedItem(itemID);
- }
- }
-
- @Override
- public void loadChapterMarks(Context context) {
- if (item == null && itemID != 0) {
- item = DBReader.getFeedItem(itemID);
- }
- if (item == null || item.getChapters() != null) {
- return;
- }
-
- List<Chapter> chapters = loadChapters(context);
- if (chapters == null) {
- // Do not try loading again. There are no chapters.
- item.setChapters(Collections.emptyList());
- } else {
- item.setChapters(chapters);
- }
- }
-
- private List<Chapter> loadChapters(Context context) {
- List<Chapter> chaptersFromDatabase = null;
- if (item.hasChapters()) {
- chaptersFromDatabase = DBReader.loadChaptersOfFeedItem(item);
- }
- List<Chapter> chaptersFromMediaFile = ChapterUtils.loadChaptersFromMediaFile(this, context);
- return ChapterMerger.merge(chaptersFromDatabase, chaptersFromMediaFile);
- }
-
- @Override
- public String getEpisodeTitle() {
- if (item == null) {
- return null;
- }
- if (item.getTitle() != null) {
- return item.getTitle();
- } else {
- return item.getIdentifyingValue();
- }
- }
-
- @Override
- public List<Chapter> getChapters() {
- if (item == null) {
- return null;
- }
- return item.getChapters();
- }
-
- @Override
- public String getWebsiteLink() {
- if (item == null) {
- return null;
- }
- return item.getLink();
- }
-
- @Override
- public String getFeedTitle() {
- if (item == null || item.getFeed() == null) {
- return null;
- }
- return item.getFeed().getTitle();
- }
-
- @Override
- public Object getIdentifier() {
- return id;
- }
-
- @Override
- public String getLocalMediaUrl() {
- return file_url;
- }
-
- @Override
- public String getStreamUrl() {
- return download_url;
- }
-
- @Override
- public String getPaymentLink() {
- if (item == null) {
- return null;
- }
- return item.getPaymentLink();
- }
-
- @Override
- public Date getPubDate() {
- if (item == null) {
- return null;
- }
- if (item.getPubDate() != null) {
- return item.getPubDate();
- } else {
- return null;
- }
- }
-
- @Override
- public boolean localFileAvailable() {
- return isDownloaded() && file_url != null;
- }
-
- @Override
- public boolean streamAvailable() {
- return download_url != null;
- }
-
- @Override
- public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timeStamp) {
- if(item != null && item.isNew()) {
- DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
- }
- setPosition(newPosition);
- setLastPlayedTime(timeStamp);
- if(startPosition>=0 && position > startPosition) {
- setPlayedDuration(playedDurationWhenStarted + position - startPosition);
- }
- DBWriter.setFeedMediaPlaybackInformation(this);
- }
-
- @Override
- public void onPlaybackStart() {
- startPosition = Math.max(position, 0);
- playedDurationWhenStarted = played_duration;
- }
-
- @Override
- public void onPlaybackPause(Context context) {
- if (position > startPosition) {
- played_duration = playedDurationWhenStarted + position - startPosition;
- playedDurationWhenStarted = played_duration;
- }
- postPlaybackTasks(context, false);
- startPosition = position;
- }
-
- @Override
- public void onPlaybackCompleted(Context context) {
- postPlaybackTasks(context, true);
- startPosition = -1;
- }
-
- private void postPlaybackTasks(Context context, boolean completed) {
- if (item != null) {
- if (startPosition >= 0 && (completed || startPosition < position) && GpodnetPreferences.loggedIn()) {
- EpisodeAction action = new EpisodeAction.Builder(item, EpisodeAction.PLAY)
- .currentTimestamp()
- .started(startPosition / 1000)
- .position((completed ? duration : position) / 1000)
- .total(duration / 1000)
- .build();
- SyncService.enqueueEpisodeAction(context, action);
- }
- }
- }
-
- @Override
- public int getPlayableType() {
- return PLAYABLE_TYPE_FEEDMEDIA;
- }
-
- @Override
- public void setChapters(List<Chapter> chapters) {
- if(item != null) {
- item.setChapters(chapters);
- }
- }
-
- @Override
- public Callable<String> loadShownotes() {
- return () -> {
- if (item == null) {
- item = DBReader.getFeedItem(itemID);
- }
- return item.loadShownotes().call();
- };
- }
-
- public static final Parcelable.Creator<FeedMedia> CREATOR = new Parcelable.Creator<FeedMedia>() {
- public FeedMedia createFromParcel(Parcel in) {
- final long id = in.readLong();
- final long itemID = in.readLong();
- FeedMedia result = new FeedMedia(id, null, in.readInt(), in.readInt(), in.readLong(), in.readString(), in.readString(),
- in.readString(), in.readByte() != 0, new Date(in.readLong()), in.readInt(), in.readLong());
- result.itemID = itemID;
- return result;
- }
-
- public FeedMedia[] newArray(int size) {
- return new FeedMedia[size];
- }
- };
-
- @Override
- public String getImageLocation() {
- if (item != null) {
- return item.getImageLocation();
- } else if (hasEmbeddedPicture()) {
- return FILENAME_PREFIX_EMBEDDED_COVER + getLocalMediaUrl();
- } else {
- return null;
- }
- }
-
- public void setHasEmbeddedPicture(Boolean hasEmbeddedPicture) {
- this.hasEmbeddedPicture = hasEmbeddedPicture;
- }
-
- @Override
- public void setDownloaded(boolean downloaded) {
- super.setDownloaded(downloaded);
- if(item != null && downloaded && item.isNew()) {
- item.setPlayed(false);
- }
- }
-
- @Override
- public void setFile_url(String file_url) {
- super.setFile_url(file_url);
- }
-
- public void checkEmbeddedPicture() {
- if (!localFileAvailable()) {
- hasEmbeddedPicture = Boolean.FALSE;
- return;
- }
- MediaMetadataRetriever mmr = new MediaMetadataRetriever();
- try {
- mmr.setDataSource(getLocalMediaUrl());
- byte[] image = mmr.getEmbeddedPicture();
- if(image != null) {
- hasEmbeddedPicture = Boolean.TRUE;
- } else {
- hasEmbeddedPicture = Boolean.FALSE;
- }
- } catch (Exception e) {
- e.printStackTrace();
- hasEmbeddedPicture = Boolean.FALSE;
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
- if (FeedMediaFlavorHelper.instanceOfRemoteMedia(o)) {
- return o.equals(this);
- }
- return super.equals(o);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
deleted file mode 100644
index 93efed7e1..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedPreferences.java
+++ /dev/null
@@ -1,254 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.database.Cursor;
-import androidx.annotation.NonNull;
-import android.text.TextUtils;
-
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-
-/**
- * Contains preferences for a single feed.
- */
-public class FeedPreferences {
-
- public static final float SPEED_USE_GLOBAL = -1;
-
- public enum AutoDeleteAction {
- GLOBAL,
- YES,
- NO
- }
-
- @NonNull
- private FeedFilter filter;
- private long feedID;
- private boolean autoDownload;
- private boolean keepUpdated;
- private AutoDeleteAction autoDeleteAction;
- private VolumeAdaptionSetting volumeAdaptionSetting;
- private String username;
- private String password;
- private float feedPlaybackSpeed;
- private int feedSkipIntro;
- private int feedSkipEnding;
- private boolean showEpisodeNotification;
-
- public FeedPreferences(long feedID, boolean autoDownload, AutoDeleteAction autoDeleteAction,
- VolumeAdaptionSetting volumeAdaptionSetting, String username, String password) {
- this(feedID, autoDownload, true, autoDeleteAction, volumeAdaptionSetting,
- username, password, new FeedFilter(), SPEED_USE_GLOBAL, 0, 0, false);
- }
-
- private FeedPreferences(long feedID, boolean autoDownload, boolean keepUpdated,
- AutoDeleteAction autoDeleteAction, VolumeAdaptionSetting volumeAdaptionSetting,
- String username, String password, @NonNull FeedFilter filter, float feedPlaybackSpeed,
- int feedSkipIntro, int feedSkipEnding, boolean showEpisodeNotification) {
- this.feedID = feedID;
- this.autoDownload = autoDownload;
- this.keepUpdated = keepUpdated;
- this.autoDeleteAction = autoDeleteAction;
- this.volumeAdaptionSetting = volumeAdaptionSetting;
- this.username = username;
- this.password = password;
- this.filter = filter;
- this.feedPlaybackSpeed = feedPlaybackSpeed;
- this.feedSkipIntro = feedSkipIntro;
- this.feedSkipEnding = feedSkipEnding;
- this.showEpisodeNotification = showEpisodeNotification;
- }
-
- public static FeedPreferences fromCursor(Cursor cursor) {
- int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
- int indexAutoDownload = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD);
- int indexAutoRefresh = cursor.getColumnIndex(PodDBAdapter.KEY_KEEP_UPDATED);
- int indexAutoDeleteAction = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DELETE_ACTION);
- int indexVolumeAdaption = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_VOLUME_ADAPTION);
- int indexUsername = cursor.getColumnIndex(PodDBAdapter.KEY_USERNAME);
- int indexPassword = cursor.getColumnIndex(PodDBAdapter.KEY_PASSWORD);
- int indexIncludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_INCLUDE_FILTER);
- int indexExcludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_EXCLUDE_FILTER);
- int indexFeedPlaybackSpeed = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_PLAYBACK_SPEED);
- int indexAutoSkipIntro = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_INTRO);
- int indexAutoSkipEnding = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_ENDING);
- int indexEpisodeNotification = cursor.getColumnIndex(PodDBAdapter.KEY_EPISODE_NOTIFICATION);
-
- long feedId = cursor.getLong(indexId);
- boolean autoDownload = cursor.getInt(indexAutoDownload) > 0;
- boolean autoRefresh = cursor.getInt(indexAutoRefresh) > 0;
- int autoDeleteActionIndex = cursor.getInt(indexAutoDeleteAction);
- AutoDeleteAction autoDeleteAction = AutoDeleteAction.values()[autoDeleteActionIndex];
- int volumeAdaptionValue = cursor.getInt(indexVolumeAdaption);
- VolumeAdaptionSetting volumeAdaptionSetting = VolumeAdaptionSetting.fromInteger(volumeAdaptionValue);
- String username = cursor.getString(indexUsername);
- String password = cursor.getString(indexPassword);
- String includeFilter = cursor.getString(indexIncludeFilter);
- String excludeFilter = cursor.getString(indexExcludeFilter);
- float feedPlaybackSpeed = cursor.getFloat(indexFeedPlaybackSpeed);
- int feedAutoSkipIntro = cursor.getInt(indexAutoSkipIntro);
- int feedAutoSkipEnding = cursor.getInt(indexAutoSkipEnding);
- boolean showNotification = cursor.getInt(indexEpisodeNotification) > 0;
- return new FeedPreferences(feedId,
- autoDownload,
- autoRefresh,
- autoDeleteAction,
- volumeAdaptionSetting,
- username,
- password,
- new FeedFilter(includeFilter, excludeFilter),
- feedPlaybackSpeed,
- feedAutoSkipIntro,
- feedAutoSkipEnding,
- showNotification
- );
- }
-
- /**
- * @return the filter for this feed
- */
- @NonNull public FeedFilter getFilter() {
- return filter;
- }
-
- public void setFilter(@NonNull FeedFilter filter) {
- this.filter = filter;
- }
-
- /**
- * @return true if this feed should be refreshed when everything else is being refreshed
- * if false the feed should only be refreshed if requested directly.
- */
- public boolean getKeepUpdated() {
- return keepUpdated;
- }
-
- public void setKeepUpdated(boolean keepUpdated) {
- this.keepUpdated = keepUpdated;
- }
-
- /**
- * Compare another FeedPreferences with this one. The feedID, autoDownload and AutoDeleteAction attribute are excluded from the
- * comparison.
- *
- * @return True if the two objects are different.
- */
- public boolean compareWithOther(FeedPreferences other) {
- if (other == null) {
- return true;
- }
- if (!TextUtils.equals(username, other.username)) {
- return true;
- }
- if (!TextUtils.equals(password, other.password)) {
- return true;
- }
- return false;
- }
-
- /**
- * Update this FeedPreferences object from another one. The feedID, autoDownload and AutoDeleteAction attributes are excluded
- * from the update.
- */
- public void updateFromOther(FeedPreferences other) {
- if (other == null)
- return;
- this.username = other.username;
- this.password = other.password;
- }
-
- public long getFeedID() {
- return feedID;
- }
-
- public void setFeedID(long feedID) {
- this.feedID = feedID;
- }
-
- public boolean getAutoDownload() {
- return autoDownload;
- }
-
- public void setAutoDownload(boolean autoDownload) {
- this.autoDownload = autoDownload;
- }
-
- public AutoDeleteAction getAutoDeleteAction() {
- return autoDeleteAction;
- }
-
- public VolumeAdaptionSetting getVolumeAdaptionSetting() {
- return volumeAdaptionSetting;
- }
-
- public void setAutoDeleteAction(AutoDeleteAction autoDeleteAction) {
- this.autoDeleteAction = autoDeleteAction;
- }
-
- public void setVolumeAdaptionSetting(VolumeAdaptionSetting volumeAdaptionSetting) {
- this.volumeAdaptionSetting = volumeAdaptionSetting;
- }
-
- public boolean getCurrentAutoDelete() {
- switch (autoDeleteAction) {
- case GLOBAL:
- return UserPreferences.isAutoDelete();
- case YES:
- return true;
- case NO:
- default: // fall-through
- return false;
- }
- }
-
- public String getUsername() {
- return username;
- }
-
- public void setUsername(String username) {
- this.username = username;
- }
-
- public String getPassword() {
- return password;
- }
-
- public void setPassword(String password) {
- this.password = password;
- }
-
- public float getFeedPlaybackSpeed() {
- return feedPlaybackSpeed;
- }
-
- public void setFeedPlaybackSpeed(float playbackSpeed) {
- feedPlaybackSpeed = playbackSpeed;
- }
-
- public void setFeedSkipIntro(int skipIntro) {
- feedSkipIntro = skipIntro;
- }
-
- public int getFeedSkipIntro() {
- return feedSkipIntro;
- }
-
- public void setFeedSkipEnding(int skipEnding) {
- feedSkipEnding = skipEnding;
- }
-
- public int getFeedSkipEnding() {
- return feedSkipEnding;
- }
-
- /**
- * getter for preference if notifications should be display for new episodes.
- * @return true for displaying notifications
- */
- public boolean getShowEpisodeNotification() {
- return showEpisodeNotification;
- }
-
- public void setShowEpisodeNotification(boolean showEpisodeNotification) {
- this.showEpisodeNotification = showEpisodeNotification;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
index b69d537fb..56d63c32e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/ID3Chapter.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.Chapter;
+
public class ID3Chapter extends Chapter {
public static final int CHAPTERTYPE_ID3CHAPTER = 2;
@@ -20,8 +22,8 @@ public class ID3Chapter extends Chapter {
@Override
public String toString() {
- return "ID3Chapter [id3ID=" + id3ID + ", title=" + title + ", start="
- + start + ", url=" + link + "]";
+ return "ID3Chapter [id3ID=" + id3ID + ", title=" + getTitle() + ", start="
+ + getStart() + ", url=" + getLink() + "]";
}
@Override
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
index d0e15d591..7a8c4969b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/LocalFeedUpdater.java
@@ -29,6 +29,11 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.DownloadError;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.playback.MediaType;
public class LocalFeedUpdater {
@@ -70,7 +75,21 @@ public class LocalFeedUpdater {
Set<String> mediaFileNames = new HashSet<>();
for (DocumentFile file : documentFolder.listFiles()) {
String mime = file.getType();
- if (mime != null && (mime.startsWith("audio/") || mime.startsWith("video/"))) {
+ if (mime == null) {
+ continue;
+ }
+
+ MediaType mediaType = MediaType.fromMimeType(mime);
+ if (mediaType == MediaType.UNKNOWN) {
+ String path = file.getUri().toString();
+ int fileExtensionPosition = path.lastIndexOf('.');
+ if (fileExtensionPosition >= 0) {
+ String extensionWithoutDot = path.substring(fileExtensionPosition + 1);
+ mediaType = MediaType.fromFileExtension(extensionWithoutDot);
+ }
+ }
+
+ if (mediaType == MediaType.AUDIO || mediaType == MediaType.VIDEO) {
mediaFiles.add(file);
mediaFileNames.add(file.getName());
}
@@ -169,7 +188,7 @@ public class LocalFeedUpdater {
try {
loadMetadata(item, file, context);
} catch (Exception e) {
- item.setDescription(e.getMessage());
+ item.setDescriptionIfLonger(e.getMessage());
}
return item;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/MediaType.java b/core/src/main/java/de/danoeh/antennapod/core/feed/MediaType.java
deleted file mode 100644
index 83ac031bf..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/MediaType.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.text.TextUtils;
-
-public enum MediaType {
- AUDIO, VIDEO, UNKNOWN;
-
- public static MediaType fromMimeType(String mime_type) {
- if (TextUtils.isEmpty(mime_type)) {
- return MediaType.UNKNOWN;
- } else if (mime_type.startsWith("audio")) {
- return MediaType.AUDIO;
- } else if (mime_type.startsWith("video")) {
- return MediaType.VIDEO;
- } else if (mime_type.equals("application/ogg")) {
- return MediaType.AUDIO;
- }
- return MediaType.UNKNOWN;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java
index 45c71a014..ca59f867b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SimpleChapter.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.Chapter;
+
public class SimpleChapter extends Chapter {
public static final int CHAPTERTYPE_SIMPLECHAPTER = 0;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java
index 93f098ecf..a083a7220 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilter.java
@@ -6,6 +6,8 @@ import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.core.util.LongIntMap;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
public class SubscriptionsFilter {
private static final String divider = ",";
@@ -20,6 +22,9 @@ public class SubscriptionsFilter {
private boolean showUpdatedEnabled = false;
private boolean showUpdatedDisabled = false;
+ private boolean showEpisodeNotificationEnabled = false;
+ private boolean showEpisodeNotificationDisabled = false;
+
public SubscriptionsFilter(String properties) {
this(TextUtils.split(properties, divider));
}
@@ -45,6 +50,12 @@ public class SubscriptionsFilter {
case "disabled_updates":
showUpdatedDisabled = true;
break;
+ case "episode_notification_enabled":
+ showEpisodeNotificationEnabled = true;
+ break;
+ case "episode_notification_disabled":
+ showEpisodeNotificationDisabled = true;
+ break;
default:
break;
}
@@ -81,6 +92,12 @@ public class SubscriptionsFilter {
continue;
}
+ if (showEpisodeNotificationEnabled && !itemPreferences.getShowEpisodeNotification()) {
+ continue;
+ } else if (showEpisodeNotificationDisabled && itemPreferences.getShowEpisodeNotification()) {
+ continue;
+ }
+
// If the item reaches here, it meets all criteria (except counter > 0)
result.add(item);
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java
index 7db0456a0..cea5d96ef 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/SubscriptionsFilterGroup.java
@@ -7,7 +7,10 @@ public enum SubscriptionsFilterGroup {
AUTO_DOWNLOAD(new ItemProperties(R.string.auto_downloaded, "enabled_auto_download"),
new ItemProperties(R.string.not_auto_downloaded, "disabled_auto_download")),
UPDATED(new ItemProperties(R.string.kept_updated, "enabled_updates"),
- new ItemProperties(R.string.not_kept_updated, "disabled_updates"));
+ new ItemProperties(R.string.not_kept_updated, "disabled_updates")),
+ NEW_EPISODE_NOTIFICATION(new ItemProperties(R.string.new_episode_notification_enabled,
+ "episode_notification_enabled"),
+ new ItemProperties(R.string.new_episode_notification_disabled, "episode_notification_disabled"));
public final ItemProperties[] values;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java b/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java
deleted file mode 100644
index bf4fc582a..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSetting.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-public enum VolumeAdaptionSetting {
- OFF(0, 1.0f),
- LIGHT_REDUCTION(1, 0.5f),
- HEAVY_REDUCTION(2, 0.2f);
-
- private final int value;
- private float adaptionFactor;
-
- VolumeAdaptionSetting(int value, float adaptionFactor) {
- this.value = value;
- this.adaptionFactor = adaptionFactor;
- }
-
- public static VolumeAdaptionSetting fromInteger(int value) {
- for (VolumeAdaptionSetting setting : values()) {
- if (setting.value == value) {
- return setting;
- }
- }
- throw new IllegalArgumentException("Cannot map value to VolumeAdaptionSetting: " + value);
- }
-
- public int toInteger() {
- return value;
- }
-
- public float getAdaptionFactor() {
- return adaptionFactor;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java b/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
index eac50aed8..6e0b6859d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/VorbisCommentChapter.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.feed;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
+import de.danoeh.antennapod.model.feed.Chapter;
public class VorbisCommentChapter extends Chapter {
public static final int CHAPTERTYPE_VORBISCOMMENT_CHAPTER = 3;
@@ -21,8 +22,8 @@ public class VorbisCommentChapter extends Chapter {
@Override
public String toString() {
- return "VorbisCommentChapter [id=" + id + ", title=" + title
- + ", link=" + link + ", start=" + start + "]";
+ return "VorbisCommentChapter [id=" + getId() + ", title=" + getTitle()
+ + ", link=" + getLink() + ", start=" + getStart() + "]";
}
public static long getStartTimeFromValue(String value)
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java b/core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java
index b0aee3d77..743cacfe2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/util/ImageResourceUtils.java
@@ -3,10 +3,10 @@ package de.danoeh.antennapod.core.feed.util;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
/**
* Utility class to use the appropriate image resource based on {@link UserPreferences}.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
index d6740994d..5eaf0be68 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/util/PlaybackSpeedUtils.java
@@ -1,15 +1,15 @@
package de.danoeh.antennapod.core.feed.util;
import android.util.Log;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
-import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
+import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
/**
* Utility class to use the appropriate playback speed based on {@link PlaybackPreferences}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
index 519d625e2..d6d63fed0 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/ChapterImageModelLoader.java
@@ -1,7 +1,6 @@
package de.danoeh.antennapod.core.glide;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.Options;
@@ -36,7 +35,6 @@ public final class ChapterImageModelLoader implements ModelLoader<EmbeddedChapte
}
}
- @Nullable
@Override
public LoadData<ByteBuffer> buildLoadData(@NonNull EmbeddedChapterImage model,
int width,
@@ -65,9 +63,9 @@ public final class ChapterImageModelLoader implements ModelLoader<EmbeddedChapte
if (image.getMedia().localFileAvailable()) {
File localFile = new File(image.getMedia().getLocalMediaUrl());
stream = new BufferedInputStream(new FileInputStream(localFile));
- stream.skip(image.getPosition());
+ IOUtils.skip(stream, image.getPosition());
byte[] imageContent = new byte[image.getLength()];
- stream.read(imageContent, 0, image.getLength());
+ IOUtils.read(stream, imageContent, 0, image.getLength());
callback.onDataReady(ByteBuffer.wrap(imageContent));
} else {
Request.Builder httpReq = new Request.Builder();
@@ -88,10 +86,13 @@ public final class ChapterImageModelLoader implements ModelLoader<EmbeddedChapte
}
}
- @Override public void cleanup() {
+ @Override
+ public void cleanup() {
// nothing to clean up
}
- @Override public void cancel() {
+
+ @Override
+ public void cancel() {
// cannot cancel
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
index 1f8ae5ad9..4de6a7315 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/FastBlurTransformation.java
@@ -8,6 +8,7 @@ import android.util.Log;
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
+import java.nio.charset.Charset;
import java.security.MessageDigest;
public class FastBlurTransformation extends BitmapTransformation {
@@ -43,7 +44,7 @@ public class FastBlurTransformation extends BitmapTransformation {
@Override
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
- messageDigest.update(TAG.getBytes());
+ messageDigest.update(TAG.getBytes(Charset.defaultCharset()));
}
private static Bitmap fastBlur(Bitmap bitmap, int radius) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/glide/MetadataRetrieverLoader.java b/core/src/main/java/de/danoeh/antennapod/core/glide/MetadataRetrieverLoader.java
index baa06e722..1be43052b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/glide/MetadataRetrieverLoader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/glide/MetadataRetrieverLoader.java
@@ -8,7 +8,7 @@ import com.bumptech.glide.load.model.ModelLoader;
import com.bumptech.glide.load.model.ModelLoaderFactory;
import com.bumptech.glide.load.model.MultiModelLoaderFactory;
import com.bumptech.glide.signature.ObjectKey;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import java.io.InputStream;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
index 209558b19..e338e0d01 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/GpodnetPreferences.java
@@ -6,7 +6,7 @@ import android.util.Log;
import de.danoeh.antennapod.core.BuildConfig;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
/**
* Manages preferences for accessing gpodder.net service
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
index 95b828e28..9c73ed9ae 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/PlaybackPreferences.java
@@ -6,13 +6,13 @@ import androidx.preference.PreferenceManager;
import android.util.Log;
import de.danoeh.antennapod.core.event.PlayerStatusEvent;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import org.greenrobot.eventbus.EventBus;
-import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
+import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
/**
* Provides access to preferences set by the playback service. A private
diff --git a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
index cbfe28ded..046e5a5a7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/preferences/UserPreferences.java
@@ -32,7 +32,7 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.service.download.ProxyConfig;
import de.danoeh.antennapod.core.storage.APCleanupAlgorithm;
@@ -41,7 +41,7 @@ import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm;
import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm;
import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
import de.danoeh.antennapod.core.util.download.AutoUpdateManager;
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
index a01b3cb52..93a58af06 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/AntennapodHttpClient.java
@@ -1,19 +1,14 @@
package de.danoeh.antennapod.core.service.download;
-import android.os.Build;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.BasicAuthorizationInterceptor;
import de.danoeh.antennapod.core.service.UserAgentInterceptor;
-import de.danoeh.antennapod.core.ssl.BackportTrustManager;
-import de.danoeh.antennapod.core.ssl.NoV1SslSocketFactory;
import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.util.Flavors;
+import de.danoeh.antennapod.net.ssl.SslClientSetup;
import okhttp3.Cache;
-import okhttp3.CipherSuite;
-import okhttp3.ConnectionSpec;
import okhttp3.Credentials;
import okhttp3.HttpUrl;
import okhttp3.JavaNetCookieJar;
@@ -21,8 +16,6 @@ import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.internal.http.StatusLine;
-
-import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.net.CookieManager;
import java.net.CookiePolicy;
@@ -30,9 +23,6 @@ import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketAddress;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.TimeUnit;
/**
@@ -128,40 +118,18 @@ public class AntennapodHttpClient {
if (config.type != Proxy.Type.DIRECT) {
int port = config.port > 0 ? config.port : ProxyConfig.DEFAULT_PORT;
SocketAddress address = InetSocketAddress.createUnresolved(config.host, port);
- Proxy proxy = new Proxy(config.type, address);
- builder.proxy(proxy);
- if (!TextUtils.isEmpty(config.username)) {
- String credentials = Credentials.basic(config.username, config.password);
- builder.interceptors().add(chain -> {
- Request request = chain.request().newBuilder()
- .header("Proxy-Authorization", credentials).build();
- return chain.proceed(request);
+ builder.proxy(new Proxy(config.type, address));
+ if (!TextUtils.isEmpty(config.username) && config.password != null) {
+ builder.proxyAuthenticator((route, response) -> {
+ String credentials = Credentials.basic(config.username, config.password);
+ return response.request().newBuilder()
+ .header("Proxy-Authorization", credentials)
+ .build();
});
}
}
- if (Flavors.FLAVOR == Flavors.FREE) {
- // The Free flavor bundles a modern conscrypt (security provider), so CustomSslSocketFactory
- // is only used to make sure that modern protocols (TLSv1.3 and TLSv1.2) are enabled and
- // that old, deprecated, protocols (like SSLv3, TLSv1.0 and TLSv1.1) are disabled.
- X509TrustManager trustManager = BackportTrustManager.create();
- builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
- } else if (Build.VERSION.SDK_INT < 21) {
- X509TrustManager trustManager = BackportTrustManager.create();
- builder.sslSocketFactory(new NoV1SslSocketFactory(trustManager), trustManager);
-
- // workaround for Android 4.x for certain web sites.
- // see: https://github.com/square/okhttp/issues/4053#issuecomment-402579554
- List<CipherSuite> cipherSuites = new ArrayList<>(ConnectionSpec.MODERN_TLS.cipherSuites());
- cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA);
- cipherSuites.add(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA);
-
- ConnectionSpec legacyTls = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
- .cipherSuites(cipherSuites.toArray(new CipherSuite[0]))
- .build();
- builder.connectionSpecs(Arrays.asList(legacyTls, ConnectionSpec.CLEARTEXT));
- }
-
+ SslClientSetup.installCertificates(builder);
return builder;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
index 3f503c6b4..079abef1b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadRequest.java
@@ -8,7 +8,7 @@ import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import de.danoeh.antennapod.core.feed.FeedFile;
+import de.danoeh.antennapod.model.feed.FeedFile;
import de.danoeh.antennapod.core.util.URLChecker;
public class DownloadRequest implements Parcelable {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
index e8e85602b..9598e3bdd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadService.java
@@ -40,9 +40,9 @@ import java.util.concurrent.atomic.AtomicInteger;
import de.danoeh.antennapod.core.event.DownloadEvent;
import de.danoeh.antennapod.core.event.FeedItemEvent;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.handler.FailedDownloadHandler;
import de.danoeh.antennapod.core.service.download.handler.FeedSyncTask;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
index 7b7879409..37d8102b6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadServiceNotification.java
@@ -9,8 +9,8 @@ import android.util.Log;
import androidx.core.app.NotificationCompat;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
import java.util.List;
@@ -205,7 +205,7 @@ public class DownloadServiceNotification {
.setContentText(context.getText(R.string.authentication_notification_msg))
.setStyle(new NotificationCompat.BigTextStyle().bigText(context.getText(R.string.authentication_notification_msg)
+ ": " + resourceTitle))
- .setSmallIcon(R.drawable.ic_key_white)
+ .setSmallIcon(R.drawable.ic_notification_key)
.setAutoCancel(true)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(context, downloadRequest));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
index bad2ba1ef..8c95dab85 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/DownloadStatus.java
@@ -5,7 +5,7 @@ import androidx.annotation.NonNull;
import java.util.Date;
-import de.danoeh.antennapod.core.feed.FeedFile;
+import de.danoeh.antennapod.model.feed.FeedFile;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.DownloadError;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
index 2d955859f..1320b7052 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/HttpDownloader.java
@@ -23,7 +23,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.util.DateUtils;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.util.StorageUtils;
@@ -188,8 +188,11 @@ public class HttpDownloader extends Downloader {
out = new RandomAccessFile(destination, "rw");
out.seek(request.getSoFar());
} else {
- destination.delete();
- destination.createNewFile();
+ boolean success = destination.delete();
+ success |= destination.createNewFile();
+ if (!success) {
+ throw new IOException("Unable to recreate partially downloaded file");
+ }
out = new RandomAccessFile(destination, "rw");
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
index 799a68037..869205b64 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/NewEpisodesNotification.java
@@ -15,8 +15,8 @@ import androidx.core.app.NotificationManagerCompat;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
index 386e5e6f7..748559909 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FailedDownloadHandler.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.core.service.download.handler;
import android.util.Log;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.storage.DBWriter;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
index d07018f13..3e3da00e7 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedParserTask.java
@@ -1,10 +1,10 @@
package de.danoeh.antennapod.core.service.download.handler;
import android.util.Log;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DownloadRequester;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
index ed94a494c..1ca4d1194 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/FeedSyncTask.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.service.download.handler;
import android.content.Context;
import android.util.Log;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBTasks;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
index 7712ca36b..8c9035621 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/download/handler/MediaDownloadedHandler.java
@@ -10,8 +10,8 @@ import java.io.File;
import java.util.concurrent.ExecutionException;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.DownloadRequest;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -19,7 +19,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.DownloadError;
import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.core.sync.model.EpisodeAction;
+import de.danoeh.antennapod.net.sync.model.EpisodeAction;
import org.greenrobot.eventbus.EventBus;
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
index 8d3633b24..0a9bf5f43 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/ExoPlayerWrapper.java
@@ -16,6 +16,7 @@ import com.google.android.exoplayer2.SeekParameters;
import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.audio.AudioAttributes;
import com.google.android.exoplayer2.extractor.DefaultExtractorsFactory;
+import com.google.android.exoplayer2.extractor.mp3.Mp3Extractor;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.ProgressiveMediaSource;
import com.google.android.exoplayer2.source.TrackGroupArray;
@@ -113,11 +114,6 @@ public class ExoPlayerWrapper implements IPlayer {
}
@Override
- public boolean canSetSpeed() {
- return true;
- }
-
- @Override
public boolean canDownmix() {
return false;
}
@@ -210,6 +206,7 @@ public class ExoPlayerWrapper implements IPlayer {
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(context, null, httpDataSourceFactory);
DefaultExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
extractorsFactory.setConstantBitrateSeekingEnabled(true);
+ extractorsFactory.setMp3ExtractorFlags(Mp3Extractor.FLAG_DISABLE_ID3_METADATA);
ProgressiveMediaSource.Factory f = new ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory);
mediaSource = f.createMediaSource(Uri.parse(s));
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
index 28d8a0e29..dc29f53d4 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/LocalPSMP.java
@@ -14,6 +14,7 @@ import android.view.SurfaceHolder;
import androidx.media.AudioAttributesCompat;
import androidx.media.AudioFocusRequestCompat;
import androidx.media.AudioManagerCompat;
+import de.danoeh.antennapod.core.storage.DBReader;
import org.antennapod.audio.MediaPlayer;
import java.io.File;
@@ -28,17 +29,16 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantLock;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.playback.MediaType;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
import de.danoeh.antennapod.core.util.playback.AudioPlayer;
import de.danoeh.antennapod.core.util.playback.IPlayer;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.core.util.playback.PlayableException;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.core.util.playback.VideoPlayer;
@@ -218,7 +218,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
* <p/>
* This method requires the playerLock and is executed on the caller's thread.
*
- * @see #playMediaObject(de.danoeh.antennapod.core.util.playback.Playable, boolean, boolean, boolean)
+ * @see #playMediaObject(Playable, boolean, boolean, boolean)
*/
private void playMediaObject(@NonNull final Playable playable, final boolean forceReset, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
if (!playerLock.isHeldByCurrentThread()) {
@@ -259,7 +259,9 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
LocalPSMP.this.startWhenPrepared.set(startWhenPrepared);
setPlayerStatus(PlayerStatus.INITIALIZING, media);
try {
- media.loadMetadata();
+ if (media instanceof FeedMedia && ((FeedMedia) media).getItem() == null) {
+ ((FeedMedia) media).setItem(DBReader.getFeedItem(((FeedMedia) media).getItemId()));
+ }
callback.onMediaChanged(false);
setPlaybackParams(PlaybackSpeedUtils.getCurrentPlaybackSpeed(media), UserPreferences.isSkipSilence());
if (stream) {
@@ -289,7 +291,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
onPrepared(startWhenPrepared);
}
- } catch (PlayableException | IOException | IllegalStateException e) {
+ } catch (IOException | IllegalStateException e) {
e.printStackTrace();
setPlayerStatus(PlayerStatus.ERROR, null);
}
@@ -606,23 +608,13 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
}
/**
- * Returns true if the playback speed can be adjusted.
- */
- @Override
- public boolean canSetSpeed() {
- return mediaPlayer != null && mediaPlayer.canSetSpeed();
- }
-
- /**
* Sets the playback speed.
* This method is executed on the caller's thread.
*/
private void setSpeedSyncAndSkipSilence(float speed, boolean skipSilence) {
playerLock.lock();
- if (mediaPlayer.canSetSpeed()) {
- Log.d(TAG, "Playback speed was set to " + speed);
- callback.playbackSpeedChanged(speed);
- }
+ Log.d(TAG, "Playback speed was set to " + speed);
+ callback.playbackSpeedChanged(speed);
mediaPlayer.setPlaybackParams(speed, skipSilence);
playerLock.unlock();
}
@@ -649,7 +641,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
if ((playerStatus == PlayerStatus.PLAYING
|| playerStatus == PlayerStatus.PAUSED
|| playerStatus == PlayerStatus.INITIALIZED
- || playerStatus == PlayerStatus.PREPARED) && mediaPlayer.canSetSpeed()) {
+ || playerStatus == PlayerStatus.PREPARED)) {
retVal = mediaPlayer.getCurrentSpeedMultiplier();
}
playerLock.unlock();
@@ -1046,7 +1038,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
ap.setOnErrorListener(audioErrorListener);
ap.setOnBufferingUpdateListener(audioBufferingUpdateListener);
ap.setOnInfoListener(audioInfoListener);
- ap.setOnSpeedAdjustmentAvailableChangedListener(audioSetSpeedAbilityListener);
} else if (mp instanceof ExoPlayerWrapper) {
ExoPlayerWrapper ap = (ExoPlayerWrapper) mp;
ap.setOnCompletionListener(audioCompletionListener);
@@ -1090,10 +1081,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
return callback.onMediaPlayerInfo(what, 0);
}
- private final MediaPlayer.OnSpeedAdjustmentAvailableChangedListener audioSetSpeedAbilityListener =
- (arg0, speedAdjustmentAvailable) -> callback.setSpeedAbilityChanged();
-
-
private final MediaPlayer.OnErrorListener audioErrorListener =
(mp, what, extra) -> {
if(mp != null && mp.canFallback()) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index e9fd5de3a..4f3962efd 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -57,12 +57,12 @@ import de.danoeh.antennapod.core.event.ServiceEvent;
import de.danoeh.antennapod.core.event.settings.SkipIntroEndingChangedEvent;
import de.danoeh.antennapod.core.event.settings.SpeedPresetChangedEvent;
import de.danoeh.antennapod.core.event.settings.VolumeAdaptionChangedEvent;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@@ -73,12 +73,12 @@ import de.danoeh.antennapod.core.storage.DBTasks;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.storage.FeedSearcher;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
+import de.danoeh.antennapod.core.sync.SyncService;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
-import de.danoeh.antennapod.core.util.playback.ExternalMedia;
-import de.danoeh.antennapod.core.util.playback.Playable;
-import de.danoeh.antennapod.core.util.playback.PlayableException;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlayableUtils;
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
import de.danoeh.antennapod.core.widget.WidgetUpdater;
@@ -93,7 +93,7 @@ import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
-import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
+import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
/**
* Controls the MediaPlayer that plays a FeedMedia-file
@@ -189,11 +189,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
public static final int NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE = 8;
/**
- * Ability to set the playback speed has changed
- */
- public static final int NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED = 9;
-
- /**
* Returned by getPositionSafe() or getDurationSafe() if the playbackService
* is in an invalid state.
*/
@@ -519,8 +514,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
boolean startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
boolean prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false);
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
- //If the user asks to play External Media, the casting session, if on, should end.
- flavorHelper.castDisconnect(playable instanceof ExternalMedia);
if (allowStreamAlways) {
UserPreferences.setAllowMobileStreaming(true);
}
@@ -617,17 +610,17 @@ public class PlaybackService extends MediaBrowserServiceCompat {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this,
NotificationUtils.CHANNEL_ID_USER_ACTION)
- .setSmallIcon(R.drawable.ic_stream_white)
+ .setSmallIcon(R.drawable.ic_notification_stream)
.setContentTitle(getString(R.string.confirm_mobile_streaming_notification_title))
.setContentText(getString(R.string.confirm_mobile_streaming_notification_message))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(getString(R.string.confirm_mobile_streaming_notification_message)))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntentAllowThisTime)
- .addAction(R.drawable.ic_stream_white,
+ .addAction(R.drawable.ic_notification_stream,
getString(R.string.confirm_mobile_streaming_button_once),
pendingIntentAllowThisTime)
- .addAction(R.drawable.ic_stream_white,
+ .addAction(R.drawable.ic_notification_stream,
getString(R.string.confirm_mobile_streaming_button_always),
pendingIntentAlwaysAllow)
.setAutoCancel(true);
@@ -896,10 +889,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE, 0);
}
- public void setSpeedAbilityChanged() {
- sendNotificationBroadcast(NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED, 0);
- }
-
@Override
public void onBufferingUpdate(int percent) {
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
@@ -978,6 +967,9 @@ public class PlaybackService extends MediaBrowserServiceCompat {
playable, position);
taskManager.cancelWidgetUpdater();
if (playable != null) {
+ if (playable instanceof FeedMedia) {
+ SyncService.enqueueEpisodePlayed(getApplicationContext(), (FeedMedia) playable, false);
+ }
playable.onPlaybackPause(getApplicationContext());
}
}
@@ -1000,11 +992,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
Log.d(TAG, "getNextInQueue()");
FeedMedia media = (FeedMedia) currentMedia;
- try {
- media.loadMetadata();
- } catch (PlayableException e) {
- Log.e(TAG, "Unable to load metadata to get next in queue", e);
- return null;
+ if (media.getItem() == null) {
+ media.setItem(DBReader.getFeedItem(media.getItemId()));
}
FeedItem item = media.getItem();
if (item == null) {
@@ -1102,7 +1091,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
FeedMedia media = (FeedMedia) playable;
FeedItem item = media.getItem();
- boolean smartMarkAsPlayed = media.hasAlmostEnded();
+ boolean smartMarkAsPlayed = FeedItemUtil.hasAlmostEnded(media);
if (!ended && smartMarkAsPlayed) {
Log.d(TAG, "smart mark as played");
}
@@ -1114,8 +1103,10 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
if (ended || smartMarkAsPlayed) {
+ SyncService.enqueueEpisodePlayed(getApplicationContext(), media, true);
media.onPlaybackCompleted(getApplicationContext());
} else {
+ SyncService.enqueueEpisodePlayed(getApplicationContext(), media, false);
media.onPlaybackPause(getApplicationContext());
}
@@ -1128,8 +1119,12 @@ public class PlaybackService extends MediaBrowserServiceCompat {
// don't know if it actually matters to not autodownload when smart mark as played is triggered
DBWriter.removeQueueItem(PlaybackService.this, ended, item);
// Delete episode if enabled
- if (item.getFeed().getPreferences().getCurrentAutoDelete()
- && (!item.isTagged(FeedItem.TAG_FAVORITE) || !UserPreferences.shouldFavoriteKeepEpisode())) {
+ FeedPreferences.AutoDeleteAction action =
+ item.getFeed().getPreferences().getCurrentAutoDelete();
+ boolean shouldAutoDelete = action == FeedPreferences.AutoDeleteAction.YES
+ || (action == FeedPreferences.AutoDeleteAction.GLOBAL && UserPreferences.isAutoDelete());
+ if (shouldAutoDelete && (!item.isTagged(FeedItem.TAG_FAVORITE)
+ || !UserPreferences.shouldFavoriteKeepEpisode())) {
DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId());
Log.d(TAG, "Episode Deleted");
}
@@ -1446,10 +1441,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
}
if (position != INVALID_TIME && duration != INVALID_TIME && playable != null) {
Log.d(TAG, "Saving current position to " + position);
- playable.saveCurrentPosition(
- PreferenceManager.getDefaultSharedPreferences(getApplicationContext()),
- position,
- System.currentTimeMillis());
+ PlayableUtils.saveCurrentPosition(playable, position, System.currentTimeMillis());
}
}
@@ -1603,6 +1595,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
+ EventBus.getDefault().post(new ServiceEvent(ServiceEvent.Action.SERVICE_SHUT_DOWN));
stateManager.stopService();
}
}
@@ -1704,10 +1697,6 @@ public class PlaybackService extends MediaBrowserServiceCompat {
return mediaPlayer.getPlayable();
}
- public boolean canSetSpeed() {
- return mediaPlayer.canSetSpeed();
- }
-
public void setSpeed(float speed) {
mediaPlayer.setPlaybackParams(speed, UserPreferences.isSkipSilence());
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
index 1f047ceee..8d9135a3a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceMediaPlayer.java
@@ -11,8 +11,8 @@ import android.view.SurfaceHolder;
import java.util.List;
import java.util.concurrent.Future;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.MediaType;
+import de.danoeh.antennapod.model.playback.Playable;
/*
@@ -144,11 +144,6 @@ public abstract class PlaybackServiceMediaPlayer {
public abstract void setStartWhenPrepared(boolean startWhenPrepared);
/**
- * Returns true if the playback speed can be adjusted.
- */
- public abstract boolean canSetSpeed();
-
- /**
* Sets the playback parameters.
* - Speed
* - SkipSilence (ExoPlayer only)
@@ -217,7 +212,7 @@ public abstract class PlaybackServiceMediaPlayer {
* could result in nonsensical results (like a status of PLAYING, but a null playable)
* @return the current player status
*/
- public PlayerStatus getPlayerStatus() {
+ public synchronized PlayerStatus getPlayerStatus() {
return playerStatus;
}
@@ -349,8 +344,6 @@ public abstract class PlaybackServiceMediaPlayer {
void playbackSpeedChanged(float s);
- void setSpeedAbilityChanged();
-
void onBufferingUpdate(int percent);
void onMediaChanged(boolean reloadUI);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
index cbfc36266..75ebf7e10 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceNotificationBuilder.java
@@ -27,7 +27,7 @@ import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import java.util.ArrayList;
import java.util.concurrent.ExecutionException;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
index b9bc0c712..8cfbfac2a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceTaskManager.java
@@ -8,6 +8,7 @@ import androidx.annotation.NonNull;
import android.util.Log;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
+import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.widget.WidgetUpdater;
import io.reactivex.disposables.Disposable;
import org.greenrobot.eventbus.EventBus;
@@ -23,10 +24,10 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import io.reactivex.Completable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
@@ -315,7 +316,7 @@ public class PlaybackServiceTaskManager {
if (media.getChapters() == null) {
chapterLoaderFuture = Completable.create(emitter -> {
- media.loadChapterMarks(context);
+ ChapterUtils.loadChapters(media, context);
emitter.onComplete();
})
.subscribeOn(Schedulers.io())
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
index d03830387..edb8bc3a9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdater.java
@@ -1,9 +1,9 @@
package de.danoeh.antennapod.core.service.playback;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.model.playback.Playable;
class PlaybackVolumeUpdater {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java
deleted file mode 100644
index 78c105e38..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportCaCerts.java
+++ /dev/null
@@ -1,105 +0,0 @@
-package de.danoeh.antennapod.core.ssl;
-
-public class BackportCaCerts {
- public static final String SECTIGO_USER_TRUST = "-----BEGIN CERTIFICATE-----\n"
- + "MIIF3jCCA8agAwIBAgIQAf1tMPyjylGoG7xkDjUDLTANBgkqhkiG9w0BAQwFADCB\n"
- + "iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl\n"
- + "cnNleSBDaXR5MR4wHAYDVQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNV\n"
- + "BAMTJVVTRVJUcnVzdCBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAw\n"
- + "MjAxMDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBiDELMAkGA1UEBhMCVVMxEzARBgNV\n"
- + "BAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0plcnNleSBDaXR5MR4wHAYDVQQKExVU\n"
- + "aGUgVVNFUlRSVVNUIE5ldHdvcmsxLjAsBgNVBAMTJVVTRVJUcnVzdCBSU0EgQ2Vy\n"
- + "dGlmaWNhdGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK\n"
- + "AoICAQCAEmUXNg7D2wiz0KxXDXbtzSfTTK1Qg2HiqiBNCS1kCdzOiZ/MPans9s/B\n"
- + "3PHTsdZ7NygRK0faOca8Ohm0X6a9fZ2jY0K2dvKpOyuR+OJv0OwWIJAJPuLodMkY\n"
- + "tJHUYmTbf6MG8YgYapAiPLz+E/CHFHv25B+O1ORRxhFnRghRy4YUVD+8M/5+bJz/\n"
- + "Fp0YvVGONaanZshyZ9shZrHUm3gDwFA66Mzw3LyeTP6vBZY1H1dat//O+T23LLb2\n"
- + "VN3I5xI6Ta5MirdcmrS3ID3KfyI0rn47aGYBROcBTkZTmzNg95S+UzeQc0PzMsNT\n"
- + "79uq/nROacdrjGCT3sTHDN/hMq7MkztReJVni+49Vv4M0GkPGw/zJSZrM233bkf6\n"
- + "c0Plfg6lZrEpfDKEY1WJxA3Bk1QwGROs0303p+tdOmw1XNtB1xLaqUkL39iAigmT\n"
- + "Yo61Zs8liM2EuLE/pDkP2QKe6xJMlXzzawWpXhaDzLhn4ugTncxbgtNMs+1b/97l\n"
- + "c6wjOy0AvzVVdAlJ2ElYGn+SNuZRkg7zJn0cTRe8yexDJtC/QV9AqURE9JnnV4ee\n"
- + "UB9XVKg+/XRjL7FQZQnmWEIuQxpMtPAlR1n6BB6T1CZGSlCBst6+eLf8ZxXhyVeE\n"
- + "Hg9j1uliutZfVS7qXMYoCAQlObgOK6nyTJccBz8NUvXt7y+CDwIDAQABo0IwQDAd\n"
- + "BgNVHQ4EFgQUU3m/WqorSs9UgOHYm8Cd8rIDZsswDgYDVR0PAQH/BAQDAgEGMA8G\n"
- + "A1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAFzUfA3P9wF9QZllDHPF\n"
- + "Up/L+M+ZBn8b2kMVn54CVVeWFPFSPCeHlCjtHzoBN6J2/FNQwISbxmtOuowhT6KO\n"
- + "VWKR82kV2LyI48SqC/3vqOlLVSoGIG1VeCkZ7l8wXEskEVX/JJpuXior7gtNn3/3\n"
- + "ATiUFJVDBwn7YKnuHKsSjKCaXqeYalltiz8I+8jRRa8YFWSQEg9zKC7F4iRO/Fjs\n"
- + "8PRF/iKz6y+O0tlFYQXBl2+odnKPi4w2r78NBc5xjeambx9spnFixdjQg3IM8WcR\n"
- + "iQycE0xyNN+81XHfqnHd4blsjDwSXWXavVcStkNr/+XeTWYRUc+ZruwXtuhxkYze\n"
- + "Sf7dNXGiFSeUHM9h4ya7b6NnJSFd5t0dCy5oGzuCr+yDZ4XUmFF0sbmZgIn/f3gZ\n"
- + "XHlKYC6SQK5MNyosycdiyA5d9zZbyuAlJQG03RoHnHcAP9Dc1ew91Pq7P8yF1m9/\n"
- + "qS3fuQL39ZeatTXaw2ewh0qpKJ4jjv9cJ2vhsE/zB+4ALtRZh8tSQZXq9EfX7mRB\n"
- + "VXyNWQKV3WKdwrnuWih0hKWbt5DHDAff9Yk2dDLWKMGwsAvgnEzDHNb842m1R0aB\n"
- + "L6KCq9NjRHDEjf8tM7qtj3u1cIiuPhnPQCjY/MiQu12ZIvVS5ljFH4gxQ+6IHdfG\n"
- + "jjxDah2nGN59PRbxYvnKkKj9\n"
- + "-----END CERTIFICATE-----\n";
-
- public static final String COMODO = "-----BEGIN CERTIFICATE-----\n"
- + "MIIF2DCCA8CgAwIBAgIQTKr5yttjb+Af907YWwOGnTANBgkqhkiG9w0BAQwFADCB\n"
- + "hTELMAkGA1UEBhMCR0IxGzAZBgNVBAgTEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4G\n"
- + "A1UEBxMHU2FsZm9yZDEaMBgGA1UEChMRQ09NT0RPIENBIExpbWl0ZWQxKzApBgNV\n"
- + "BAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMTAwMTE5\n"
- + "MDAwMDAwWhcNMzgwMTE4MjM1OTU5WjCBhTELMAkGA1UEBhMCR0IxGzAZBgNVBAgT\n"
- + "EkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UEBxMHU2FsZm9yZDEaMBgGA1UEChMR\n"
- + "Q09NT0RPIENBIExpbWl0ZWQxKzApBgNVBAMTIkNPTU9ETyBSU0EgQ2VydGlmaWNh\n"
- + "dGlvbiBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCR\n"
- + "6FSS0gpWsawNJN3Fz0RndJkrN6N9I3AAcbxT38T6KhKPS38QVr2fcHK3YX/JSw8X\n"
- + "pz3jsARh7v8Rl8f0hj4K+j5c+ZPmNHrZFGvnnLOFoIJ6dq9xkNfs/Q36nGz637CC\n"
- + "9BR++b7Epi9Pf5l/tfxnQ3K9DADWietrLNPtj5gcFKt+5eNu/Nio5JIk2kNrYrhV\n"
- + "/erBvGy2i/MOjZrkm2xpmfh4SDBF1a3hDTxFYPwyllEnvGfDyi62a+pGx8cgoLEf\n"
- + "Zd5ICLqkTqnyg0Y3hOvozIFIQ2dOciqbXL1MGyiKXCJ7tKuY2e7gUYPDCUZObT6Z\n"
- + "+pUX2nwzV0E8jVHtC7ZcryxjGt9XyD+86V3Em69FmeKjWiS0uqlWPc9vqv9JWL7w\n"
- + "qP/0uK3pN/u6uPQLOvnoQ0IeidiEyxPx2bvhiWC4jChWrBQdnArncevPDt09qZah\n"
- + "SL0896+1DSJMwBGB7FY79tOi4lu3sgQiUpWAk2nojkxl8ZEDLXB0AuqLZxUpaVIC\n"
- + "u9ffUGpVRr+goyhhf3DQw6KqLCGqR84onAZFdr+CGCe01a60y1Dma/RMhnEw6abf\n"
- + "Fobg2P9A3fvQQoh/ozM6LlweQRGBY84YcWsr7KaKtzFcOmpH4MN5WdYgGq/yapiq\n"
- + "crxXStJLnbsQ/LBMQeXtHT1eKJ2czL+zUdqnR+WEUwIDAQABo0IwQDAdBgNVHQ4E\n"
- + "FgQUu69+Aj36pvE8hI6t7jiY7NkyMtQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB\n"
- + "/wQFMAMBAf8wDQYJKoZIhvcNAQEMBQADggIBAArx1UaEt65Ru2yyTUEUAJNMnMvl\n"
- + "wFTPoCWOAvn9sKIN9SCYPBMtrFaisNZ+EZLpLrqeLppysb0ZRGxhNaKatBYSaVqM\n"
- + "4dc+pBroLwP0rmEdEBsqpIt6xf4FpuHA1sj+nq6PK7o9mfjYcwlYRm6mnPTXJ9OV\n"
- + "2jeDchzTc+CiR5kDOF3VSXkAKRzH7JsgHAckaVd4sjn8OoSgtZx8jb8uk2Intzna\n"
- + "FxiuvTwJaP+EmzzV1gsD41eeFPfR60/IvYcjt7ZJQ3mFXLrrkguhxuhoqEwWsRqZ\n"
- + "CuhTLJK7oQkYdQxlqHvLI7cawiiFwxv/0Cti76R7CZGYZ4wUAc1oBmpjIXUDgIiK\n"
- + "boHGhfKppC3n9KUkEEeDys30jXlYsQab5xoq2Z0B15R97QNKyvDb6KkBPvVWmcke\n"
- + "jkk9u+UJueBPSZI9FoJAzMxZxuY67RIuaTxslbH9qh17f4a+Hg4yRvv7E491f0yL\n"
- + "S0Zj/gA0QHDBw7mh3aZw4gSzQbzpgJHqZJx64SIDqZxubw5lT2yHh17zbqD5daWb\n"
- + "QOhTsiedSrnAdyGN/4fy3ryM7xfft0kL0fJuMAsaDk527RH89elWsn2/x20Kk4yl\n"
- + "0MC2Hb46TpSi125sC8KKfPog88Tk5c0NqMuRkrF8hey1FGlmDoLnzc7ILaZRfyHB\n"
- + "NVOFBkpdn627G190\n"
- + "-----END CERTIFICATE-----";
-
- public static final String LETSENCRYPT_ISRG = "-----BEGIN CERTIFICATE-----\n"
- + "MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw\n"
- + "TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh\n"
- + "cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4\n"
- + "WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu\n"
- + "ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY\n"
- + "MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc\n"
- + "h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+\n"
- + "0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U\n"
- + "A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW\n"
- + "T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH\n"
- + "B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC\n"
- + "B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv\n"
- + "KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn\n"
- + "OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn\n"
- + "jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw\n"
- + "qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI\n"
- + "rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV\n"
- + "HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq\n"
- + "hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL\n"
- + "ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ\n"
- + "3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK\n"
- + "NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5\n"
- + "ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur\n"
- + "TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC\n"
- + "jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc\n"
- + "oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq\n"
- + "4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA\n"
- + "mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d\n"
- + "emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=\n"
- + "-----END CERTIFICATE-----";
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java
deleted file mode 100644
index 81d2a0709..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/ssl/BackportTrustManager.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package de.danoeh.antennapod.core.ssl;
-
-import android.util.Log;
-
-import javax.net.ssl.TrustManager;
-import javax.net.ssl.TrustManagerFactory;
-import javax.net.ssl.X509TrustManager;
-import java.io.ByteArrayInputStream;
-import java.nio.charset.Charset;
-import java.security.KeyStore;
-import java.security.KeyStoreException;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertificateFactory;
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * SSL trust manager that allows old Android systems to use modern certificates.
- */
-public class BackportTrustManager {
- private static final String TAG = "BackportTrustManager";
-
- private static X509TrustManager getSystemTrustManager(KeyStore keystore) {
- TrustManagerFactory factory;
- try {
- factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
- factory.init(keystore);
- for (TrustManager manager : factory.getTrustManagers()) {
- if (manager instanceof X509TrustManager) {
- return (X509TrustManager) manager;
- }
- }
- } catch (NoSuchAlgorithmException | KeyStoreException e) {
- e.printStackTrace();
- }
- throw new IllegalStateException("Unexpected default trust managers");
- }
-
- public static X509TrustManager create() {
- try {
- KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
- keystore.load(null); // Clear
- CertificateFactory cf = CertificateFactory.getInstance("X.509");
- keystore.setCertificateEntry("BACKPORT_COMODO_ROOT_CA", cf.generateCertificate(
- new ByteArrayInputStream(BackportCaCerts.COMODO.getBytes(Charset.forName("UTF-8")))));
- keystore.setCertificateEntry("SECTIGO_USER_TRUST_CA", cf.generateCertificate(
- new ByteArrayInputStream(BackportCaCerts.SECTIGO_USER_TRUST.getBytes(Charset.forName("UTF-8")))));
- keystore.setCertificateEntry("LETSENCRYPT_ISRG_CA", cf.generateCertificate(
- new ByteArrayInputStream(BackportCaCerts.LETSENCRYPT_ISRG.getBytes(Charset.forName("UTF-8")))));
-
- List<X509TrustManager> managers = new ArrayList<>();
- managers.add(getSystemTrustManager(keystore));
- managers.add(getSystemTrustManager(null));
- return new CompositeX509TrustManager(managers);
- } catch (Exception e) {
- Log.e(TAG, Log.getStackTraceString(e));
- return null;
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java
deleted file mode 100644
index 7af96a492..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/ssl/CompositeX509TrustManager.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package de.danoeh.antennapod.core.ssl;
-
-import javax.net.ssl.X509TrustManager;
-import java.security.cert.CertificateException;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-
-/**
- * Represents an ordered list of {@link X509TrustManager}s with additive trust. If any one of the composed managers
- * trusts a certificate chain, then it is trusted by the composite manager.
- * Based on https://stackoverflow.com/a/16229909
- */
-public class CompositeX509TrustManager implements X509TrustManager {
- private final List<X509TrustManager> trustManagers;
-
- public CompositeX509TrustManager(List<X509TrustManager> trustManagers) {
- this.trustManagers = trustManagers;
- }
-
- @Override
- public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
- CertificateException reason = null;
- for (X509TrustManager trustManager : trustManagers) {
- try {
- trustManager.checkClientTrusted(chain, authType);
- return; // someone trusts them. success!
- } catch (CertificateException e) {
- // maybe someone else will trust them
- reason = e;
- }
- }
- throw reason;
- }
-
- @Override
- public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
- CertificateException reason = null;
- for (X509TrustManager trustManager : trustManagers) {
- try {
- trustManager.checkServerTrusted(chain, authType);
- return; // someone trusts them. success!
- } catch (CertificateException e) {
- // maybe someone else will trust them
- reason = e;
- }
- }
- throw reason;
- }
-
- @Override
- public X509Certificate[] getAcceptedIssuers() {
- List<X509Certificate> certificates = new ArrayList<>();
- for (X509TrustManager trustManager : trustManagers) {
- certificates.addAll(Arrays.asList(trustManager.getAcceptedIssuers()));
- }
- return certificates.toArray(new X509Certificate[0]);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java b/core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java
deleted file mode 100644
index 96a42f22d..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/ssl/NoV1SslSocketFactory.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package de.danoeh.antennapod.core.ssl;
-
-import de.danoeh.antennapod.core.util.Flavors;
-
-import javax.net.ssl.SSLContext;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-import javax.net.ssl.TrustManager;
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.security.GeneralSecurityException;
-
-/**
- * SSLSocketFactory that does not use TLS 1.0
- * This fixes issues with old Android versions that abort if the server does not know TLS 1.0
- */
-public class NoV1SslSocketFactory extends SSLSocketFactory {
- private SSLSocketFactory factory;
-
- public NoV1SslSocketFactory(TrustManager trustManager) {
- try {
- SSLContext sslContext;
-
- if (Flavors.FLAVOR == Flavors.FREE) {
- // Free flavor (bundles modern conscrypt): support for TLSv1.3 is guaranteed.
- sslContext = SSLContext.getInstance("TLSv1.3");
- } else {
- // Play flavor (security provider can vary): only TLSv1.2 is guaranteed.
- sslContext = SSLContext.getInstance("TLSv1.2");
- }
-
- sslContext.init(null, new TrustManager[] {trustManager}, null);
- factory = sslContext.getSocketFactory();
- } catch (GeneralSecurityException e) {
- e.printStackTrace();
- }
- }
-
- @Override
- public String[] getDefaultCipherSuites() {
- return factory.getDefaultCipherSuites();
- }
-
- @Override
- public String[] getSupportedCipherSuites() {
- return factory.getSupportedCipherSuites();
- }
-
- public Socket createSocket() throws IOException {
- SSLSocket result = (SSLSocket) factory.createSocket();
- configureSocket(result);
- return result;
- }
-
- public Socket createSocket(String var1, int var2) throws IOException {
- SSLSocket result = (SSLSocket) factory.createSocket(var1, var2);
- configureSocket(result);
- return result;
- }
-
- public Socket createSocket(Socket var1, String var2, int var3, boolean var4) throws IOException {
- SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4);
- configureSocket(result);
- return result;
- }
-
- public Socket createSocket(InetAddress var1, int var2) throws IOException {
- SSLSocket result = (SSLSocket) factory.createSocket(var1, var2);
- configureSocket(result);
- return result;
- }
-
- public Socket createSocket(String var1, int var2, InetAddress var3, int var4) throws IOException {
- SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4);
- configureSocket(result);
- return result;
- }
-
- public Socket createSocket(InetAddress var1, int var2, InetAddress var3, int var4) throws IOException {
- SSLSocket result = (SSLSocket) factory.createSocket(var1, var2, var3, var4);
- configureSocket(result);
- return result;
- }
-
- private void configureSocket(SSLSocket s) {
- if (Flavors.FLAVOR == Flavors.FREE) {
- // Free flavor (bundles modern conscrypt): TLSv1.3 and modern cipher suites are
- // guaranteed. Protocols older than TLSv1.2 are now deprecated and can be disabled.
- s.setEnabledProtocols(new String[] { "TLSv1.3", "TLSv1.2" });
- } else {
- // Play flavor (security provider can vary): only TLSv1.2 is guaranteed, supported
- // cipher suites may vary. Old protocols might be necessary to keep things working.
-
- // TLS 1.0 is enabled by default on some old systems, which causes connection errors.
- // This disables that.
- s.setEnabledProtocols(new String[] { "TLSv1.2", "TLSv1.1", "TLSv1" });
- }
- }
-} \ No newline at end of file
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
index 4765f9f4f..2be330a3e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APCleanupAlgorithm.java
@@ -13,8 +13,8 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
/**
* Implementation of the EpisodeCleanupAlgorithm interface used by AntennaPod.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
index 4f33af959..9fce34a44 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/APQueueCleanupAlgorithm.java
@@ -11,7 +11,7 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
/**
* A cleanup algorithm that removes any item that isn't in the queue and isn't a favorite
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
index f8b643ccf..2456a3917 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/AutomaticDownloadAlgorithm.java
@@ -7,10 +7,11 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import de.danoeh.antennapod.core.feed.FeedFilter;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.FeedFilter;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.PowerUtils;
@@ -64,7 +65,8 @@ public class AutomaticDownloadAlgorithm {
Iterator<FeedItem> it = candidates.iterator();
while (it.hasNext()) {
FeedItem item = it.next();
- if (!item.isAutoDownloadable() || item.getFeed().isLocalFeed()) {
+ if (!item.isAutoDownloadable() || FeedItemUtil.isPlaying(item.getMedia())
+ || item.getFeed().isLocalFeed()) {
it.remove();
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
index fcf61b070..dc15b8a9a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java
@@ -11,19 +11,24 @@ import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import de.danoeh.antennapod.core.feed.Chapter;
-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.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.SubscriptionsFilter;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
+import de.danoeh.antennapod.core.storage.mapper.ChapterCursorMapper;
import de.danoeh.antennapod.core.storage.mapper.FeedCursorMapper;
+import de.danoeh.antennapod.core.storage.mapper.FeedItemCursorMapper;
+import de.danoeh.antennapod.core.storage.mapper.FeedMediaCursorMapper;
+import de.danoeh.antennapod.core.storage.mapper.FeedPreferencesCursorMapper;
import de.danoeh.antennapod.core.util.LongIntMap;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator;
@@ -161,11 +166,15 @@ public final class DBReader {
* The method does NOT change the items-attribute of the feed.
*/
public static List<FeedItem> getFeedItemList(final Feed feed) {
+ return getFeedItemList(feed, FeedItemFilter.unfiltered());
+ }
+
+ public static List<FeedItem> getFeedItemList(final Feed feed, final FeedItemFilter filter) {
Log.d(TAG, "getFeedItemList() called with: " + "feed = [" + feed + "]");
PodDBAdapter adapter = PodDBAdapter.getInstance();
adapter.open();
- try (Cursor cursor = adapter.getAllItemsOfFeedCursor(feed)) {
+ try (Cursor cursor = adapter.getItemsOfFeedCursor(feed, filter)) {
List<FeedItem> items = extractItemlistFromCursor(adapter, cursor);
Collections.sort(items, new FeedItemPubdateComparator());
for (FeedItem item : items) {
@@ -194,10 +203,10 @@ public final class DBReader {
if (cursor.moveToFirst()) {
int indexMediaId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_MEDIA_ID);
do {
- FeedItem item = FeedItem.fromCursor(cursor);
+ FeedItem item = FeedItemCursorMapper.convert(cursor);
result.add(item);
if (!cursor.isNull(indexMediaId)) {
- item.setMedia(FeedMedia.fromCursor(cursor));
+ item.setMedia(FeedMediaCursorMapper.convert(cursor));
}
} while (cursor.moveToNext());
}
@@ -206,7 +215,7 @@ public final class DBReader {
private static Feed extractFeedFromCursorRow(Cursor cursor) {
Feed feed = FeedCursorMapper.convert(cursor);
- FeedPreferences preferences = FeedPreferences.fromCursor(cursor);
+ FeedPreferences preferences = FeedPreferencesCursorMapper.convert(cursor);
feed.setPreferences(preferences);
return feed;
}
@@ -480,31 +489,41 @@ public final class DBReader {
*
* @param feedId The ID of the Feed
* @return The Feed or null if the Feed could not be found. The Feeds FeedItems will also be loaded from the
- * database and the items-attribute will be set correctly.
+ * database and the items-attribute will be set correctly.
*/
+ @Nullable
public static Feed getFeed(final long feedId) {
- Log.d(TAG, "getFeed() called with: " + "feedId = [" + feedId + "]");
-
- PodDBAdapter adapter = PodDBAdapter.getInstance();
- adapter.open();
- try {
- return getFeed(feedId, adapter);
- } finally {
- adapter.close();
- }
+ return getFeed(feedId, false);
}
+ /**
+ * Loads a specific Feed from the database.
+ *
+ * @param feedId The ID of the Feed
+ * @param filtered <code>true</code> if only the visible items should be loaded according to the feed filter.
+ * @return The Feed or null if the Feed could not be found. The Feeds FeedItems will also be loaded from the
+ * database and the items-attribute will be set correctly.
+ */
@Nullable
- static Feed getFeed(final long feedId, PodDBAdapter adapter) {
+ public static Feed getFeed(final long feedId, boolean filtered) {
+ Log.d(TAG, "getFeed() called with: " + "feedId = [" + feedId + "]");
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
Feed feed = null;
try (Cursor cursor = adapter.getFeedCursor(feedId)) {
if (cursor.moveToNext()) {
feed = extractFeedFromCursorRow(cursor);
- feed.setItems(getFeedItemList(feed));
+ if (filtered) {
+ feed.setItems(getFeedItemList(feed, feed.getItemFilter()));
+ } else {
+ feed.setItems(getFeedItemList(feed));
+ }
} else {
Log.e(TAG, "getFeed could not find feed with id " + feedId);
}
return feed;
+ } finally {
+ adapter.close();
}
}
@@ -637,10 +656,7 @@ public final class DBReader {
if (cursor.moveToFirst()) {
int indexDescription = cursor.getColumnIndex(PodDBAdapter.KEY_DESCRIPTION);
String description = cursor.getString(indexDescription);
- int indexContentEncoded = cursor.getColumnIndex(PodDBAdapter.KEY_CONTENT_ENCODED);
- String contentEncoded = cursor.getString(indexContentEncoded);
- item.setDescription(description);
- item.setContentEncoded(contentEncoded);
+ item.setDescriptionIfLonger(description);
}
} finally {
adapter.close();
@@ -675,7 +691,7 @@ public final class DBReader {
}
ArrayList<Chapter> chapters = new ArrayList<>();
while (cursor.moveToNext()) {
- chapters.add(Chapter.fromCursor(cursor));
+ chapters.add(ChapterCursorMapper.convert(cursor));
}
return chapters;
}
@@ -717,7 +733,7 @@ public final class DBReader {
int indexFeedItem = mediaCursor.getColumnIndex(PodDBAdapter.KEY_FEEDITEM);
long itemId = mediaCursor.getLong(indexFeedItem);
- FeedMedia media = FeedMedia.fromCursor(mediaCursor);
+ FeedMedia media = FeedMediaCursorMapper.convert(mediaCursor);
FeedItem item = getFeedItem(itemId);
if (item != null) {
media.setItem(item);
@@ -863,32 +879,34 @@ public final class DBReader {
int numNewItems = adapter.getNumberOfNewItems();
int numDownloadedItems = adapter.getNumberOfDownloadedEpisodes();
- NavDrawerData result = new NavDrawerData(feeds, queueSize, numNewItems, numDownloadedItems,
+ List<NavDrawerData.DrawerItem> items = new ArrayList<>();
+ Map<String, NavDrawerData.FolderDrawerItem> folders = new HashMap<>();
+ for (Feed feed : feeds) {
+ for (String tag : feed.getPreferences().getTags()) {
+ NavDrawerData.FeedDrawerItem drawerItem = new NavDrawerData.FeedDrawerItem(feed, feed.getId(),
+ feedCounters.get(feed.getId()));
+ if (FeedPreferences.TAG_ROOT.equals(tag)) {
+ items.add(drawerItem);
+ continue;
+ }
+ NavDrawerData.FolderDrawerItem folder;
+ if (folders.containsKey(tag)) {
+ folder = folders.get(tag);
+ } else {
+ folder = new NavDrawerData.FolderDrawerItem(tag);
+ folders.put(tag, folder);
+ }
+ drawerItem.id |= folder.id;
+ folder.children.add(drawerItem);
+ }
+ }
+ List<NavDrawerData.FolderDrawerItem> foldersSorted = new ArrayList<>(folders.values());
+ Collections.sort(foldersSorted, (o1, o2) -> o1.getTitle().compareToIgnoreCase(o2.getTitle()));
+ items.addAll(foldersSorted);
+
+ NavDrawerData result = new NavDrawerData(items, queueSize, numNewItems, numDownloadedItems,
feedCounters, UserPreferences.getEpisodeCleanupAlgorithm().getReclaimableItems());
adapter.close();
return result;
}
-
- public static class NavDrawerData {
- public final List<Feed> feeds;
- public final int queueSize;
- public final int numNewItems;
- public final int numDownloadedItems;
- public final LongIntMap feedCounters;
- public final int reclaimableSpace;
-
- public NavDrawerData(List<Feed> feeds,
- int queueSize,
- int numNewItems,
- int numDownloadedItems,
- LongIntMap feedIndicatorValues,
- int reclaimableSpace) {
- this.feeds = feeds;
- this.queueSize = queueSize;
- this.numNewItems = numNewItems;
- this.numDownloadedItems = numDownloadedItems;
- this.feedCounters = feedIndicatorValues;
- this.reclaimableSpace = reclaimableSpace;
- }
- }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
index 2d3294284..4ccd34e28 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBTasks.java
@@ -13,9 +13,9 @@ import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.FeedItemEvent;
import de.danoeh.antennapod.core.event.FeedListUpdateEvent;
import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
@@ -320,7 +320,7 @@ public final class DBTasks {
private static Feed searchFeedByIdentifyingValueOrID(PodDBAdapter adapter,
Feed feed) {
if (feed.getId() != 0) {
- return DBReader.getFeed(feed.getId(), adapter);
+ return DBReader.getFeed(feed.getId());
} else {
List<Feed> feeds = DBReader.getFeedList();
for (Feed f : feeds) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
index 93bc74ea8..46ab7502b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBUpgrader.java
@@ -6,9 +6,9 @@ import android.database.sqlite.SQLiteDatabase;
import android.media.MediaMetadataRetriever;
import android.util.Log;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
-import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
+import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
class DBUpgrader {
/**
@@ -305,15 +305,23 @@ class DBUpgrader {
+ " ADD COLUMN " + PodDBAdapter.KEY_IMAGE_URL + " TEXT DEFAULT NULL");
}
if (oldVersion < 1090001) {
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS +
- " ADD COLUMN " + PodDBAdapter.KEY_FEED_SKIP_INTRO + " INTEGER DEFAULT 0;");
- db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS +
- " ADD COLUMN " + PodDBAdapter.KEY_FEED_SKIP_ENDING + " INTEGER DEFAULT 0;");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_FEED_SKIP_INTRO + " INTEGER DEFAULT 0;");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_FEED_SKIP_ENDING + " INTEGER DEFAULT 0;");
}
if (oldVersion < 2020000) {
db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ " ADD COLUMN " + PodDBAdapter.KEY_EPISODE_NOTIFICATION + " INTEGER DEFAULT 0;");
}
+ if (oldVersion < 2030000) {
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS
+ + " SET " + PodDBAdapter.KEY_DESCRIPTION + " = content_encoded, content_encoded = NULL "
+ + "WHERE length(" + PodDBAdapter.KEY_DESCRIPTION + ") < length(content_encoded)");
+ db.execSQL("UPDATE " + PodDBAdapter.TABLE_NAME_FEED_ITEMS + " SET content_encoded = NULL");
+ db.execSQL("ALTER TABLE " + PodDBAdapter.TABLE_NAME_FEEDS
+ + " ADD COLUMN " + PodDBAdapter.KEY_FEED_TAGS + " TEXT;");
+ }
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
index 827e30394..3231c3779 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBWriter.java
@@ -8,7 +8,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import de.danoeh.antennapod.core.sync.SyncService;
-import de.danoeh.antennapod.core.sync.model.EpisodeAction;
+import de.danoeh.antennapod.net.sync.model.EpisodeAction;
import org.greenrobot.eventbus.EventBus;
import java.io.File;
@@ -32,11 +32,11 @@ import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.PlaybackHistoryEvent;
import de.danoeh.antennapod.core.event.QueueEvent;
import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedEvent;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -46,8 +46,8 @@ import de.danoeh.antennapod.core.util.FeedItemPermutors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.Permutor;
-import de.danoeh.antennapod.core.util.SortOrder;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.feed.SortOrder;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlayableUtils;
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java
index 271babc6e..a0fac0c74 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DatabaseExporter.java
@@ -91,7 +91,10 @@ public class DatabaseExporter {
db.close();
File currentDB = context.getDatabasePath(PodDBAdapter.DATABASE_NAME);
- currentDB.delete();
+ boolean success = currentDB.delete();
+ if (!success) {
+ throw new IOException("Unable to delete old database");
+ }
FileUtils.moveFile(tempDB, currentDB);
} catch (IOException | SQLiteException e) {
Log.e(TAG, Log.getStackTraceString(e));
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
index 638c1bef5..f45cb2b66 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadRequester.java
@@ -23,10 +23,10 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedFile;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedFile;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.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;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java
index ece40353f..051dc03db 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DownloadStateProvider.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.storage;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.feed.FeedFile;
+import de.danoeh.antennapod.model.feed.FeedFile;
/**
* Allow callers to query the states of downloads, but not affect them.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java b/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java
index f0788db33..f04daf16e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithm.java
@@ -12,7 +12,7 @@ import java.util.List;
import java.util.Locale;
import java.util.concurrent.ExecutionException;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
index ea62065fc..c3dd52b49 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/FeedSearcher.java
@@ -2,8 +2,8 @@ package de.danoeh.antennapod.core.storage;
import android.content.Context;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import java.util.Collections;
import java.util.List;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java b/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
index 4b28d36b5..ed88ac1a2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculator.java
@@ -8,10 +8,10 @@ import androidx.annotation.VisibleForTesting;
import java.util.List;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
/**
* @see DBWriter#addQueueItem(Context, boolean, long...) it uses the class to determine
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/NavDrawerData.java b/core/src/main/java/de/danoeh/antennapod/core/storage/NavDrawerData.java
new file mode 100644
index 000000000..7ca90d687
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/NavDrawerData.java
@@ -0,0 +1,100 @@
+package de.danoeh.antennapod.core.storage;
+
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.core.util.LongIntMap;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class NavDrawerData {
+ public final List<DrawerItem> items;
+ public final int queueSize;
+ public final int numNewItems;
+ public final int numDownloadedItems;
+ public final LongIntMap feedCounters;
+ public final int reclaimableSpace;
+
+ public NavDrawerData(List<DrawerItem> feeds,
+ int queueSize,
+ int numNewItems,
+ int numDownloadedItems,
+ LongIntMap feedIndicatorValues,
+ int reclaimableSpace) {
+ this.items = feeds;
+ this.queueSize = queueSize;
+ this.numNewItems = numNewItems;
+ this.numDownloadedItems = numDownloadedItems;
+ this.feedCounters = feedIndicatorValues;
+ this.reclaimableSpace = reclaimableSpace;
+ }
+
+ public abstract static class DrawerItem {
+ public enum Type {
+ FOLDER, FEED
+ }
+
+ public final Type type;
+ private int layer;
+ public long id;
+
+ public DrawerItem(Type type, long id) {
+ this.type = type;
+ this.id = id;
+ }
+
+ public int getLayer() {
+ return layer;
+ }
+
+ public void setLayer(int layer) {
+ this.layer = layer;
+ }
+
+ public abstract String getTitle();
+
+ public abstract int getCounter();
+ }
+
+ public static class FolderDrawerItem extends DrawerItem {
+ public final List<DrawerItem> children = new ArrayList<>();
+ public final String name;
+ public boolean isOpen;
+
+ public FolderDrawerItem(String name) {
+ // Keep IDs >0 but make room for many feeds
+ super(DrawerItem.Type.FOLDER, Math.abs((long) name.hashCode()) << 20);
+ this.name = name;
+ }
+
+ public String getTitle() {
+ return name;
+ }
+
+ public int getCounter() {
+ int sum = 0;
+ for (DrawerItem item : children) {
+ sum += item.getCounter();
+ }
+ return sum;
+ }
+ }
+
+ public static class FeedDrawerItem extends DrawerItem {
+ public final Feed feed;
+ public final int counter;
+
+ public FeedDrawerItem(Feed feed, long id, int counter) {
+ super(DrawerItem.Type.FEED, id);
+ this.feed = feed;
+ this.counter = counter;
+ }
+
+ public String getTitle() {
+ return feed.getTitle();
+ }
+
+ public int getCounter() {
+ return counter;
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
index 69c6eef93..18810aff9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java
@@ -17,6 +17,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
+import de.danoeh.antennapod.model.feed.FeedFunding;
import de.danoeh.antennapod.core.storage.mapper.FeedItemFilterQuery;
import org.apache.commons.io.FileUtils;
@@ -29,19 +30,19 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
-import de.danoeh.antennapod.core.feed.Chapter;
-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.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus;
import de.danoeh.antennapod.core.util.LongIntMap;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
-import static de.danoeh.antennapod.core.feed.FeedPreferences.SPEED_USE_GLOBAL;
-import static de.danoeh.antennapod.core.util.SortOrder.toCodeString;
+import static de.danoeh.antennapod.model.feed.FeedPreferences.SPEED_USE_GLOBAL;
+import static de.danoeh.antennapod.model.feed.SortOrder.toCodeString;
// TODO Remove media column from feeditem table
@@ -52,7 +53,7 @@ public class PodDBAdapter {
private static final String TAG = "PodDBAdapter";
public static final String DATABASE_NAME = "Antennapod.db";
- public static final int VERSION = 2020000;
+ public static final int VERSION = 2030000;
/**
* Maximum number of arguments for IN-operator.
@@ -84,7 +85,6 @@ public class PodDBAdapter {
public static final String KEY_FEEDFILETYPE = "feedfile_type";
public static final String KEY_COMPLETION_DATE = "completion_date";
public static final String KEY_FEEDITEM = "feeditem";
- public static final String KEY_CONTENT_ENCODED = "content_encoded";
public static final String KEY_PAYMENT_LINK = "payment_link";
public static final String KEY_START = "start";
public static final String KEY_LANGUAGE = "language";
@@ -116,6 +116,7 @@ public class PodDBAdapter {
public static final String KEY_FEED_PLAYBACK_SPEED = "feed_playback_speed";
public static final String KEY_FEED_SKIP_INTRO = "feed_skip_intro";
public static final String KEY_FEED_SKIP_ENDING = "feed_skip_ending";
+ public static final String KEY_FEED_TAGS = "tags";
public static final String KEY_EPISODE_NOTIFICATION = "episode_notification";
// Table names
@@ -153,14 +154,15 @@ public class PodDBAdapter {
+ KEY_AUTO_DELETE_ACTION + " INTEGER DEFAULT 0,"
+ KEY_FEED_PLAYBACK_SPEED + " REAL DEFAULT " + SPEED_USE_GLOBAL + ","
+ KEY_FEED_VOLUME_ADAPTION + " INTEGER DEFAULT 0,"
+ + KEY_FEED_TAGS + " TEXT,"
+ KEY_FEED_SKIP_INTRO + " INTEGER DEFAULT 0,"
+ KEY_FEED_SKIP_ENDING + " INTEGER DEFAULT 0,"
+ KEY_EPISODE_NOTIFICATION + " INTEGER DEFAULT 0)";
private static final String CREATE_TABLE_FEED_ITEMS = "CREATE TABLE "
- + TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY + KEY_TITLE
- + " TEXT," + KEY_CONTENT_ENCODED + " TEXT," + KEY_PUBDATE
- + " INTEGER," + KEY_READ + " INTEGER," + KEY_LINK + " TEXT,"
+ + TABLE_NAME_FEED_ITEMS + " (" + TABLE_PRIMARY_KEY
+ + KEY_TITLE + " TEXT," + KEY_PUBDATE + " INTEGER,"
+ + KEY_READ + " INTEGER," + KEY_LINK + " TEXT,"
+ KEY_DESCRIPTION + " TEXT," + KEY_PAYMENT_LINK + " TEXT,"
+ KEY_MEDIA + " INTEGER," + KEY_FEED + " INTEGER,"
+ KEY_HAS_CHAPTERS + " INTEGER," + KEY_ITEM_IDENTIFIER + " TEXT,"
@@ -256,6 +258,7 @@ public class PodDBAdapter {
TABLE_NAME_FEEDS + "." + KEY_INCLUDE_FILTER,
TABLE_NAME_FEEDS + "." + KEY_EXCLUDE_FILTER,
TABLE_NAME_FEEDS + "." + KEY_FEED_PLAYBACK_SPEED,
+ TABLE_NAME_FEEDS + "." + KEY_FEED_TAGS,
TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_INTRO,
TABLE_NAME_FEEDS + "." + KEY_FEED_SKIP_ENDING,
TABLE_NAME_FEEDS + "." + KEY_EPISODE_NOTIFICATION
@@ -311,8 +314,7 @@ public class PodDBAdapter {
private static final String SELECT_FEED_ITEMS_AND_MEDIA_WITH_DESCRIPTION =
"SELECT " + KEYS_FEED_ITEM_WITHOUT_DESCRIPTION + ", " + KEYS_FEED_MEDIA + ", "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_DESCRIPTION + ", "
- + TABLE_NAME_FEED_ITEMS + "." + KEY_CONTENT_ENCODED
+ + TABLE_NAME_FEED_ITEMS + "." + KEY_DESCRIPTION
+ " FROM " + TABLE_NAME_FEED_ITEMS
+ JOIN_FEED_ITEM_AND_MEDIA;
private static final String SELECT_FEED_ITEMS_AND_MEDIA =
@@ -402,7 +404,7 @@ public class PodDBAdapter {
values.put(KEY_TITLE, feed.getFeedTitle());
values.put(KEY_LINK, feed.getLink());
values.put(KEY_DESCRIPTION, feed.getDescription());
- values.put(KEY_PAYMENT_LINK, feed.getPaymentLink());
+ values.put(KEY_PAYMENT_LINK, FeedFunding.getPaymentLinksAsString(feed.getPaymentLinks()));
values.put(KEY_AUTHOR, feed.getAuthor());
values.put(KEY_LANGUAGE, feed.getLanguage());
values.put(KEY_IMAGE_URL, feed.getImageUrl());
@@ -449,6 +451,7 @@ public class PodDBAdapter {
values.put(KEY_INCLUDE_FILTER, prefs.getFilter().getIncludeFilter());
values.put(KEY_EXCLUDE_FILTER, prefs.getFilter().getExcludeFilter());
values.put(KEY_FEED_PLAYBACK_SPEED, prefs.getFeedPlaybackSpeed());
+ values.put(KEY_FEED_TAGS, prefs.getTagsAsString());
values.put(KEY_FEED_SKIP_INTRO, prefs.getFeedSkipIntro());
values.put(KEY_FEED_SKIP_ENDING, prefs.getFeedSkipEnding());
values.put(KEY_EPISODE_NOTIFICATION, prefs.getShowEpisodeNotification());
@@ -627,9 +630,6 @@ public class PodDBAdapter {
if (item.getDescription() != null) {
values.put(KEY_DESCRIPTION, item.getDescription());
}
- if (item.getContentEncoded() != null) {
- values.put(KEY_CONTENT_ENCODED, item.getContentEncoded());
- }
values.put(KEY_PUBDATE, item.getPubDate().getTime());
values.put(KEY_PAYMENT_LINK, item.getPaymentLink());
if (saveFeed && item.getFeed() != null) {
@@ -951,9 +951,12 @@ public class PodDBAdapter {
* @param feed The feed you want to get the FeedItems from.
* @return The cursor of the query
*/
- public final Cursor getAllItemsOfFeedCursor(final Feed feed) {
+ public final Cursor getItemsOfFeedCursor(final Feed feed, FeedItemFilter filter) {
+ String filterQuery = FeedItemFilterQuery.generateFrom(filter);
+ String whereClauseAnd = "".equals(filterQuery) ? "" : " AND " + filterQuery;
final String query = SELECT_FEED_ITEMS_AND_MEDIA
- + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + feed.getId();
+ + " WHERE " + TABLE_NAME_FEED_ITEMS + "." + KEY_FEED + "=" + feed.getId()
+ + whereClauseAnd;
return db.rawQuery(query, null);
}
@@ -961,7 +964,7 @@ public class PodDBAdapter {
* Return the description and content_encoded of item
*/
public final Cursor getDescriptionOfItem(final FeedItem item) {
- final String query = "SELECT " + KEY_DESCRIPTION + ", " + KEY_CONTENT_ENCODED
+ final String query = "SELECT " + KEY_DESCRIPTION
+ " FROM " + TABLE_NAME_FEED_ITEMS
+ " WHERE " + KEY_ID + "=" + item.getId();
return db.rawQuery(query, null);
@@ -1314,8 +1317,6 @@ public class PodDBAdapter {
.append("(")
.append(KEY_DESCRIPTION + " LIKE '%").append(queryWords[i])
.append("%' OR ")
- .append(KEY_CONTENT_ENCODED).append(" LIKE '%").append(queryWords[i])
- .append("%' OR ")
.append(KEY_TITLE).append(" LIKE '%").append(queryWords[i])
.append("%') ");
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java
index 18a5403a7..90978d6b8 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/StatisticsItem.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.core.storage;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
public class StatisticsItem {
public final Feed feed;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java
index 08a531d17..b171f2bcc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/feed/Chapter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/ChapterCursorMapper.java
@@ -1,34 +1,22 @@
-package de.danoeh.antennapod.core.feed;
+package de.danoeh.antennapod.core.storage.mapper;
import android.database.Cursor;
-
+import androidx.annotation.NonNull;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.core.feed.ID3Chapter;
+import de.danoeh.antennapod.core.feed.SimpleChapter;
+import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-public abstract class Chapter extends FeedComponent {
-
- /** Defines starting point in milliseconds. */
- long start;
- String title;
- String link;
- String imageUrl;
-
- Chapter() {
- }
-
- Chapter(long start) {
- super();
- this.start = start;
- }
-
- Chapter(long start, String title, String link, String imageUrl) {
- super();
- this.start = start;
- this.title = title;
- this.link = link;
- this.imageUrl = imageUrl;
- }
-
- public static Chapter fromCursor(Cursor cursor) {
+/**
+ * Converts a {@link Cursor} to a {@link Chapter} object.
+ */
+public abstract class ChapterCursorMapper {
+ /**
+ * Create a {@link Chapter} instance from a database row (cursor).
+ */
+ @NonNull
+ public static Chapter convert(@NonNull Cursor cursor) {
int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
int indexTitle = cursor.getColumnIndex(PodDBAdapter.KEY_TITLE);
int indexStart = cursor.getColumnIndex(PodDBAdapter.KEY_START);
@@ -43,7 +31,7 @@ public abstract class Chapter extends FeedComponent {
String imageUrl = cursor.getString(indexImage);
int chapterType = cursor.getInt(indexChapterType);
- Chapter chapter = null;
+ Chapter chapter;
switch (chapterType) {
case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER:
chapter = new SimpleChapter(start, title, link, imageUrl);
@@ -54,47 +42,10 @@ public abstract class Chapter extends FeedComponent {
case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER:
chapter = new VorbisCommentChapter(start, title, link, imageUrl);
break;
+ default:
+ throw new IllegalArgumentException("Unknown chapter type");
}
chapter.setId(id);
return chapter;
}
-
- public abstract int getChapterType();
-
- public long getStart() {
- return start;
- }
-
- public String getTitle() {
- return title;
- }
-
- public String getLink() {
- return link;
- }
-
- public void setStart(long start) {
- this.start = start;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public void setLink(String link) {
- this.link = link;
- }
-
- public String getImageUrl() {
- return imageUrl;
- }
-
- public void setImageUrl(String imageUrl) {
- this.imageUrl = imageUrl;
- }
-
- @Override
- public String getHumanReadableIdentifier() {
- return title;
- }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapper.java
index 783fba596..a2a180735 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapper.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapper.java
@@ -4,10 +4,10 @@ import android.database.Cursor;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
/**
* Converts a {@link Cursor} to a {@link Feed} object.
@@ -63,7 +63,7 @@ public abstract class FeedCursorMapper {
cursor.getInt(indexLastUpdateFailed) > 0
);
- FeedPreferences preferences = FeedPreferences.fromCursor(cursor);
+ FeedPreferences preferences = FeedPreferencesCursorMapper.convert(cursor);
feed.setPreferences(preferences);
return feed;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java
new file mode 100644
index 000000000..19695ca95
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemCursorMapper.java
@@ -0,0 +1,46 @@
+package de.danoeh.antennapod.core.storage.mapper;
+
+import android.database.Cursor;
+import androidx.annotation.NonNull;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+
+import java.util.Date;
+
+/**
+ * Converts a {@link Cursor} to a {@link FeedItem} object.
+ */
+public abstract class FeedItemCursorMapper {
+ /**
+ * Create a {@link FeedItem} instance from a database row (cursor).
+ */
+ @NonNull
+ public static FeedItem convert(@NonNull Cursor cursor) {
+ int indexId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_ITEM_ID);
+ int indexTitle = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_TITLE);
+ int indexLink = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_LINK);
+ int indexPubDate = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PUBDATE);
+ int indexPaymentLink = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PAYMENT_LINK);
+ int indexFeedId = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_FEED);
+ int indexHasChapters = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_HAS_CHAPTERS);
+ int indexRead = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_READ);
+ int indexItemIdentifier = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_ITEM_IDENTIFIER);
+ int indexAutoDownload = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_AUTO_DOWNLOAD);
+ int indexImageUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_IMAGE_URL);
+
+ long id = cursor.getInt(indexId);
+ String title = cursor.getString(indexTitle);
+ String link = cursor.getString(indexLink);
+ Date pubDate = new Date(cursor.getLong(indexPubDate));
+ String paymentLink = cursor.getString(indexPaymentLink);
+ long feedId = cursor.getLong(indexFeedId);
+ boolean hasChapters = cursor.getInt(indexHasChapters) > 0;
+ int state = cursor.getInt(indexRead);
+ String itemIdentifier = cursor.getString(indexItemIdentifier);
+ long autoDownload = cursor.getLong(indexAutoDownload);
+ String imageUrl = cursor.getString(indexImageUrl);
+
+ return new FeedItem(id, title, link, pubDate, paymentLink, feedId,
+ hasChapters, imageUrl, state, itemIdentifier, autoDownload);
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemFilterQuery.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemFilterQuery.java
index f6963b5ac..1d9c8a9e6 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemFilterQuery.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedItemFilterQuery.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.core.storage.mapper;
-import de.danoeh.antennapod.core.feed.FeedItemFilter;
+import de.danoeh.antennapod.model.feed.FeedItemFilter;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import java.util.ArrayList;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedMediaCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedMediaCursorMapper.java
new file mode 100644
index 000000000..608fce5c4
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedMediaCursorMapper.java
@@ -0,0 +1,67 @@
+package de.danoeh.antennapod.core.storage.mapper;
+
+import android.database.Cursor;
+import androidx.annotation.NonNull;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+
+import java.util.Date;
+
+/**
+ * Converts a {@link Cursor} to a {@link FeedMedia} object.
+ */
+public abstract class FeedMediaCursorMapper {
+ /**
+ * Create a {@link FeedMedia} instance from a database row (cursor).
+ */
+ @NonNull
+ public static FeedMedia convert(@NonNull Cursor cursor) {
+ int indexId = cursor.getColumnIndexOrThrow(PodDBAdapter.SELECT_KEY_MEDIA_ID);
+ int indexPlaybackCompletionDate = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE);
+ int indexDuration = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DURATION);
+ int indexPosition = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_POSITION);
+ int indexSize = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_SIZE);
+ int indexMimeType = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_MIME_TYPE);
+ int indexFileUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_FILE_URL);
+ int indexDownloadUrl = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DOWNLOAD_URL);
+ int indexDownloaded = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_DOWNLOADED);
+ int indexPlayedDuration = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_PLAYED_DURATION);
+ int indexLastPlayedTime = cursor.getColumnIndexOrThrow(PodDBAdapter.KEY_LAST_PLAYED_TIME);
+
+ long mediaId = cursor.getLong(indexId);
+ Date playbackCompletionDate = null;
+ long playbackCompletionTime = cursor.getLong(indexPlaybackCompletionDate);
+ if (playbackCompletionTime > 0) {
+ playbackCompletionDate = new Date(playbackCompletionTime);
+ }
+
+ Boolean hasEmbeddedPicture;
+ switch (cursor.getInt(cursor.getColumnIndex(PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE))) {
+ case 1:
+ hasEmbeddedPicture = Boolean.TRUE;
+ break;
+ case 0:
+ hasEmbeddedPicture = Boolean.FALSE;
+ break;
+ default:
+ hasEmbeddedPicture = null;
+ break;
+ }
+
+ return new FeedMedia(
+ mediaId,
+ null,
+ cursor.getInt(indexDuration),
+ cursor.getInt(indexPosition),
+ cursor.getLong(indexSize),
+ cursor.getString(indexMimeType),
+ cursor.getString(indexFileUrl),
+ cursor.getString(indexDownloadUrl),
+ cursor.getInt(indexDownloaded) > 0,
+ playbackCompletionDate,
+ cursor.getInt(indexPlayedDuration),
+ hasEmbeddedPicture,
+ cursor.getLong(indexLastPlayedTime)
+ );
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java
new file mode 100644
index 000000000..cab6ea618
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/storage/mapper/FeedPreferencesCursorMapper.java
@@ -0,0 +1,72 @@
+package de.danoeh.antennapod.core.storage.mapper;
+
+import android.database.Cursor;
+import android.text.TextUtils;
+import androidx.annotation.NonNull;
+import de.danoeh.antennapod.model.feed.FeedFilter;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.core.storage.PodDBAdapter;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * Converts a {@link Cursor} to a {@link FeedPreferences} object.
+ */
+public abstract class FeedPreferencesCursorMapper {
+ /**
+ * Create a {@link FeedPreferences} instance from a database row (cursor).
+ */
+ @NonNull
+ public static FeedPreferences convert(@NonNull Cursor cursor) {
+ int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
+ int indexAutoDownload = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DOWNLOAD);
+ int indexAutoRefresh = cursor.getColumnIndex(PodDBAdapter.KEY_KEEP_UPDATED);
+ int indexAutoDeleteAction = cursor.getColumnIndex(PodDBAdapter.KEY_AUTO_DELETE_ACTION);
+ int indexVolumeAdaption = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_VOLUME_ADAPTION);
+ int indexUsername = cursor.getColumnIndex(PodDBAdapter.KEY_USERNAME);
+ int indexPassword = cursor.getColumnIndex(PodDBAdapter.KEY_PASSWORD);
+ int indexIncludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_INCLUDE_FILTER);
+ int indexExcludeFilter = cursor.getColumnIndex(PodDBAdapter.KEY_EXCLUDE_FILTER);
+ int indexFeedPlaybackSpeed = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_PLAYBACK_SPEED);
+ int indexAutoSkipIntro = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_INTRO);
+ int indexAutoSkipEnding = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_SKIP_ENDING);
+ int indexEpisodeNotification = cursor.getColumnIndex(PodDBAdapter.KEY_EPISODE_NOTIFICATION);
+ int indexTags = cursor.getColumnIndex(PodDBAdapter.KEY_FEED_TAGS);
+
+ long feedId = cursor.getLong(indexId);
+ boolean autoDownload = cursor.getInt(indexAutoDownload) > 0;
+ boolean autoRefresh = cursor.getInt(indexAutoRefresh) > 0;
+ int autoDeleteActionIndex = cursor.getInt(indexAutoDeleteAction);
+ FeedPreferences.AutoDeleteAction autoDeleteAction =
+ FeedPreferences.AutoDeleteAction.values()[autoDeleteActionIndex];
+ int volumeAdaptionValue = cursor.getInt(indexVolumeAdaption);
+ VolumeAdaptionSetting volumeAdaptionSetting = VolumeAdaptionSetting.fromInteger(volumeAdaptionValue);
+ String username = cursor.getString(indexUsername);
+ String password = cursor.getString(indexPassword);
+ String includeFilter = cursor.getString(indexIncludeFilter);
+ String excludeFilter = cursor.getString(indexExcludeFilter);
+ float feedPlaybackSpeed = cursor.getFloat(indexFeedPlaybackSpeed);
+ int feedAutoSkipIntro = cursor.getInt(indexAutoSkipIntro);
+ int feedAutoSkipEnding = cursor.getInt(indexAutoSkipEnding);
+ boolean showNotification = cursor.getInt(indexEpisodeNotification) > 0;
+ String tagsString = cursor.getString(indexTags);
+ if (TextUtils.isEmpty(tagsString)) {
+ tagsString = FeedPreferences.TAG_ROOT;
+ }
+ return new FeedPreferences(feedId,
+ autoDownload,
+ autoRefresh,
+ autoDeleteAction,
+ volumeAdaptionSetting,
+ username,
+ password,
+ new FeedFilter(includeFilter, excludeFilter),
+ feedPlaybackSpeed,
+ feedAutoSkipIntro,
+ feedAutoSkipEnding,
+ showNotification,
+ new HashSet<>(Arrays.asList(tagsString.split(FeedPreferences.TAG_SEPARATOR))));
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
index 670a65e44..4736a2c33 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/sync/SyncService.java
@@ -21,9 +21,9 @@ import androidx.work.Worker;
import androidx.work.WorkerParameters;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.SyncServiceEvent;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
@@ -32,16 +32,17 @@ 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.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.model.EpisodeAction;
-import de.danoeh.antennapod.core.sync.model.EpisodeActionChanges;
-import de.danoeh.antennapod.core.sync.model.ISyncService;
-import de.danoeh.antennapod.core.sync.model.SubscriptionChanges;
-import de.danoeh.antennapod.core.sync.model.SyncServiceException;
-import de.danoeh.antennapod.core.sync.model.UploadChangesResponse;
+import de.danoeh.antennapod.core.util.FeedItemUtil;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.URLChecker;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
+import de.danoeh.antennapod.net.sync.gpoddernet.GpodnetService;
+import de.danoeh.antennapod.net.sync.model.EpisodeAction;
+import de.danoeh.antennapod.net.sync.model.EpisodeActionChanges;
+import de.danoeh.antennapod.net.sync.model.ISyncService;
+import de.danoeh.antennapod.net.sync.model.SubscriptionChanges;
+import de.danoeh.antennapod.net.sync.model.SyncServiceException;
+import de.danoeh.antennapod.net.sync.model.UploadChangesResponse;
import io.reactivex.Completable;
import io.reactivex.schedulers.Schedulers;
import org.apache.commons.lang3.StringUtils;
@@ -80,7 +81,9 @@ public class SyncService extends Worker {
if (!GpodnetPreferences.loggedIn()) {
return Result.success();
}
- syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(), GpodnetPreferences.getHosturl());
+ syncServiceImpl = new GpodnetService(AntennapodHttpClient.getHttpClient(),
+ GpodnetPreferences.getHosturl(), GpodnetPreferences.getDeviceID(),
+ GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
SharedPreferences.Editor prefs = getApplicationContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.edit();
prefs.putLong(PREF_LAST_SYNC_ATTEMPT_TIMESTAMP, System.currentTimeMillis()).apply();
@@ -172,6 +175,25 @@ public class SyncService extends Worker {
});
}
+ public static void enqueueEpisodePlayed(Context context, FeedMedia media, boolean completed) {
+ if (!GpodnetPreferences.loggedIn()) {
+ return;
+ }
+ if (media.getItem() == null) {
+ return;
+ }
+ if (media.getStartPosition() < 0 || (!completed && media.getStartPosition() >= media.getPosition())) {
+ return;
+ }
+ EpisodeAction action = new EpisodeAction.Builder(media.getItem(), EpisodeAction.PLAY)
+ .currentTimestamp()
+ .started(media.getStartPosition() / 1000)
+ .position((completed ? media.getDuration() : media.getPosition()) / 1000)
+ .total(media.getDuration() / 1000)
+ .build();
+ SyncService.enqueueEpisodeAction(context, action);
+ }
+
public static void sync(Context context) {
OneTimeWorkRequest workRequest = getWorkRequest().build();
WorkManager.getInstance(context).enqueueUniqueWork(WORK_ID_SYNC, ExistingWorkPolicy.REPLACE, workRequest);
@@ -465,7 +487,7 @@ public class SyncService extends Worker {
if (playItem != null) {
FeedMedia media = playItem.getMedia();
media.setPosition(action.getPosition() * 1000);
- if (playItem.getMedia().hasAlmostEnded()) {
+ if (FeedItemUtil.hasAlmostEnded(playItem.getMedia())) {
Log.d(TAG, "Marking as played");
playItem.setPlayed(true);
queueToBeRemoved.add(playItem.getId());
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java
deleted file mode 100644
index cecfc0d2c..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetService.java
+++ /dev/null
@@ -1,826 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet;
-
-import android.util.Log;
-import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetDevice;
-import de.danoeh.antennapod.core.sync.model.EpisodeAction;
-import de.danoeh.antennapod.core.sync.model.EpisodeActionChanges;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetEpisodeActionPostResponse;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetPodcast;
-import de.danoeh.antennapod.core.sync.model.ISyncService;
-import de.danoeh.antennapod.core.sync.model.SubscriptionChanges;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetTag;
-import de.danoeh.antennapod.core.sync.gpoddernet.model.GpodnetUploadChangesResponse;
-import de.danoeh.antennapod.core.sync.model.SyncServiceException;
-import de.danoeh.antennapod.core.sync.model.UploadChangesResponse;
-import okhttp3.Credentials;
-import okhttp3.MediaType;
-import okhttp3.OkHttpClient;
-import okhttp3.Request;
-import okhttp3.RequestBody;
-import okhttp3.Response;
-import okhttp3.ResponseBody;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.net.URL;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Locale;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * Communicates with the gpodder.net service.
- */
-public class GpodnetService implements ISyncService {
- public static final String TAG = "GpodnetService";
- public static final String DEFAULT_BASE_HOST = "gpodder.net";
- private static final int UPLOAD_BULK_SIZE = 30;
- private static final MediaType TEXT = MediaType.parse("plain/text; charset=utf-8");
- private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");
- private String baseScheme;
- private String baseHost;
- private int basePort;
-
- private final OkHttpClient httpClient;
- private String username = null;
-
- // split into schema, host and port - missing parts are null
- private static Pattern urlsplit_regex = Pattern.compile("(?:(https?)://)?([^:]+)(?::(\\d+))?");
-
- public GpodnetService(OkHttpClient httpClient, String baseHosturl) {
- this.httpClient = httpClient;
-
- Matcher m = urlsplit_regex.matcher(baseHosturl);
- if (m.matches()) {
- this.baseScheme = m.group(1);
- this.baseHost = m.group(2);
- if (m.group(3) == null) {
- this.basePort = -1;
- } else {
- this.basePort = Integer.parseInt(m.group(3)); // regex -> can only be digits
- }
- } else {
- // URL does not match regex: use it anyway -> this will cause an exception on connect
- this.baseScheme = "https";
- this.baseHost = baseHosturl;
- this.basePort = 443;
- }
-
- if (this.baseScheme == null) { // assume https
- this.baseScheme = "https";
- }
-
- if (this.baseScheme.equals("https") && this.basePort == -1) {
- this.basePort = 443;
- }
-
- if (this.baseScheme.equals("http") && this.basePort == -1) {
- this.basePort = 80;
- }
- }
-
- private void requireLoggedIn() {
- if (username == null) {
- throw new IllegalStateException("Not logged in");
- }
- }
-
- /**
- * Returns the [count] most used tags.
- */
- public List<GpodnetTag> getTopTags(int count) throws GpodnetServiceException {
- URL url;
- try {
- url = new URI(baseScheme, null, baseHost, basePort,
- String.format(Locale.US, "/api/2/tags/%d.json", count), null, null).toURL();
- } catch (MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
-
- Request.Builder request = new Request.Builder().url(url);
- String response = executeRequest(request);
- try {
- JSONArray jsonTagList = new JSONArray(response);
- List<GpodnetTag> tagList = new ArrayList<>(jsonTagList.length());
- for (int i = 0; i < jsonTagList.length(); i++) {
- JSONObject jsonObject = jsonTagList.getJSONObject(i);
- String title = jsonObject.getString("title");
- String tag = jsonObject.getString("tag");
- int usage = jsonObject.getInt("usage");
- tagList.add(new GpodnetTag(title, tag, usage));
- }
- return tagList;
- } catch (JSONException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Returns the [count] most subscribed podcasts for the given tag.
- *
- * @throws IllegalArgumentException if tag is null
- */
- public List<GpodnetPodcast> getPodcastsForTag(@NonNull GpodnetTag tag, int count)
- throws GpodnetServiceException {
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format(Locale.US, "/api/2/tag/%s/%d.json", tag.getTag(), count), null, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- String response = executeRequest(request);
-
- JSONArray jsonArray = new JSONArray(response);
- return readPodcastListFromJsonArray(jsonArray);
-
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Returns the toplist of podcast.
- *
- * @param count of elements that should be returned. Must be in range 1..100.
- * @throws IllegalArgumentException if count is out of range.
- */
- public List<GpodnetPodcast> getPodcastToplist(int count) throws GpodnetServiceException {
- if (count < 1 || count > 100) {
- throw new IllegalArgumentException("Count must be in range 1..100");
- }
-
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format(Locale.US, "/toplist/%d.json", count), null, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- String response = executeRequest(request);
-
- JSONArray jsonArray = new JSONArray(response);
- return readPodcastListFromJsonArray(jsonArray);
-
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Returns a list of suggested podcasts for the user that is currently
- * logged in.
- * <p/>
- * This method requires authentication.
- *
- * @param count The
- * number of elements that should be returned. Must be in range
- * 1..100.
- * @throws IllegalArgumentException if count is out of range.
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public List<GpodnetPodcast> getSuggestions(int count) throws GpodnetServiceException {
- if (count < 1 || count > 100) {
- throw new IllegalArgumentException("Count must be in range 1..100");
- }
-
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format(Locale.US, "/suggestions/%d.json", count), null, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- String response = executeRequest(request);
-
- JSONArray jsonArray = new JSONArray(response);
- return readPodcastListFromJsonArray(jsonArray);
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Searches the podcast directory for a given string.
- *
- * @param query The search query
- * @param scaledLogoSize The size of the logos that are returned by the search query.
- * Must be in range 1..256. If the value is out of range, the
- * default value defined by the gpodder.net API will be used.
- */
- public List<GpodnetPodcast> searchPodcasts(String query, int scaledLogoSize) throws GpodnetServiceException {
- String parameters = (scaledLogoSize > 0 && scaledLogoSize <= 256) ? String
- .format(Locale.US, "q=%s&scale_logo=%d", query, scaledLogoSize) : String
- .format("q=%s", query);
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort, "/search.json",
- parameters, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- String response = executeRequest(request);
-
- JSONArray jsonArray = new JSONArray(response);
- return readPodcastListFromJsonArray(jsonArray);
-
- } catch (JSONException | MalformedURLException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- } catch (URISyntaxException e) {
- e.printStackTrace();
- throw new IllegalStateException(e);
- }
- }
-
- /**
- * Returns all devices of a given user.
- * <p/>
- * This method requires authentication.
- *
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public List<GpodnetDevice> getDevices() throws GpodnetServiceException {
- requireLoggedIn();
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/api/2/devices/%s.json", username), null, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- String response = executeRequest(request);
- JSONArray devicesArray = new JSONArray(response);
- return readDeviceListFromJsonArray(devicesArray);
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Returns synchronization status of devices.
- * <p/>
- * This method requires authentication.
- *
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public List<List<String>> getSynchronizedDevices() throws GpodnetServiceException {
- requireLoggedIn();
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/api/2/sync-devices/%s.json", username), null, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- String response = executeRequest(request);
- JSONObject syncStatus = new JSONObject(response);
- List<List<String>> result = new ArrayList<>();
-
- JSONArray synchronizedDevices = syncStatus.getJSONArray("synchronized");
- for (int i = 0; i < synchronizedDevices.length(); i++) {
- JSONArray groupDevices = synchronizedDevices.getJSONArray(i);
- List<String> group = new ArrayList<>();
- for (int j = 0; j < groupDevices.length(); j++) {
- group.add(groupDevices.getString(j));
- }
- result.add(group);
- }
-
- JSONArray notSynchronizedDevices = syncStatus.getJSONArray("not-synchronized");
- for (int i = 0; i < notSynchronizedDevices.length(); i++) {
- result.add(Collections.singletonList(notSynchronizedDevices.getString(i)));
- }
-
- return result;
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Configures the device of a given user.
- * <p/>
- * This method requires authentication.
- *
- * @param deviceId The ID of the device that should be configured.
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public void configureDevice(@NonNull String deviceId, String caption, GpodnetDevice.DeviceType type)
- throws GpodnetServiceException {
- requireLoggedIn();
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/api/2/devices/%s/%s.json", username, deviceId), null, null).toURL();
- String content;
- if (caption != null || type != null) {
- JSONObject jsonContent = new JSONObject();
- if (caption != null) {
- jsonContent.put("caption", caption);
- }
- if (type != null) {
- jsonContent.put("type", type.toString());
- }
- content = jsonContent.toString();
- } else {
- content = "";
- }
- RequestBody body = RequestBody.create(JSON, content);
- Request.Builder request = new Request.Builder().post(body).url(url);
- executeRequest(request);
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Links devices for synchronization.
- * <p/>
- * This method requires authentication.
- *
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public void linkDevices(@NonNull List<String> deviceIds) throws GpodnetServiceException {
- requireLoggedIn();
- try {
- final URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/api/2/sync-devices/%s.json", username), null, null).toURL();
- JSONObject jsonContent = new JSONObject();
- JSONArray group = new JSONArray();
- for (String deviceId : deviceIds) {
- group.put(deviceId);
- }
-
- JSONArray synchronizedGroups = new JSONArray();
- synchronizedGroups.put(group);
- jsonContent.put("synchronize", synchronizedGroups);
- jsonContent.put("stop-synchronize", new JSONArray());
-
- Log.d("aaaa", jsonContent.toString());
- RequestBody body = RequestBody.create(JSON, jsonContent.toString());
- Request.Builder request = new Request.Builder().post(body).url(url);
- executeRequest(request);
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Returns the subscriptions of a specific device.
- * <p/>
- * This method requires authentication.
- *
- * @param deviceId The ID of the device whose subscriptions should be returned.
- * @return A list of subscriptions in OPML format.
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public String getSubscriptionsOfDevice(@NonNull String deviceId) throws GpodnetServiceException {
- requireLoggedIn();
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/subscriptions/%s/%s.opml", username, deviceId), null, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- return executeRequest(request);
- } catch (MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Returns all subscriptions of a specific user.
- * <p/>
- * This method requires authentication.
- *
- * @return A list of subscriptions in OPML format.
- * @throws IllegalArgumentException If username is null.
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public String getSubscriptionsOfUser() throws GpodnetServiceException {
- requireLoggedIn();
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/subscriptions/%s.opml", username), null, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
- return executeRequest(request);
- } catch (MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- /**
- * Uploads the subscriptions of a specific device.
- * <p/>
- * This method requires authentication.
- *
- * @param deviceId The ID of the device whose subscriptions should be updated.
- * @param subscriptions A list of feed URLs containing all subscriptions of the
- * device.
- * @throws IllegalArgumentException If username, deviceId or subscriptions is null.
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public void uploadSubscriptions(@NonNull String deviceId, @NonNull List<String> subscriptions)
- throws GpodnetServiceException {
- requireLoggedIn();
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/subscriptions/%s/%s.txt", username, deviceId), null, null).toURL();
- StringBuilder builder = new StringBuilder();
- for (String s : subscriptions) {
- builder.append(s);
- builder.append("\n");
- }
- RequestBody body = RequestBody.create(TEXT, builder.toString());
- Request.Builder request = new Request.Builder().put(body).url(url);
- executeRequest(request);
- } catch (MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
-
- }
-
- /**
- * Updates the subscription list of a specific device.
- * <p/>
- * This method requires authentication.
- *
- * @param deviceId The ID of the device whose subscriptions should be updated.
- * @param added Collection of feed URLs of added feeds. This Collection MUST NOT contain any duplicates
- * @param removed Collection of feed URLs of removed feeds. This Collection MUST NOT contain any duplicates
- * @return a GpodnetUploadChangesResponse. See {@link GpodnetUploadChangesResponse}
- * for details.
- * @throws GpodnetServiceException if added or removed contain duplicates or if there
- * is an authentication error.
- */
- public GpodnetUploadChangesResponse uploadChanges(@NonNull String deviceId, @NonNull Collection<String> added,
- @NonNull Collection<String> removed) throws GpodnetServiceException {
- requireLoggedIn();
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/api/2/subscriptions/%s/%s.json", username, deviceId), null, null).toURL();
-
- final JSONObject requestObject = new JSONObject();
- requestObject.put("add", new JSONArray(added));
- requestObject.put("remove", new JSONArray(removed));
-
- RequestBody body = RequestBody.create(JSON, requestObject.toString());
- Request.Builder request = new Request.Builder().post(body).url(url);
-
- final String response = executeRequest(request);
- return GpodnetUploadChangesResponse.fromJSONObject(response);
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
-
- }
-
- /**
- * Returns all subscription changes of a specific device.
- * <p/>
- * This method requires authentication.
- *
- * @param deviceId The ID of the device whose subscription changes should be
- * downloaded.
- * @param timestamp A timestamp that can be used to receive all changes since a
- * specific point in time.
- * @throws GpodnetServiceAuthenticationException If there is an authentication error.
- */
- public SubscriptionChanges getSubscriptionChanges(@NonNull String deviceId, long timestamp)
- throws GpodnetServiceException {
- requireLoggedIn();
- String params = String.format(Locale.US, "since=%d", timestamp);
- String path = String.format("/api/2/subscriptions/%s/%s.json", username, deviceId);
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort, path, params, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
-
- String response = executeRequest(request);
- JSONObject changes = new JSONObject(response);
- return readSubscriptionChangesFromJsonObject(changes);
- } catch (URISyntaxException e) {
- e.printStackTrace();
- throw new IllegalStateException(e);
- } catch (JSONException | MalformedURLException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
-
- }
-
- /**
- * Updates the episode actions
- * <p/>
- * This method requires authentication.
- *
- * @param episodeActions Collection of episode actions.
- * @return a GpodnetUploadChangesResponse. See {@link GpodnetUploadChangesResponse}
- * for details.
- * @throws GpodnetServiceException if added or removed contain duplicates or if there
- * is an authentication error.
- */
- @Override
- public UploadChangesResponse uploadEpisodeActions(List<EpisodeAction> episodeActions) throws SyncServiceException {
- requireLoggedIn();
- UploadChangesResponse response = null;
- for (int i = 0; i < episodeActions.size(); i += UPLOAD_BULK_SIZE) {
- response = uploadEpisodeActionsPartial(episodeActions,
- i, Math.min(episodeActions.size(), i + UPLOAD_BULK_SIZE));
- }
- return response;
- }
-
- private UploadChangesResponse uploadEpisodeActionsPartial(List<EpisodeAction> episodeActions, int from, int to)
- throws SyncServiceException {
- try {
- Log.d(TAG, "Uploading partial actions " + from + " to " + to + " of " + episodeActions.size());
- URL url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/api/2/episodes/%s.json", username), null, null).toURL();
-
- final JSONArray list = new JSONArray();
- for (int i = from; i < to; i++) {
- EpisodeAction episodeAction = episodeActions.get(i);
- JSONObject obj = episodeAction.writeToJsonObject();
- if (obj != null) {
- obj.put("device", GpodnetPreferences.getDeviceID());
- list.put(obj);
- }
- }
-
- RequestBody body = RequestBody.create(JSON, list.toString());
- Request.Builder request = new Request.Builder().post(body).url(url);
-
- final String response = executeRequest(request);
- return GpodnetEpisodeActionPostResponse.fromJSONObject(response);
- } catch (JSONException | MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new SyncServiceException(e);
- }
- }
-
- /**
- * Returns all subscription changes of a specific device.
- * <p/>
- * This method requires authentication.
- *
- * @param timestamp A timestamp that can be used to receive all changes since a
- * specific point in time.
- * @throws SyncServiceException If there is an authentication error.
- */
- @Override
- public EpisodeActionChanges getEpisodeActionChanges(long timestamp) throws SyncServiceException {
- requireLoggedIn();
- String params = String.format(Locale.US, "since=%d", timestamp);
- String path = String.format("/api/2/episodes/%s.json", username);
- try {
- URL url = new URI(baseScheme, null, baseHost, basePort, path, params, null).toURL();
- Request.Builder request = new Request.Builder().url(url);
-
- String response = executeRequest(request);
- JSONObject json = new JSONObject(response);
- return readEpisodeActionsFromJsonObject(json);
- } catch (URISyntaxException e) {
- e.printStackTrace();
- throw new IllegalStateException(e);
- } catch (JSONException | MalformedURLException e) {
- e.printStackTrace();
- throw new SyncServiceException(e);
- }
-
- }
-
-
- /**
- * Logs in a specific user. This method must be called if any of the methods
- * that require authentication is used.
- *
- * @throws IllegalArgumentException If username or password is null.
- */
- public void authenticate(@NonNull String username, @NonNull String password) throws GpodnetServiceException {
- URL url;
- try {
- url = new URI(baseScheme, null, baseHost, basePort,
- String.format("/api/2/auth/%s/login.json", username), null, null).toURL();
- } catch (MalformedURLException | URISyntaxException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- RequestBody requestBody = RequestBody.create(TEXT, "");
- Request request = new Request.Builder().url(url).post(requestBody).build();
- try {
- String credential = Credentials.basic(username, password, Charset.forName("UTF-8"));
- Request authRequest = request.newBuilder().header("Authorization", credential).build();
- Response response = httpClient.newCall(authRequest).execute();
- checkStatusCode(response);
- response.body().close();
- this.username = username;
- } catch (Exception e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- }
-
- private String executeRequest(@NonNull Request.Builder requestB) throws GpodnetServiceException {
- Request request = requestB.build();
- String responseString;
- Response response;
- ResponseBody body = null;
- try {
-
- response = httpClient.newCall(request).execute();
- checkStatusCode(response);
- body = response.body();
- responseString = getStringFromResponseBody(body);
- } catch (IOException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- } finally {
- if (body != null) {
- body.close();
- }
- }
- return responseString;
- }
-
- private String getStringFromResponseBody(@NonNull ResponseBody body) throws GpodnetServiceException {
- ByteArrayOutputStream outputStream;
- int contentLength = (int) body.contentLength();
- if (contentLength > 0) {
- outputStream = new ByteArrayOutputStream(contentLength);
- } else {
- outputStream = new ByteArrayOutputStream();
- }
- try {
- byte[] buffer = new byte[8 * 1024];
- InputStream in = body.byteStream();
- int count;
- while ((count = in.read(buffer)) > 0) {
- outputStream.write(buffer, 0, count);
- }
- } catch (IOException e) {
- e.printStackTrace();
- throw new GpodnetServiceException(e);
- }
- return outputStream.toString();
- }
-
- private void checkStatusCode(@NonNull Response response) throws GpodnetServiceException {
- int responseCode = response.code();
- if (responseCode != HttpURLConnection.HTTP_OK) {
- if (responseCode == HttpURLConnection.HTTP_UNAUTHORIZED) {
- throw new GpodnetServiceAuthenticationException("Wrong username or password");
- } else {
- if (BuildConfig.DEBUG) {
- try {
- Log.d(TAG, response.body().string());
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- throw new GpodnetServiceBadStatusCodeException("Bad response code: " + responseCode, responseCode);
- }
- }
- }
-
- private List<GpodnetPodcast> readPodcastListFromJsonArray(@NonNull JSONArray array) throws JSONException {
- List<GpodnetPodcast> result = new ArrayList<>(array.length());
- for (int i = 0; i < array.length(); i++) {
- result.add(readPodcastFromJsonObject(array.getJSONObject(i)));
- }
- return result;
- }
-
- private GpodnetPodcast readPodcastFromJsonObject(JSONObject object) throws JSONException {
- String url = object.getString("url");
-
- String title;
- Object titleObj = object.opt("title");
- if (titleObj instanceof String) {
- title = (String) titleObj;
- } else {
- title = url;
- }
-
- String description;
- Object descriptionObj = object.opt("description");
- if (descriptionObj instanceof String) {
- description = (String) descriptionObj;
- } else {
- description = "";
- }
-
- int subscribers = object.getInt("subscribers");
-
- Object logoUrlObj = object.opt("logo_url");
- String logoUrl = (logoUrlObj instanceof String) ? (String) logoUrlObj : null;
- if (logoUrl == null) {
- Object scaledLogoUrl = object.opt("scaled_logo_url");
- if (scaledLogoUrl instanceof String) {
- logoUrl = (String) scaledLogoUrl;
- }
- }
-
- String website = null;
- Object websiteObj = object.opt("website");
- if (websiteObj instanceof String) {
- website = (String) websiteObj;
- }
- String mygpoLink = object.getString("mygpo_link");
-
- String author = null;
- Object authorObj = object.opt("author");
- if (authorObj instanceof String) {
- author = (String) authorObj;
- }
- return new GpodnetPodcast(url, title, description, subscribers, logoUrl, website, mygpoLink, author);
- }
-
- private List<GpodnetDevice> readDeviceListFromJsonArray(@NonNull JSONArray array) throws JSONException {
- List<GpodnetDevice> result = new ArrayList<>(array.length());
- for (int i = 0; i < array.length(); i++) {
- result.add(readDeviceFromJsonObject(array.getJSONObject(i)));
- }
- return result;
- }
-
- private GpodnetDevice readDeviceFromJsonObject(JSONObject object) throws JSONException {
- String id = object.getString("id");
- String caption = object.getString("caption");
- String type = object.getString("type");
- int subscriptions = object.getInt("subscriptions");
- return new GpodnetDevice(id, caption, type, subscriptions);
- }
-
- private SubscriptionChanges readSubscriptionChangesFromJsonObject(@NonNull JSONObject object)
- throws JSONException {
-
- List<String> added = new LinkedList<>();
- JSONArray jsonAdded = object.getJSONArray("add");
- for (int i = 0; i < jsonAdded.length(); i++) {
- String addedUrl = jsonAdded.getString(i);
- // gpodder escapes colons unnecessarily
- addedUrl = addedUrl.replace("%3A", ":");
- added.add(addedUrl);
- }
-
- List<String> removed = new LinkedList<>();
- JSONArray jsonRemoved = object.getJSONArray("remove");
- for (int i = 0; i < jsonRemoved.length(); i++) {
- String removedUrl = jsonRemoved.getString(i);
- // gpodder escapes colons unnecessarily
- removedUrl = removedUrl.replace("%3A", ":");
- removed.add(removedUrl);
- }
-
- long timestamp = object.getLong("timestamp");
- return new SubscriptionChanges(added, removed, timestamp);
- }
-
- private EpisodeActionChanges readEpisodeActionsFromJsonObject(@NonNull JSONObject object)
- throws JSONException {
-
- List<EpisodeAction> episodeActions = new ArrayList<>();
-
- long timestamp = object.getLong("timestamp");
- JSONArray jsonActions = object.getJSONArray("actions");
- for (int i = 0; i < jsonActions.length(); i++) {
- JSONObject jsonAction = jsonActions.getJSONObject(i);
- EpisodeAction episodeAction = EpisodeAction.readFromJsonObject(jsonAction);
- if (episodeAction != null) {
- episodeActions.add(episodeAction);
- }
- }
- return new EpisodeActionChanges(episodeActions, timestamp);
- }
-
- @Override
- public void login() throws GpodnetServiceException {
- authenticate(GpodnetPreferences.getUsername(), GpodnetPreferences.getPassword());
- }
-
- @Override
- public SubscriptionChanges getSubscriptionChanges(long lastSync) throws GpodnetServiceException {
- return getSubscriptionChanges(GpodnetPreferences.getDeviceID(), lastSync);
- }
-
- @Override
- public UploadChangesResponse uploadSubscriptionChanges(List<String> addedFeeds, List<String> removedFeeds)
- throws GpodnetServiceException {
- return uploadChanges(GpodnetPreferences.getDeviceID(), addedFeeds, removedFeeds);
- }
-
- @Override
- public void logout() {
-
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceAuthenticationException.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceAuthenticationException.java
deleted file mode 100644
index 0aec8e97e..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceAuthenticationException.java
+++ /dev/null
@@ -1,9 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet;
-
-public class GpodnetServiceAuthenticationException extends GpodnetServiceException {
- private static final long serialVersionUID = 1L;
-
- public GpodnetServiceAuthenticationException(String message) {
- super(message);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceBadStatusCodeException.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceBadStatusCodeException.java
deleted file mode 100644
index c24b5fc0a..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceBadStatusCodeException.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet;
-
-class GpodnetServiceBadStatusCodeException extends GpodnetServiceException {
- private static final long serialVersionUID = 1L;
-
- private final int statusCode;
-
- public GpodnetServiceBadStatusCodeException(String message, int statusCode) {
- super(message);
- this.statusCode = statusCode;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceException.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceException.java
deleted file mode 100644
index 10c4fdc11..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/GpodnetServiceException.java
+++ /dev/null
@@ -1,15 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet;
-
-import de.danoeh.antennapod.core.sync.model.SyncServiceException;
-
-public class GpodnetServiceException extends SyncServiceException {
- private static final long serialVersionUID = 1L;
-
- public GpodnetServiceException(String message) {
- super(message);
- }
-
- public GpodnetServiceException(Throwable e) {
- super(e);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetDevice.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetDevice.java
deleted file mode 100644
index 454b3301d..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetDevice.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet.model;
-
-import androidx.annotation.NonNull;
-
-public class GpodnetDevice {
-
- private final String id;
- private final String caption;
- private final DeviceType type;
- private final int subscriptions;
-
- public GpodnetDevice(@NonNull String id,
- String caption,
- String type,
- int subscriptions) {
- this.id = id;
- this.caption = caption;
- this.type = DeviceType.fromString(type);
- this.subscriptions = subscriptions;
- }
-
- @Override
- public String toString() {
- return "GpodnetDevice [id=" + id + ", caption=" + caption + ", type="
- + type + ", subscriptions=" + subscriptions + "]";
- }
-
- public enum DeviceType {
- DESKTOP, LAPTOP, MOBILE, SERVER, OTHER;
-
- static DeviceType fromString(String s) {
- if (s == null) {
- return OTHER;
- }
-
- switch (s) {
- case "desktop":
- return DESKTOP;
- case "laptop":
- return LAPTOP;
- case "mobile":
- return MOBILE;
- case "server":
- return SERVER;
- default:
- return OTHER;
- }
- }
-
- @Override
- public String toString() {
- return super.toString().toLowerCase();
- }
-
- }
-
- public String getId() {
- return id;
- }
-
- public String getCaption() {
- return caption;
- }
-
- public DeviceType getType() {
- return type;
- }
-
- public int getSubscriptions() {
- return subscriptions;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetEpisodeActionPostResponse.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetEpisodeActionPostResponse.java
deleted file mode 100644
index ae9ab9d70..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetEpisodeActionPostResponse.java
+++ /dev/null
@@ -1,49 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet.model;
-
-import androidx.collection.ArrayMap;
-
-import de.danoeh.antennapod.core.sync.model.UploadChangesResponse;
-import org.apache.commons.lang3.builder.ToStringBuilder;
-import org.apache.commons.lang3.builder.ToStringStyle;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Map;
-
-public class GpodnetEpisodeActionPostResponse extends UploadChangesResponse {
- /**
- * URLs that should be updated. The key of the map is the original URL, the value of the map
- * is the sanitized URL.
- */
- private final Map<String, String> updatedUrls;
-
- private GpodnetEpisodeActionPostResponse(long timestamp, Map<String, String> updatedUrls) {
- super(timestamp);
- this.updatedUrls = updatedUrls;
- }
-
- /**
- * Creates a new GpodnetUploadChangesResponse-object from a JSON object that was
- * returned by an uploadChanges call.
- *
- * @throws org.json.JSONException If the method could not parse the JSONObject.
- */
- public static GpodnetEpisodeActionPostResponse fromJSONObject(String objectString) throws JSONException {
- final JSONObject object = new JSONObject(objectString);
- final long timestamp = object.getLong("timestamp");
- JSONArray urls = object.getJSONArray("update_urls");
- Map<String, String> updatedUrls = new ArrayMap<>(urls.length());
- for (int i = 0; i < urls.length(); i++) {
- JSONArray urlPair = urls.getJSONArray(i);
- updatedUrls.put(urlPair.getString(0), urlPair.getString(1));
- }
- return new GpodnetEpisodeActionPostResponse(timestamp, updatedUrls);
- }
-
- @Override
- public String toString() {
- return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
- }
-}
-
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetPodcast.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetPodcast.java
deleted file mode 100644
index bc4969758..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetPodcast.java
+++ /dev/null
@@ -1,72 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet.model;
-
-import androidx.annotation.NonNull;
-
-public class GpodnetPodcast {
- private final String url;
- private final String title;
- private final String description;
- private final int subscribers;
- private final String logoUrl;
- private final String website;
- private final String mygpoLink;
- private final String author;
-
- public GpodnetPodcast(@NonNull String url,
- @NonNull String title,
- @NonNull String description,
- int subscribers,
- String logoUrl,
- String website,
- String mygpoLink,
- String author
- ) {
- this.url = url;
- this.title = title;
- this.description = description;
- this.subscribers = subscribers;
- this.logoUrl = logoUrl;
- this.website = website;
- this.mygpoLink = mygpoLink;
- this.author = author;
- }
-
- @Override
- public String toString() {
- return "GpodnetPodcast [url=" + url + ", title=" + title
- + ", description=" + description + ", subscribers="
- + subscribers + ", logoUrl=" + logoUrl + ", website=" + website
- + ", mygpoLink=" + mygpoLink + "]";
- }
-
- public String getUrl() {
- return url;
- }
-
- public String getTitle() {
- return title;
- }
-
- public String getDescription() {
- return description;
- }
-
- public int getSubscribers() {
- return subscribers;
- }
-
- public String getLogoUrl() {
- return logoUrl;
- }
-
- public String getWebsite() {
- return website;
- }
-
- public String getAuthor() { return author; }
-
- public String getMygpoLink() {
- return mygpoLink;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetTag.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetTag.java
deleted file mode 100644
index 93abf4688..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetTag.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet.model;
-
-import android.os.Parcel;
-import android.os.Parcelable;
-import androidx.annotation.NonNull;
-
-public class GpodnetTag implements Parcelable {
-
- private final String title;
- private final String tag;
- private final int usage;
-
- public GpodnetTag(@NonNull String title, @NonNull String tag, int usage) {
- this.title = title;
- this.tag = tag;
- this.usage = usage;
- }
-
- private GpodnetTag(Parcel in) {
- title = in.readString();
- tag = in.readString();
- usage = in.readInt();
- }
-
- @Override
- public String toString() {
- return "GpodnetTag [title="+title+", tag=" + tag + ", usage=" + usage + "]";
- }
-
- public String getTitle() {
- return title;
- }
-
- public String getTag() {
- return tag;
- }
-
- public int getUsage() {
- return usage;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(title);
- dest.writeString(tag);
- dest.writeInt(usage);
- }
-
- public static final Creator<GpodnetTag> CREATOR = new Creator<GpodnetTag>() {
- @Override
- public GpodnetTag createFromParcel(Parcel in) {
- return new GpodnetTag(in);
- }
-
- @Override
- public GpodnetTag[] newArray(int size) {
- return new GpodnetTag[size];
- }
- };
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetUploadChangesResponse.java b/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetUploadChangesResponse.java
deleted file mode 100644
index 790ba547f..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/gpoddernet/model/GpodnetUploadChangesResponse.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package de.danoeh.antennapod.core.sync.gpoddernet.model;
-
-import androidx.collection.ArrayMap;
-
-import de.danoeh.antennapod.core.sync.gpoddernet.GpodnetService;
-import de.danoeh.antennapod.core.sync.model.UploadChangesResponse;
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.Map;
-
-/**
- * Object returned by {@link GpodnetService} in uploadChanges method.
- */
-public class GpodnetUploadChangesResponse extends UploadChangesResponse {
- /**
- * URLs that should be updated. The key of the map is the original URL, the value of the map
- * is the sanitized URL.
- */
- public final Map<String, String> updatedUrls;
-
- public GpodnetUploadChangesResponse(long timestamp, Map<String, String> updatedUrls) {
- super(timestamp);
- this.updatedUrls = updatedUrls;
- }
-
- /**
- * Creates a new GpodnetUploadChangesResponse-object from a JSON object that was
- * returned by an uploadChanges call.
- *
- * @throws org.json.JSONException If the method could not parse the JSONObject.
- */
- public static GpodnetUploadChangesResponse fromJSONObject(String objectString) throws JSONException {
- final JSONObject object = new JSONObject(objectString);
- final long timestamp = object.getLong("timestamp");
- Map<String, String> updatedUrls = new ArrayMap<>();
- JSONArray urls = object.getJSONArray("update_urls");
- for (int i = 0; i < urls.length(); i++) {
- JSONArray urlPair = urls.getJSONArray(i);
- updatedUrls.put(urlPair.getString(0), urlPair.getString(1));
- }
- return new GpodnetUploadChangesResponse(timestamp, updatedUrls);
- }
-
- @Override
- public String toString() {
- return "GpodnetUploadChangesResponse{" +
- "timestamp=" + timestamp +
- ", updatedUrls=" + updatedUrls +
- '}';
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeAction.java b/core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeAction.java
deleted file mode 100644
index 798be8d96..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeAction.java
+++ /dev/null
@@ -1,260 +0,0 @@
-package de.danoeh.antennapod.core.sync.model;
-
-import android.text.TextUtils;
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-import androidx.core.util.ObjectsCompat;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.util.DateUtils;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Locale;
-import java.util.TimeZone;
-
-public class EpisodeAction {
- private static final String TAG = "EpisodeAction";
- public static final Action NEW = Action.NEW;
- public static final Action DOWNLOAD = Action.DOWNLOAD;
- public static final Action PLAY = Action.PLAY;
- public static final Action DELETE = Action.DELETE;
-
- private final String podcast;
- private final String episode;
- private final Action action;
- private final Date timestamp;
- private final int started;
- private final int position;
- private final int total;
-
- private EpisodeAction(Builder builder) {
- this.podcast = builder.podcast;
- this.episode = builder.episode;
- this.action = builder.action;
- this.timestamp = builder.timestamp;
- this.started = builder.started;
- this.position = builder.position;
- this.total = builder.total;
- }
-
- /**
- * Create an episode action object from JSON representation. Mandatory fields are "podcast",
- * "episode" and "action".
- *
- * @param object JSON representation
- * @return episode action object, or null if mandatory values are missing
- */
- public static EpisodeAction readFromJsonObject(JSONObject object) {
- String podcast = object.optString("podcast", null);
- String episode = object.optString("episode", null);
- String actionString = object.optString("action", null);
- if (TextUtils.isEmpty(podcast) || TextUtils.isEmpty(episode) || TextUtils.isEmpty(actionString)) {
- return null;
- }
- EpisodeAction.Action action;
- try {
- action = EpisodeAction.Action.valueOf(actionString.toUpperCase());
- } catch (IllegalArgumentException e) {
- return null;
- }
- EpisodeAction.Builder builder = new EpisodeAction.Builder(podcast, episode, action);
- String utcTimestamp = object.optString("timestamp", null);
- if (!TextUtils.isEmpty(utcTimestamp)) {
- builder.timestamp(DateUtils.parse(utcTimestamp));
- }
- if (action == EpisodeAction.Action.PLAY) {
- int started = object.optInt("started", -1);
- int position = object.optInt("position", -1);
- int total = object.optInt("total", -1);
- if (started >= 0 && position > 0 && total > 0) {
- builder
- .started(started)
- .position(position)
- .total(total);
- }
- }
- return builder.build();
- }
-
- public String getPodcast() {
- return this.podcast;
- }
-
- public String getEpisode() {
- return this.episode;
- }
-
- public Action getAction() {
- return this.action;
- }
-
- private String getActionString() {
- return this.action.name().toLowerCase();
- }
-
- public Date getTimestamp() {
- return this.timestamp;
- }
-
- /**
- * Returns the position (in seconds) at which the client started playback.
- *
- * @return start position (in seconds)
- */
- public int getStarted() {
- return this.started;
- }
-
- /**
- * Returns the position (in seconds) at which the client stopped playback.
- *
- * @return stop position (in seconds)
- */
- public int getPosition() {
- return this.position;
- }
-
- /**
- * Returns the total length of the file in seconds.
- *
- * @return total length in seconds
- */
- public int getTotal() {
- return this.total;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (!(o instanceof EpisodeAction)) {
- return false;
- }
-
- EpisodeAction that = (EpisodeAction) o;
- return started == that.started && position == that.position && total == that.total && action != that.action
- && ObjectsCompat.equals(podcast, that.podcast)
- && ObjectsCompat.equals(episode, that.episode)
- && ObjectsCompat.equals(timestamp, that.timestamp);
- }
-
- @Override
- public int hashCode() {
- int result = podcast != null ? podcast.hashCode() : 0;
- result = 31 * result + (episode != null ? episode.hashCode() : 0);
- result = 31 * result + (action != null ? action.hashCode() : 0);
- result = 31 * result + (timestamp != null ? timestamp.hashCode() : 0);
- result = 31 * result + started;
- result = 31 * result + position;
- result = 31 * result + total;
- return result;
- }
-
- /**
- * Returns a JSON object representation of this object.
- *
- * @return JSON object representation, or null if the object is invalid
- */
- public JSONObject writeToJsonObject() {
- JSONObject obj = new JSONObject();
- try {
- obj.putOpt("podcast", this.podcast);
- obj.putOpt("episode", this.episode);
- obj.put("action", this.getActionString());
- SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US);
- formatter.setTimeZone(TimeZone.getTimeZone("UTC"));
- obj.put("timestamp", formatter.format(this.timestamp));
- if (this.getAction() == Action.PLAY) {
- obj.put("started", this.started);
- obj.put("position", this.position);
- obj.put("total", this.total);
- }
- } catch (JSONException e) {
- Log.e(TAG, "writeToJSONObject(): " + e.getMessage());
- return null;
- }
- return obj;
- }
-
- @NonNull
- @Override
- public String toString() {
- return "EpisodeAction{"
- + "podcast='" + podcast + '\''
- + ", episode='" + episode + '\''
- + ", action=" + action
- + ", timestamp=" + timestamp
- + ", started=" + started
- + ", position=" + position
- + ", total=" + total
- + '}';
- }
-
- public enum Action {
- NEW, DOWNLOAD, PLAY, DELETE
- }
-
- public static class Builder {
-
- // mandatory
- private final String podcast;
- private final String episode;
- private final Action action;
-
- // optional
- private Date timestamp;
- private int started = -1;
- private int position = -1;
- private int total = -1;
-
- public Builder(FeedItem item, Action action) {
- this(item.getFeed().getDownload_url(), item.getMedia().getDownload_url(), action);
- }
-
- public Builder(String podcast, String episode, Action action) {
- this.podcast = podcast;
- this.episode = episode;
- this.action = action;
- }
-
- public Builder timestamp(Date timestamp) {
- this.timestamp = timestamp;
- return this;
- }
-
- public Builder currentTimestamp() {
- return timestamp(new Date());
- }
-
- public Builder started(int seconds) {
- if (action == Action.PLAY) {
- this.started = seconds;
- }
- return this;
- }
-
- public Builder position(int seconds) {
- if (action == Action.PLAY) {
- this.position = seconds;
- }
- return this;
- }
-
- public Builder total(int seconds) {
- if (action == Action.PLAY) {
- this.total = seconds;
- }
- return this;
- }
-
- public EpisodeAction build() {
- return new EpisodeAction(this);
- }
-
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeActionChanges.java b/core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeActionChanges.java
deleted file mode 100644
index 90af585af..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/model/EpisodeActionChanges.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package de.danoeh.antennapod.core.sync.model;
-
-
-import androidx.annotation.NonNull;
-
-import java.util.List;
-
-public class EpisodeActionChanges {
-
- private final List<EpisodeAction> episodeActions;
- private final long timestamp;
-
- public EpisodeActionChanges(@NonNull List<EpisodeAction> episodeActions, long timestamp) {
- this.episodeActions = episodeActions;
- this.timestamp = timestamp;
- }
-
- public List<EpisodeAction> getEpisodeActions() {
- return this.episodeActions;
- }
-
- public long getTimestamp() {
- return this.timestamp;
- }
-
- @NonNull
- @Override
- public String toString() {
- return "EpisodeActionGetResponse{"
- + "episodeActions=" + episodeActions
- + ", timestamp=" + timestamp
- + '}';
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/model/ISyncService.java b/core/src/main/java/de/danoeh/antennapod/core/sync/model/ISyncService.java
deleted file mode 100644
index 473072b97..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/model/ISyncService.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package de.danoeh.antennapod.core.sync.model;
-
-import java.util.List;
-
-public interface ISyncService {
-
- void login() throws SyncServiceException;
-
- SubscriptionChanges getSubscriptionChanges(long lastSync) throws SyncServiceException;
-
- UploadChangesResponse uploadSubscriptionChanges(
- List<String> addedFeeds, List<String> removedFeeds) throws SyncServiceException;
-
- EpisodeActionChanges getEpisodeActionChanges(long lastSync) throws SyncServiceException;
-
- UploadChangesResponse uploadEpisodeActions(List<EpisodeAction> queuedEpisodeActions)
- throws SyncServiceException;
-
- void logout() throws SyncServiceException;
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/model/SubscriptionChanges.java b/core/src/main/java/de/danoeh/antennapod/core/sync/model/SubscriptionChanges.java
deleted file mode 100644
index 51f2ed10d..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/model/SubscriptionChanges.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package de.danoeh.antennapod.core.sync.model;
-
-import androidx.annotation.NonNull;
-
-import java.util.List;
-
-public class SubscriptionChanges {
- private final List<String> added;
- private final List<String> removed;
- private final long timestamp;
-
- public SubscriptionChanges(@NonNull List<String> added,
- @NonNull List<String> removed,
- long timestamp) {
- this.added = added;
- this.removed = removed;
- this.timestamp = timestamp;
- }
-
- @Override
- public String toString() {
- return "SubscriptionChange [added=" + added.toString()
- + ", removed=" + removed.toString() + ", timestamp="
- + timestamp + "]";
- }
-
- public List<String> getAdded() {
- return added;
- }
-
- public List<String> getRemoved() {
- return removed;
- }
-
- public long getTimestamp() {
- return timestamp;
- }
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/model/SyncServiceException.java b/core/src/main/java/de/danoeh/antennapod/core/sync/model/SyncServiceException.java
deleted file mode 100644
index d7e999b45..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/model/SyncServiceException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.danoeh.antennapod.core.sync.model;
-
-public class SyncServiceException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public SyncServiceException(String message) {
- super(message);
- }
-
- public SyncServiceException(Throwable cause) {
- super(cause);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/sync/model/UploadChangesResponse.java b/core/src/main/java/de/danoeh/antennapod/core/sync/model/UploadChangesResponse.java
deleted file mode 100644
index 44850bb03..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/sync/model/UploadChangesResponse.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.danoeh.antennapod.core.sync.model;
-
-public abstract class UploadChangesResponse {
-
- /**
- * timestamp/ID that can be used for requesting changes since this upload.
- */
- public final long timestamp;
-
- public UploadChangesResponse(long timestamp) {
- this.timestamp = timestamp;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java
index 8f2ce5465..c9e6ce5fa 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandler.java
@@ -12,7 +12,7 @@ import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
public class FeedHandler {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
index 77300d864..fb4bf4707 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/FeedHandlerResult.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.syndication.handler;
import java.util.Map;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
/**
* Container for results returned by the Feed parser
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java
index 608cade88..2fecb0536 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/HandlerState.java
@@ -6,8 +6,9 @@ import java.util.ArrayList;
import java.util.Map;
import java.util.Stack;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedFunding;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.syndication.namespace.Namespace;
import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
@@ -28,6 +29,7 @@ public class HandlerState {
final Map<String, String> alternateUrls;
private final ArrayList<FeedItem> items;
private FeedItem currentItem;
+ private FeedFunding currentFunding;
final Stack<SyndElement> tagstack;
/**
* Namespaces that have been defined so far.
@@ -78,6 +80,14 @@ public class HandlerState {
this.currentItem = currentItem;
}
+ public FeedFunding getCurrentFunding() {
+ return currentFunding;
+ }
+
+ public void setCurrentFunding(FeedFunding currentFunding) {
+ this.currentFunding = currentFunding;
+ }
+
/**
* Returns the SyndElement that comes after the top element of the tagstack.
*/
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
index ab66b912b..9c09be714 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/SyndHandler.java
@@ -6,7 +6,7 @@ import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.syndication.namespace.NSContent;
import de.danoeh.antennapod.core.syndication.namespace.NSDublinCore;
import de.danoeh.antennapod.core.syndication.namespace.NSITunes;
@@ -14,6 +14,7 @@ import de.danoeh.antennapod.core.syndication.namespace.NSMedia;
import de.danoeh.antennapod.core.syndication.namespace.NSRSS20;
import de.danoeh.antennapod.core.syndication.namespace.NSSimpleChapters;
import de.danoeh.antennapod.core.syndication.namespace.Namespace;
+import de.danoeh.antennapod.core.syndication.namespace.PodcastIndex;
import de.danoeh.antennapod.core.syndication.namespace.SyndElement;
import de.danoeh.antennapod.core.syndication.namespace.atom.NSAtom;
@@ -107,9 +108,13 @@ class SyndHandler extends DefaultHandler {
&& prefix.equals(NSDublinCore.NSTAG)) {
state.namespaces.put(uri, new NSDublinCore());
Log.d(TAG, "Recognized DublinCore namespace");
+ } else if (uri.equals(PodcastIndex.NSURI) || uri.equals(PodcastIndex.NSURI2)
+ && prefix.equals(PodcastIndex.NSTAG)) {
+ state.namespaces.put(uri, new PodcastIndex());
+ Log.d(TAG, "Recognized PodcastIndex namespace");
}
- }
- }
+ }
+ }
private Namespace getHandlingNamespace(String uri, String qName) {
Namespace handler = state.namespaces.get(uri);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java
index b4c77e58d..e6011e3fa 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/handler/TypeGetter.java
@@ -13,7 +13,7 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Reader;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
/** Gets the type of a specific feed by reading the root element. */
public class TypeGetter {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java
index 306b79c15..bedf377aa 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSContent.java
@@ -5,23 +5,21 @@ import org.xml.sax.Attributes;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
public class NSContent extends Namespace {
- public static final String NSTAG = "content";
- public static final String NSURI = "http://purl.org/rss/1.0/modules/content/";
-
- private static final String ENCODED = "encoded";
-
- @Override
- public SyndElement handleElementStart(String localName, HandlerState state,
- Attributes attributes) {
- return new SyndElement(localName, this);
- }
+ public static final String NSTAG = "content";
+ public static final String NSURI = "http://purl.org/rss/1.0/modules/content/";
- @Override
- public void handleElementEnd(String localName, HandlerState state) {
- if (ENCODED.equals(localName) && state.getCurrentItem() != null &&
- state.getContentBuf() != null) {
- state.getCurrentItem().setContentEncoded(state.getContentBuf().toString());
- }
- }
+ private static final String ENCODED = "encoded";
+
+ @Override
+ public SyndElement handleElementStart(String localName, HandlerState state, Attributes attributes) {
+ return new SyndElement(localName, this);
+ }
+
+ @Override
+ public void handleElementEnd(String localName, HandlerState state) {
+ if (ENCODED.equals(localName) && state.getCurrentItem() != null && state.getContentBuf() != null) {
+ state.getCurrentItem().setDescriptionIfLonger(state.getContentBuf().toString());
+ }
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java
index 59d66a97e..0394b754a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSDublinCore.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.syndication.namespace;
import org.xml.sax.Attributes;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import de.danoeh.antennapod.core.util.DateUtils;
@@ -29,7 +29,7 @@ public class NSDublinCore extends Namespace {
String second = state.getSecondTag().getName();
if (DATE.equals(top) && ITEM.equals(second)) {
String content = state.getContentBuf().toString();
- currentItem.setPubDate(DateUtils.parse(content));
+ currentItem.setPubDate(DateUtils.parseOrNullIfFuture(content));
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java
index c57d6a5d1..1dc8d8af3 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSITunes.java
@@ -7,7 +7,6 @@ import androidx.core.text.HtmlCompat;
import org.xml.sax.Attributes;
-import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import de.danoeh.antennapod.core.syndication.parsers.DurationParser;
@@ -90,7 +89,7 @@ public class NSITunes extends Namespace {
}
if (state.getCurrentItem() != null) {
if (TextUtils.isEmpty(state.getCurrentItem().getDescription())) {
- state.getCurrentItem().setDescription(subtitle);
+ state.getCurrentItem().setDescriptionIfLonger(subtitle);
}
} else {
if (state.getFeed() != null && TextUtils.isEmpty(state.getFeed().getDescription())) {
@@ -105,16 +104,10 @@ public class NSITunes extends Namespace {
return;
}
- FeedItem currentItem = state.getCurrentItem();
- String description = getDescription(currentItem);
- if (currentItem != null && description.length() * 1.25 < summary.length()) {
- currentItem.setDescription(summary);
+ if (state.getCurrentItem() != null) {
+ state.getCurrentItem().setDescriptionIfLonger(summary);
} else if (NSRSS20.CHANNEL.equals(secondElementName) && state.getFeed() != null) {
state.getFeed().setDescription(summary);
}
}
-
- private String getDescription(FeedItem item) {
- return (item != null && item.getDescription() != null) ? item.getDescription() : "";
- }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java
index 30b01f0bc..348ffaa60 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSMedia.java
@@ -7,7 +7,7 @@ import org.xml.sax.Attributes;
import java.util.concurrent.TimeUnit;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import de.danoeh.antennapod.core.syndication.namespace.atom.AtomText;
import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils;
@@ -121,9 +121,8 @@ public class NSMedia extends Namespace {
public void handleElementEnd(String localName, HandlerState state) {
if (DESCRIPTION.equals(localName)) {
String content = state.getContentBuf().toString();
- if (state.getCurrentItem() != null && content != null
- && state.getCurrentItem().getDescription() == null) {
- state.getCurrentItem().setDescription(content);
+ if (state.getCurrentItem() != null) {
+ state.getCurrentItem().setDescriptionIfLonger(content);
}
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
index 45c5d4884..50c2dc118 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java
@@ -6,17 +6,14 @@ import android.util.Log;
import de.danoeh.antennapod.core.syndication.util.SyndStringUtils;
import org.xml.sax.Attributes;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import de.danoeh.antennapod.core.syndication.util.SyndTypeUtils;
import de.danoeh.antennapod.core.util.DateUtils;
/**
- * SAX-Parser for reading RSS-Feeds
- *
- * @author daniel
- *
+ * SAX-Parser for reading RSS-Feeds.
*/
public class NSRSS20 extends Namespace {
@@ -83,8 +80,7 @@ public class NSRSS20 extends Namespace {
if (state.getCurrentItem() != null) {
FeedItem currentItem = state.getCurrentItem();
// the title tag is optional in RSS 2.0. The description is used
- // as a
- // title if the item has no title-tag.
+ // as a title if the item has no title-tag.
if (currentItem.getTitle() == null) {
currentItem.setTitle(currentItem.getDescription());
}
@@ -128,7 +124,7 @@ public class NSRSS20 extends Namespace {
state.getCurrentItem().setLink(content);
}
} else if (PUBDATE.equals(top) && ITEM.equals(second) && state.getCurrentItem() != null) {
- state.getCurrentItem().setPubDate(DateUtils.parse(content));
+ state.getCurrentItem().setPubDate(DateUtils.parseOrNullIfFuture(content));
} else if (URL.equals(top) && IMAGE.equals(second) && CHANNEL.equals(third)) {
// prefer itunes:image
if (state.getFeed() != null && state.getFeed().getImageUrl() == null) {
@@ -138,7 +134,7 @@ public class NSRSS20 extends Namespace {
if (CHANNEL.equals(second) && state.getFeed() != null) {
state.getFeed().setDescription(content);
} else if (ITEM.equals(second) && state.getCurrentItem() != null) {
- state.getCurrentItem().setDescription(content);
+ state.getCurrentItem().setDescriptionIfLonger(content);
}
} else if (LANGUAGE.equals(localName) && state.getFeed() != null) {
state.getFeed().setLanguage(content.toLowerCase());
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
index 5761f37c8..97d0ebb53 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java
@@ -6,7 +6,7 @@ import org.xml.sax.Attributes;
import java.util.ArrayList;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.feed.SimpleChapter;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import de.danoeh.antennapod.core.util.DateUtils;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java
new file mode 100644
index 000000000..ee150f839
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/PodcastIndex.java
@@ -0,0 +1,38 @@
+package de.danoeh.antennapod.core.syndication.namespace;
+
+import org.jsoup.helper.StringUtil;
+import org.xml.sax.Attributes;
+import de.danoeh.antennapod.model.feed.FeedFunding;
+import de.danoeh.antennapod.core.syndication.handler.HandlerState;
+
+public class PodcastIndex extends Namespace {
+
+ public static final String NSTAG = "podcast";
+ public static final String NSURI = "https://github.com/Podcastindex-org/podcast-namespace/blob/main/docs/1.0.md";
+ public static final String NSURI2 = "https://podcastindex.org/namespace/1.0";
+ private static final String URL = "url";
+ private static final String FUNDING = "funding";
+
+ @Override
+ public SyndElement handleElementStart(String localName, HandlerState state,
+ Attributes attributes) {
+ if (FUNDING.equals(localName)) {
+ String href = attributes.getValue(URL);
+ FeedFunding funding = new FeedFunding(href, "");
+ state.setCurrentFunding(funding);
+ state.getFeed().addPayment(state.getCurrentFunding());
+ }
+ return new SyndElement(localName, this);
+ }
+
+ @Override
+ public void handleElementEnd(String localName, HandlerState state) {
+ if (state.getContentBuf() == null) {
+ return;
+ }
+ String content = state.getContentBuf().toString();
+ if (FUNDING.equals(localName) && state.getCurrentFunding() != null && !StringUtil.isBlank(content)) {
+ state.getCurrentFunding().setContent(content);
+ }
+ }
+}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
index 7e4350fd4..6d459075a 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/atom/NSAtom.java
@@ -3,11 +3,12 @@ package de.danoeh.antennapod.core.syndication.namespace.atom;
import android.text.TextUtils;
import android.util.Log;
+import de.danoeh.antennapod.model.feed.FeedFunding;
import de.danoeh.antennapod.core.syndication.util.SyndStringUtils;
import org.xml.sax.Attributes;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.syndication.handler.HandlerState;
import de.danoeh.antennapod.core.syndication.namespace.NSITunes;
import de.danoeh.antennapod.core.syndication.namespace.NSRSS20;
@@ -137,7 +138,7 @@ public class NSAtom extends Namespace {
//A Link such as to a directory such as iTunes
}
} else if (LINK_REL_PAYMENT.equals(rel) && state.getFeed() != null) {
- state.getFeed().setPaymentLink(href);
+ state.getFeed().addPayment(new FeedFunding(href, ""));
} else if (LINK_REL_NEXT.equals(rel) && state.getFeed() != null) {
state.getFeed().setPaged(true);
state.getFeed().setNextPageLink(href);
@@ -198,15 +199,15 @@ public class NSAtom extends Namespace {
state.getFeed().setDescription(textElement.getProcessedContent());
} else if (CONTENT.equals(top) && ENTRY.equals(second) && textElement != null &&
state.getCurrentItem() != null) {
- state.getCurrentItem().setDescription(textElement.getProcessedContent());
- } else if (SUMMARY.equals(top) && ENTRY.equals(second) && textElement != null &&
- state.getCurrentItem() != null && state.getCurrentItem().getDescription() == null) {
- state.getCurrentItem().setDescription(textElement.getProcessedContent());
+ state.getCurrentItem().setDescriptionIfLonger(textElement.getProcessedContent());
+ } else if (SUMMARY.equals(top) && ENTRY.equals(second) && textElement != null
+ && state.getCurrentItem() != null) {
+ state.getCurrentItem().setDescriptionIfLonger(textElement.getProcessedContent());
} else if (UPDATED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null &&
state.getCurrentItem().getPubDate() == null) {
- state.getCurrentItem().setPubDate(DateUtils.parse(content));
+ state.getCurrentItem().setPubDate(DateUtils.parseOrNullIfFuture(content));
} else if (PUBLISHED.equals(top) && ENTRY.equals(second) && state.getCurrentItem() != null) {
- state.getCurrentItem().setPubDate(DateUtils.parse(content));
+ state.getCurrentItem().setPubDate(DateUtils.parseOrNullIfFuture(content));
} else if (IMAGE_LOGO.equals(top) && state.getFeed() != null && state.getFeed().getImageUrl() == null) {
state.getFeed().setImageUrl(content);
} else if (IMAGE_ICON.equals(top) && state.getFeed() != null) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
index 4ad35d0c2..1bc2c13ee 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java
@@ -5,12 +5,15 @@ import android.content.Context;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Chapter;
+import de.danoeh.antennapod.core.feed.ChapterMerger;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
+import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.util.comparator.ChapterStartTimeComparator;
import de.danoeh.antennapod.core.util.id3reader.ChapterReader;
import de.danoeh.antennapod.core.util.id3reader.ID3ReaderException;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentChapterReader;
import de.danoeh.antennapod.core.util.vorbiscommentreader.VorbisCommentReaderException;
import okhttp3.Request;
@@ -47,6 +50,33 @@ public class ChapterUtils {
return chapters.size() - 1;
}
+ public static void loadChapters(Playable playable, Context context) {
+ if (playable.getChapters() != null) {
+ // Already loaded
+ return;
+ }
+
+ List<Chapter> chaptersFromDatabase = null;
+ if (playable instanceof FeedMedia) {
+ FeedMedia feedMedia = (FeedMedia) playable;
+ if (feedMedia.getItem() == null) {
+ feedMedia.setItem(DBReader.getFeedItem(feedMedia.getItemId()));
+ }
+ if (feedMedia.getItem().hasChapters()) {
+ chaptersFromDatabase = DBReader.loadChaptersOfFeedItem(feedMedia.getItem());
+ }
+ }
+
+ List<Chapter> chaptersFromMediaFile = ChapterUtils.loadChaptersFromMediaFile(playable, context);
+ List<Chapter> chapters = ChapterMerger.merge(chaptersFromDatabase, chaptersFromMediaFile);
+ if (chapters == null) {
+ // Do not try loading again. There are no chapters.
+ playable.setChapters(Collections.emptyList());
+ } else {
+ playable.setChapters(chapters);
+ }
+ }
+
public static List<Chapter> loadChaptersFromMediaFile(Playable playable, Context context) {
try (CountingInputStream in = openStream(playable, context)) {
List<Chapter> chapters = readId3ChaptersFrom(in);
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
index 196583bcd..a0b9fbef9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java
@@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.util;
import android.content.Context;
import android.util.Log;
+import androidx.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
import java.text.DateFormat;
@@ -122,6 +123,21 @@ public class DateUtils {
return null;
}
+ /**
+ * Parses the date but if the date is in the future, returns null.
+ */
+ @Nullable
+ public static Date parseOrNullIfFuture(final String input) {
+ Date date = parse(input);
+ if (date == null) {
+ return null;
+ }
+ Date now = new Date();
+ if (date.after(now)) {
+ return null;
+ }
+ return date;
+ }
/**
* Takes a string of the form [HH:]MM:SS[.mmm] and converts it to
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
index deeba9238..a3966a24c 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/EmbeddedChapterImage.java
@@ -1,7 +1,7 @@
package de.danoeh.antennapod.core.util;
import android.text.TextUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
index 9bf9b9db0..e5f60d64b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemPermutors.java
@@ -11,7 +11,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.SortOrder;
/**
* Provides method for sorting the a list of {@link FeedItem} according to rules.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
index 5ae8dbcc7..82c132dc1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/FeedItemUtil.java
@@ -2,12 +2,16 @@ package de.danoeh.antennapod.core.util;
import androidx.annotation.NonNull;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.core.service.playback.PlaybackService;
import org.apache.commons.lang3.StringUtils;
import java.util.ArrayList;
import java.util.List;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
public class FeedItemUtil {
private FeedItemUtil(){}
@@ -66,4 +70,24 @@ public class FeedItemUtil {
}
return null;
}
+
+ public static boolean hasAlmostEnded(FeedMedia media) {
+ int smartMarkAsPlayedSecs = UserPreferences.getSmartMarkAsPlayedSecs();
+ return media.getDuration() > 0 && media.getPosition() >= media.getDuration() - smartMarkAsPlayedSecs * 1000;
+ }
+
+ /**
+ * Reads playback preferences to determine whether this FeedMedia object is
+ * currently being played and the current player status is playing.
+ */
+ public static boolean isCurrentlyPlaying(FeedMedia media) {
+ return isPlaying(media) && PlaybackService.isRunning
+ && ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING));
+ }
+
+ public static boolean isPlaying(FeedMedia media) {
+ return PlaybackPreferences.getCurrentlyPlayingMediaType() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA
+ && media != null
+ && PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == media.getId();
+ }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java b/core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java
deleted file mode 100644
index 5feb232e7..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import de.danoeh.antennapod.core.BuildConfig;
-
-/**
- * Helper class to handle the different build flavors.
- */
-public enum Flavors {
- FREE,
- PLAY,
- UNKNOWN;
-
- public static final Flavors FLAVOR;
-
- static {
- if (BuildConfig.FLAVOR.equals("free")) {
- FLAVOR = FREE;
- } else if (BuildConfig.FLAVOR.equals("play")) {
- FLAVOR = PLAY;
- } else {
- FLAVOR = UNKNOWN;
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
index 8cca2f28f..12f1e98f9 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/NetworkUtils.java
@@ -17,7 +17,7 @@ import java.io.IOException;
import java.util.Arrays;
import java.util.List;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.storage.DBWriter;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java b/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
deleted file mode 100644
index 37f12c01c..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/Optional.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation. Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package de.danoeh.antennapod.core.util;
-
-import java.util.NoSuchElementException;
-import java.util.Objects;
-
-// AntennaPod's stripped-down version of Java/Android platform's java.util.Optional
-// so that it can be used on lower API level (API level 14)
-
-// Android-changed: removed ValueBased paragraph.
-/**
- * A container object which may or may not contain a non-null value.
- * If a value is present, {@code isPresent()} will return {@code true} and
- * {@code get()} will return the value.
- *
- * <p>Additional methods that depend on the presence or absence of a contained
- * value are provided, such as {@link #orElse(java.lang.Object) orElse()}
- * (return a default value if value not present) and
- * {@link #ifPresent(java.util.function.Consumer) ifPresent()} (execute a block
- * of code if the value is present).
- *
- * @since 1.8
- */
-public final class Optional<T> {
- /**
- * Common instance for {@code empty()}.
- */
- private static final Optional<?> EMPTY = new Optional<>();
-
- /**
- * If non-null, the value; if null, indicates no value is present
- */
- private final T value;
-
- /**
- * Constructs an empty instance.
- *
- * @implNote Generally only one empty instance, {@link Optional#EMPTY},
- * should exist per VM.
- */
- private Optional() {
- this.value = null;
- }
-
- /**
- * Returns an empty {@code Optional} instance. No value is present for this
- * Optional.
- *
- * @apiNote Though it may be tempting to do so, avoid testing if an object
- * is empty by comparing with {@code ==} against instances returned by
- * {@code Option.empty()}. There is no guarantee that it is a singleton.
- * Instead, use {@link #isPresent()}.
- *
- * @param <T> Type of the non-existent value
- * @return an empty {@code Optional}
- */
- public static <T> Optional<T> empty() {
- @SuppressWarnings("unchecked")
- Optional<T> t = (Optional<T>) EMPTY;
- return t;
- }
-
- /**
- * Constructs an instance with the value present.
- *
- * @param value the non-null value to be present
- * @throws NullPointerException if value is null
- */
- private Optional(T value) {
- this.value = Objects.requireNonNull(value);
- }
-
- /**
- * Returns an {@code Optional} with the specified present non-null value.
- *
- * @param <T> the class of the value
- * @param value the value to be present, which must be non-null
- * @return an {@code Optional} with the value present
- * @throws NullPointerException if value is null
- */
- public static <T> Optional<T> of(T value) {
- return new Optional<>(value);
- }
-
- /**
- * Returns an {@code Optional} describing the specified value, if non-null,
- * otherwise returns an empty {@code Optional}.
- *
- * @param <T> the class of the value
- * @param value the possibly-null value to describe
- * @return an {@code Optional} with a present value if the specified value
- * is non-null, otherwise an empty {@code Optional}
- */
- public static <T> Optional<T> ofNullable(T value) {
- return value == null ? empty() : of(value);
- }
-
- /**
- * If a value is present in this {@code Optional}, returns the value,
- * otherwise throws {@code NoSuchElementException}.
- *
- * @return the non-null value held by this {@code Optional}
- * @throws NoSuchElementException if there is no value present
- *
- * @see Optional#isPresent()
- */
- public T get() {
- if (value == null) {
- throw new NoSuchElementException("No value present");
- }
- return value;
- }
-
- /**
- * Return {@code true} if there is a value present, otherwise {@code false}.
- *
- * @return {@code true} if there is a value present, otherwise {@code false}
- */
- public boolean isPresent() {
- return value != null;
- }
-
-
- /**
- * Return the value if present, otherwise return {@code other}.
- *
- * @param other the value to be returned if there is no value present, may
- * be null
- * @return the value, if present, otherwise {@code other}
- */
- public T orElse(T other) {
- return value != null ? value : other;
- }
-
- /**
- * Indicates whether some other object is "equal to" this Optional. The
- * other object is considered equal if:
- * <ul>
- * <li>it is also an {@code Optional} and;
- * <li>both instances have no value present or;
- * <li>the present values are "equal to" each other via {@code equals()}.
- * </ul>
- *
- * @param obj an object to be tested for equality
- * @return {code true} if the other object is "equal to" this object
- * otherwise {@code false}
- */
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
-
- if (!(obj instanceof Optional)) {
- return false;
- }
-
- Optional<?> other = (Optional<?>) obj;
- return (value == other.value) || (value != null && value.equals(other.value));
- }
-
- /**
- * Returns the hash code value of the present value, if any, or 0 (zero) if
- * no value is present.
- *
- * @return hash code value of the present value or 0 if no value is present
- */
- @Override
- public int hashCode() {
- return value != null ? value.hashCode() : 0;
- }
-
- /**
- * Returns a non-empty string representation of this Optional suitable for
- * debugging. The exact presentation format is unspecified and may vary
- * between implementations and versions.
- *
- * @implSpec If a value is present the result must include its string
- * representation in the result. Empty and present Optionals must be
- * unambiguously differentiable.
- *
- * @return the string representation of this instance
- */
- @Override
- public String toString() {
- return value != null
- ? String.format("Optional[%s]", value)
- : "Optional.empty";
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
index 2622d81aa..c1c48f70d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/ShareUtils.java
@@ -13,9 +13,9 @@ import java.io.File;
import java.util.List;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
/** Utility methods for sharing data */
public class ShareUtils {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/ShownotesProvider.java b/core/src/main/java/de/danoeh/antennapod/core/util/ShownotesProvider.java
deleted file mode 100644
index a4cd83f70..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/ShownotesProvider.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import java.util.concurrent.Callable;
-
-/**
- * Created by daniel on 04.08.13.
- */
-public interface ShownotesProvider {
- /**
- * Loads shownotes. If the shownotes have to be loaded from a file or from a
- * database, it should be done in a separate thread. After the shownotes
- * have been loaded, callback.onShownotesLoaded should be called.
- */
- Callable<String> loadShownotes();
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java b/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java
deleted file mode 100644
index e3a10b14a..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/SortOrder.java
+++ /dev/null
@@ -1,71 +0,0 @@
-package de.danoeh.antennapod.core.util;
-
-import android.text.TextUtils;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import static de.danoeh.antennapod.core.util.SortOrder.Scope.INTER_FEED;
-import static de.danoeh.antennapod.core.util.SortOrder.Scope.INTRA_FEED;
-
-/**
- * Provides sort orders to sort a list of episodes.
- */
-public enum SortOrder {
- DATE_OLD_NEW(1, INTRA_FEED),
- DATE_NEW_OLD(2, INTRA_FEED),
- EPISODE_TITLE_A_Z(3, INTRA_FEED),
- EPISODE_TITLE_Z_A(4, INTRA_FEED),
- DURATION_SHORT_LONG(5, INTRA_FEED),
- DURATION_LONG_SHORT(6, INTRA_FEED),
- FEED_TITLE_A_Z(101, INTER_FEED),
- FEED_TITLE_Z_A(102, INTER_FEED),
- RANDOM(103, INTER_FEED),
- SMART_SHUFFLE_OLD_NEW(104, INTER_FEED),
- SMART_SHUFFLE_NEW_OLD(105, INTER_FEED);
-
- public enum Scope {
- INTRA_FEED, INTER_FEED
- }
-
- public final int code;
-
- @NonNull
- public final Scope scope;
-
- SortOrder(int code, @NonNull Scope scope) {
- this.code = code;
- this.scope = scope;
- }
-
- /**
- * Converts the string representation to its enum value. If the string value is unknown,
- * the given default value is returned.
- */
- public static SortOrder parseWithDefault(String value, SortOrder defaultValue) {
- try {
- return valueOf(value);
- } catch (IllegalArgumentException e) {
- return defaultValue;
- }
- }
-
- @Nullable
- public static SortOrder fromCodeString(@Nullable String codeStr) {
- if (TextUtils.isEmpty(codeStr)) {
- return null;
- }
- int code = Integer.parseInt(codeStr);
- for (SortOrder sortOrder : values()) {
- if (sortOrder.code == code) {
- return sortOrder;
- }
- }
- throw new IllegalArgumentException("Unsupported code: " + code);
- }
-
- @Nullable
- public static String toCodeString(@Nullable SortOrder sortOrder) {
- return sortOrder != null ? Integer.toString(sortOrder.code) : null;
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java
index 920a1ef8a..e36f8d0a1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/ChapterStartTimeComparator.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.util.comparator;
import java.util.Comparator;
-import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Chapter;
public class ChapterStartTimeComparator implements Comparator<Chapter> {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
index 766986bed..b16f0f1aa 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/FeedItemPubdateComparator.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.util.comparator;
import java.util.Comparator;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
/**
* Compares the pubDate of two FeedItems for sorting.
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java
index d65eb3e0b..b1e62bcf1 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/comparator/PlaybackCompletionDateComparator.java
@@ -2,7 +2,7 @@ package de.danoeh.antennapod.core.util.comparator;
import java.util.Comparator;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedItem;
public class PlaybackCompletionDateComparator implements Comparator<FeedItem> {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
index 69d8316c2..adc38b0d2 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/id3reader/ChapterReader.java
@@ -3,7 +3,7 @@ package de.danoeh.antennapod.core.util.id3reader;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
-import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.core.feed.ID3Chapter;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
deleted file mode 100644
index 007658626..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/ExternalMedia.java
+++ /dev/null
@@ -1,287 +0,0 @@
-package de.danoeh.antennapod.core.util.playback;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.media.MediaMetadataRetriever;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.util.ChapterUtils;
-import de.danoeh.antennapod.core.util.DateUtils;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.Callable;
-import org.apache.commons.io.FilenameUtils;
-
-/** Represents a media file that is stored on the local storage device. */
-public class ExternalMedia implements Playable {
- public static final int PLAYABLE_TYPE_EXTERNAL_MEDIA = 2;
- public static final String PREF_SOURCE_URL = "ExternalMedia.PrefSourceUrl";
- public static final String PREF_POSITION = "ExternalMedia.PrefPosition";
- public static final String PREF_MEDIA_TYPE = "ExternalMedia.PrefMediaType";
- public static final String PREF_LAST_PLAYED_TIME = "ExternalMedia.PrefLastPlayedTime";
-
- private final String source;
- private String episodeTitle;
- private String feedTitle;
- private MediaType mediaType;
- private Date pubDate;
- private List<Chapter> chapters;
- private int duration;
- private int position;
- private long lastPlayedTime;
-
- /**
- * Creates a new playable for files on the sd card.
- * @param source File path of the file
- * @param mediaType Type of the file
- */
- public ExternalMedia(String source, MediaType mediaType) {
- super();
- this.source = source;
- this.mediaType = mediaType;
- }
-
- /**
- * Creates a new playable for files on the sd card.
- * @param source File path of the file
- * @param mediaType Type of the file
- * @param position Position to start from
- * @param lastPlayedTime Timestamp when it was played last
- */
- public ExternalMedia(String source, MediaType mediaType, int position, long lastPlayedTime) {
- this(source, mediaType);
- this.position = position;
- this.lastPlayedTime = lastPlayedTime;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(source);
- dest.writeString(mediaType.toString());
- dest.writeInt(position);
- dest.writeLong(lastPlayedTime);
- }
-
- @Override
- public void writeToPreferences(Editor prefEditor) {
- prefEditor.putString(PREF_SOURCE_URL, source);
- prefEditor.putString(PREF_MEDIA_TYPE, mediaType.toString());
- prefEditor.putInt(PREF_POSITION, position);
- prefEditor.putLong(PREF_LAST_PLAYED_TIME, lastPlayedTime);
- }
-
- @Override
- public void loadMetadata() throws PlayableException {
- MediaMetadataRetriever mmr = new MediaMetadataRetriever();
- try {
- mmr.setDataSource(source);
- } catch (IllegalArgumentException e) {
- e.printStackTrace();
- throw new PlayableException("IllegalArgumentException when setting up MediaMetadataReceiver");
- } catch (RuntimeException e) {
- // http://code.google.com/p/android/issues/detail?id=39770
- e.printStackTrace();
- throw new PlayableException("RuntimeException when setting up MediaMetadataRetriever");
- }
- episodeTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
- if (episodeTitle == null) {
- episodeTitle = FilenameUtils.getName(source);
- }
- feedTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ALBUM);
- try {
- duration = Integer.parseInt(mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
- } catch (NumberFormatException e) {
- e.printStackTrace();
- throw new PlayableException("NumberFormatException when reading duration of media file");
- }
-
- String dateStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
- if (!TextUtils.isEmpty(dateStr)) {
- try {
- SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd'T'HHmmss", Locale.getDefault());
- pubDate = simpleDateFormat.parse(dateStr);
- } catch (ParseException parseException) {
- pubDate = DateUtils.parse(dateStr);
- }
- } else {
- pubDate = null;
- }
- }
-
- @Override
- public void loadChapterMarks(Context context) {
- setChapters(ChapterUtils.loadChaptersFromMediaFile(this, context));
- }
-
- @Override
- public String getEpisodeTitle() {
- return episodeTitle;
- }
-
- @Override
- public Callable<String> loadShownotes() {
- return () -> "";
- }
-
- @Override
- public List<Chapter> getChapters() {
- return chapters;
- }
-
- @Override
- public String getWebsiteLink() {
- return null;
- }
-
- @Override
- public String getPaymentLink() {
- return null;
- }
-
- @Override
- public String getFeedTitle() {
- return feedTitle;
- }
-
- @Override
- public Object getIdentifier() {
- return source;
- }
-
- @Override
- public int getDuration() {
- return duration;
- }
-
- @Override
- public Date getPubDate() {
- return pubDate;
- }
-
- @Override
- public int getPosition() {
- return position;
- }
-
- @Override
- public long getLastPlayedTime() {
- return lastPlayedTime;
- }
-
- @Override
- public MediaType getMediaType() {
- return mediaType;
- }
-
- @Override
- public String getLocalMediaUrl() {
- return source;
- }
-
- @Override
- public String getStreamUrl() {
- return null;
- }
-
- @Override
- public boolean localFileAvailable() {
- return true;
- }
-
- @Override
- public boolean streamAvailable() {
- return false;
- }
-
- @Override
- public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp) {
- SharedPreferences.Editor editor = pref.edit();
- editor.putInt(PREF_POSITION, newPosition);
- editor.putLong(PREF_LAST_PLAYED_TIME, timestamp);
- position = newPosition;
- lastPlayedTime = timestamp;
- editor.apply();
- }
-
- @Override
- public void setPosition(int newPosition) {
- position = newPosition;
- }
-
- @Override
- public void setDuration(int newDuration) {
- duration = newDuration;
- }
-
- @Override
- public void setLastPlayedTime(long lastPlayedTime) {
- this.lastPlayedTime = lastPlayedTime;
- }
-
- @Override
- public void onPlaybackStart() {
-
- }
-
- @Override
- public void onPlaybackPause(Context context) {
-
- }
-
- @Override
- public void onPlaybackCompleted(Context context) {
-
- }
-
- @Override
- public int getPlayableType() {
- return PLAYABLE_TYPE_EXTERNAL_MEDIA;
- }
-
- @Override
- public void setChapters(List<Chapter> chapters) {
- this.chapters = chapters;
- }
-
- public static final Parcelable.Creator<ExternalMedia> CREATOR = new Parcelable.Creator<ExternalMedia>() {
- public ExternalMedia createFromParcel(Parcel in) {
- String source = in.readString();
- MediaType type = MediaType.valueOf(in.readString());
- int position = 0;
- if (in.dataAvail() > 0) {
- position = in.readInt();
- }
- long lastPlayedTime = 0;
- if (in.dataAvail() > 0) {
- lastPlayedTime = in.readLong();
- }
-
- return new ExternalMedia(source, type, position, lastPlayedTime);
- }
-
- public ExternalMedia[] newArray(int size) {
- return new ExternalMedia[size];
- }
- };
-
- @Override
- public String getImageLocation() {
- if (localFileAvailable()) {
- return getLocalMediaUrl();
- } else {
- return null;
- }
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
index a511916fa..c726c5b5e 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/IPlayer.java
@@ -7,9 +7,6 @@ import java.io.IOException;
import java.util.List;
public interface IPlayer {
-
- boolean canSetSpeed();
-
boolean canDownmix();
int getCurrentPosition();
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
deleted file mode 100644
index 8a4c561f4..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
+++ /dev/null
@@ -1,182 +0,0 @@
-package de.danoeh.antennapod.core.util.playback;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Parcelable;
-
-import androidx.annotation.Nullable;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.util.ShownotesProvider;
-import java.util.Date;
-import java.util.List;
-
-/**
- * Interface for objects that can be played by the PlaybackService.
- */
-public interface Playable extends Parcelable, ShownotesProvider {
- public static final int INVALID_TIME = -1;
-
- /**
- * Save information about the playable in a preference so that it can be
- * restored later via PlayableUtils.createInstanceFromPreferences.
- * Implementations must NOT call commit() after they have written the values
- * to the preferences file.
- */
- void writeToPreferences(SharedPreferences.Editor prefEditor);
-
- /**
- * This method is called from a separate thread by the PlaybackService.
- * Playable objects should load their metadata in this method. This method
- * should execute as quickly as possible and NOT load chapter marks if no
- * local file is available.
- */
- void loadMetadata() throws PlayableException;
-
- /**
- * This method is called from a separate thread by the PlaybackService.
- * Playable objects should load their chapter marks in this method if no
- * local file was available when loadMetadata() was called.
- */
- void loadChapterMarks(Context context);
-
- /**
- * Returns the title of the episode that this playable represents
- */
- String getEpisodeTitle();
-
- /**
- * Returns a list of chapter marks or null if this Playable has no chapters.
- */
- List<Chapter> getChapters();
-
- /**
- * Returns a link to a website that is meant to be shown in a browser
- */
- String getWebsiteLink();
-
- String getPaymentLink();
-
- /**
- * Returns the title of the feed this Playable belongs to.
- */
- String getFeedTitle();
-
- /**
- * Returns the published date
- */
- Date getPubDate();
-
- /**
- * Returns a unique identifier, for example a file url or an ID from a
- * database.
- */
- Object getIdentifier();
-
- /**
- * Return duration of object or 0 if duration is unknown.
- */
- int getDuration();
-
- /**
- * Return position of object or 0 if position is unknown.
- */
- int getPosition();
-
- /**
- * Returns last time (in ms) when this playable was played or 0
- * if last played time is unknown.
- */
- long getLastPlayedTime();
-
- /**
- * Returns the type of media. This method should return the correct value
- * BEFORE loadMetadata() is called.
- */
- MediaType getMediaType();
-
- /**
- * Returns an url to a local file that can be played or null if this file
- * does not exist.
- */
- String getLocalMediaUrl();
-
- /**
- * Returns an url to a file that can be streamed by the player or null if
- * this url is not known.
- */
- String getStreamUrl();
-
- /**
- * Returns true if a local file that can be played is available. getFileUrl
- * MUST return a non-null string if this method returns true.
- */
- boolean localFileAvailable();
-
- /**
- * Returns true if a streamable file is available. getStreamUrl MUST return
- * a non-null string if this method returns true.
- */
- boolean streamAvailable();
-
- /**
- * Saves the current position of this object. Implementations can use the
- * provided SharedPreference to save this information and retrieve it later
- * via PlayableUtils.createInstanceFromPreferences.
- *
- * @param pref shared prefs that might be used to store this object
- * @param newPosition new playback position in ms
- * @param timestamp current time in ms
- */
- void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp);
-
- void setPosition(int newPosition);
-
- void setDuration(int newDuration);
-
- /**
- * @param lastPlayedTimestamp timestamp in ms
- */
- void setLastPlayedTime(long lastPlayedTimestamp);
-
- /**
- * This method should be called every time playback starts on this object.
- * <p/>
- * Position held by this Playable should be set accurately before a call to this method is made.
- */
- void onPlaybackStart();
-
- /**
- * This method should be called every time playback pauses or stops on this object,
- * including just before a seeking operation is performed, after which a call to
- * {@link #onPlaybackStart()} should be made. If playback completes, calling this method is not
- * necessary, as long as a call to {@link #onPlaybackCompleted(Context)} is made.
- * <p/>
- * Position held by this Playable should be set accurately before a call to this method is made.
- */
- void onPlaybackPause(Context context);
-
- /**
- * This method should be called when playback completes for this object.
- * @param context
- */
- void onPlaybackCompleted(Context context);
-
- /**
- * Returns an integer that must be unique among all Playable classes. The
- * return value is later used by PlayableUtils to determine the type of the
- * Playable object that is restored.
- */
- int getPlayableType();
-
- void setChapters(List<Chapter> chapters);
-
- /**
- * Returns the location of the image or null if no image is available.
- * This can be the feed item image URL, the local embedded media image path, the feed image URL,
- * or the remote media image URL, depending on what's available.
- */
- @Nullable
- String getImageLocation();
-
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableException.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableException.java
deleted file mode 100644
index c0c21d647..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableException.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.danoeh.antennapod.core.util.playback;
-
-/**
- * Exception thrown by {@link Playable} implementations.
- */
-public class PlayableException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- public PlayableException(String detailMessage) {
- super(detailMessage);
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java
index 413058758..56b46903d 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlayableUtils.java
@@ -8,10 +8,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceManager;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.model.playback.Playable;
/**
* Provides utility methods for Playable objects.
@@ -53,9 +55,6 @@ public abstract class PlayableUtils {
case FeedMedia.PLAYABLE_TYPE_FEEDMEDIA:
result = createFeedMediaInstance(pref);
break;
- case ExternalMedia.PLAYABLE_TYPE_EXTERNAL_MEDIA:
- result = createExternalMediaInstance(pref);
- break;
default:
result = null;
break;
@@ -75,16 +74,27 @@ public abstract class PlayableUtils {
return result;
}
- private static Playable createExternalMediaInstance(SharedPreferences pref) {
- Playable result = null;
- String source = pref.getString(ExternalMedia.PREF_SOURCE_URL, null);
- String mediaType = pref.getString(ExternalMedia.PREF_MEDIA_TYPE, null);
- if (source != null && mediaType != null) {
- int position = pref.getInt(ExternalMedia.PREF_POSITION, 0);
- long lastPlayedTime = pref.getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, 0);
- result = new ExternalMedia(source, MediaType.valueOf(mediaType),
- position, lastPlayedTime);
+ /**
+ * Saves the current position of this object.
+ *
+ * @param newPosition new playback position in ms
+ * @param timestamp current time in ms
+ */
+ public static void saveCurrentPosition(Playable playable, int newPosition, long timestamp) {
+ playable.setPosition(newPosition);
+ playable.setLastPlayedTime(timestamp);
+
+ if (playable instanceof FeedMedia) {
+ FeedMedia media = (FeedMedia) playable;
+ FeedItem item = media.getItem();
+ if (item != null && item.isNew()) {
+ DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
+ }
+ if (media.getStartPosition() >= 0 && playable.getPosition() > media.getStartPosition()) {
+ media.setPlayedDuration(media.getPlayedDurationWhenStarted()
+ + playable.getPosition() - media.getStartPosition());
+ }
+ DBWriter.setFeedMediaPlaybackInformation(media);
}
- return result;
}
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
index 949a718c6..6fe2502ab 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackController.java
@@ -7,33 +7,23 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
-import android.content.res.TypedArray;
import android.media.MediaPlayer;
-import android.os.Build;
import android.os.IBinder;
-import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.SurfaceHolder;
-import android.widget.ImageButton;
import androidx.annotation.NonNull;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.event.ServiceEvent;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.service.playback.PlaybackServiceMediaPlayer;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.ui.common.ThemeUtils;
-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 de.danoeh.antennapod.model.playback.Playable;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
@@ -61,8 +51,6 @@ public abstract class PlaybackController {
private boolean eventsRegistered = false;
private long loadedFeedMedia = -1;
- private Disposable mediaLoader;
-
public PlaybackController(@NonNull Activity activity) {
this.activity = activity;
}
@@ -78,7 +66,7 @@ public abstract class PlaybackController {
if (PlaybackService.isRunning) {
initServiceRunning();
} else {
- initServiceNotRunning();
+ updatePlayButtonShowsPlay(true);
}
}
@@ -101,9 +89,6 @@ public abstract class PlaybackController {
activity.registerReceiver(notificationReceiver, new IntentFilter(
PlaybackService.ACTION_PLAYER_NOTIFICATION));
- activity.registerReceiver(shutdownReceiver, new IntentFilter(
- PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE));
-
if (!released) {
bindToService();
} else {
@@ -131,12 +116,6 @@ public abstract class PlaybackController {
// ignore
}
unbind();
-
- try {
- activity.unregisterReceiver(shutdownReceiver);
- } catch (IllegalArgumentException e) {
- // ignore
- }
media = null;
released = true;
@@ -262,35 +241,16 @@ public abstract class PlaybackController {
case PlaybackService.NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE:
onPlaybackSpeedChange();
break;
- case PlaybackService.NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED:
- onSetSpeedAbilityChanged();
- break;
}
}
};
- private final BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (playbackService != null) {
- if (TextUtils.equals(intent.getAction(), PlaybackService.ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- unbind();
- onShutdownNotification();
- }
- }
- }
- };
-
public void onPositionObserverUpdate() {}
public void onPlaybackSpeedChange() {}
- public void onSetSpeedAbilityChanged() {}
-
- public void onShutdownNotification() {}
-
/**
* Called when the currently displayed information should be refreshed.
*/
@@ -313,21 +273,6 @@ public abstract class PlaybackController {
* should be used to update the GUI or start/cancel background threads.
*/
private void handleStatus() {
- final int playResource;
- final int pauseResource;
- final CharSequence playText = activity.getString(R.string.play_label);
- final CharSequence pauseText = activity.getString(R.string.pause_label);
-
- if (PlaybackService.getCurrentMediaType() == MediaType.AUDIO || PlaybackService.isCasting()) {
- TypedArray res = activity.obtainStyledAttributes(new int[]{ R.attr.av_play, R.attr.av_pause});
- playResource = res.getResourceId(0, R.drawable.ic_av_play_black_48dp);
- pauseResource = res.getResourceId(1, R.drawable.ic_av_pause_black_48dp);
- res.recycle();
- } else {
- playResource = R.drawable.ic_av_play_white_80dp;
- pauseResource = R.drawable.ic_av_pause_white_80dp;
- }
-
Log.d(TAG, "status: " + status.toString());
switch (status) {
case ERROR:
@@ -337,37 +282,31 @@ public abstract class PlaybackController {
case PAUSED:
checkMediaInfoLoaded();
onPositionObserverUpdate();
- updatePlayButtonAppearance(playResource, playText);
- if (!PlaybackService.isCasting() &&
- PlaybackService.getCurrentMediaType() == MediaType.VIDEO) {
+ updatePlayButtonShowsPlay(true);
+ if (!PlaybackService.isCasting() && PlaybackService.getCurrentMediaType() == MediaType.VIDEO) {
setScreenOn(false);
}
break;
case PLAYING:
checkMediaInfoLoaded();
- if (!PlaybackService.isCasting() &&
- PlaybackService.getCurrentMediaType() == MediaType.VIDEO) {
+ if (!PlaybackService.isCasting() && PlaybackService.getCurrentMediaType() == MediaType.VIDEO) {
onAwaitingVideoSurface();
setScreenOn(true);
}
- updatePlayButtonAppearance(pauseResource, pauseText);
+ updatePlayButtonShowsPlay(false);
break;
case PREPARING:
checkMediaInfoLoaded();
if (playbackService != null) {
- if (playbackService.isStartWhenPrepared()) {
- updatePlayButtonAppearance(pauseResource, pauseText);
- } else {
- updatePlayButtonAppearance(playResource, playText);
- }
+ updatePlayButtonShowsPlay(!playbackService.isStartWhenPrepared());
}
break;
case STOPPED:
- updatePlayButtonAppearance(playResource, playText);
+ updatePlayButtonShowsPlay(true);
break;
case PREPARED:
checkMediaInfoLoaded();
- updatePlayButtonAppearance(playResource, playText);
+ updatePlayButtonShowsPlay(true);
onPositionObserverUpdate();
break;
case SEEKING:
@@ -375,7 +314,7 @@ public abstract class PlaybackController {
break;
case INITIALIZED:
checkMediaInfoLoaded();
- updatePlayButtonAppearance(playResource, playText);
+ updatePlayButtonShowsPlay(true);
break;
}
}
@@ -388,16 +327,8 @@ public abstract class PlaybackController {
mediaInfoLoaded = true;
}
- private void updatePlayButtonAppearance(int resource, CharSequence contentDescription) {
- ImageButton butPlay = getPlayButton();
- if(butPlay != null) {
- butPlay.setImageResource(resource);
- butPlay.setContentDescription(contentDescription);
- }
- }
+ protected void updatePlayButtonShowsPlay(boolean showPlay) {
- public ImageButton getPlayButton() {
- return null;
}
public abstract void loadMediaInfo();
@@ -528,12 +459,6 @@ public abstract class PlaybackController {
}
}
- public void seekToChapter(Chapter chapter) {
- if (playbackService != null) {
- playbackService.seekToChapter(chapter);
- }
- }
-
public void seekTo(int time) {
if (playbackService != null) {
playbackService.seekTo(time);
@@ -550,13 +475,6 @@ public abstract class PlaybackController {
return status;
}
- public boolean canSetPlaybackSpeed() {
- return UserPreferences.useSonic()
- || UserPreferences.useExoplayer()
- || Build.VERSION.SDK_INT >= 23
- || (playbackService != null && playbackService.canSetSpeed());
- }
-
public void setPlaybackSpeed(float speed) {
PlaybackPreferences.setCurrentlyPlayingTemporaryPlaybackSpeed(speed);
if (getMedia() != null && getMedia().getMediaType() == MediaType.VIDEO) {
@@ -585,7 +503,7 @@ public abstract class PlaybackController {
}
public float getCurrentPlaybackSpeedMultiplier() {
- if (playbackService != null && canSetPlaybackSpeed()) {
+ if (playbackService != null) {
return playbackService.getCurrentPlaybackSpeed();
} else {
return PlaybackSpeedUtils.getCurrentPlaybackSpeed(getMedia());
@@ -664,29 +582,4 @@ public abstract class PlaybackController {
public boolean isStreaming() {
return playbackService != null && playbackService.isStreaming();
}
-
- private void initServiceNotRunning() {
- if (getPlayButton() == null) {
- return;
- }
- Log.v(TAG, "initServiceNotRunning()");
- mediaLoader = Maybe.create((MaybeOnSubscribe<Playable>) emitter -> {
- Playable media = getMedia();
- if (media != null) {
- emitter.onSuccess(media);
- } else {
- emitter.onComplete();
- }
- })
- .subscribeOn(Schedulers.io())
- .observeOn(AndroidSchedulers.mainThread())
- .subscribe(media -> {
- if (media.getMediaType() == MediaType.AUDIO) {
- getPlayButton().setImageResource(
- ThemeUtils.getDrawableFromAttr(activity, de.danoeh.antennapod.core.R.attr.av_play));
- } else {
- getPlayButton().setImageResource(R.drawable.ic_av_play_white_80dp);
- }
- }, error -> Log.e(TAG, Log.getStackTraceString(error)));
- }
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
index 107399e60..061a976dc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/PlaybackServiceStarter.java
@@ -6,6 +6,7 @@ import androidx.core.content.ContextCompat;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.service.playback.PlaybackService;
+import de.danoeh.antennapod.model.playback.Playable;
public class PlaybackServiceStarter {
private final Context context;
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/RemoteMedia.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/RemoteMedia.java
deleted file mode 100644
index 5a9afaeaf..000000000
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/RemoteMedia.java
+++ /dev/null
@@ -1,344 +0,0 @@
-package de.danoeh.antennapod.core.util.playback;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import androidx.annotation.Nullable;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.util.ChapterUtils;
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.Callable;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-
-/**
- * Playable implementation for media for which a local version of
- * {@link de.danoeh.antennapod.core.feed.FeedMedia} hasn't been found.
- * Used for Casting and for previewing unsubscribed feeds.
- */
-public class RemoteMedia implements Playable {
- public static final String TAG = "RemoteMedia";
-
- public static final int PLAYABLE_TYPE_REMOTE_MEDIA = 3;
-
- private final String downloadUrl;
- private final String itemIdentifier;
- private final String feedUrl;
- private final String feedTitle;
- private final String episodeTitle;
- private final String episodeLink;
- private final String feedAuthor;
- private final String imageUrl;
- private final String feedLink;
- private final String mimeType;
- private final Date pubDate;
- private final String notes;
- private List<Chapter> chapters;
- private int duration;
- private int position;
- private long lastPlayedTime;
-
- public RemoteMedia(String downloadUrl, String itemId, String feedUrl, String feedTitle,
- String episodeTitle, String episodeLink, String feedAuthor,
- String imageUrl, String feedLink, String mimeType, Date pubDate,
- String notes) {
- this.downloadUrl = downloadUrl;
- this.itemIdentifier = itemId;
- this.feedUrl = feedUrl;
- this.feedTitle = feedTitle;
- this.episodeTitle = episodeTitle;
- this.episodeLink = episodeLink;
- this.feedAuthor = feedAuthor;
- this.imageUrl = imageUrl;
- this.feedLink = feedLink;
- this.mimeType = mimeType;
- this.pubDate = pubDate;
- this.notes = notes;
- }
-
- public RemoteMedia(FeedItem item) {
- this.downloadUrl = item.getMedia().getDownload_url();
- this.itemIdentifier = item.getItemIdentifier();
- this.feedUrl = item.getFeed().getDownload_url();
- this.feedTitle = item.getFeed().getTitle();
- this.episodeTitle = item.getTitle();
- this.episodeLink = item.getLink();
- this.feedAuthor = item.getFeed().getAuthor();
- if (!TextUtils.isEmpty(item.getImageUrl())) {
- this.imageUrl = item.getImageUrl();
- } else {
- this.imageUrl = item.getFeed().getImageUrl();
- }
- this.feedLink = item.getFeed().getLink();
- this.mimeType = item.getMedia().getMime_type();
- this.pubDate = item.getPubDate();
- this.notes = item.getDescription();
- }
-
- public String getEpisodeIdentifier() {
- return itemIdentifier;
- }
-
- public String getFeedUrl() {
- return feedUrl;
- }
-
- public String getDownloadUrl() {
- return downloadUrl;
- }
-
- public String getEpisodeLink() {
- return episodeLink;
- }
-
- public String getFeedAuthor() {
- return feedAuthor;
- }
-
- public String getImageUrl() {
- return imageUrl;
- }
-
- public String getFeedLink() {
- return feedLink;
- }
-
- public String getMimeType() {
- return mimeType;
- }
-
- public Date getPubDate() {
- return pubDate;
- }
-
- public String getNotes() {
- return notes;
- }
-
- @Override
- public void writeToPreferences(SharedPreferences.Editor prefEditor) {
- //it seems pointless to do it, since the session should be kept by the remote device.
- }
-
- @Override
- public void loadMetadata() throws PlayableException {
- //Already loaded
- }
-
- @Override
- public void loadChapterMarks(Context context) {
- setChapters(ChapterUtils.loadChaptersFromMediaFile(this, context));
- }
-
- @Override
- public String getEpisodeTitle() {
- return episodeTitle;
- }
-
- @Override
- public List<Chapter> getChapters() {
- return chapters;
- }
-
- @Override
- public String getWebsiteLink() {
- if (episodeLink != null) {
- return episodeLink;
- } else {
- return feedUrl;
- }
- }
-
- @Override
- public String getPaymentLink() {
- return null;
- }
-
- @Override
- public String getFeedTitle() {
- return feedTitle;
- }
-
- @Override
- public Object getIdentifier() {
- return itemIdentifier + "@" + feedUrl;
- }
-
- @Override
- public int getDuration() {
- return duration;
- }
-
- @Override
- public int getPosition() {
- return position;
- }
-
- @Override
- public long getLastPlayedTime() {
- return lastPlayedTime;
- }
-
- @Override
- public MediaType getMediaType() {
- return MediaType.fromMimeType(mimeType);
- }
-
- @Override
- public String getLocalMediaUrl() {
- return null;
- }
-
- @Override
- public String getStreamUrl() {
- return downloadUrl;
- }
-
- @Override
- public boolean localFileAvailable() {
- return false;
- }
-
- @Override
- public boolean streamAvailable() {
- return true;
- }
-
- @Override
- public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp) {
- //we're not saving playback information for this kind of items on preferences
- setPosition(newPosition);
- setLastPlayedTime(timestamp);
- }
-
- @Override
- public void setPosition(int newPosition) {
- position = newPosition;
- }
-
- @Override
- public void setDuration(int newDuration) {
- duration = newDuration;
- }
-
- @Override
- public void setLastPlayedTime(long lastPlayedTimestamp) {
- lastPlayedTime = lastPlayedTimestamp;
- }
-
- @Override
- public void onPlaybackStart() {
- // no-op
- }
-
- @Override
- public void onPlaybackPause(Context context) {
- // no-op
- }
-
- @Override
- public void onPlaybackCompleted(Context context) {
- // no-op
- }
-
- @Override
- public int getPlayableType() {
- return PLAYABLE_TYPE_REMOTE_MEDIA;
- }
-
- @Override
- public void setChapters(List<Chapter> chapters) {
- this.chapters = chapters;
- }
-
- @Override
- @Nullable
- public String getImageLocation() {
- return imageUrl;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public Callable<String> loadShownotes() {
- return () -> (notes != null) ? notes : "";
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeString(downloadUrl);
- dest.writeString(itemIdentifier);
- dest.writeString(feedUrl);
- dest.writeString(feedTitle);
- dest.writeString(episodeTitle);
- dest.writeString(episodeLink);
- dest.writeString(feedAuthor);
- dest.writeString(imageUrl);
- dest.writeString(feedLink);
- dest.writeString(mimeType);
- dest.writeLong((pubDate != null) ? pubDate.getTime() : 0);
- dest.writeString(notes);
- dest.writeInt(duration);
- dest.writeInt(position);
- dest.writeLong(lastPlayedTime);
- }
-
- public static final Parcelable.Creator<RemoteMedia> CREATOR = new Parcelable.Creator<RemoteMedia>() {
- @Override
- public RemoteMedia createFromParcel(Parcel in) {
- RemoteMedia result = new RemoteMedia(in.readString(), in.readString(), in.readString(),
- in.readString(), in.readString(), in.readString(), in.readString(), in.readString(),
- in.readString(), in.readString(), new Date(in.readLong()), in.readString());
- result.setDuration(in.readInt());
- result.setPosition(in.readInt());
- result.setLastPlayedTime(in.readLong());
- return result;
- }
-
- @Override
- public RemoteMedia[] newArray(int size) {
- return new RemoteMedia[size];
- }
- };
-
- @Override
- public boolean equals(Object other) {
- if (other instanceof RemoteMedia) {
- RemoteMedia rm = (RemoteMedia) other;
- return TextUtils.equals(downloadUrl, rm.downloadUrl)
- && TextUtils.equals(feedUrl, rm.feedUrl)
- && TextUtils.equals(itemIdentifier, rm.itemIdentifier);
- }
- if (other instanceof FeedMedia) {
- FeedMedia fm = (FeedMedia) other;
- if (!TextUtils.equals(downloadUrl, fm.getStreamUrl())) {
- return false;
- }
- FeedItem fi = fm.getItem();
- if (fi == null || !TextUtils.equals(itemIdentifier, fi.getItemIdentifier())) {
- return false;
- }
- Feed feed = fi.getFeed();
- return feed != null && TextUtils.equals(feedUrl, feed.getDownload_url());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return new HashCodeBuilder()
- .append(downloadUrl)
- .append(feedUrl)
- .append(itemIdentifier)
- .toHashCode();
- }
-}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java
index 40849a262..e125c7e66 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Timeline.java
@@ -9,7 +9,7 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import androidx.annotation.Nullable;
import org.apache.commons.io.IOUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
@@ -24,7 +24,6 @@ import java.util.regex.Pattern;
import de.danoeh.antennapod.core.R;
import de.danoeh.antennapod.core.util.Converter;
-import de.danoeh.antennapod.core.util.ShownotesProvider;
/**
* Connects chapter information and shownotes of a shownotesProvider, for example by making it possible to use the
@@ -42,17 +41,16 @@ public class Timeline {
private static final Pattern TIMECODE_REGEX = Pattern.compile("\\b((\\d+):)?(\\d+):(\\d{2})\\b");
private static final Pattern LINE_BREAK_REGEX = Pattern.compile("<br */?>");
- private final ShownotesProvider shownotesProvider;
+ private final String rawShownotes;
private final String noShownotesLabel;
+ private final int playableDuration;
private final String webviewStyle;
- public Timeline(Context context, ShownotesProvider shownotesProvider) {
- if (shownotesProvider == null) {
- throw new IllegalArgumentException("shownotesProvider = null");
- }
- this.shownotesProvider = shownotesProvider;
+ public Timeline(Context context, @Nullable String rawShownotes, int playableDuration) {
+ this.rawShownotes = rawShownotes;
noShownotesLabel = context.getString(R.string.no_shownotes_label);
+ this.playableDuration = playableDuration;
final String colorPrimary = colorToHtml(context, android.R.attr.textColorPrimary);
final String colorAccent = colorToHtml(context, R.attr.colorAccent);
final int margin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 8,
@@ -87,13 +85,7 @@ public class Timeline {
*/
@NonNull
public String processShownotes() {
- String shownotes;
- try {
- shownotes = shownotesProvider.loadShownotes().call();
- } catch (Exception e) {
- Log.e(TAG, "processShownotes() - encounters exceptions unexpectedly in load, treat as if no shownotes.", e);
- shownotes = "";
- }
+ String shownotes = rawShownotes;
if (TextUtils.isEmpty(shownotes)) {
Log.d(TAG, "shownotesProvider contained no shownotes. Returning 'no shownotes' message");
@@ -147,14 +139,6 @@ public class Timeline {
// No elements with timecodes
return;
}
-
- int playableDuration = Integer.MAX_VALUE;
- if (shownotesProvider instanceof Playable) {
- playableDuration = ((Playable) shownotesProvider).getDuration();
- } else if (shownotesProvider instanceof FeedItem && ((FeedItem) shownotesProvider).getMedia() != null) {
- playableDuration = ((FeedItem) shownotesProvider).getMedia().getDuration();
- }
-
boolean useHourFormat = true;
if (playableDuration != Integer.MAX_VALUE) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
index 6728c027d..ecf47f8ae 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/VideoPlayer.java
@@ -11,11 +11,6 @@ public class VideoPlayer extends MediaPlayer implements IPlayer {
private static final String TAG = "VideoPlayer";
@Override
- public boolean canSetSpeed() {
- return false;
- }
-
- @Override
public boolean canDownmix() {
return false;
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java
index baa467acf..37dee0486 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/syndication/HtmlToPlainText.java
@@ -34,8 +34,7 @@ import java.util.regex.Pattern;
public class HtmlToPlainText {
/**
- * Use this method to strip off HTML encoding from given text
- * <p>
+ * Use this method to strip off HTML encoding from given text.
* Replaces bullet points with *, ignores colors/bold/...
*
* @param str String with any encoding
@@ -60,10 +59,8 @@ public class HtmlToPlainText {
* @return <b>True</b> if text contains any HTML tags<br /><b>False</b> is no HTML tag is found
*/
private static boolean isHtml(String str) {
- final String HTML_TAG_PATTERN = "<(\"[^\"]*\"|'[^']*'|[^'\">])*>";
- Pattern htmlValidator = TextUtils.isEmpty(HTML_TAG_PATTERN) ? null : Pattern.compile(HTML_TAG_PATTERN);
-
- return htmlValidator.matcher(str).find();
+ final String htmlTagPattern = "<(\"[^\"]*\"|'[^']*'|[^'\">])*>";
+ return Pattern.compile(htmlTagPattern).matcher(str).find();
}
/**
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
index c4fe76780..26955effc 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReader.java
@@ -6,7 +6,7 @@ import java.util.ArrayList;
import java.util.List;
import de.danoeh.antennapod.core.BuildConfig;
-import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.core.feed.VorbisCommentChapter;
public class VorbisCommentChapterReader extends VorbisCommentReader {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
index afbe6526b..0ccdb2d81 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdater.java
@@ -19,7 +19,7 @@ import com.bumptech.glide.request.RequestOptions;
import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.glide.ApGlideSettings;
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.core.receiver.PlayerWidget;
@@ -27,7 +27,7 @@ import de.danoeh.antennapod.core.service.playback.PlayerStatus;
import de.danoeh.antennapod.core.util.Converter;
import de.danoeh.antennapod.core.feed.util.ImageResourceUtils;
import de.danoeh.antennapod.core.util.TimeSpeedConverter;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter;
import de.danoeh.antennapod.ui.appstartintent.VideoPlayerActivityStarter;
@@ -122,14 +122,14 @@ public abstract class WidgetUpdater {
}
if (widgetState.status == PlayerStatus.PLAYING) {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_pause_white_48dp);
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause);
views.setContentDescription(R.id.butPlay, context.getString(R.string.pause_label));
- views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_av_pause_white_48dp);
+ views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_pause);
views.setContentDescription(R.id.butPlayExtended, context.getString(R.string.pause_label));
} else {
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_play_white_48dp);
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_48dp);
views.setContentDescription(R.id.butPlay, context.getString(R.string.play_label));
- views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_av_play_white_48dp);
+ views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_play_48dp);
views.setContentDescription(R.id.butPlayExtended, context.getString(R.string.play_label));
}
views.setOnClickPendingIntent(R.id.butPlay,
@@ -152,8 +152,8 @@ public abstract class WidgetUpdater {
views.setViewVisibility(R.id.txtvTitle, View.GONE);
views.setViewVisibility(R.id.txtNoPlaying, View.VISIBLE);
views.setImageViewResource(R.id.imgvCover, R.mipmap.ic_launcher_round);
- views.setImageViewResource(R.id.butPlay, R.drawable.ic_av_play_white_48dp);
- views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_av_play_white_48dp);
+ views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_48dp);
+ views.setImageViewResource(R.id.butPlayExtended, R.drawable.ic_play_48dp);
}
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
diff --git a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java
index 004588945..b14fb3b0b 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/widget/WidgetUpdaterJobService.java
@@ -7,7 +7,7 @@ import androidx.core.app.SafeJobIntentService;
import de.danoeh.antennapod.core.feed.util.PlaybackSpeedUtils;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.service.playback.PlayerStatus;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import de.danoeh.antennapod.core.util.playback.PlayableUtils;
public class WidgetUpdaterJobService extends SafeJobIntentService {
diff --git a/core/src/main/res/drawable/filter_dialog_background_light.xml b/core/src/main/res/drawable/filter_dialog_background_light.xml
index e0a80737c..09af585a6 100644
--- a/core/src/main/res/drawable/filter_dialog_background_light.xml
+++ b/core/src/main/res/drawable/filter_dialog_background_light.xml
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:drawable="@color/accent_light" android:state_checked="true" />
+ <item android:drawable="@color/accent_light" android:state_checked="true"/>
<item android:drawable="@color/dialog_filter_inactive_light" />
</selector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_add_black.xml b/core/src/main/res/drawable/ic_add.xml
index 624c18b7b..404275dd3 100644
--- a/core/src/main/res/drawable/ic_add_black.xml
+++ b/core/src/main/res/drawable/ic_add.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_appearance.xml b/core/src/main/res/drawable/ic_appearance.xml
new file mode 100644
index 000000000..b65bc0194
--- /dev/null
+++ b/core/src/main/res/drawable/ic_appearance.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M17,19V5H7V19H17M17,1A2,2 0 0,1 19,3V21A2,2 0 0,1 17,23H7C5.89,23 5,22.1 5,21V3C5,1.89 5.89,1 7,1H17M9,7H15V9H9V7M9,11H13V13H9V11Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_appearance_black.xml b/core/src/main/res/drawable/ic_appearance_black.xml
deleted file mode 100644
index 7c574b336..000000000
--- a/core/src/main/res/drawable/ic_appearance_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M17,19V5H7V19H17M17,1A2,2 0 0,1 19,3V21A2,2 0 0,1 17,23H7C5.89,23 5,22.1 5,21V3C5,1.89 5.89,1 7,1H17M9,7H15V9H9V7M9,11H13V13H9V11Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_appearance_white.xml b/core/src/main/res/drawable/ic_appearance_white.xml
deleted file mode 100644
index 882f47937..000000000
--- a/core/src/main/res/drawable/ic_appearance_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M17,19V5H7V19H17M17,1A2,2 0 0,1 19,3V21A2,2 0 0,1 17,23H7C5.89,23 5,22.1 5,21V3C5,1.89 5.89,1 7,1H17M9,7H15V9H9V7M9,11H13V13H9V11Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml b/core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml
deleted file mode 100644
index aca5bcf29..000000000
--- a/core/src/main/res/drawable/ic_av_fast_forward_white_48dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_pause_white_48dp.xml b/core/src/main/res/drawable/ic_av_pause_white_48dp.xml
deleted file mode 100644
index 3512563ec..000000000
--- a/core/src/main/res/drawable/ic_av_pause_white_48dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_white_48dp.xml b/core/src/main/res/drawable/ic_av_play_white_48dp.xml
deleted file mode 100644
index bf94a000b..000000000
--- a/core/src/main/res/drawable/ic_av_play_white_48dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_replay_white_48dp.xml b/core/src/main/res/drawable/ic_av_replay_white_48dp.xml
deleted file mode 100644
index b6343effc..000000000
--- a/core/src/main/res/drawable/ic_av_replay_white_48dp.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
- android:viewportWidth="24"
- android:viewportHeight="24"
- android:width="48dp"
- android:height="48dp">
- <path
- android:pathData="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6 -6 6s-6 -2.69 -6 -6H4c0 4.42 3.58 8 8 8s8 -3.58 8 -8S16.42 5 12 5z"
- android:fillColor="#FFFFFF" />
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_skip_black_24dp.xml b/core/src/main/res/drawable/ic_av_skip_black_24dp.xml
deleted file mode 100644
index 3ca3734a8..000000000
--- a/core/src/main/res/drawable/ic_av_skip_black_24dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FF000000" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_skip_black_48dp.xml b/core/src/main/res/drawable/ic_av_skip_black_48dp.xml
deleted file mode 100644
index 24a795dbc..000000000
--- a/core/src/main/res/drawable/ic_av_skip_black_48dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FF000000" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_skip_white_24dp.xml b/core/src/main/res/drawable/ic_av_skip_white_24dp.xml
deleted file mode 100644
index 9df98de70..000000000
--- a/core/src/main/res/drawable/ic_av_skip_white_24dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_skip_white_48dp.xml b/core/src/main/res/drawable/ic_av_skip_white_48dp.xml
deleted file mode 100644
index 7e0073e88..000000000
--- a/core/src/main/res/drawable/ic_av_skip_white_48dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48dp"
- android:height="48dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_bug.xml b/core/src/main/res/drawable/ic_bug.xml
new file mode 100644
index 000000000..c2086502c
--- /dev/null
+++ b/core/src/main/res/drawable/ic_bug.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_bug_black.xml b/core/src/main/res/drawable/ic_bug_black.xml
deleted file mode 100644
index 59d6fa653..000000000
--- a/core/src/main/res/drawable/ic_bug_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_bug_white.xml b/core/src/main/res/drawable/ic_bug_white.xml
deleted file mode 100644
index c22514bfe..000000000
--- a/core/src/main/res/drawable/ic_bug_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_cancel.xml b/core/src/main/res/drawable/ic_cancel.xml
new file mode 100644
index 000000000..dcb2e0fc7
--- /dev/null
+++ b/core/src/main/res/drawable/ic_cancel.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_cancel_black.xml b/core/src/main/res/drawable/ic_cancel_black.xml
deleted file mode 100644
index e254218af..000000000
--- a/core/src/main/res/drawable/ic_cancel_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_cancel_white.xml b/core/src/main/res/drawable/ic_cancel_white.xml
deleted file mode 100644
index a5480c71f..000000000
--- a/core/src/main/res/drawable/ic_cancel_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,2C6.47,2 2,6.47 2,12s4.47,10 10,10 10,-4.47 10,-10S17.53,2 12,2zM17,15.59L15.59,17 12,13.41 8.41,17 7,15.59 10.59,12 7,8.41 8.41,7 12,10.59 15.59,7 17,8.41 13.41,12 17,15.59z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_chapter_next.xml b/core/src/main/res/drawable/ic_chapter_next.xml
new file mode 100644
index 000000000..bcf38aeb6
--- /dev/null
+++ b/core/src/main/res/drawable/ic_chapter_next.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/action_icon_color"
+ android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6 -6,-6z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_chapter_prev.xml b/core/src/main/res/drawable/ic_chapter_prev.xml
new file mode 100644
index 000000000..f01b30364
--- /dev/null
+++ b/core/src/main/res/drawable/ic_chapter_prev.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/action_icon_color"
+ android:pathData="M15.41,7.41L14,6l-6,6 6,6 1.41,-1.41L10.83,12z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_chat.xml b/core/src/main/res/drawable/ic_chat.xml
new file mode 100644
index 000000000..63044c237
--- /dev/null
+++ b/core/src/main/res/drawable/ic_chat.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M15,4V11H5.17L4,12.17V4H15M16,2H3A1,1 0 0,0 2,3V17L6,13H16A1,1 0 0,0 17,12V3A1,1 0 0,0 16,2M21,6H19V15H6V17A1,1 0 0,0 7,18H18L22,22V7A1,1 0 0,0 21,6Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_chat_black.xml b/core/src/main/res/drawable/ic_chat_black.xml
deleted file mode 100644
index 3f2692d39..000000000
--- a/core/src/main/res/drawable/ic_chat_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M15,4V11H5.17L4,12.17V4H15M16,2H3A1,1 0 0,0 2,3V17L6,13H16A1,1 0 0,0 17,12V3A1,1 0 0,0 16,2M21,6H19V15H6V17A1,1 0 0,0 7,18H18L22,22V7A1,1 0 0,0 21,6Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_chat_white.xml b/core/src/main/res/drawable/ic_chat_white.xml
deleted file mode 100644
index 222615bfe..000000000
--- a/core/src/main/res/drawable/ic_chat_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M15,4V11H5.17L4,12.17V4H15M16,2H3A1,1 0 0,0 2,3V17L6,13H16A1,1 0 0,0 17,12V3A1,1 0 0,0 16,2M21,6H19V15H6V17A1,1 0 0,0 7,18H18L22,22V7A1,1 0 0,0 21,6Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_check_white.xml b/core/src/main/res/drawable/ic_check.xml
index 80651f6ee..4338bd156 100644
--- a/core/src/main/res/drawable/ic_check_white.xml
+++ b/core/src/main/res/drawable/ic_check.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_check_multiple.xml b/core/src/main/res/drawable/ic_check_multiple.xml
new file mode 100644
index 000000000..0522009ba
--- /dev/null
+++ b/core/src/main/res/drawable/ic_check_multiple.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M20,16V10H22V16A2,2 0 0,1 20,18H8C6.89,18 6,17.1 6,16V4C6,2.89 6.89,2 8,2H16V4H8V16H20M10.91,7.08L14,10.17L20.59,3.58L22,5L14,13L9.5,8.5L10.91,7.08M16,20V22H4A2,2 0 0,1 2,20V7H4V20H16Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_check_multiple_black.xml b/core/src/main/res/drawable/ic_check_multiple_black.xml
deleted file mode 100644
index a0a79299d..000000000
--- a/core/src/main/res/drawable/ic_check_multiple_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M20,16V10H22V16A2,2 0 0,1 20,18H8C6.89,18 6,17.1 6,16V4C6,2.89 6.89,2 8,2H16V4H8V16H20M10.91,7.08L14,10.17L20.59,3.58L22,5L14,13L9.5,8.5L10.91,7.08M16,20V22H4A2,2 0 0,1 2,20V7H4V20H16Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_check_multiple_white.xml b/core/src/main/res/drawable/ic_check_multiple_white.xml
deleted file mode 100644
index 777f6eb59..000000000
--- a/core/src/main/res/drawable/ic_check_multiple_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M20,16V10H22V16A2,2 0 0,1 20,18H8C6.89,18 6,17.1 6,16V4C6,2.89 6.89,2 8,2H16V4H8V16H20M10.91,7.08L14,10.17L20.59,3.58L22,5L14,13L9.5,8.5L10.91,7.08M16,20V22H4A2,2 0 0,1 2,20V7H4V20H16Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_contribute.xml b/core/src/main/res/drawable/ic_contribute.xml
new file mode 100644
index 000000000..b934453b1
--- /dev/null
+++ b/core/src/main/res/drawable/ic_contribute.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="?attr/action_icon_color"
+ android:pathData="M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_delete.xml b/core/src/main/res/drawable/ic_delete.xml
new file mode 100644
index 000000000..6d3cb6a27
--- /dev/null
+++ b/core/src/main/res/drawable/ic_delete.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_delete_black.xml b/core/src/main/res/drawable/ic_delete_black.xml
deleted file mode 100644
index a4d94b247..000000000
--- a/core/src/main/res/drawable/ic_delete_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_delete_white.xml b/core/src/main/res/drawable/ic_delete_white.xml
deleted file mode 100644
index 1f2b6e734..000000000
--- a/core/src/main/res/drawable/ic_delete_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_add_white.xml b/core/src/main/res/drawable/ic_download.xml
index 3052692a0..ebc50613f 100644
--- a/core/src/main/res/drawable/ic_add_white.xml
+++ b/core/src/main/res/drawable/ic_download.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_download_black.xml b/core/src/main/res/drawable/ic_download_black.xml
deleted file mode 100644
index 0e0b47c88..000000000
--- a/core/src/main/res/drawable/ic_download_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_download_white.xml b/core/src/main/res/drawable/ic_download_white.xml
deleted file mode 100644
index 8a9cfa3cd..000000000
--- a/core/src/main/res/drawable/ic_download_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_drag_darktheme.xml b/core/src/main/res/drawable/ic_drag_darktheme.xml
index b2ef2e5ec..20c7db484 100644
--- a/core/src/main/res/drawable/ic_drag_darktheme.xml
+++ b/core/src/main/res/drawable/ic_drag_darktheme.xml
@@ -4,6 +4,6 @@
android:viewportWidth="3"
android:viewportHeight="4.5">
<path
- android:pathData="M0.74983,0.25011A0.5,0.5 0,0 0,0.25011 0.74983A0.5,0.5 135,0 0,0.74983 1.25005A0.5,0.5 0,0 0,1.25005 0.74983A0.5,0.5 45,0 0,0.74983 0.25011zM2.24999,0.25011A0.5,0.5 0,0 0,1.74976 0.74983A0.5,0.5 0,0 0,2.24999 1.25005A0.5,0.5 0,0 0,2.75022 0.74983A0.5,0.5 0,0 0,2.24999 0.25011zM0.74983,1.74976A0.5,0.5 0,0 0,0.25011 2.24999A0.5,0.5 0,0 0,0.74983 2.75022A0.5,0.5 0,0 0,1.25005 2.24999A0.5,0.5 0,0 0,0.74983 1.74976zM2.24999,1.74976A0.5,0.5 0,0 0,1.74976 2.24999A0.5,0.5 0,0 0,2.24999 2.75022A0.5,0.5 0,0 0,2.75022 2.24999A0.5,0.5 0,0 0,2.24999 1.74976zM0.74983,3.24993A0.5,0.5 0,0 0,0.25011 3.75016A0.5,0.5 45,0 0,0.74983 4.24987A0.5,0.5 45,0 0,1.25005 3.75016A0.5,0.5 0,0 0,0.74983 3.24993zM2.24999,3.24993A0.5,0.5 0,0 0,1.74976 3.75016A0.5,0.5 45,0 0,2.24999 4.24987A0.5,0.5 45,0 0,2.75022 3.75016A0.5,0.5 0,0 0,2.24999 3.24993z"
+ android:pathData="M 0.75 0.25 A 0.5 0.5 0 0 0 0.25 0.75 A 0.5 0.5 135 0 0 0.75 1.25 A 0.5 0.5 0 0 0 1.25 0.75 A 0.5 0.5 45 0 0 0.75 0.25 z M 2.25 0.25 A 0.5 0.5 0 0 0 1.75 0.75 A 0.5 0.5 0 0 0 2.25 1.25 A 0.5 0.5 0 0 0 2.75 0.75 A 0.5 0.5 0 0 0 2.25 0.25 z M 0.75 1.75 A 0.5 0.5 0 0 0 0.25 2.25 A 0.5 0.5 0 0 0 0.75 2.75 A 0.5 0.5 0 0 0 1.25 2.25 A 0.5 0.5 0 0 0 0.75 1.75 z M 2.25 1.75 A 0.5 0.5 0 0 0 1.75 2.25 A 0.5 0.5 0 0 0 2.25 2.75 A 0.5 0.5 0 0 0 2.75 2.25 A 0.5 0.5 0 0 0 2.25 1.75 z M 0.75 3.25 A 0.5 0.5 0 0 0 0.25 3.75 A 0.5 0.5 45 0 0 0.75 4.25 A 0.5 0.5 45 0 0 1.25 3.75 A 0.5 0.5 0 0 0 0.75 3.25 z M 2.25 3.25 A 0.5 0.5 0 0 0 1.75 3.75 A 0.5 0.5 45 0 0 2.25 4.25 A 0.5 0.5 45 0 0 2.75 3.75 A 0.5 0.5 0 0 0 2.25 3.25 z"
android:fillColor="#a9a9a9"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_drag_lighttheme.xml b/core/src/main/res/drawable/ic_drag_lighttheme.xml
index 42714d5db..46ff2f495 100644
--- a/core/src/main/res/drawable/ic_drag_lighttheme.xml
+++ b/core/src/main/res/drawable/ic_drag_lighttheme.xml
@@ -4,6 +4,6 @@
android:viewportWidth="3"
android:viewportHeight="4.5">
<path
- android:pathData="M0.74983,0.25011A0.5,0.5 0,0 0,0.25011 0.74983A0.5,0.5 135,0 0,0.74983 1.25005A0.5,0.5 0,0 0,1.25005 0.74983A0.5,0.5 45,0 0,0.74983 0.25011zM2.24999,0.25011A0.5,0.5 0,0 0,1.74976 0.74983A0.5,0.5 0,0 0,2.24999 1.25005A0.5,0.5 0,0 0,2.75022 0.74983A0.5,0.5 0,0 0,2.24999 0.25011zM0.74983,1.74976A0.5,0.5 0,0 0,0.25011 2.24999A0.5,0.5 0,0 0,0.74983 2.75022A0.5,0.5 0,0 0,1.25005 2.24999A0.5,0.5 0,0 0,0.74983 1.74976zM2.24999,1.74976A0.5,0.5 0,0 0,1.74976 2.24999A0.5,0.5 0,0 0,2.24999 2.75022A0.5,0.5 0,0 0,2.75022 2.24999A0.5,0.5 0,0 0,2.24999 1.74976zM0.74983,3.24993A0.5,0.5 0,0 0,0.25011 3.75016A0.5,0.5 45,0 0,0.74983 4.24987A0.5,0.5 45,0 0,1.25005 3.75016A0.5,0.5 0,0 0,0.74983 3.24993zM2.24999,3.24993A0.5,0.5 0,0 0,1.74976 3.75016A0.5,0.5 45,0 0,2.24999 4.24987A0.5,0.5 45,0 0,2.75022 3.75016A0.5,0.5 0,0 0,2.24999 3.24993z"
+ android:pathData="M 0.75 0.25 A 0.5 0.5 0 0 0 0.25 0.75 A 0.5 0.5 135 0 0 0.75 1.25 A 0.5 0.5 0 0 0 1.25 0.75 A 0.5 0.5 45 0 0 0.75 0.25 z M 2.25 0.25 A 0.5 0.5 0 0 0 1.75 0.75 A 0.5 0.5 0 0 0 2.25 1.25 A 0.5 0.5 0 0 0 2.75 0.75 A 0.5 0.5 0 0 0 2.25 0.25 z M 0.75 1.75 A 0.5 0.5 0 0 0 0.25 2.25 A 0.5 0.5 0 0 0 0.75 2.75 A 0.5 0.5 0 0 0 1.25 2.25 A 0.5 0.5 0 0 0 0.75 1.75 z M 2.25 1.75 A 0.5 0.5 0 0 0 1.75 2.25 A 0.5 0.5 0 0 0 2.25 2.75 A 0.5 0.5 0 0 0 2.75 2.25 A 0.5 0.5 0 0 0 2.25 1.75 z M 0.75 3.25 A 0.5 0.5 0 0 0 0.25 3.75 A 0.5 0.5 45 0 0 0.75 4.25 A 0.5 0.5 45 0 0 1.25 3.75 A 0.5 0.5 0 0 0 0.75 3.25 z M 2.25 3.25 A 0.5 0.5 0 0 0 1.75 3.75 A 0.5 0.5 45 0 0 2.25 4.25 A 0.5 0.5 45 0 0 2.75 3.75 A 0.5 0.5 0 0 0 2.25 3.25 z"
android:fillColor="#9d9d9d"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_fab_edit.xml b/core/src/main/res/drawable/ic_fab_edit.xml
new file mode 100644
index 000000000..6fd80e29f
--- /dev/null
+++ b/core/src/main/res/drawable/ic_fab_edit.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/colorOnPrimary" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_fab_edit_black.xml b/core/src/main/res/drawable/ic_fab_edit_black.xml
deleted file mode 100644
index a9fec7080..000000000
--- a/core/src/main/res/drawable/ic_fab_edit_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_fab_edit_white.xml b/core/src/main/res/drawable/ic_fab_edit_white.xml
deleted file mode 100644
index cb2e394b0..000000000
--- a/core/src/main/res/drawable/ic_fab_edit_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_rewind_black_48dp.xml b/core/src/main/res/drawable/ic_fast_forward.xml
index e97e29955..29a607074 100644
--- a/core/src/main/res/drawable/ic_av_fast_rewind_black_48dp.xml
+++ b/core/src/main/res/drawable/ic_fast_forward.xml
@@ -3,5 +3,5 @@
android:height="48dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FF000000" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_forward_white_80dp.xml b/core/src/main/res/drawable/ic_fast_forward_video_white.xml
index a60145f1d..a60145f1d 100644
--- a/core/src/main/res/drawable/ic_av_fast_forward_white_80dp.xml
+++ b/core/src/main/res/drawable/ic_fast_forward_video_white.xml
diff --git a/core/src/main/res/drawable/ic_av_fast_forward_black_48dp.xml b/core/src/main/res/drawable/ic_fast_rewind.xml
index f55701e95..634bae8e8 100644
--- a/core/src/main/res/drawable/ic_av_fast_forward_black_48dp.xml
+++ b/core/src/main/res/drawable/ic_fast_rewind.xml
@@ -3,5 +3,5 @@
android:height="48dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FF000000" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_rewind_white_80dp.xml b/core/src/main/res/drawable/ic_fast_rewind_video_white.xml
index 4609e0c3e..4609e0c3e 100644
--- a/core/src/main/res/drawable/ic_av_fast_rewind_white_80dp.xml
+++ b/core/src/main/res/drawable/ic_fast_rewind_video_white.xml
diff --git a/core/src/main/res/drawable/ic_feed.xml b/core/src/main/res/drawable/ic_feed.xml
new file mode 100644
index 000000000..e1d650aba
--- /dev/null
+++ b/core/src/main/res/drawable/ic_feed.xml
@@ -0,0 +1,6 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M6.18,17.82m-2.18,0a2.18,2.18 0,1 1,4.36 0a2.18,2.18 0,1 1,-4.36 0"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M4,4.44v2.83c7.03,0 12.73,5.7 12.73,12.73h2.83c0,-8.59 -6.97,-15.56 -15.56,-15.56zM4,10.1v2.83c3.9,0 7.07,3.17 7.07,7.07h2.83c0,-5.47 -4.43,-9.9 -9.9,-9.9z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_feed_black.xml b/core/src/main/res/drawable/ic_feed_black.xml
index 54c9286de..1d64ff5f8 100644
--- a/core/src/main/res/drawable/ic_feed_black.xml
+++ b/core/src/main/res/drawable/ic_feed_black.xml
@@ -1,6 +1,6 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M6.18,17.82m-2.18,0a2.18,2.18 0,1 1,4.36 0a2.18,2.18 0,1 1,-4.36 0"/>
- <path android:fillColor="#FF000000" android:pathData="M4,4.44v2.83c7.03,0 12.73,5.7 12.73,12.73h2.83c0,-8.59 -6.97,-15.56 -15.56,-15.56zM4,10.1v2.83c3.9,0 7.07,3.17 7.07,7.07h2.83c0,-5.47 -4.43,-9.9 -9.9,-9.9z"/>
+ <path android:fillColor="#000000" android:pathData="M6.18,17.82m-2.18,0a2.18,2.18 0,1 1,4.36 0a2.18,2.18 0,1 1,-4.36 0"/>
+ <path android:fillColor="#000000" android:pathData="M4,4.44v2.83c7.03,0 12.73,5.7 12.73,12.73h2.83c0,-8.59 -6.97,-15.56 -15.56,-15.56zM4,10.1v2.83c3.9,0 7.07,3.17 7.07,7.07h2.83c0,-5.47 -4.43,-9.9 -9.9,-9.9z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_feed_white.xml b/core/src/main/res/drawable/ic_feed_white.xml
deleted file mode 100644
index da0139ff5..000000000
--- a/core/src/main/res/drawable/ic_feed_white.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M6.18,17.82m-2.18,0a2.18,2.18 0,1 1,4.36 0a2.18,2.18 0,1 1,-4.36 0"/>
- <path android:fillColor="#FFFFFFFF" android:pathData="M4,4.44v2.83c7.03,0 12.73,5.7 12.73,12.73h2.83c0,-8.59 -6.97,-15.56 -15.56,-15.56zM4,10.1v2.83c3.9,0 7.07,3.17 7.07,7.07h2.83c0,-5.47 -4.43,-9.9 -9.9,-9.9z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_filter.xml b/core/src/main/res/drawable/ic_filter.xml
new file mode 100644
index 000000000..d634fe7a0
--- /dev/null
+++ b/core/src/main/res/drawable/ic_filter.xml
@@ -0,0 +1,7 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z" />
+</vector>
diff --git a/core/src/main/res/drawable/ic_filter_black_24dp.xml b/core/src/main/res/drawable/ic_filter_black_24dp.xml
deleted file mode 100644
index 54c5123d7..000000000
--- a/core/src/main/res/drawable/ic_filter_black_24dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path android:fillColor="#FF000000" android:pathData="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z" />
-</vector>
diff --git a/core/src/main/res/drawable/ic_filter_white_24dp.xml b/core/src/main/res/drawable/ic_filter_white_24dp.xml
deleted file mode 100644
index 4b1b88581..000000000
--- a/core/src/main/res/drawable/ic_filter_white_24dp.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path android:fillColor="#FFFFFFFF" android:pathData="M15,19.88C15.04,20.18 14.94,20.5 14.71,20.71C14.32,21.1 13.69,21.1 13.3,20.71L9.29,16.7C9.06,16.47 8.96,16.16 9,15.87V10.75L4.21,4.62C3.87,4.19 3.95,3.56 4.38,3.22C4.57,3.08 4.78,3 5,3V3H19V3C19.22,3 19.43,3.08 19.62,3.22C20.05,3.56 20.13,4.19 19.79,4.62L15,10.75V19.88M7.04,5L11,10.06V15.58L13,17.58V10.05L16.96,5H7.04Z" />
-</vector>
diff --git a/core/src/main/res/drawable/ic_folder.xml b/core/src/main/res/drawable/ic_folder.xml
new file mode 100644
index 000000000..5b0e845e7
--- /dev/null
+++ b/core/src/main/res/drawable/ic_folder.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_folder_black.xml b/core/src/main/res/drawable/ic_folder_black.xml
index b1b259ba4..8096fa582 100644
--- a/core/src/main/res/drawable/ic_folder_black.xml
+++ b/core/src/main/res/drawable/ic_folder_black.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
+ <path android:fillColor="#000000" android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_folder_white.xml b/core/src/main/res/drawable/ic_folder_white.xml
deleted file mode 100644
index 91506ec4d..000000000
--- a/core/src/main/res/drawable/ic_folder_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8h16v10z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_history.xml b/core/src/main/res/drawable/ic_history.xml
new file mode 100644
index 000000000..bfa35267c
--- /dev/null
+++ b/core/src/main/res/drawable/ic_history.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_history_black.xml b/core/src/main/res/drawable/ic_history_black.xml
deleted file mode 100644
index d7130c9a8..000000000
--- a/core/src/main/res/drawable/ic_history_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_history_white.xml b/core/src/main/res/drawable/ic_history_white.xml
deleted file mode 100644
index 1ffb08b91..000000000
--- a/core/src/main/res/drawable/ic_history_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_info.xml b/core/src/main/res/drawable/ic_info.xml
new file mode 100644
index 000000000..3fd641ec3
--- /dev/null
+++ b/core/src/main/res/drawable/ic_info.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_info_black.xml b/core/src/main/res/drawable/ic_info_black.xml
deleted file mode 100644
index f52a15fb6..000000000
--- a/core/src/main/res/drawable/ic_info_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_key_black.xml b/core/src/main/res/drawable/ic_key.xml
index 2c78e7c39..cae7afb97 100644
--- a/core/src/main/res/drawable/ic_key_black.xml
+++ b/core/src/main/res/drawable/ic_key.xml
@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp" android:viewportHeight="24.0"
android:viewportWidth="24.0" android:width="24dp">
- <path android:fillColor="#FF000000"
+ <path android:fillColor="?attr/action_icon_color"
android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_key_white.xml b/core/src/main/res/drawable/ic_key_white.xml
deleted file mode 100644
index c8a817eeb..000000000
--- a/core/src/main/res/drawable/ic_key_white.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="24dp">
- <path android:fillColor="#FFFFFFFF"
- android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_check_black.xml b/core/src/main/res/drawable/ic_load_more.xml
index c4654dd52..bd88f5c11 100644
--- a/core/src/main/res/drawable/ic_check_black.xml
+++ b/core/src/main/res/drawable/ic_load_more.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_load_more_black.xml b/core/src/main/res/drawable/ic_load_more_black.xml
deleted file mode 100644
index 534478772..000000000
--- a/core/src/main/res/drawable/ic_load_more_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_load_more_white.xml b/core/src/main/res/drawable/ic_load_more_white.xml
deleted file mode 100644
index c47933164..000000000
--- a/core/src/main/res/drawable/ic_load_more_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_lock_closed.xml b/core/src/main/res/drawable/ic_lock_closed.xml
new file mode 100644
index 000000000..5610eb7be
--- /dev/null
+++ b/core/src/main/res/drawable/ic_lock_closed.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2L8.9,8L8.9,6zM18,20L6,20L6,10h12v10z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_lock_closed_black.xml b/core/src/main/res/drawable/ic_lock_closed_black.xml
deleted file mode 100644
index 63ea6d78d..000000000
--- a/core/src/main/res/drawable/ic_lock_closed_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2L8.9,8L8.9,6zM18,20L6,20L6,10h12v10z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_lock_closed_white.xml b/core/src/main/res/drawable/ic_lock_closed_white.xml
deleted file mode 100644
index 8c923f1d1..000000000
--- a/core/src/main/res/drawable/ic_lock_closed_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1s3.1,1.39 3.1,3.1v2L8.9,8L8.9,6zM18,20L6,20L6,10h12v10z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_lock_open.xml b/core/src/main/res/drawable/ic_lock_open.xml
new file mode 100644
index 000000000..64be87b48
--- /dev/null
+++ b/core/src/main/res/drawable/ic_lock_open.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_lock_open_black.xml b/core/src/main/res/drawable/ic_lock_open_black.xml
deleted file mode 100644
index e91724c22..000000000
--- a/core/src/main/res/drawable/ic_lock_open_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_lock_open_white.xml b/core/src/main/res/drawable/ic_lock_open_white.xml
deleted file mode 100644
index 87d5cf09a..000000000
--- a/core/src/main/res/drawable/ic_lock_open_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,17c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6h1.9c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM18,20L6,20L6,10h12v10z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_bookmark_white_24dp.xml b/core/src/main/res/drawable/ic_network.xml
index 798df2d24..52f5889b8 100644
--- a/core/src/main/res/drawable/ic_bookmark_white_24dp.xml
+++ b/core/src/main/res/drawable/ic_network.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M16,17.01V10h-2v7.01h-3L15,21l4,-3.99h-3zM9,3L5,6.99h3V14h2V6.99h3L9,3z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_network_black.xml b/core/src/main/res/drawable/ic_network_black.xml
deleted file mode 100644
index d9be81776..000000000
--- a/core/src/main/res/drawable/ic_network_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M16,17.01V10h-2v7.01h-3L15,21l4,-3.99h-3zM9,3L5,6.99h3V14h2V6.99h3L9,3z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_network_white.xml b/core/src/main/res/drawable/ic_network_white.xml
deleted file mode 100644
index cb90ee547..000000000
--- a/core/src/main/res/drawable/ic_network_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M16,17.01V10h-2v7.01h-3L15,21l4,-3.99h-3zM9,3L5,6.99h3V14h2V6.99h3L9,3z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notification_cast_off.xml b/core/src/main/res/drawable/ic_notification_cast_off.xml
deleted file mode 100644
index 3e3accd0b..000000000
--- a/core/src/main/res/drawable/ic_notification_cast_off.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="30dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="30dp">
- <path android:fillColor="#FFFFFFFF" android:pathData="M1.6,1.27L0.25,2.75L1.41,3.8C1.16,4.13 1,4.55 1,5V8H3V5.23L18.2,19H14V21H20.41L22.31,22.72L23.65,21.24M6.5,3L8.7,5H21V16.14L23,17.95V5C23,3.89 22.1,3 21,3M1,10V12A9,9 0 0,1 10,21H12C12,14.92 7.08,10 1,10M1,14V16A5,5 0 0,1 6,21H8A7,7 0 0,0 1,14M1,18V21H4A3,3 0 0,0 1,18Z" />
-</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_notification_fast_forward.xml b/core/src/main/res/drawable/ic_notification_fast_forward.xml
deleted file mode 100644
index 8ee82f4ed..000000000
--- a/core/src/main/res/drawable/ic_notification_fast_forward.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="30dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="30dp">
- <path android:fillColor="#FFFFFFFF" android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notification_fast_rewind.xml b/core/src/main/res/drawable/ic_notification_fast_rewind.xml
deleted file mode 100644
index 261ed7e6f..000000000
--- a/core/src/main/res/drawable/ic_notification_fast_rewind.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="30dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="30dp">
- <path android:fillColor="#FFFFFFFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notification_pause.xml b/core/src/main/res/drawable/ic_notification_pause.xml
deleted file mode 100644
index 16ebd4eab..000000000
--- a/core/src/main/res/drawable/ic_notification_pause.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="30dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="30dp">
- <path android:fillColor="#FFFFFFFF" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notification_play.xml b/core/src/main/res/drawable/ic_notification_play.xml
deleted file mode 100644
index eb4acd983..000000000
--- a/core/src/main/res/drawable/ic_notification_play.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="30dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="30dp">
- <path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notification_skip.xml b/core/src/main/res/drawable/ic_notification_skip.xml
deleted file mode 100644
index 6bf03002a..000000000
--- a/core/src/main/res/drawable/ic_notification_skip.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="30dp" android:viewportHeight="24.0"
- android:viewportWidth="24.0" android:width="30dp">
- <path android:fillColor="#FFFFFFFF" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notification_sync.xml b/core/src/main/res/drawable/ic_notification_sync.xml
deleted file mode 100644
index c42a78d7d..000000000
--- a/core/src/main/res/drawable/ic_notification_sync.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,4L12,1L8,5l4,4L12,6c3.31,0 6,2.69 6,6 0,1.01 -0.25,1.97 -0.7,2.8l1.46,1.46C19.54,15.03 20,13.57 20,12c0,-4.42 -3.58,-8 -8,-8zM12,18c-3.31,0 -6,-2.69 -6,-6 0,-1.01 0.25,-1.97 0.7,-2.8L5.24,7.74C4.46,8.97 4,10.43 4,12c0,4.42 3.58,8 8,8v3l4,-4 -4,-4v3z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notification_sync_error.xml b/core/src/main/res/drawable/ic_notification_sync_error.xml
deleted file mode 100644
index b5be1b747..000000000
--- a/core/src/main/res/drawable/ic_notification_sync_error.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M3,12c0,2.21 0.91,4.2 2.36,5.64L3,20h6v-6l-2.24,2.24C5.68,15.15 5,13.66 5,12c0,-2.61 1.67,-4.83 4,-5.65L9,4.26C5.55,5.15 3,8.27 3,12zM11,17h2v-2h-2v2zM21,4h-6v6l2.24,-2.24C18.32,8.85 19,10.34 19,12c0,2.61 -1.67,4.83 -4,5.65v2.09c3.45,-0.89 6,-4.01 6,-7.74 0,-2.21 -0.91,-4.2 -2.36,-5.64L21,4zM11,13h2L13,7h-2v6z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notifications.xml b/core/src/main/res/drawable/ic_notifications.xml
new file mode 100644
index 000000000..927a72d4a
--- /dev/null
+++ b/core/src/main/res/drawable/ic_notifications.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_notifications_black.xml b/core/src/main/res/drawable/ic_notifications_black.xml
deleted file mode 100644
index 7009a6763..000000000
--- a/core/src/main/res/drawable/ic_notifications_black.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_notifications_white.xml b/core/src/main/res/drawable/ic_notifications_white.xml
deleted file mode 100644
index 10239aadd..000000000
--- a/core/src/main/res/drawable/ic_notifications_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp" android:tint="#FFFFFF"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_fast_rewind_white_48dp.xml b/core/src/main/res/drawable/ic_pause.xml
index ba2b8ae4f..9132bb8b4 100644
--- a/core/src/main/res/drawable/ic_av_fast_rewind_white_48dp.xml
+++ b/core/src/main/res/drawable/ic_pause.xml
@@ -3,5 +3,5 @@
android:height="48dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF" android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_av_pause_white_80dp.xml b/core/src/main/res/drawable/ic_pause_video_white.xml
index ad8847cb7..ad8847cb7 100644
--- a/core/src/main/res/drawable/ic_av_pause_white_80dp.xml
+++ b/core/src/main/res/drawable/ic_pause_video_white.xml
diff --git a/core/src/main/res/drawable/ic_av_play_white_24dp.xml b/core/src/main/res/drawable/ic_play_24dp.xml
index 0e896a8d4..515996b3d 100644
--- a/core/src/main/res/drawable/ic_av_play_white_24dp.xml
+++ b/core/src/main/res/drawable/ic_play_24dp.xml
@@ -3,5 +3,5 @@
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF" android:pathData="M8,5v14l11,-7z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M8,5v14l11,-7z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_black_48dp.xml b/core/src/main/res/drawable/ic_play_48dp.xml
index 2e0d011aa..01c156e02 100644
--- a/core/src/main/res/drawable/ic_av_play_black_48dp.xml
+++ b/core/src/main/res/drawable/ic_play_48dp.xml
@@ -3,5 +3,5 @@
android:height="48dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FF000000" android:pathData="M8,5v14l11,-7z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M8,5v14l11,-7z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_white_80dp.xml b/core/src/main/res/drawable/ic_play_video_white.xml
index 9f9dbb1b1..9f9dbb1b1 100644
--- a/core/src/main/res/drawable/ic_av_play_white_80dp.xml
+++ b/core/src/main/res/drawable/ic_play_video_white.xml
diff --git a/core/src/main/res/drawable/ic_playback_speed.xml b/core/src/main/res/drawable/ic_playback_speed.xml
new file mode 100644
index 000000000..2813532cf
--- /dev/null
+++ b/core/src/main/res/drawable/ic_playback_speed.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:height="24dp"
+ android:width="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M 12 15.98 A 2.98 2.98 0 0 1 9.02 12.99 c 0 -1.11 0.61 -2.09 1.49 -2.6 L 20.17 4.81 L 14.67 14.34 C 14.17 15.31 13.16 15.98 12 15.98 M 12 3.05 c 1.8 0 3.48 0.5 4.94 1.31 l -2.09 1.2 C 13.99 5.22 12.99 5.04 12 5.04 a 7.96 7.96 0 0 0 -7.96 7.96 c 0 2.2 0.89 4.19 2.33 5.62 h 0.01 c 0.39 0.39 0.39 1.01 0 1.4 c -0.39 0.39 -1.02 0.39 -1.41 0.01 v 0 C 3.17 18.22 2.05 15.74 2.05 12.99 A 9.95 9.95 0 0 1 12 3.05 m 9.95 9.95 c 0 2.75 -1.11 5.23 -2.91 7.03 v 0 c -0.39 0.38 -1.01 0.38 -1.4 -0.01 c -0.39 -0.39 -0.39 -1.01 0 -1.4 v 0 c 1.44 -1.44 2.33 -3.42 2.33 -5.62 c 0 -0.99 -0.19 -1.99 -0.54 -2.88 L 20.62 8.02 c 0.83 1.49 1.32 3.16 1.32 4.97 z" />
+</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_playback_speed_black.xml b/core/src/main/res/drawable/ic_playback_speed_black.xml
deleted file mode 100644
index 81b1bdd5c..000000000
--- a/core/src/main/res/drawable/ic_playback_speed_black.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path android:fillColor="#FF000000" android:pathData="M 12,15.976673 A 2.9838134,2.9838134 0 0 1 9.0161867,12.992861 c 0,-1.113957 0.6067085,-2.08867 1.4919063,-2.595918 L 20.165701,4.8072659 14.66554,14.335576 C 14.168238,15.310289 13.163688,15.976673 12,15.976673 M 12,3.0468162 c 1.800234,0 3.481116,0.4973021 4.943185,1.3128776 l -2.08867,1.2034715 C 13.989209,5.2249998 12.994606,5.036025 12,5.036025 a 7.9568354,7.9568354 0 0 0 -7.9568354,7.956836 c 0,2.198075 0.885198,4.187283 2.3273743,5.619514 h 0.00995 c 0.3878957,0.387896 0.3878957,1.014497 0,1.402393 -0.3878958,0.387895 -1.0244427,0.387895 -1.4123384,0.01 v 0 C 3.1679127,18.224479 2.0539558,15.737969 2.0539558,12.992861 A 9.9460442,9.9460442 0 0 1 12,3.0468162 m 9.946045,9.9460448 c 0,2.745108 -1.113958,5.231618 -2.914192,7.031853 v 0 c -0.387896,0.377949 -1.014497,0.377949 -1.402392,-0.01 -0.387896,-0.387897 -0.387896,-1.014497 0,-1.402392 v 0 c 1.442176,-1.442178 2.327374,-3.421439 2.327374,-5.619515 0,-0.994605 -0.188974,-1.989209 -0.537086,-2.884354 L 20.62322,8.0197843 c 0.825522,1.4919063 1.322825,3.1628417 1.322825,4.9730227 z" />
-</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_playback_speed_white.xml b/core/src/main/res/drawable/ic_playback_speed_white.xml
deleted file mode 100644
index 183bb595a..000000000
--- a/core/src/main/res/drawable/ic_playback_speed_white.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path android:fillColor="#ffffffff" android:pathData="M 12,15.976673 A 2.9838134,2.9838134 0 0 1 9.0161867,12.992861 c 0,-1.113957 0.6067085,-2.08867 1.4919063,-2.595918 L 20.165701,4.8072659 14.66554,14.335576 C 14.168238,15.310289 13.163688,15.976673 12,15.976673 M 12,3.0468162 c 1.800234,0 3.481116,0.4973021 4.943185,1.3128776 l -2.08867,1.2034715 C 13.989209,5.2249998 12.994606,5.036025 12,5.036025 a 7.9568354,7.9568354 0 0 0 -7.9568354,7.956836 c 0,2.198075 0.885198,4.187283 2.3273743,5.619514 h 0.00995 c 0.3878957,0.387896 0.3878957,1.014497 0,1.402393 -0.3878958,0.387895 -1.0244427,0.387895 -1.4123384,0.01 v 0 C 3.1679127,18.224479 2.0539558,15.737969 2.0539558,12.992861 A 9.9460442,9.9460442 0 0 1 12,3.0468162 m 9.946045,9.9460448 c 0,2.745108 -1.113958,5.231618 -2.914192,7.031853 v 0 c -0.387896,0.377949 -1.014497,0.377949 -1.402392,-0.01 -0.387896,-0.387897 -0.387896,-1.014497 0,-1.402392 v 0 c 1.442176,-1.442178 2.327374,-3.421439 2.327374,-5.619515 0,-0.994605 -0.188974,-1.989209 -0.537086,-2.884354 L 20.62322,8.0197843 c 0.825522,1.4919063 1.322825,3.1628417 1.322825,4.9730227 z" />
-</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_playlist.xml b/core/src/main/res/drawable/ic_playlist.xml
new file mode 100644
index 000000000..c609f9fb3
--- /dev/null
+++ b/core/src/main/res/drawable/ic_playlist.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M19,9L2,9v2h17L19,9zM19,5L2,5v2h17L19,5zM2,15h13v-2L2,13v2zM17,13v6l5,-3 -5,-3z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_playlist_black.xml b/core/src/main/res/drawable/ic_playlist_black.xml
index 8516d8a36..c7b0a356c 100644
--- a/core/src/main/res/drawable/ic_playlist_black.xml
+++ b/core/src/main/res/drawable/ic_playlist_black.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M19,9L2,9v2h17L19,9zM19,5L2,5v2h17L19,5zM2,15h13v-2L2,13v2zM17,13v6l5,-3 -5,-3z"/>
+ <path android:fillColor="#000000" android:pathData="M19,9L2,9v2h17L19,9zM19,5L2,5v2h17L19,5zM2,15h13v-2L2,13v2zM17,13v6l5,-3 -5,-3z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_playlist_white.xml b/core/src/main/res/drawable/ic_playlist_white.xml
deleted file mode 100644
index f1554b9e5..000000000
--- a/core/src/main/res/drawable/ic_playlist_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M19,9L2,9v2h17L19,9zM19,5L2,5v2h17L19,5zM2,15h13v-2L2,13v2zM17,13v6l5,-3 -5,-3z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_questionmark.xml b/core/src/main/res/drawable/ic_questionmark.xml
new file mode 100644
index 000000000..d00e3ae0d
--- /dev/null
+++ b/core/src/main/res/drawable/ic_questionmark.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_questionmark_black.xml b/core/src/main/res/drawable/ic_questionmark_black.xml
deleted file mode 100644
index 8a187e813..000000000
--- a/core/src/main/res/drawable/ic_questionmark_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_questionmark_white.xml b/core/src/main/res/drawable/ic_questionmark_white.xml
deleted file mode 100644
index d7a5ba7bf..000000000
--- a/core/src/main/res/drawable/ic_questionmark_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M11,18H13V16H11V18M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2M12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20M12,6A4,4 0 0,0 8,10H10A2,2 0 0,1 12,8A2,2 0 0,1 14,10C14,12 11,11.75 11,15H13C13,12.75 16,12.5 16,10A4,4 0 0,0 12,6Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_refresh.xml b/core/src/main/res/drawable/ic_refresh.xml
new file mode 100644
index 000000000..487ceb3a1
--- /dev/null
+++ b/core/src/main/res/drawable/ic_refresh.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_refresh_black.xml b/core/src/main/res/drawable/ic_refresh_black.xml
index 59d1caca4..3d96b1739 100644
--- a/core/src/main/res/drawable/ic_refresh_black.xml
+++ b/core/src/main/res/drawable/ic_refresh_black.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
+ <path android:fillColor="#000000" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_refresh_white.xml b/core/src/main/res/drawable/ic_refresh_white.xml
deleted file mode 100644
index c6e841f57..000000000
--- a/core/src/main/res/drawable/ic_refresh_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_remove_black.xml b/core/src/main/res/drawable/ic_remove.xml
index c988cb5f3..da2ea9f1a 100644
--- a/core/src/main/res/drawable/ic_remove_black.xml
+++ b/core/src/main/res/drawable/ic_remove.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M19,13H5v-2h14v2z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M19,13H5v-2h14v2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_remove_white.xml b/core/src/main/res/drawable/ic_remove_white.xml
deleted file mode 100644
index d812091fb..000000000
--- a/core/src/main/res/drawable/ic_remove_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M19,13H5v-2h14v2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_replay_black_48dp.xml b/core/src/main/res/drawable/ic_replay.xml
index 1446ae48b..b7442ff27 100644
--- a/core/src/main/res/drawable/ic_av_replay_black_48dp.xml
+++ b/core/src/main/res/drawable/ic_replay.xml
@@ -5,5 +5,5 @@
android:height="48dp">
<path
android:pathData="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6 -6 6 -6 -2.69 -6 -6H4c0 4.42 3.58 8 8 8s8 -3.58 8 -8 -3.58 -8 -8 -8z"
- android:fillColor="#000000" />
+ android:fillColor="?attr/action_icon_color" />
</vector>
diff --git a/core/src/main/res/drawable/ic_search.xml b/core/src/main/res/drawable/ic_search.xml
new file mode 100644
index 000000000..d21ba4894
--- /dev/null
+++ b/core/src/main/res/drawable/ic_search.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_search_black.xml b/core/src/main/res/drawable/ic_search_black.xml
deleted file mode 100644
index 272c0e746..000000000
--- a/core/src/main/res/drawable/ic_search_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_search_white.xml b/core/src/main/res/drawable/ic_search_white.xml
deleted file mode 100644
index 7d86ce939..000000000
--- a/core/src/main/res/drawable/ic_search_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_select_all_black.xml b/core/src/main/res/drawable/ic_select_all.xml
index 68702c1fe..29e4dbfff 100644
--- a/core/src/main/res/drawable/ic_select_all_black.xml
+++ b/core/src/main/res/drawable/ic_select_all.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FF000000"
+ android:fillColor="?attr/action_icon_color"
android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_select_all_white.xml b/core/src/main/res/drawable/ic_select_all_white.xml
deleted file mode 100644
index 0fc49c923..000000000
--- a/core/src/main/res/drawable/ic_select_all_white.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_select_none_white.xml b/core/src/main/res/drawable/ic_select_none.xml
index 3e1f6b884..c2b3cd481 100644
--- a/core/src/main/res/drawable/ic_select_none_white.xml
+++ b/core/src/main/res/drawable/ic_select_none.xml
@@ -4,8 +4,8 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="?attr/action_icon_color"
android:pathData="m3,5l2,0l0,-2c-1.1,0 -2,0.9 -2,2zm0,8l2,0l0,-2l-2,0l0,2zm4,8l2,0l0,-2l-2,0l0,2zm-4,-12l2,0l0,-2l-2,0l0,2zm10,-6l-2,0l0,2l2,0l0,-2zm6,0l0,2l2,0c0,-1.1 -0.9,-2 -2,-2zm-14,18l0,-2l-2,0c0,1.1 0.9,2 2,2zm-2,-4l2,0l0,-2l-2,0l0,2zm6,-14l-2,0l0,2l2,0l0,-2zm2,18l2,0l0,-2l-2,0l0,2zm8,-8l2,0l0,-2l-2,0l0,2zm0,8c1.1,0 2,-0.9 2,-2l-2,0l0,2zm0,-12l2,0l0,-2l-2,0l0,2zm0,8l2,0l0,-2l-2,0l0,2zm-4,4l2,0l0,-2l-2,0l0,2zm0,-16l2,0l0,-2l-2,0l0,2z"/>
- <path android:fillColor="#FFFFFFFF"
+ <path android:fillColor="?attr/action_icon_color"
android:pathData="M17,8.41L15.59,7 12,10.59 8.41,7 7,8.41 10.59,12 7,15.59 8.41,17 12,13.41 15.59,17 17,15.59 13.41,12z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_select_none_black.xml b/core/src/main/res/drawable/ic_select_none_black.xml
deleted file mode 100644
index 6d4ff62fa..000000000
--- a/core/src/main/res/drawable/ic_select_none_black.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24.0"
- android:viewportHeight="24.0">
- <path
- android:fillColor="#FF000000"
- android:pathData="m3,5l2,0l0,-2c-1.1,0 -2,0.9 -2,2zm0,8l2,0l0,-2l-2,0l0,2zm4,8l2,0l0,-2l-2,0l0,2zm-4,-12l2,0l0,-2l-2,0l0,2zm10,-6l-2,0l0,2l2,0l0,-2zm6,0l0,2l2,0c0,-1.1 -0.9,-2 -2,-2zm-14,18l0,-2l-2,0c0,1.1 0.9,2 2,2zm-2,-4l2,0l0,-2l-2,0l0,2zm6,-14l-2,0l0,2l2,0l0,-2zm2,18l2,0l0,-2l-2,0l0,2zm8,-8l2,0l0,-2l-2,0l0,2zm0,8c1.1,0 2,-0.9 2,-2l-2,0l0,2zm0,-12l2,0l0,-2l-2,0l0,2zm0,8l2,0l0,-2l-2,0l0,2zm-4,4l2,0l0,-2l-2,0l0,2zm0,-16l2,0l0,-2l-2,0l0,2z"/>
- <path android:fillColor="#FF000000"
- android:pathData="M17,8.41L15.59,7 12,10.59 8.41,7 7,8.41 10.59,12 7,15.59 8.41,17 12,13.41 15.59,17 17,15.59 13.41,12z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_settings.xml b/core/src/main/res/drawable/ic_settings.xml
new file mode 100644
index 000000000..ea655eaa9
--- /dev/null
+++ b/core/src/main/res/drawable/ic_settings.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_settings_black.xml b/core/src/main/res/drawable/ic_settings_black.xml
deleted file mode 100644
index 8b2b9f733..000000000
--- a/core/src/main/res/drawable/ic_settings_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M12,8A4,4 0 0,1 16,12A4,4 0 0,1 12,16A4,4 0 0,1 8,12A4,4 0 0,1 12,8M12,10A2,2 0 0,0 10,12A2,2 0 0,0 12,14A2,2 0 0,0 14,12A2,2 0 0,0 12,10M10,22C9.75,22 9.54,21.82 9.5,21.58L9.13,18.93C8.5,18.68 7.96,18.34 7.44,17.94L4.95,18.95C4.73,19.03 4.46,18.95 4.34,18.73L2.34,15.27C2.21,15.05 2.27,14.78 2.46,14.63L4.57,12.97L4.5,12L4.57,11L2.46,9.37C2.27,9.22 2.21,8.95 2.34,8.73L4.34,5.27C4.46,5.05 4.73,4.96 4.95,5.05L7.44,6.05C7.96,5.66 8.5,5.32 9.13,5.07L9.5,2.42C9.54,2.18 9.75,2 10,2H14C14.25,2 14.46,2.18 14.5,2.42L14.87,5.07C15.5,5.32 16.04,5.66 16.56,6.05L19.05,5.05C19.27,4.96 19.54,5.05 19.66,5.27L21.66,8.73C21.79,8.95 21.73,9.22 21.54,9.37L19.43,11L19.5,12L19.43,13L21.54,14.63C21.73,14.78 21.79,15.05 21.66,15.27L19.66,18.73C19.54,18.95 19.27,19.04 19.05,18.95L16.56,17.95C16.04,18.34 15.5,18.68 14.87,18.93L14.5,21.58C14.46,21.82 14.25,22 14,22H10M11.25,4L10.88,6.61C9.68,6.86 8.62,7.5 7.85,8.39L5.44,7.35L4.69,8.65L6.8,10.2C6.4,11.37 6.4,12.64 6.8,13.8L4.68,15.36L5.43,16.66L7.86,15.62C8.63,16.5 9.68,17.14 10.87,17.38L11.24,20H12.76L13.13,17.39C14.32,17.14 15.37,16.5 16.14,15.62L18.57,16.66L19.32,15.36L17.2,13.81C17.6,12.64 17.6,11.37 17.2,10.2L19.31,8.65L18.56,7.35L16.15,8.39C15.38,7.5 14.32,6.86 13.12,6.62L12.75,4H11.25Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_share_white.xml b/core/src/main/res/drawable/ic_share.xml
index ae1b3d12b..574707dd9 100644
--- a/core/src/main/res/drawable/ic_share_white.xml
+++ b/core/src/main/res/drawable/ic_share.xml
@@ -2,6 +2,6 @@
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="?attr/action_icon_color"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_share_black.xml b/core/src/main/res/drawable/ic_share_black.xml
deleted file mode 100644
index f396c50de..000000000
--- a/core/src/main/res/drawable/ic_share_black.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path
- android:fillColor="#FF000000"
- android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_av_play_black_24dp.xml b/core/src/main/res/drawable/ic_skip_24dp.xml
index a93221fb6..76d0976f0 100644
--- a/core/src/main/res/drawable/ic_av_play_black_24dp.xml
+++ b/core/src/main/res/drawable/ic_skip_24dp.xml
@@ -3,5 +3,5 @@
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FF000000" android:pathData="M8,5v14l11,-7z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_av_pause_black_48dp.xml b/core/src/main/res/drawable/ic_skip_48dp.xml
index c5bf76f38..9e4b00ef0 100644
--- a/core/src/main/res/drawable/ic_av_pause_black_48dp.xml
+++ b/core/src/main/res/drawable/ic_skip_48dp.xml
@@ -3,5 +3,5 @@
android:height="48dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FF000000" android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_sleep.xml b/core/src/main/res/drawable/ic_sleep.xml
new file mode 100644
index 000000000..e0ef41607
--- /dev/null
+++ b/core/src/main/res/drawable/ic_sleep.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M15,16H9V14L12.39,10H9V8H15V10L11.62,14H15V16M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_sleep_black.xml b/core/src/main/res/drawable/ic_sleep_black.xml
deleted file mode 100644
index f30d5209f..000000000
--- a/core/src/main/res/drawable/ic_sleep_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M15,16H9V14L12.39,10H9V8H15V10L11.62,14H15V16M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_sleep_off.xml b/core/src/main/res/drawable/ic_sleep_off.xml
new file mode 100644
index 000000000..a9a6f78d0
--- /dev/null
+++ b/core/src/main/res/drawable/ic_sleep_off.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M2,5.27L3.28,4L20,20.72L18.73,22L12.73,16H9V14L9.79,13.06L2,5.27M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M9.82,8H15V10L13.54,11.72L9.82,8M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_sleep_off_black.xml b/core/src/main/res/drawable/ic_sleep_off_black.xml
deleted file mode 100644
index a45f2b91d..000000000
--- a/core/src/main/res/drawable/ic_sleep_off_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M2,5.27L3.28,4L20,20.72L18.73,22L12.73,16H9V14L9.79,13.06L2,5.27M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M9.82,8H15V10L13.54,11.72L9.82,8M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_sleep_off_white.xml b/core/src/main/res/drawable/ic_sleep_off_white.xml
deleted file mode 100644
index e06505a9f..000000000
--- a/core/src/main/res/drawable/ic_sleep_off_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M2,5.27L3.28,4L20,20.72L18.73,22L12.73,16H9V14L9.79,13.06L2,5.27M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M9.82,8H15V10L13.54,11.72L9.82,8M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_sleep_white.xml b/core/src/main/res/drawable/ic_sleep_white.xml
deleted file mode 100644
index 11678c89b..000000000
--- a/core/src/main/res/drawable/ic_sleep_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M23,12H17V10L20.39,6H17V4H23V6L19.62,10H23V12M15,16H9V14L12.39,10H9V8H15V10L11.62,14H15V16M7,20H1V18L4.39,14H1V12H7V14L3.62,18H7V20Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_sliders.xml b/core/src/main/res/drawable/ic_sliders.xml
new file mode 100644
index 000000000..c6f3de7b4
--- /dev/null
+++ b/core/src/main/res/drawable/ic_sliders.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M3,17V19H9V17H3M3,5V7H13V5H3M13,21V19H21V17H13V15H11V21H13M7,9V11H3V13H7V15H9V9H7M21,13V11H11V13H21M15,9H17V7H21V5H17V3H15V9Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_sliders_black.xml b/core/src/main/res/drawable/ic_sliders_black.xml
deleted file mode 100644
index fe3a3476f..000000000
--- a/core/src/main/res/drawable/ic_sliders_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M3,17V19H9V17H3M3,5V7H13V5H3M13,21V19H21V17H13V15H11V21H13M7,9V11H3V13H7V15H9V9H7M21,13V11H11V13H21M15,9H17V7H21V5H17V3H15V9Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_sliders_white.xml b/core/src/main/res/drawable/ic_sliders_white.xml
deleted file mode 100644
index c74b7e494..000000000
--- a/core/src/main/res/drawable/ic_sliders_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M3,17V19H9V17H3M3,5V7H13V5H3M13,21V19H21V17H13V15H11V21H13M7,9V11H3V13H7V15H9V9H7M21,13V11H11V13H21M15,9H17V7H21V5H17V3H15V9Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_bookmark_black_24dp.xml b/core/src/main/res/drawable/ic_sort.xml
index 6af633f12..5a43ea587 100644
--- a/core/src/main/res/drawable/ic_bookmark_black_24dp.xml
+++ b/core/src/main/res/drawable/ic_sort.xml
@@ -1,5 +1,5 @@
<vector android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/>
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_sort_black.xml b/core/src/main/res/drawable/ic_sort_black.xml
deleted file mode 100644
index f3f46335d..000000000
--- a/core/src/main/res/drawable/ic_sort_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_sort_white.xml b/core/src/main/res/drawable/ic_sort_white.xml
deleted file mode 100644
index 9b76aab9f..000000000
--- a/core/src/main/res/drawable/ic_sort_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_star.xml b/core/src/main/res/drawable/ic_star.xml
new file mode 100644
index 000000000..66cba0557
--- /dev/null
+++ b/core/src/main/res/drawable/ic_star.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_star_black.xml b/core/src/main/res/drawable/ic_star_black.xml
deleted file mode 100644
index d0f1012ca..000000000
--- a/core/src/main/res/drawable/ic_star_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_star_border.xml b/core/src/main/res/drawable/ic_star_border.xml
new file mode 100644
index 000000000..d4f68de44
--- /dev/null
+++ b/core/src/main/res/drawable/ic_star_border.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_star_border_black.xml b/core/src/main/res/drawable/ic_star_border_black.xml
deleted file mode 100644
index 5e9f484c4..000000000
--- a/core/src/main/res/drawable/ic_star_border_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_star_border_white.xml b/core/src/main/res/drawable/ic_star_border_white.xml
deleted file mode 100644
index 6df0e2919..000000000
--- a/core/src/main/res/drawable/ic_star_border_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M22,9.24l-7.19,-0.62L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21 12,17.27 18.18,21l-1.63,-7.03L22,9.24zM12,15.4l-3.76,2.27 1,-4.28 -3.32,-2.88 4.38,-0.38L12,6.1l1.71,4.04 4.38,0.38 -3.32,2.88 1,4.28L12,15.4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_star_white.xml b/core/src/main/res/drawable/ic_star_white.xml
deleted file mode 100644
index c8a628241..000000000
--- a/core/src/main/res/drawable/ic_star_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M12,17.27L18.18,21l-1.64,-7.03L22,9.24l-7.19,-0.61L12,2 9.19,8.63 2,9.24l5.46,4.73L5.82,21z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_statistics.xml b/core/src/main/res/drawable/ic_statistics.xml
new file mode 100644
index 000000000..77272a527
--- /dev/null
+++ b/core/src/main/res/drawable/ic_statistics.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M13,2.05V5.08C16.39,5.57 19,8.47 19,12C19,12.9 18.82,13.75 18.5,14.54L21.12,16.07C21.68,14.83 22,13.45 22,12C22,6.82 18.05,2.55 13,2.05M12,19A7,7 0 0,1 5,12C5,8.47 7.61,5.57 11,5.08V2.05C5.94,2.55 2,6.81 2,12A10,10 0 0,0 12,22C15.3,22 18.23,20.39 20.05,17.91L17.45,16.38C16.17,18 14.21,19 12,19Z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_statistics_black.xml b/core/src/main/res/drawable/ic_statistics_black.xml
deleted file mode 100644
index f5e7f7153..000000000
--- a/core/src/main/res/drawable/ic_statistics_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M13,2.05V5.08C16.39,5.57 19,8.47 19,12C19,12.9 18.82,13.75 18.5,14.54L21.12,16.07C21.68,14.83 22,13.45 22,12C22,6.82 18.05,2.55 13,2.05M12,19A7,7 0 0,1 5,12C5,8.47 7.61,5.57 11,5.08V2.05C5.94,2.55 2,6.81 2,12A10,10 0 0,0 12,22C15.3,22 18.23,20.39 20.05,17.91L17.45,16.38C16.17,18 14.21,19 12,19Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_statistics_white.xml b/core/src/main/res/drawable/ic_statistics_white.xml
deleted file mode 100644
index 7d180b568..000000000
--- a/core/src/main/res/drawable/ic_statistics_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M13,2.05V5.08C16.39,5.57 19,8.47 19,12C19,12.9 18.82,13.75 18.5,14.54L21.12,16.07C21.68,14.83 22,13.45 22,12C22,6.82 18.05,2.55 13,2.05M12,19A7,7 0 0,1 5,12C5,8.47 7.61,5.57 11,5.08V2.05C5.94,2.55 2,6.81 2,12A10,10 0 0,0 12,22C15.3,22 18.23,20.39 20.05,17.91L17.45,16.38C16.17,18 14.21,19 12,19Z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_storage.xml b/core/src/main/res/drawable/ic_storage.xml
new file mode 100644
index 000000000..8b0c76c11
--- /dev/null
+++ b/core/src/main/res/drawable/ic_storage.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM12,8h-2L10,4h2v4zM15,8h-2L13,4h2v4zM18,8h-2L16,4h2v4z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_storage_black.xml b/core/src/main/res/drawable/ic_storage_black.xml
deleted file mode 100644
index 89a6417f5..000000000
--- a/core/src/main/res/drawable/ic_storage_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM12,8h-2L10,4h2v4zM15,8h-2L13,4h2v4zM18,8h-2L16,4h2v4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_storage_white.xml b/core/src/main/res/drawable/ic_storage_white.xml
deleted file mode 100644
index db7e76bd5..000000000
--- a/core/src/main/res/drawable/ic_storage_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M18,2h-8L4.02,8 4,20c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,4c0,-1.1 -0.9,-2 -2,-2zM12,8h-2L10,4h2v4zM15,8h-2L13,4h2v4zM18,8h-2L16,4h2v4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_stream_black.xml b/core/src/main/res/drawable/ic_stream.xml
index 26b473a35..cbc40701a 100644
--- a/core/src/main/res/drawable/ic_stream_black.xml
+++ b/core/src/main/res/drawable/ic_stream.xml
@@ -4,11 +4,11 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:fillColor="#FF000000"
+ android:fillColor="?attr/action_icon_color"
android:pathData="m20.5099,12.0219c0,-1.41 -1.143,-2.553 -2.553,-2.553h-1.2765v-0.4255c0,-2.5849 -2.0955,-4.6804 -4.6804,-4.6804 -2.1275,0 -3.9401,1.4382 -4.5017,3.4039H6.8941c-1.8799,0 -3.4039,1.524 -3.4039,3.4039 0,1.8799 1.524,3.4039 3.4039,3.4039h2.553v1.702H6.8941c-2.8199,0 -5.1059,-2.286 -5.1059,-5.1059 0,-2.6381 1.9913,-4.7996 4.5528,-5.0719C7.4047,4.0566 9.5407,2.661 12,2.661c3.0976,0 5.6761,2.2041 6.2633,5.14 2.2041,0.1532 3.9486,1.9743 3.9486,4.2209 0,1.4041 -0.6445,2.3463 -1.5471,3.1885 -0.4185,-0.2876 -0.8313,-0.5368 -1.3724,-0.8908 1.092,-0.8808 1.2175,-1.8722 1.2175,-2.2977z"
android:strokeWidth="0.85098612"/>
<path
- android:fillColor="#FF000000"
+ android:fillColor="?attr/action_icon_color"
android:pathData="m12.2085,13.3476v9.4907l7.4569,-4.7453z"
android:strokeWidth="0.67790419"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_stream_white.xml b/core/src/main/res/drawable/ic_stream_white.xml
deleted file mode 100644
index aca761365..000000000
--- a/core/src/main/res/drawable/ic_stream_white.xml
+++ /dev/null
@@ -1,14 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="m20.5099,12.0219c0,-1.41 -1.143,-2.553 -2.553,-2.553h-1.2765v-0.4255c0,-2.5849 -2.0955,-4.6804 -4.6804,-4.6804 -2.1275,0 -3.9401,1.4382 -4.5017,3.4039H6.8941c-1.8799,0 -3.4039,1.524 -3.4039,3.4039 0,1.8799 1.524,3.4039 3.4039,3.4039h2.553v1.702H6.8941c-2.8199,0 -5.1059,-2.286 -5.1059,-5.1059 0,-2.6381 1.9913,-4.7996 4.5528,-5.0719C7.4047,4.0566 9.5407,2.661 12,2.661c3.0976,0 5.6761,2.2041 6.2633,5.14 2.2041,0.1532 3.9486,1.9743 3.9486,4.2209 0,1.4041 -0.6445,2.3463 -1.5471,3.1885 -0.4185,-0.2876 -0.8313,-0.5368 -1.3724,-0.8908 1.092,-0.8808 1.2175,-1.8722 1.2175,-2.2977z"
- android:strokeWidth="0.85098612"/>
- <path
- android:fillColor="#FFFFFFFF"
- android:pathData="m12.2085,13.3476v9.4907l7.4569,-4.7453z"
- android:strokeWidth="0.67790419"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_videocam_black_24dp.xml b/core/src/main/res/drawable/ic_videocam.xml
index 86283557e..fd4adb0e4 100644
--- a/core/src/main/res/drawable/ic_videocam_black_24dp.xml
+++ b/core/src/main/res/drawable/ic_videocam.xml
@@ -3,6 +3,6 @@
android:width="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
- <path android:fillColor="#FF000000"
+ <path android:fillColor="?attr/action_icon_color"
android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
</vector>
diff --git a/core/src/main/res/drawable/ic_videocam_white_24dp.xml b/core/src/main/res/drawable/ic_videocam_white_24dp.xml
deleted file mode 100644
index a8cfd71e3..000000000
--- a/core/src/main/res/drawable/ic_videocam_white_24dp.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:height="24dp"
- android:width="24dp"
- android:viewportHeight="24.0"
- android:viewportWidth="24.0">
- <path android:fillColor="#FFFFFFFF"
- android:pathData="M17,10.5V7c0,-0.55 -0.45,-1 -1,-1H4c-0.55,0 -1,0.45 -1,1v10c0,0.55 0.45,1 1,1h12c0.55,0 1,-0.45 1,-1v-3.5l4,4v-11l-4,4z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_volume_adaption_white.xml b/core/src/main/res/drawable/ic_volume_adaption.xml
index 27d7c6e7b..0ebaceea2 100644
--- a/core/src/main/res/drawable/ic_volume_adaption_white.xml
+++ b/core/src/main/res/drawable/ic_volume_adaption.xml
@@ -4,6 +4,6 @@
android:viewportWidth="24"
android:viewportHeight="24">
<path
- android:fillColor="#FFFFFFFF"
+ android:fillColor="?attr/action_icon_color"
android:pathData="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" />
</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_volume_adaption_black.xml b/core/src/main/res/drawable/ic_volume_adaption_black.xml
deleted file mode 100644
index 5aac62e0a..000000000
--- a/core/src/main/res/drawable/ic_volume_adaption_black.xml
+++ /dev/null
@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:fillColor="#FF000000"
- android:pathData="M14,3.23V5.29C16.89,6.15 19,8.83 19,12C19,15.17 16.89,17.84 14,18.7V20.77C18,19.86 21,16.28 21,12C21,7.72 18,4.14 14,3.23M16.5,12C16.5,10.23 15.5,8.71 14,7.97V16C15.5,15.29 16.5,13.76 16.5,12M3,9V15H7L12,20V4L7,9H3Z" />
-</vector> \ No newline at end of file
diff --git a/core/src/main/res/drawable/ic_web.xml b/core/src/main/res/drawable/ic_web.xml
new file mode 100644
index 000000000..2212102a2
--- /dev/null
+++ b/core/src/main/res/drawable/ic_web.xml
@@ -0,0 +1,5 @@
+<vector android:height="24dp"
+ android:viewportHeight="24.0" android:viewportWidth="24.0"
+ android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="?attr/action_icon_color" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,18L4,18v-4h11v4zM15,13L4,13L4,9h11v4zM20,18h-4L16,9h4v9z"/>
+</vector>
diff --git a/core/src/main/res/drawable/ic_web_black.xml b/core/src/main/res/drawable/ic_web_black.xml
deleted file mode 100644
index 0ad45e031..000000000
--- a/core/src/main/res/drawable/ic_web_black.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FF000000" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,18L4,18v-4h11v4zM15,13L4,13L4,9h11v4zM20,18h-4L16,9h4v9z"/>
-</vector>
diff --git a/core/src/main/res/drawable/ic_web_white.xml b/core/src/main/res/drawable/ic_web_white.xml
deleted file mode 100644
index 02dab111c..000000000
--- a/core/src/main/res/drawable/ic_web_white.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<vector android:height="24dp"
- android:viewportHeight="24.0" android:viewportWidth="24.0"
- android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
- <path android:fillColor="#FFFFFFFF" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM15,18L4,18v-4h11v4zM15,13L4,13L4,9h11v4zM20,18h-4L16,9h4v9z"/>
-</vector>
diff --git a/core/src/main/res/layout/more_content_list_footer.xml b/core/src/main/res/layout/more_content_list_footer.xml
index f6d6a313c..2869ef7de 100644
--- a/core/src/main/res/layout/more_content_list_footer.xml
+++ b/core/src/main/res/layout/more_content_list_footer.xml
@@ -16,7 +16,7 @@
android:layout_height="16dp"
android:layout_gravity="center"
android:contentDescription="@string/load_next_page_label"
- app:srcCompat="?attr/ic_load_more" />
+ app:srcCompat="@drawable/ic_load_more" />
<ProgressBar
android:id="@+id/progBar"
diff --git a/core/src/main/res/layout/player_widget.xml b/core/src/main/res/layout/player_widget.xml
index ab42e4cb4..ec8472517 100644
--- a/core/src/main/res/layout/player_widget.xml
+++ b/core/src/main/res/layout/player_widget.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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -23,7 +24,7 @@
android:background="?android:attr/selectableItemBackground"
android:scaleType="fitCenter"
android:padding="8dp"
- android:src="@drawable/ic_av_play_white_24dp" />
+ app:srcCompat="@drawable/ic_play_24dp" />
<LinearLayout
android:id="@+id/layout_left"
@@ -41,7 +42,7 @@
android:id="@+id/imgvCover"
android:layout_width="@android:dimen/app_icon_size"
android:layout_height="match_parent"
- android:src="@mipmap/ic_launcher_round"
+ app:srcCompat="@mipmap/ic_launcher_round"
android:importantForAccessibility="no"
android:layout_margin="12dp" />
@@ -98,7 +99,7 @@
android:layout_marginRight="2dp"
android:layout_marginEnd="2dp"
android:scaleType="fitXY"
- android:src="@drawable/ic_av_fast_rewind_white_48dp"/>
+ app:srcCompat="@drawable/ic_fast_rewind"/>
<ImageButton
android:id="@+id/butPlayExtended"
@@ -109,7 +110,7 @@
android:layout_marginRight="2dp"
android:layout_marginEnd="2dp"
android:scaleType="fitXY"
- android:src="@drawable/ic_av_play_white_48dp"/>
+ app:srcCompat="@drawable/ic_play_48dp"/>
<ImageButton
android:id="@+id/butFastForward"
@@ -120,7 +121,7 @@
android:layout_marginRight="2dp"
android:layout_marginEnd="2dp"
android:scaleType="fitXY"
- android:src="@drawable/ic_av_fast_forward_white_48dp"/>
+ app:srcCompat="@drawable/ic_fast_forward"/>
<ImageButton
android:id="@+id/butSkip"
@@ -131,7 +132,7 @@
android:layout_marginRight="2dp"
android:layout_marginEnd="2dp"
android:scaleType="fitXY"
- android:src="@drawable/ic_av_skip_white_24dp"/>
+ app:srcCompat="@drawable/ic_skip_24dp"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
diff --git a/core/src/main/res/values-large/dimens.xml b/core/src/main/res/values-large/dimens.xml
deleted file mode 100644
index 27e269099..000000000
--- a/core/src/main/res/values-large/dimens.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
-
- <dimen name="thumbnail_length">170dp</dimen>
-</resources> \ No newline at end of file
diff --git a/core/src/main/res/values/arrays.xml b/core/src/main/res/values/arrays.xml
index 6b3a10f46..a8e204d85 100644
--- a/core/src/main/res/values/arrays.xml
+++ b/core/src/main/res/values/arrays.xml
@@ -45,16 +45,6 @@
<item>60</item>
</integer-array>
- <string-array name="update_intervall_values">
- <item>1</item>
- <item>2</item>
- <item>4</item>
- <item>8</item>
- <item>12</item>
- <item>24</item>
- <item>72</item>
- </string-array>
-
<string-array name="episode_cache_size_entries">
<item>5</item>
<item>10</item>
diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml
index 91ecae93d..baab3be7a 100644
--- a/core/src/main/res/values/attrs.xml
+++ b/core/src/main/res/values/attrs.xml
@@ -1,67 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
- <attr name="action_about" format="reference"/>
- <attr name="action_search" format="reference"/>
- <attr name="action_stream" format="reference"/>
- <attr name="av_download" format="reference"/>
- <attr name="av_fast_forward" format="reference"/>
- <attr name="av_pause" format="reference"/>
- <attr name="av_play" format="reference"/>
- <attr name="av_replay" format="reference"/>
- <attr name="av_skip" format="reference"/>
- <attr name="av_rewind" format="reference"/>
- <attr name="ic_delete" format="reference"/>
- <attr name="content_new" format="reference"/>
- <attr name="content_remove_from_queue" format="reference"/>
- <attr name="storage" format="reference"/>
- <attr name="statistics" format="reference"/>
- <attr name="feed" format="reference"/>
- <attr name="location_web_site" format="reference"/>
- <attr name="navigation_accept" format="reference"/>
- <attr name="navigation_cancel" format="reference"/>
- <attr name="ic_load_more" format="reference"/>
- <attr name="navigation_refresh" format="reference"/>
<attr name="navigation_up" format="reference"/>
- <attr name="stat_playlist" format="reference"/>
- <attr name="ic_folder" format="reference"/>
- <attr name="type_video" format="reference"/>
<attr name="dragview_background" format="reference"/>
- <attr name="ic_history" format="reference"/>
- <attr name="ic_sliders" format="reference"/>
- <attr name="ic_settings_playback" format="reference"/>
- <attr name="ic_settings" format="reference"/>
- <attr name="ic_lock_open" format="reference"/>
- <attr name="ic_lock_closed" format="reference"/>
- <attr name="ic_filter" format="reference"/>
<attr name="progressBarTheme" format="reference"/>
- <attr name="ic_fav" format="reference"/>
- <attr name="ic_unfav" format="reference"/>
- <attr name="ic_sleep" format="reference"/>
- <attr name="ic_sleep_off" format="reference"/>
- <attr name="checkbox_multiple" format="reference"/>
- <attr name="ic_select_all" format="reference"/>
- <attr name="ic_select_none" format="reference"/>
- <attr name="ic_sort" format="reference"/>
- <attr name="ic_key" format="reference"/>
- <attr name="ic_volume_adaption" format="reference"/>
- <attr name="ic_network" format="reference"/>
- <attr name="ic_appearance" format="reference"/>
- <attr name="ic_questionmark" format="reference" />
- <attr name="ic_chat" format="reference"/>
- <attr name="ic_bug" format="reference" />
<attr name="master_switch_background" format="color"/>
<attr name="currently_playing_background" format="color"/>
- <attr name="ic_bookmark" format="reference"/>
- <attr name="ic_settings_speed" format="reference" />
- <attr name="ic_settings_skip" format="reference" />
<attr name="drawer_activated_color" format="color"/>
- <attr name="batch_edit_fab_icon" format="reference"/>
<attr name="action_icon_color" format="color"/>
<attr name="scrollbar_thumb" format="reference"/>
<attr name="background_elevated" format="color"/>
<attr name="filter_dialog_clear" format="color"/>
<attr name="filter_dialog_button_background" format="reference"/>
- <attr name="ic_notifications" format="reference"/>
<attr name="seek_background" format="color" />
- <attr name="ic_share" format="reference"/>
</resources>
diff --git a/core/src/main/res/values/dimens.xml b/core/src/main/res/values/dimens.xml
index 4702a5302..d1e200d1d 100644
--- a/core/src/main/res/values/dimens.xml
+++ b/core/src/main/res/values/dimens.xml
@@ -2,21 +2,17 @@
<resources>
<dimen name="widget_margin">0dp</dimen>
- <dimen name="thumbnail_length">70dp</dimen>
<dimen name="external_player_height">64dp</dimen>
- <dimen name="enc_icons_size">20dp</dimen>
<dimen name="text_size_micro">12sp</dimen>
<dimen name="text_size_small">14sp</dimen>
<dimen name="text_size_navdrawer">16sp</dimen>
<dimen name="text_size_medium">18sp</dimen>
<dimen name="text_size_large">22sp</dimen>
- <dimen name="thumbnail_length_itemlist">56dp</dimen>
+ <dimen name="thumbnail_length_itemlist">56dp</dimen>
<dimen name="thumbnail_length_queue_item">56dp</dimen>
- <dimen name="thumbnail_length_downloaded_item">64dp</dimen>
<dimen name="thumbnail_length_onlinefeedview">100dp</dimen>
<dimen name="feeditemlist_header_height">132dp</dimen>
<dimen name="thumbnail_length_navlist">40dp</dimen>
- <dimen name="listview_secondary_button_width">48dp</dimen>
<dimen name="listitem_iconwithtext_height">48dp</dimen>
<dimen name="listitem_iconwithtext_textleftpadding">16dp</dimen>
@@ -27,7 +23,6 @@
<dimen name="list_vertical_padding">8dp</dimen>
<dimen name="listitem_icon_leftpadding">16dp</dimen>
- <dimen name="listitem_icon_rightpadding">16dp</dimen>
<dimen name="audioplayer_playercontrols_length">48dp</dimen>
<dimen name="audioplayer_playercontrols_length_big">64dp</dimen>
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index 1bf8fc081..c8ec07292 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -18,7 +18,7 @@
<string name="new_label">New</string>
<string name="settings_label">Settings</string>
<string name="downloads_label">Downloads</string>
- <string name="downloads_running_label">Running</string>
+ <string name="open_autodownload_settings">Open Settings</string>
<string name="downloads_completed_label">Completed</string>
<string name="downloads_log_label">Log</string>
<string name="subscriptions_label">Subscriptions</string>
@@ -26,7 +26,6 @@
<string name="cancel_download_label">Cancel Download</string>
<string name="playback_history_label">Playback History</string>
<string name="gpodnet_main_label">gpodder.net</string>
- <string name="gpodnet_auth_label">gpodder.net Login</string>
<string name="episode_cache_full_title">Episode cache full</string>
<string name="episode_cache_full_message">The episode cache limit has been reached. You can increase the cache size in the Settings.</string>
<string name="playback_statistics_label">Playback</string>
@@ -85,19 +84,18 @@
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="reset">Reset</string>
- <string name="author_label">Author(s)</string>
- <string name="language_label">Language</string>
<string name="url_label">URL</string>
- <string name="cover_label">Cover</string>
+ <string name="support_funding_label">Support</string>
+ <string name="support_podcast">Support this Podcast</string>
<string name="error_label">Error</string>
<string name="error_msg_prefix">An error occurred:</string>
- <string name="needs_storage_permission">Storage permission is needed for this operation</string>
<string name="refresh_label">Refresh</string>
<string name="external_storage_error_msg">No external storage is available. Please make sure that external storage is mounted so that the app can work properly.</string>
<string name="chapters_label">Chapters</string>
<string name="chapter_duration">Duration: %1$s</string>
<string name="description_label">Description</string>
<string name="shownotes_label">Shownotes</string>
+ <string name="shownotes_contentdescription">swipe up to read shownotes</string>
<string name="episodes_suffix">\u0020episodes</string>
<string name="processing_label">Processing</string>
<string name="close_label">Close</string>
@@ -159,7 +157,6 @@
<string name="remove_all_new_flags_confirmation_msg">Please confirm that you want to remove the \"new\" flag from all episodes.</string>
<string name="show_info_label">Show information</string>
<string name="show_feed_settings_label">Show podcast settings</string>
- <string name="feed_info_label">Podcast info</string>
<string name="feed_settings_label">Podcast settings</string>
<string name="rename_feed_label">Rename podcast</string>
<string name="remove_feed_label">Remove podcast</string>
@@ -175,14 +172,11 @@
<string name="multi_select">Multi select</string>
<string name="select_all_above">Select all above</string>
<string name="select_all_below">Select all below</string>
- <string name="hide_unplayed_episodes_label">Unplayed</string>
- <string name="hide_queued_episodes_label">Queued</string>
- <string name="hide_not_queued_episodes_label">Not queued</string>
- <string name="hide_has_media_label">Has media</string>
<string name="filtered_label">Filtered</string>
<string name="refresh_failed_msg">{fa-exclamation-circle} Last Refresh failed. Tap to view details.</string>
<string name="open_podcast">Open Podcast</string>
<string name="please_wait_for_data">Please wait until the data is loaded</string>
+ <string name="updates_disabled_label">Updates disabled</string>
<!-- actions on feeditems -->
<string name="download_label">Download</string>
@@ -203,9 +197,7 @@
<string name="remove_new_flag_label">Remove \"new\" flag</string>
<string name="removed_new_flag_label">Removed \"new\" flag</string>
<string name="mark_read_label">Mark as played</string>
- <string name="marked_as_read_label">Marked as played</string>
<string name="mark_read_no_media_label">Mark as read</string>
- <string name="marked_as_read_no_media_label">Marked as read</string>
<string name="play_this_to_seek_position">To jump to positions, you need to play the episode</string>
<plurals name="marked_read_batch_label">
<item quantity="one">%d episode marked as played.</item>
@@ -229,9 +221,7 @@
<item quantity="other">%d episodes removed from queue.</item>
</plurals>
<string name="add_to_favorite_label">Add to Favorites</string>
- <string name="added_to_favorites">Added to Favorites</string>
<string name="remove_from_favorite_label">Remove from Favorites</string>
- <string name="removed_from_favorites">Removed from Favorites</string>
<string name="visit_website_label">Visit Website</string>
<string name="skip_episode_label">Skip episode</string>
<string name="activate_auto_download">Activate Auto Download</string>
@@ -267,8 +257,6 @@
<string name="download_canceled_autodownload_enabled_msg">Download canceled\nDisabled <i>Auto Download</i> for this item</string>
<string name="download_report_title">Downloads completed with error(s)</string>
<string name="auto_download_report_title">Auto-downloads completed</string>
- <string name="download_report_content_title">Download Report</string>
- <string name="download_error_malformed_url">Malformed URL</string>
<string name="download_error_io_error">IO Error</string>
<string name="download_error_request_error">Request Error</string>
<string name="download_error_db_access">Database Access Error</string>
@@ -297,10 +285,6 @@
<!-- Mediaplayer messages -->
<string name="player_error_msg">Error!</string>
- <string name="player_stopped_msg">No media playing</string>
- <string name="player_preparing_msg">Preparing</string>
- <string name="player_ready_msg">Ready</string>
- <string name="player_seeking_msg">Seeking</string>
<string name="playback_error_server_died">Server died</string>
<string name="playback_error_unsupported">Unsupported media type</string>
<string name="playback_error_timeout">Operation timed out</string>
@@ -308,7 +292,6 @@
<string name="playback_error_unknown">Unknown Error</string>
<string name="no_media_playing_label">No media playing</string>
<string name="position_default_label" translatable="false">00:00:00</string>
- <string name="player_buffering_msg">Buffering</string>
<string name="player_go_to_picture_in_picture">Picture-in-picture mode</string>
<string name="unknown_media_key">AntennaPod - Unknown media key: %1$d</string>
<string name="error_file_not_found">File not found</string>
@@ -333,16 +316,10 @@
<string name="feed_title">Podcast title</string>
<string name="random">Random</string>
<string name="smart_shuffle">Smart Shuffle</string>
- <string name="ascending">Ascending</string>
- <string name="descending">Descending</string>
<string name="clear_queue_confirmation_msg">Please confirm that you want to clear the queue of ALL of the episodes in it</string>
<string name="time_left_label">Time left:\u0020</string>
<!-- Variable Speed -->
- <string name="download_plugin_label">Download Plugin</string>
- <string name="no_playback_plugin_title">Plugin Not Installed</string>
- <string name="no_playback_plugin_or_sonic_msg">For variable speed playback to work, we recommend to enable the built-in Sonic mediaplayer.</string>
- <string name="enable_sonic">Enable Sonic</string>
<string name="speed_presets">Presets</string>
<string name="preset_already_exists">%1$.2fx is already saved as a preset.</string>
@@ -350,8 +327,6 @@
<string name="no_items_header_label">No queued episodes</string>
<string name="no_items_label">Add an episode by downloading it, or long press an episode and select \"Add to queue\".</string>
<string name="no_shownotes_label">This episode has no shownotes.</string>
- <string name="no_run_downloads_head_label">No downloads running</string>
- <string name="no_run_downloads_label">You can download episodes on the podcast details screen.</string>
<string name="no_comp_downloads_head_label">No downloaded episodes</string>
<string name="no_comp_downloads_label">You can download episodes on the podcast details screen.</string>
<string name="no_log_downloads_head_label">No download log</string>
@@ -364,8 +339,6 @@
<string name="no_new_episodes_label">When new episodes arrive, they will be shown here.</string>
<string name="no_fav_episodes_head_label">No favorite episodes</string>
<string name="no_fav_episodes_label">You can add episodes to the favorites by long-pressing them.</string>
- <string name="no_chapters_head_label">No chapters</string>
- <string name="no_chapters_label">This episode has no chapters.</string>
<string name="no_subscriptions_head_label">No subscriptions</string>
<string name="no_subscriptions_label">To subscribe to a podcast, press the plus icon below.</string>
@@ -414,14 +387,13 @@
<string name="playback_pref_sum">Headphone controls, Skip intervals, Queue</string>
<string name="network_pref">Network</string>
<string name="network_pref_sum">Update interval, Download controls, Mobile data</string>
- <string name="pref_autoUpdateIntervallOrTime_title">Update Interval or Time of Day</string>
- <string name="pref_autoUpdateIntervallOrTime_sum">Specify an interval or a specific time of day to refresh the podcasts automatically</string>
- <string name="pref_autoUpdateIntervallOrTime_message">You can set an <i>interval</i> like \"every 2 hours\", set a specific <i>time of day</i> like \"7:00 AM\" or <i>disable</i> automatic updates altogether.\n\n<small>Please note: Update times are inexact. You may encounter a short delay.</small></string>
- <string name="pref_autoUpdateIntervallOrTime_Disable">Disable</string>
- <string name="pref_autoUpdateIntervallOrTime_Interval">Set Interval</string>
- <string name="pref_autoUpdateIntervallOrTime_TimeOfDay">Set Time of Day</string>
- <string name="pref_autoUpdateIntervallOrTime_at">at %1$s</string>
- <plurals name="pref_autoUpdateIntervallOrTime_every_hours">
+ <string name="feed_refresh_title">Refresh podcasts</string>
+ <string name="feed_refresh_sum">Specify an interval or a specific time to look for new episodes automatically</string>
+ <string name="feed_refresh_interval">Interval</string>
+ <string name="feed_refresh_time">Time</string>
+ <string name="feed_refresh_never">Never</string>
+ <string name="feed_refresh_interval_at">at %1$s</string>
+ <plurals name="feed_refresh_every_x_hours">
<item quantity="one">Every hour</item>
<item quantity="other">Every %d hours</item>
</plurals>
@@ -466,9 +438,6 @@
<string name="pref_theme_title_dark">Dark</string>
<string name="pref_theme_title_trueblack">Black (AMOLED ready)</string>
<string name="pref_episode_cache_unlimited">Unlimited</string>
- <string name="pref_update_interval_hours_plural">hours</string>
- <string name="pref_update_interval_hours_singular">hour</string>
- <string name="pref_update_interval_hours_manual">Manual</string>
<string name="pref_gpodnet_authenticate_title">Login</string>
<string name="pref_gpodnet_authenticate_sum">Login with your gpodder.net account in order to sync your subscriptions.</string>
<string name="pref_gpodnet_logout_title">Logout</string>
@@ -504,7 +473,6 @@
<string name="pref_compact_notification_buttons_dialog_error">You can only select a maximum of %1$d items.</string>
<string name="pref_lockscreen_background_title">Set Lockscreen Background</string>
<string name="pref_lockscreen_background_sum">Set the lockscreen background to the current episode\'s image. As a side effect, this will also show the image in third party apps.</string>
- <string name="pref_expand_notify_unsupport_toast">Android versions before 4.1 do not support expanded notifications.</string>
<string name="pref_enqueue_location_title">Enqueue Location</string>
<string name="pref_enqueue_location_sum">Add episodes to: %1$s</string>
<string name="enqueue_location_back">Back</string>
@@ -517,7 +485,6 @@
<string name="visit_user_forum">User forum</string>
<string name="bug_report_title">Report bug</string>
<string name="open_bug_tracker">Open bug tracker</string>
- <string name="export_logs">Export logs</string>
<string name="copy_to_clipboard">Copy to clipboard</string>
<string name="copied_to_clipboard">Copied to clipboard</string>
<string name="experimental_pref">Experimental</string>
@@ -528,7 +495,7 @@
<string name="pref_no_browser_found">No web browser found.</string>
<string name="pref_cast_title">Chromecast support</string>
<string name="pref_cast_message_play_flavor">Enable support for remote media playback on Cast devices (such as Chromecast, Audio Speakers or Android TV)</string>
- <string name="pref_cast_message_free_flavor">Chromecast requires third party proprietary libraries that are disabled in this version of AntennaPod</string>
+ <string name="pref_cast_message_free_flavor" tools:ignore="UnusedResources">Chromecast requires third party proprietary libraries that are disabled in this version of AntennaPod</string>
<string name="pref_enqueue_downloaded_title">Enqueue Downloaded</string>
<string name="pref_enqueue_downloaded_summary">Add downloaded episodes to the queue</string>
<string name="media_player_builtin">Built-in Android player (deprecated) </string>
@@ -556,13 +523,16 @@
<string name="pref_delete_removes_from_queue_sum">Automatically remove an episode from the queue when it is deleted.</string>
<string name="pref_filter_feed_title">Subscription Filter</string>
<string name="pref_filter_feed_sum">Filter your subscriptions in navigation drawer and subscriptions screen.</string>
- <string name="no_filter_label">None</string>
<string name="subscriptions_are_filtered">Subscriptions are filtered.</string>
<string name="subscriptions_counter_greater_zero">Counter greater than zero</string>
<string name="auto_downloaded">Auto downloaded</string>
<string name="not_auto_downloaded">Not auto downloaded</string>
<string name="kept_updated">Kept updated</string>
<string name="not_kept_updated">Not kept updated</string>
+ <string name="new_episode_notification_enabled">Notification enabled</string>
+ <string name="new_episode_notification_disabled">Notification disabled</string>
+ <string name="pref_feed_settings_dialog_msg">This setting is unique to each podcast. You can change it by opening the podcast page.</string>
+ <string name="pref_contribute">Contribute</string>
<!-- About screen -->
<string name="about_pref">About</string>
@@ -654,7 +624,6 @@
<string name="gpodnet_toplist_header">TOP PODCASTS</string>
<string name="gpodnet_suggestions_header">SUGGESTIONS</string>
<string name="gpodnet_search_hint">Search gpodder.net</string>
- <string name="gpodnetauth_login_title">Login</string>
<string name="gpodnetauth_login_butLabel">Login</string>
<string name="gpodnetauth_encryption_warning">Password and data are not encrypted!</string>
<string name="create_account">Create account</string>
@@ -665,20 +634,13 @@
<string name="gpodnetauth_server_custom">Custom server</string>
<string name="gpodnetauth_host">Hostname</string>
<string name="gpodnetauth_select_server">Select server</string>
- <string name="gpodnetauth_device_descr">Create a new device to use for your gpodder.net account or choose an existing one:</string>
<string name="gpodnetauth_device_name">Device name</string>
<string name="gpodnetauth_device_name_default">AntennaPod on %1$s</string>
- <string name="gpodnetauth_device_caption_errorEmpty">Caption must not be empty</string>
<string name="gpodnetauth_existing_devices">Existing devices</string>
<string name="gpodnetauth_create_device">Create device</string>
-
- <string name="gpodnetauth_device_butChoose">Choose</string>
<string name="gpodnetauth_finish_descr">Congratulations! Your gpodder.net account is now linked with your device. AntennaPod will from now on automatically sync subscriptions on your device with your gpodder.net account.</string>
<string name="gpodnetauth_finish_butsyncnow">Start sync now</string>
- <string name="gpodnetauth_finish_butgomainscreen">Go to main screen</string>
- <string name="gpodnetsync_auth_error_title">gpodder.net authentication error</string>
- <string name="gpodnetsync_auth_error_descr">Wrong username or password</string>
<string name="gpodnetsync_error_title">gpodder.net sync error</string>
<string name="gpodnetsync_error_descr">An error occurred during syncing:\u0020</string>
<string name="gpodnetsync_pref_report_successful">Successful</string>
@@ -686,23 +648,9 @@
<string name="gpodnetsync_username_characters_error">Usernames may only contain letters, digits, hyphens and underscores.</string>
<!-- Directory chooser -->
- <string name="selected_folder_label">Selected folder:</string>
- <string name="create_folder_label">Create folder</string>
<string name="choose_data_directory">Choose Data Folder</string>
<string name="choose_data_directory_message">Please choose the base of your data folder. AntennaPod will create the appropriate sub-directories.</string>
- <string name="choose_data_directory_permission_rationale">Access to external storage is required to change the data folder</string>
<string name="choose_data_directory_available_space">%1$s of %2$s free</string>
- <string name="create_folder_msg">Create new folder with name "%1$s"?</string>
- <string name="create_folder_success">Created new folder</string>
- <string name="create_folder_error_no_write_access">Cannot write to this folder</string>
- <string name="create_folder_error_already_exists">Folder already exists</string>
- <string name="create_folder_error">Could not create folder</string>
- <string name="folder_does_not_exist_error">"%1$s" does not exist</string>
- <string name="folder_not_readable_error">"%1$s" is not readable</string>
- <string name="folder_not_writable_error">"%1$s" is not writable</string>
- <string name="folder_not_empty_dialog_title">Folder is not empty</string>
- <string name="folder_not_empty_dialog_msg">The folder you have selected is not empty. Media downloads and other files will be placed directly in this folder. Continue anyway?</string>
- <string name="set_to_default_folder">Choose default folder</string>
<string name="pref_pausePlaybackForFocusLoss_sum">Pause playback instead of lowering volume when another app wants to play sounds</string>
<string name="pref_pausePlaybackForFocusLoss_title">Pause for Interruptions</string>
<string name="pref_resumeAfterCall_sum">Resume playback after a phone call completes</string>
@@ -720,21 +668,23 @@
<string name="fast_forward_label">Fast forward</string>
<string name="increase_speed">Increase speed</string>
<string name="decrease_speed">Decrease speed</string>
- <string name="media_type_audio_label">Audio</string>
<string name="media_type_video_label">Video</string>
<string name="status_downloading_label">Episode is being downloaded</string>
<string name="in_queue_label">Episode is in the queue</string>
<string name="is_favorite_label">Episode is marked as favorite</string>
- <string name="drag_handle_content_description">Drag to change the position of this item</string>
<string name="load_next_page_label">Load next page</string>
- <string name="switch_pages">Switch pages</string>
<string name="position">Position: %1$s</string>
<string name="apply_action">Apply action</string>
<string name="play_chapter">Play chapter</string>
+ <string name="prev_chapter">Previous chapter</string>
+ <string name="next_chapter">Next chapter</string>
- <!-- Feed information screen -->
+ <!-- Feed settings/information screen -->
<string name="authentication_label">Authentication</string>
<string name="authentication_descr">Change your username and password for this podcast and its episodes.</string>
+ <string name="feed_folders_label">Folders</string>
+ <string name="feed_folders_summary">Change the folders in which this podcast is displayed.</string>
+ <string name="feed_folders_include_root">Show in main list</string>
<string name="auto_download_settings_label">Auto Download Settings</string>
<string name="episode_filters_label">Episode Filter</string>
<string name="episode_filters_description">List of terms used to decide if an episode should be included or excluded when auto downloading</string>
@@ -749,9 +699,6 @@
<string name="statistics_space_used">Space used:</string>
<string name="statistics_view_all">View for all podcasts »</string>
- <!-- Progress information -->
- <string name="progress_upgrading_database">Upgrading the database</string>
-
<!-- AntennaPodSP -->
<string name="sp_apps_importing_feeds_msg">Importing subscriptions from single-purpose apps&#8230;</string>
@@ -827,11 +774,11 @@
<string name="sort_long_short">Long \u2192 Short</string>
<!-- Rating dialog -->
- <string name="rating_title">Like AntennaPod?</string>
- <string name="rating_message">We would appreciate it if you take the time to rate AntennaPod.</string>
- <string name="rating_never_label">Leave me alone</string>
- <string name="rating_later_label">Remind me later</string>
- <string name="rating_now_label">Sure, let\'s do this!</string>
+ <string name="rating_title" tools:ignore="UnusedResources">Like AntennaPod?</string>
+ <string name="rating_message" tools:ignore="UnusedResources">We would appreciate it if you take the time to rate AntennaPod.</string>
+ <string name="rating_never_label" tools:ignore="UnusedResources">Leave me alone</string>
+ <string name="rating_later_label" tools:ignore="UnusedResources">Remind me later</string>
+ <string name="rating_now_label" tools:ignore="UnusedResources">Sure, let\'s do this!</string>
<!-- Share episode dialog -->
<string name="share_dialog_include_label">Include:</string>
diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml
index 533fa8420..2f287155f 100644
--- a/core/src/main/res/values/styles.xml
+++ b/core/src/main/res/values/styles.xml
@@ -22,64 +22,11 @@
<item name="android:textAllCaps">false</item>
<item name="android:textColorHint">@color/grey600</item>
<item name="seek_background">@color/seek_background_light</item>
-
- <item name="storage">@drawable/ic_storage_black</item>
- <item name="ic_network">@drawable/ic_network_black</item>
- <item name="statistics">@drawable/ic_statistics_black</item>
- <item name="action_about">@drawable/ic_info_black</item>
- <item name="checkbox_multiple">@drawable/ic_check_multiple_black</item>
- <item name="action_search">@drawable/ic_search_black</item>
- <item name="action_stream">@drawable/ic_stream_black</item>
- <item name="av_download">@drawable/ic_download_black</item>
- <item name="av_pause">@drawable/ic_av_pause_black_48dp</item>
- <item name="av_play">@drawable/ic_av_play_black_48dp</item>
- <item name="av_replay">@drawable/ic_av_replay_black_48dp</item>
- <item name="av_rewind">@drawable/ic_av_fast_rewind_black_48dp</item>
- <item name="av_fast_forward">@drawable/ic_av_fast_forward_black_48dp</item>
- <item name="av_skip">@drawable/ic_av_skip_black_48dp</item>
- <item name="ic_settings_speed">@drawable/ic_playback_speed_black</item>
- <item name="ic_settings_skip">@drawable/ic_av_skip_black_24dp</item>
- <item name="ic_delete">@drawable/ic_delete_black</item>
- <item name="content_new">@drawable/ic_add_black</item>
- <item name="content_remove_from_queue">@drawable/ic_remove_black</item>
- <item name="feed">@drawable/ic_feed_black</item>
- <item name="location_web_site">@drawable/ic_web_black</item>
- <item name="navigation_accept">@drawable/ic_check_black</item>
- <item name="navigation_cancel">@drawable/ic_cancel_black</item>
- <item name="ic_load_more">@drawable/ic_load_more_black</item>
- <item name="navigation_refresh">@drawable/ic_refresh_black</item>
<item name="navigation_up">@drawable/navigation_up</item>
- <item name="stat_playlist">@drawable/ic_playlist_black</item>
- <item name="type_video">@drawable/ic_videocam_black_24dp</item>
<item name="dragview_background">@drawable/ic_drag_lighttheme</item>
- <item name="ic_history">@drawable/ic_history_black</item>
- <item name="ic_folder">@drawable/ic_folder_black</item>
- <item name="ic_settings_playback">@drawable/ic_av_play_black_24dp</item>
- <item name="ic_fav">@drawable/ic_star_border_black</item>
- <item name="ic_unfav">@drawable/ic_star_black</item>
- <item name="ic_settings">@drawable/ic_settings_black</item>
- <item name="ic_lock_open">@drawable/ic_lock_open_black</item>
- <item name="ic_lock_closed">@drawable/ic_lock_closed_black</item>
- <item name="ic_filter">@drawable/ic_filter_black_24dp</item>
- <item name="ic_sleep">@drawable/ic_sleep_black</item>
- <item name="ic_sleep_off">@drawable/ic_sleep_off_black</item>
- <item name="ic_select_all">@drawable/ic_select_all_black</item>
- <item name="ic_select_none">@drawable/ic_select_none_black</item>
- <item name="ic_sort">@drawable/ic_sort_black</item>
- <item name="ic_sliders">@drawable/ic_sliders_black</item>
- <item name="ic_appearance">@drawable/ic_appearance_black</item>
- <item name="ic_questionmark">@drawable/ic_questionmark_black</item>
- <item name="ic_chat">@drawable/ic_chat_black</item>
- <item name="ic_bug">@drawable/ic_bug_black</item>
- <item name="ic_bookmark">@drawable/ic_bookmark_black_24dp</item>
- <item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
- <item name="ic_key">@drawable/ic_key_black</item>
- <item name="ic_volume_adaption">@drawable/ic_volume_adaption_black</item>
<item name="scrollbar_thumb">@drawable/scrollbar_thumb_light</item>
<item name="filter_dialog_clear">@color/filter_dialog_clear_light</item>
<item name="filter_dialog_button_background">@drawable/filter_dialog_background_light</item>
- <item name="ic_notifications">@drawable/ic_notifications_black</item>
- <item name="ic_share">@drawable/ic_share_black</item>
</style>
<style name="Theme.AntennaPod.Dark" parent="Theme.Base.AntennaPod.Dark">
@@ -104,64 +51,11 @@
<item name="android:textAllCaps">false</item>
<item name="android:textColorHint">@color/medium_gray</item>
<item name="seek_background">@color/seek_background_dark</item>
-
- <item name="storage">@drawable/ic_storage_white</item>
- <item name="ic_network">@drawable/ic_network_white</item>
- <item name="statistics">@drawable/ic_statistics_white</item>
- <item name="action_about">@drawable/ic_info_white</item>
- <item name="checkbox_multiple">@drawable/ic_check_multiple_white</item>
- <item name="action_search">@drawable/ic_search_white</item>
- <item name="action_stream">@drawable/ic_stream_white</item>
- <item name="av_download">@drawable/ic_download_white</item>
- <item name="av_rewind">@drawable/ic_av_fast_rewind_white_48dp</item>
- <item name="av_fast_forward">@drawable/ic_av_fast_forward_white_48dp</item>
- <item name="av_pause">@drawable/ic_av_pause_white_48dp</item>
- <item name="av_play">@drawable/ic_av_play_white_48dp</item>
- <item name="av_replay">@drawable/ic_av_replay_white_48dp</item>
- <item name="av_skip">@drawable/ic_av_skip_white_48dp</item>
- <item name="ic_settings_speed">@drawable/ic_playback_speed_white</item>
- <item name="ic_settings_skip">@drawable/ic_av_skip_white_24dp</item>
- <item name="ic_delete">@drawable/ic_delete_white</item>
- <item name="content_new">@drawable/ic_add_white</item>
- <item name="content_remove_from_queue">@drawable/ic_remove_white</item>
- <item name="feed">@drawable/ic_feed_white</item>
- <item name="location_web_site">@drawable/ic_web_white</item>
- <item name="navigation_accept">@drawable/ic_check_white</item>
- <item name="navigation_cancel">@drawable/ic_cancel_white</item>
- <item name="ic_load_more">@drawable/ic_load_more_white</item>
- <item name="navigation_refresh">@drawable/ic_refresh_white</item>
<item name="navigation_up">@drawable/navigation_up_dark</item>
- <item name="stat_playlist">@drawable/ic_playlist_white</item>
- <item name="type_video">@drawable/ic_videocam_white_24dp</item>
<item name="dragview_background">@drawable/ic_drag_darktheme</item>
- <item name="ic_history">@drawable/ic_history_white</item>
- <item name="ic_folder">@drawable/ic_folder_white</item>
- <item name="ic_settings_playback">@drawable/ic_av_play_white_24dp</item>
- <item name="ic_fav">@drawable/ic_star_border_white</item>
- <item name="ic_unfav">@drawable/ic_star_white</item>
- <item name="ic_settings">@drawable/ic_settings_white</item>
- <item name="ic_lock_open">@drawable/ic_lock_open_white</item>
- <item name="ic_lock_closed">@drawable/ic_lock_closed_white</item>
- <item name="ic_filter">@drawable/ic_filter_white_24dp</item>
- <item name="ic_sleep">@drawable/ic_sleep_white</item>
- <item name="ic_sleep_off">@drawable/ic_sleep_off_white</item>
- <item name="ic_select_all">@drawable/ic_select_all_white</item>
- <item name="ic_select_none">@drawable/ic_select_none_white</item>
- <item name="ic_sort">@drawable/ic_sort_white</item>
- <item name="ic_sliders">@drawable/ic_sliders_white</item>
- <item name="ic_appearance">@drawable/ic_appearance_white</item>
- <item name="ic_questionmark">@drawable/ic_questionmark_white</item>
- <item name="ic_chat">@drawable/ic_chat_white</item>
- <item name="ic_bug">@drawable/ic_bug_white</item>
- <item name="ic_bookmark">@drawable/ic_bookmark_white_24dp</item>
- <item name="batch_edit_fab_icon">@drawable/ic_fab_edit_white</item>
- <item name="ic_key">@drawable/ic_key_white</item>
- <item name="ic_volume_adaption">@drawable/ic_volume_adaption_white</item>
<item name="scrollbar_thumb">@drawable/scrollbar_thumb_dark</item>
<item name="filter_dialog_clear">@color/filter_dialog_clear_dark</item>
<item name="filter_dialog_button_background">@drawable/filter_dialog_background_dark</item>
- <item name="ic_notifications">@drawable/ic_notifications_white</item>
- <item name="ic_share">@drawable/ic_share_white</item>
</style>
<style name="Theme.AntennaPod.TrueBlack" parent="Theme.Base.AntennaPod.TrueBlack">
diff --git a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
index 0225c508a..48de7c6e1 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/ClientConfig.java
@@ -2,10 +2,6 @@ package de.danoeh.antennapod.core;
import android.content.Context;
import android.util.Log;
-import com.google.android.gms.common.GoogleApiAvailability;
-import com.google.android.gms.common.GooglePlayServicesNotAvailableException;
-import com.google.android.gms.common.GooglePlayServicesRepairableException;
-import com.google.android.gms.security.ProviderInstaller;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.SleepTimerPreferences;
@@ -15,6 +11,7 @@ import de.danoeh.antennapod.core.service.download.AntennapodHttpClient;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import de.danoeh.antennapod.core.util.NetworkUtils;
import de.danoeh.antennapod.core.util.gui.NotificationUtils;
+import de.danoeh.antennapod.net.ssl.SslProviderInstaller;
import java.io.File;
@@ -48,7 +45,7 @@ public class ClientConfig {
UserPreferences.init(context);
UsageStatistics.init(context);
PlaybackPreferences.init(context);
- installSslProvider(context);
+ SslProviderInstaller.install(context);
NetworkUtils.init(context);
// Don't initialize Cast-related logic unless it is enabled, to avoid the unnecessary
// Google Play Service usage.
@@ -64,15 +61,4 @@ public class ClientConfig {
NotificationUtils.createChannels(context);
initialized = true;
}
-
- private static void installSslProvider(Context context) {
- try {
- ProviderInstaller.installIfNeeded(context);
- } catch (GooglePlayServicesRepairableException e) {
- e.printStackTrace();
- GoogleApiAvailability.getInstance().showErrorNotification(context, e.getConnectionStatusCode());
- } catch (GooglePlayServicesNotAvailableException e) {
- e.printStackTrace();
- }
- }
}
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
index 48c3bfbfc..dd07b9cd8 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastManager.java
@@ -23,13 +23,11 @@
package de.danoeh.antennapod.core.cast;
import android.content.Context;
-import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.view.ActionProvider;
import androidx.core.view.MenuItemCompat;
import androidx.mediarouter.media.MediaRouter;
import android.util.Log;
-import android.view.KeyEvent;
import android.view.MenuItem;
import com.google.android.gms.cast.ApplicationMetadata;
@@ -46,7 +44,6 @@ import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.libraries.cast.companionlibrary.cast.BaseCastManager;
import com.google.android.libraries.cast.companionlibrary.cast.CastConfiguration;
-import com.google.android.libraries.cast.companionlibrary.cast.MediaQueue;
import com.google.android.libraries.cast.companionlibrary.cast.exceptions.CastException;
import com.google.android.libraries.cast.companionlibrary.cast.exceptions.NoConnectionException;
import com.google.android.libraries.cast.companionlibrary.cast.exceptions.OnFailedListener;
@@ -58,9 +55,7 @@ import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.ClientConfig;
import de.danoeh.antennapod.core.R;
@@ -97,32 +92,14 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
public static final String CAST_APP_ID = CastMediaControlIntent.DEFAULT_MEDIA_RECEIVER_APPLICATION_ID;
- public static final double DEFAULT_VOLUME_STEP = 0.05;
- public static final long DEFAULT_LIVE_STREAM_DURATION_MS = TimeUnit.HOURS.toMillis(2);
- private double volumeStep = DEFAULT_VOLUME_STEP;
- private MediaQueue mediaQueue;
private MediaStatus mediaStatus;
-
private static CastManager INSTANCE;
private RemoteMediaPlayer remoteMediaPlayer;
private int state = MediaStatus.PLAYER_STATE_IDLE;
- private int idleReason;
private final Set<CastConsumer> castConsumers = new CopyOnWriteArraySet<>();
- private long liveStreamDuration = DEFAULT_LIVE_STREAM_DURATION_MS;
- private MediaQueueItem preLoadingItem;
public static final int QUEUE_OPERATION_LOAD = 1;
- public static final int QUEUE_OPERATION_INSERT_ITEMS = 2;
- public static final int QUEUE_OPERATION_UPDATE_ITEMS = 3;
- public static final int QUEUE_OPERATION_JUMP = 4;
- public static final int QUEUE_OPERATION_REMOVE_ITEM = 5;
- public static final int QUEUE_OPERATION_REMOVE_ITEMS = 6;
- public static final int QUEUE_OPERATION_REORDER = 7;
- public static final int QUEUE_OPERATION_MOVE = 8;
public static final int QUEUE_OPERATION_APPEND = 9;
- public static final int QUEUE_OPERATION_NEXT = 10;
- public static final int QUEUE_OPERATION_PREV = 11;
- public static final int QUEUE_OPERATION_SET_REPEAT = 12;
private CastManager(Context context, CastConfiguration castConfiguration) {
super(context, castConfiguration);
@@ -176,19 +153,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
return remoteMediaPlayer;
}
- /**
- * Determines if the media that is loaded remotely is a live stream or not.
- *
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public final boolean isRemoteStreamLive() throws TransientNetworkDisconnectionException,
- NoConnectionException {
- checkConnectivity();
- MediaInfo info = getRemoteMediaInformation();
- return (info != null) && (info.getStreamType() == MediaInfo.STREAM_TYPE_LIVE);
- }
-
/*
* A simple check to make sure remoteMediaPlayer is not null
*/
@@ -199,25 +163,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
}
/**
- * Returns the url for the media that is currently playing on the remote device. If there is no
- * connection, this will return <code>null</code>.
- *
- * @throws NoConnectionException If no connectivity to the device exists
- * @throws TransientNetworkDisconnectionException If framework is still trying to recover from
- * a possibly transient loss of network
- */
- public String getRemoteMediaUrl() throws TransientNetworkDisconnectionException,
- NoConnectionException {
- checkConnectivity();
- if (remoteMediaPlayer != null && remoteMediaPlayer.getMediaInfo() != null) {
- MediaInfo info = remoteMediaPlayer.getMediaInfo();
- remoteMediaPlayer.getMediaStatus().getPlayerState();
- return info.getContentId();
- }
- throw new NoConnectionException();
- }
-
- /**
* Indicates if the remote media is currently playing (or buffering).
*
* @throws NoConnectionException
@@ -256,20 +201,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
}
/**
- * Returns the {@link MediaInfo} for the current media
- *
- * @throws NoConnectionException If no connectivity to the device exists
- * @throws TransientNetworkDisconnectionException If framework is still trying to recover from
- * a possibly transient loss of network
- */
- public MediaInfo getRemoteMediaInformation() throws TransientNetworkDisconnectionException,
- NoConnectionException {
- checkConnectivity();
- checkRemoteMediaPlayerAvailable();
- return remoteMediaPlayer.getMediaInfo();
- }
-
- /**
* Gets the remote's system volume. It internally detects what type of volume is used.
*
* @throws NoConnectionException If no connectivity to the device exists
@@ -327,30 +258,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
}
/**
- * Returns <code>true</code> if remote device is muted.
- *
- * @throws NoConnectionException
- * @throws TransientNetworkDisconnectionException
- */
- public boolean isMute() throws TransientNetworkDisconnectionException, NoConnectionException {
- return isStreamMute() || isDeviceMute();
- }
-
- /**
- * Mutes or un-mutes the stream volume.
- *
- * @throws CastException
- * @throws NoConnectionException
- * @throws TransientNetworkDisconnectionException
- */
- public void setStreamMute(boolean mute) throws CastException, TransientNetworkDisconnectionException,
- NoConnectionException {
- checkConnectivity();
- checkRemoteMediaPlayerAvailable();
- remoteMediaPlayer.setStreamMute(mApiClient, mute);
- }
-
- /**
* Returns the duration of the media that is loaded, in milliseconds.
*
* @throws NoConnectionException
@@ -364,23 +271,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
}
/**
- * Returns the time left (in milliseconds) of the current media. If there is no
- * {@code RemoteMediaPlayer}, it returns -1.
- *
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public long getMediaTimeRemaining()
- throws TransientNetworkDisconnectionException, NoConnectionException {
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- return -1;
- }
- return isRemoteStreamLive() ? liveStreamDuration : remoteMediaPlayer.getStreamDuration()
- - remoteMediaPlayer.getApproximateStreamPosition();
- }
-
- /**
* Returns the current (approximate) position of the current media, in milliseconds.
*
* @throws NoConnectionException
@@ -671,435 +561,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
}
/**
- * Inserts a list of new media items into the queue.
- *
- * @param itemsToInsert List of items to insert into the queue, in the order that they should be
- * played. The itemId field of the items should be unassigned or the
- * request will fail with an INVALID_PARAMS error. Must not be {@code null}
- * or empty.
- * @param insertBeforeItemId ID of the item that will be located immediately after the inserted
- * list. If the value is {@link MediaQueueItem#INVALID_ITEM_ID} or
- * invalid, the inserted list will be appended to the end of the
- * queue.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- * @throws IllegalArgumentException
- */
- public void queueInsertItems(final MediaQueueItem[] itemsToInsert, final int insertBeforeItemId,
- final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- Log.d(TAG, "queueInsertItems");
- checkConnectivity();
- if (itemsToInsert == null || itemsToInsert.length == 0) {
- throw new IllegalArgumentException("items cannot be empty or null");
- }
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to insert into queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueInsertItems(mApiClient, itemsToInsert, insertBeforeItemId, customData)
- .setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(
- QUEUE_OPERATION_INSERT_ITEMS,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Updates properties of a subset of the existing items in the media queue.
- *
- * @param itemsToUpdate List of queue items to be updated. The items will retain the existing
- * order and will be fully replaced with the ones provided, including the
- * media information. Any other items currently in the queue will remain
- * unchanged. The tracks information can not change once the item is loaded
- * (if the item is the currentItem). If any of the items does not exist it
- * will be ignored.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public void queueUpdateItems(final MediaQueueItem[] itemsToUpdate, final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to update the queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueUpdateItems(mApiClient, itemsToUpdate, customData).setResultCallback(
- result -> {
- Log.d(TAG, "queueUpdateItems() " + result.getStatus() + result.getStatus()
- .isSuccess());
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_UPDATE_ITEMS,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Plays the item with {@code itemId} in the queue.
- * <p>
- * If {@code itemId} is not found in the queue, this method will report success without sending
- * a request to the receiver.
- *
- * @param itemId The ID of the item to which to jump.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- * @throws IllegalArgumentException
- */
- public void queueJumpToItem(int itemId, final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException,
- IllegalArgumentException {
- checkConnectivity();
- if (itemId == MediaQueueItem.INVALID_ITEM_ID) {
- throw new IllegalArgumentException("itemId is not valid");
- }
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to jump in a queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueJumpToItem(mApiClient, itemId, customData).setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_JUMP,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Removes a list of items from the queue. If the remaining queue is empty, the media session
- * will be terminated.
- *
- * @param itemIdsToRemove The list of media item IDs to remove. Must not be {@code null} or
- * empty.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- * @throws IllegalArgumentException
- */
- public void queueRemoveItems(final int[] itemIdsToRemove, final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException,
- IllegalArgumentException {
- Log.d(TAG, "queueRemoveItems");
- checkConnectivity();
- if (itemIdsToRemove == null || itemIdsToRemove.length == 0) {
- throw new IllegalArgumentException("itemIds cannot be empty or null");
- }
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to remove items from queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueRemoveItems(mApiClient, itemIdsToRemove, customData).setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_REMOVE_ITEMS,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Removes the item with {@code itemId} from the queue.
- * <p>
- * If {@code itemId} is not found in the queue, this method will silently return without sending
- * a request to the receiver. A {@code itemId} may not be in the queue because it wasn't
- * originally in the queue, or it was removed by another sender.
- *
- * @param itemId The ID of the item to be removed.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- * @throws IllegalArgumentException
- */
- public void queueRemoveItem(final int itemId, final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException,
- IllegalArgumentException {
- Log.d(TAG, "queueRemoveItem");
- checkConnectivity();
- if (itemId == MediaQueueItem.INVALID_ITEM_ID) {
- throw new IllegalArgumentException("itemId is invalid");
- }
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to remove an item from queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueRemoveItem(mApiClient, itemId, customData).setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_REMOVE_ITEM,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Reorder a list of media items in the queue.
- *
- * @param itemIdsToReorder The list of media item IDs to reorder, in the new order. Any other
- * items currently in the queue will maintain their existing order. The
- * list will be inserted just before the item specified by
- * {@code insertBeforeItemId}, or at the end of the queue if
- * {@code insertBeforeItemId} is {@link MediaQueueItem#INVALID_ITEM_ID}.
- * <p>
- * For example:
- * <p>
- * If insertBeforeItemId is not specified <br>
- * Existing queue: "A","D","G","H","B","E" <br>
- * itemIds: "D","H","B" <br>
- * New Order: "A","G","E","D","H","B" <br>
- * <p>
- * If insertBeforeItemId is "A" <br>
- * Existing queue: "A","D","G","H","B" <br>
- * itemIds: "D","H","B" <br>
- * New Order: "D","H","B","A","G","E" <br>
- * <p>
- * If insertBeforeItemId is "G" <br>
- * Existing queue: "A","D","G","H","B" <br>
- * itemIds: "D","H","B" <br>
- * New Order: "A","D","H","B","G","E" <br>
- * <p>
- * If any of the items does not exist it will be ignored.
- * Must not be {@code null} or empty.
- * @param insertBeforeItemId ID of the item that will be located immediately after the reordered
- * list. If set to {@link MediaQueueItem#INVALID_ITEM_ID}, the
- * reordered list will be appended at the end of the queue.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public void queueReorderItems(final int[] itemIdsToReorder, final int insertBeforeItemId,
- final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException,
- IllegalArgumentException {
- Log.d(TAG, "queueReorderItems");
- checkConnectivity();
- if (itemIdsToReorder == null || itemIdsToReorder.length == 0) {
- throw new IllegalArgumentException("itemIdsToReorder cannot be empty or null");
- }
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to reorder items in a queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueReorderItems(mApiClient, itemIdsToReorder, insertBeforeItemId, customData)
- .setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_REORDER,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Moves the item with {@code itemId} to a new position in the queue.
- * <p>
- * If {@code itemId} is not found in the queue, either because it wasn't there originally or it
- * was removed by another sender before calling this function, this function will silently
- * return without sending a request to the receiver.
- *
- * @param itemId The ID of the item to be moved.
- * @param newIndex The new index of the item. If the value is negative, an error will be
- * returned. If the value is out of bounds, or becomes out of bounds because the
- * queue was shortened by another sender while this request is in progress, the
- * item will be moved to the end of the queue.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public void queueMoveItemToNewIndex(int itemId, int newIndex, final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- Log.d(TAG, "queueMoveItemToNewIndex");
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to mote item to new index with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueMoveItemToNewIndex(mApiClient, itemId, newIndex, customData)
- .setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_MOVE,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Appends a new media item to the end of the queue.
- *
- * @param item The item to append. Must not be {@code null}.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public void queueAppendItem(MediaQueueItem item, final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- Log.d(TAG, "queueAppendItem");
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to append item with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueAppendItem(mApiClient, item, customData)
- .setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_APPEND,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Jumps to the next item in the queue.
- *
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public void queueNext(final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- Log.d(TAG, "queueNext");
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to update the queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueNext(mApiClient, customData).setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_NEXT,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Jumps to the previous item in the queue.
- *
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public void queuePrev(final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- Log.d(TAG, "queuePrev");
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to update the queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queuePrev(mApiClient, customData).setResultCallback(
- result -> {
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_PREV,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Inserts an item in the queue and starts the playback of that newly inserted item. It is
- * assumed that we are inserting before the "current item"
- *
- * @param item The item to be inserted
- * @param insertBeforeItemId ID of the item that will be located immediately after the inserted
- * and is assumed to be the "current item"
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- * @throws IllegalArgumentException
- */
- public void queueInsertBeforeCurrentAndPlay(MediaQueueItem item, int insertBeforeItemId,
- final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- Log.d(TAG, "queueInsertBeforeCurrentAndPlay");
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to insert into queue with no active media session");
- throw new NoConnectionException();
- }
- if (item == null || insertBeforeItemId == MediaQueueItem.INVALID_ITEM_ID) {
- throw new IllegalArgumentException(
- "item cannot be empty or insertBeforeItemId cannot be invalid");
- }
- remoteMediaPlayer.queueInsertItems(mApiClient, new MediaQueueItem[]{item},
- insertBeforeItemId, customData).setResultCallback(
- result -> {
- if (result.getStatus().isSuccess()) {
-
- try {
- queuePrev(customData);
- } catch (TransientNetworkDisconnectionException |
- NoConnectionException e) {
- Log.e(TAG, "queuePrev() Failed to skip to previous", e);
- }
- }
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_INSERT_ITEMS,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
- * Sets the repeat mode of the queue.
- *
- * @param repeatMode The repeat playback mode for the queue.
- * @param customData Custom application-specific data to pass along with the request. May be
- * {@code null}.
- * @throws TransientNetworkDisconnectionException
- * @throws NoConnectionException
- */
- public void queueSetRepeatMode(final int repeatMode, final JSONObject customData)
- throws TransientNetworkDisconnectionException, NoConnectionException {
- Log.d(TAG, "queueSetRepeatMode");
- checkConnectivity();
- if (remoteMediaPlayer == null) {
- Log.e(TAG, "Trying to update the queue with no active media session");
- throw new NoConnectionException();
- }
- remoteMediaPlayer
- .queueSetRepeatMode(mApiClient, repeatMode, customData).setResultCallback(
- result -> {
- if (!result.getStatus().isSuccess()) {
- Log.d(TAG, "Failed with status: " + result.getStatus());
- }
- for (CastConsumer consumer : castConsumers) {
- consumer.onMediaQueueOperationResult(QUEUE_OPERATION_SET_REPEAT,
- result.getStatus().getStatusCode());
- }
- });
- }
-
- /**
* Plays the loaded media.
*
* @param position Where to start the playback. Units is milliseconds.
@@ -1294,29 +755,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
});
}
- /**
- * Toggles the playback of the media.
- *
- * @throws CastException
- * @throws NoConnectionException
- * @throws TransientNetworkDisconnectionException
- */
- public void togglePlayback() throws CastException, TransientNetworkDisconnectionException,
- NoConnectionException {
- checkConnectivity();
- boolean isPlaying = isRemoteMediaPlaying();
- if (isPlaying) {
- pause();
- } else {
- if (state == MediaStatus.PLAYER_STATE_IDLE
- && idleReason == MediaStatus.IDLE_REASON_FINISHED) {
- loadMedia(getRemoteMediaInformation(), true, 0);
- } else {
- play();
- }
- }
- }
-
private void attachMediaChannel() throws TransientNetworkDisconnectionException,
NoConnectionException {
Log.d(TAG, "attachMediaChannel()");
@@ -1400,22 +838,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
}
/**
- * Returns the playback status of the remote device.
- *
- * @return Returns one of the values
- * <ul>
- * <li> <code>MediaStatus.PLAYER_STATE_UNKNOWN</code></li>
- * <li> <code>MediaStatus.PLAYER_STATE_IDLE</code></li>
- * <li> <code>MediaStatus.PLAYER_STATE_PLAYING</code></li>
- * <li> <code>MediaStatus.PLAYER_STATE_PAUSED</code></li>
- * <li> <code>MediaStatus.PLAYER_STATE_BUFFERING</code></li>
- * </ul>
- */
- public int getPlaybackStatus() {
- return state;
- }
-
- /**
* Returns the latest retrieved value for the {@link MediaStatus}. This value is updated
* whenever the onStatusUpdated callback is called.
*/
@@ -1423,24 +845,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
return mediaStatus;
}
- /**
- * Returns the Idle reason, defined in <code>MediaStatus.IDLE_*</code>. Note that the returned
- * value is only meaningful if the status is truly <code>MediaStatus.PLAYER_STATE_IDLE
- * </code>
- *
- * <p>Possible values are:
- * <ul>
- * <li>IDLE_REASON_NONE</li>
- * <li>IDLE_REASON_FINISHED</li>
- * <li>IDLE_REASON_CANCELED</li>
- * <li>IDLE_REASON_INTERRUPTED</li>
- * <li>IDLE_REASON_ERROR</li>
- * </ul>
- */
- public int getIdleReason() {
- return idleReason;
- }
-
/*
* This is called by onStatusUpdated() of the RemoteMediaPlayer
*/
@@ -1465,7 +869,7 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
onQueueUpdated(null, null, MediaStatus.REPEAT_MODE_REPEAT_OFF, false);
}
state = mediaStatus.getPlayerState();
- idleReason = mediaStatus.getIdleReason();
+ int idleReason = mediaStatus.getIdleReason();
if (state == MediaStatus.PLAYER_STATE_PLAYING) {
Log.d(TAG, "onRemoteMediaPlayerStatusUpdated(): Player status = playing");
@@ -1503,17 +907,12 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
if (mediaStatus != null) {
item = mediaStatus.getQueueItemById(mediaStatus.getPreloadedItemId());
}
- preLoadingItem = item;
Log.d(TAG, "onRemoteMediaPreloadStatusUpdated() " + item);
for (CastConsumer consumer : castConsumers) {
consumer.onRemoteMediaPreloadStatusUpdated(item);
}
}
- public MediaQueueItem getPreLoadingItem() {
- return preLoadingItem;
- }
-
/*
* This is called by onQueueStatusUpdated() of RemoteMediaPlayer
*/
@@ -1522,13 +921,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
Log.d(TAG, "onQueueUpdated() reached");
Log.d(TAG, String.format(Locale.US, "Queue Items size: %d, Item: %s, Repeat Mode: %d, Shuffle: %s",
queueItems == null ? 0 : queueItems.size(), item, repeatMode, shuffle));
- if (queueItems != null) {
- mediaQueue = new MediaQueue(new CopyOnWriteArrayList<>(queueItems), item, shuffle,
- repeatMode);
- } else {
- mediaQueue = new MediaQueue(new CopyOnWriteArrayList<>(), null, false,
- MediaStatus.REPEAT_MODE_REPEAT_OFF);
- }
for (CastConsumer consumer : castConsumers) {
consumer.onMediaQueueUpdated(queueItems, item, repeatMode, shuffle);
}
@@ -1599,7 +991,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
super.onDisconnected(stopAppOnExit, clearPersistedConnectionData, setDefaultRoute);
state = MediaStatus.PLAYER_STATE_IDLE;
mediaStatus = null;
- mediaQueue = null;
}
class CastListener extends Cast.Listener {
@@ -1635,77 +1026,6 @@ public class CastManager extends BaseCastManager implements OnFailedListener {
}
/**
- * Clients can call this method to delegate handling of the volume. Clients should override
- * {@code dispatchEvent} and call this method:
- * <pre>
- public boolean dispatchKeyEvent(KeyEvent event) {
- if (mCastManager.onDispatchVolumeKeyEvent(event, VOLUME_DELTA)) {
- return true;
- }
- return super.dispatchKeyEvent(event);
- }
- * </pre>
- * @param event The dispatched event.
- * @param volumeDelta The amount by which volume should be increased or decreased in each step
- * @return <code>true</code> if volume is handled by the library, <code>false</code> otherwise.
- */
- public boolean onDispatchVolumeKeyEvent(KeyEvent event, double volumeDelta) {
- if (isConnected()) {
- boolean isKeyDown = event.getAction() == KeyEvent.ACTION_DOWN;
- switch (event.getKeyCode()) {
- case KeyEvent.KEYCODE_VOLUME_UP:
- return changeVolume(volumeDelta, isKeyDown);
- case KeyEvent.KEYCODE_VOLUME_DOWN:
- return changeVolume(-volumeDelta, isKeyDown);
- }
- }
- return false;
- }
-
- private boolean changeVolume(double volumeIncrement, boolean isKeyDown) {
- if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
- && getPlaybackStatus() == MediaStatus.PLAYER_STATE_PLAYING
- && isFeatureEnabled(CastConfiguration.FEATURE_LOCKSCREEN)) {
- return false;
- }
-
- if (isKeyDown) {
- try {
- adjustDeviceVolume(volumeIncrement);
- } catch (CastException | TransientNetworkDisconnectionException |
- NoConnectionException e) {
- Log.e(TAG, "Failed to change volume", e);
- }
- }
- return true;
- }
-
- /**
- * Sets the volume step, i.e. the fraction by which volume will increase or decrease each time
- * user presses the hard volume buttons on the device.
- *
- * @param volumeStep Should be a double between 0 and 1, inclusive.
- */
- public CastManager setVolumeStep(double volumeStep) {
- if ((volumeStep > 1) || (volumeStep < 0)) {
- throw new IllegalArgumentException("Volume Step should be between 0 and 1, inclusive");
- }
- this.volumeStep = volumeStep;
- return this;
- }
-
- /**
- * Returns the volume step. The default value is {@code DEFAULT_VOLUME_STEP}.
- */
- public double getVolumeStep() {
- return volumeStep;
- }
-
- public final MediaQueue getMediaQueue() {
- return mediaQueue;
- }
-
- /**
* Checks whether the selected Cast Device has the specified audio or video capabilities.
*
* @param capability capability from:
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java b/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java
index 2c9772298..e7daf866c 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/CastUtils.java
@@ -10,17 +10,15 @@ import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.common.images.WebImage;
-import de.danoeh.antennapod.core.util.playback.PlayableException;
-import de.danoeh.antennapod.core.util.playback.RemoteMedia;
import java.util.Calendar;
import java.util.List;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.Playable;
+import de.danoeh.antennapod.model.playback.RemoteMedia;
import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.util.playback.ExternalMedia;
-import de.danoeh.antennapod.core.util.playback.Playable;
/**
* Helper functions for Cast support.
@@ -37,7 +35,6 @@ public class CastUtils {
public static final String KEY_FEED_URL = "de.danoeh.antennapod.core.cast.FeedUrl";
public static final String KEY_FEED_WEBSITE = "de.danoeh.antennapod.core.cast.FeedWebsite";
public static final String KEY_EPISODE_NOTES = "de.danoeh.antennapod.core.cast.EpisodeNotes";
- public static final int EPISODE_NOTES_MAX_LENGTH = Integer.MAX_VALUE;
/**
* The field <code>AntennaPod.FormatVersion</code> specifies which version of MediaMetaData
@@ -53,7 +50,7 @@ public class CastUtils {
public static final int MAX_VERSION_FORWARD_COMPATIBILITY = 9999;
public static boolean isCastable(Playable media) {
- if (media == null || media instanceof ExternalMedia) {
+ if (media == null) {
return false;
}
if (media instanceof FeedMedia || media instanceof RemoteMedia) {
@@ -79,23 +76,18 @@ public class CastUtils {
/**
* Converts {@link FeedMedia} objects into a format suitable for sending to a Cast Device.
* Before using this method, one should make sure {@link #isCastable(Playable)} returns
- * {@code true}.
- *
- * Unless media.{@link FeedMedia#loadMetadata() loadMetadata()} has already been called,
- * this method should not run on the main thread.
+ * {@code true}. This method should not run on the main thread.
*
* @param media The {@link FeedMedia} object to be converted.
* @return {@link MediaInfo} object in a format proper for casting.
*/
public static MediaInfo convertFromFeedMedia(FeedMedia media){
- if(media == null) {
+ if (media == null) {
return null;
}
MediaMetadata metadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_GENERIC);
- try{
- media.loadMetadata();
- } catch (PlayableException e) {
- Log.e(TAG, "Unable to load FeedMedia metadata", e);
+ if (media.getItem() == null) {
+ media.setItem(DBReader.getFeedItem(media.getItemId()));
}
FeedItem feedItem = media.getItem();
if (feedItem != null) {
@@ -131,18 +123,12 @@ public class CastUtils {
if (!TextUtils.isEmpty(feedItem.getLink())) {
metadata.putString(KEY_EPISODE_LINK, feedItem.getLink());
}
- }
- String notes = null;
- try {
- notes = media.loadShownotes().call();
- } catch (Exception e) {
- Log.e(TAG, "Unable to load FeedMedia notes", e);
- }
- if (notes != null) {
- if (notes.length() > EPISODE_NOTES_MAX_LENGTH) {
- notes = notes.substring(0, EPISODE_NOTES_MAX_LENGTH);
+ try {
+ DBReader.loadDescriptionOfFeedItem(feedItem);
+ metadata.putString(KEY_EPISODE_NOTES, feedItem.getDescription());
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to load FeedMedia notes", e);
}
- metadata.putString(KEY_EPISODE_NOTES, notes);
}
// This field only identifies the id on the device that has the original version.
// Idea is to perhaps, on a first approach, check if the version on the local DB with the
@@ -195,16 +181,11 @@ public class CastUtils {
if (mediaId > 0) {
FeedMedia fMedia = DBReader.getFeedMedia(mediaId);
if (fMedia != null) {
- try {
- fMedia.loadMetadata();
- if (matches(media, fMedia)) {
- result = fMedia;
- Log.d(TAG, "FeedMedia object obtained matches the MediaInfo provided. id=" + mediaId);
- } else {
- Log.d(TAG, "FeedMedia object obtained does NOT match the MediaInfo provided. id=" + mediaId);
- }
- } catch (PlayableException e) {
- Log.e(TAG, "Unable to load FeedMedia metadata to compare with MediaInfo", e);
+ if (matches(media, fMedia)) {
+ result = fMedia;
+ Log.d(TAG, "FeedMedia object obtained matches the MediaInfo provided. id=" + mediaId);
+ } else {
+ Log.d(TAG, "FeedMedia object obtained does NOT match the MediaInfo provided. id=" + mediaId);
}
} else {
Log.d(TAG, "Unable to find in database a FeedMedia with id=" + mediaId);
diff --git a/core/src/play/java/de/danoeh/antennapod/core/cast/MediaInfoCreator.java b/core/src/play/java/de/danoeh/antennapod/core/cast/MediaInfoCreator.java
index 91de5418d..00011ef05 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/cast/MediaInfoCreator.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/cast/MediaInfoCreator.java
@@ -5,7 +5,7 @@ import android.text.TextUtils;
import com.google.android.gms.cast.MediaInfo;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.common.images.WebImage;
-import de.danoeh.antennapod.core.util.playback.RemoteMedia;
+import de.danoeh.antennapod.model.playback.RemoteMedia;
import java.util.Calendar;
public class MediaInfoCreator {
@@ -39,9 +39,6 @@ public class MediaInfoCreator {
}
String notes = media.getNotes();
if (notes != null) {
- if (notes.length() > CastUtils.EPISODE_NOTES_MAX_LENGTH) {
- notes = notes.substring(0, CastUtils.EPISODE_NOTES_MAX_LENGTH);
- }
metadata.putString(CastUtils.KEY_EPISODE_NOTES, notes);
}
// Default id value
diff --git a/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
deleted file mode 100644
index 2502f17c0..000000000
--- a/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import de.danoeh.antennapod.core.util.playback.RemoteMedia;
-
-/**
- * Implements methods for FeedMedia that are flavor dependent.
- */
-public class FeedMediaFlavorHelper {
- private FeedMediaFlavorHelper(){}
- static boolean instanceOfRemoteMedia(Object o) {
- return o instanceof RemoteMedia;
- }
-}
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
index 31e955017..38e84017f 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -27,7 +27,7 @@ import de.danoeh.antennapod.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
import de.danoeh.antennapod.core.event.MessageEvent;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.NetworkUtils;
import org.greenrobot.eventbus.EventBus;
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
index e61896965..9fd99f236 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
@@ -28,13 +28,13 @@ import de.danoeh.antennapod.core.cast.CastConsumer;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.cast.CastUtils;
import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
-import de.danoeh.antennapod.core.util.playback.PlayableException;
-import de.danoeh.antennapod.core.util.playback.RemoteMedia;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.model.playback.RemoteMedia;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
/**
* Implementation of PlaybackServiceMediaPlayer suitable for remote playback on Cast Devices.
@@ -305,7 +305,7 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
* Internal implementation of playMediaObject. This method has an additional parameter that allows the caller to force a media player reset even if
* the given playable parameter is the same object as the currently playing media.
*
- * @see #playMediaObject(de.danoeh.antennapod.core.util.playback.Playable, boolean, boolean, boolean)
+ * @see #playMediaObject(Playable, boolean, boolean, boolean)
*/
private void playMediaObject(@NonNull final Playable playable, final boolean forceReset, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
if (!CastUtils.isCastable(playable)) {
@@ -354,16 +354,13 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
this.mediaType = media.getMediaType();
this.startWhenPrepared.set(startWhenPrepared);
setPlayerStatus(PlayerStatus.INITIALIZING, media);
- try {
- media.loadMetadata();
- callback.onMediaChanged(true);
- setPlayerStatus(PlayerStatus.INITIALIZED, media);
- if (prepareImmediately) {
- prepare();
- }
- } catch (PlayableException e) {
- Log.e(TAG, "Error while loading media metadata", e);
- setPlayerStatus(PlayerStatus.STOPPED, null);
+ if (media instanceof FeedMedia && ((FeedMedia) media).getItem() == null) {
+ ((FeedMedia) media).setItem(DBReader.getFeedItem(((FeedMedia) media).getItemId()));
+ }
+ callback.onMediaChanged(true);
+ setPlayerStatus(PlayerStatus.INITIALIZED, media);
+ if (prepareImmediately) {
+ prepare();
}
}
@@ -514,13 +511,6 @@ public class RemotePSMP extends PlaybackServiceMediaPlayer {
this.startWhenPrepared.set(startWhenPrepared);
}
- // As things are right now, changing the return value of this function is not enough to ensure
- // all other components recognize it.
- @Override
- public boolean canSetSpeed() {
- return false;
- }
-
@Override
public void setPlaybackParams(float speed, boolean skipSilence) {
//Can be safely ignored as neither set speed not skipSilence is supported
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java
index 8b4a13473..4ad578727 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedFilterTest.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.FeedFilter;
+import de.danoeh.antennapod.model.feed.FeedItem;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java
index b78cecc23..cd96dca89 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemMother.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+
import java.util.Date;
import static de.danoeh.antennapod.core.feed.FeedMother.anyFeed;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
index 5bcbed97a..c4860d818 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedItemTest.java
@@ -1,15 +1,12 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import org.junit.Before;
import org.junit.Test;
-import org.mockito.MockedStatic;
-import org.mockito.Mockito;
import java.text.SimpleDateFormat;
import java.util.Date;
-import de.danoeh.antennapod.core.storage.DBReader;
-
import static de.danoeh.antennapod.core.feed.FeedItemMother.anyFeedItemWithImage;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -113,7 +110,7 @@ public class FeedItemTest {
* If one of `description` or `content:encoded` is null, use the other one.
*/
@Test
- public void testShownotesNullValues() throws Exception {
+ public void testShownotesNullValues() {
testShownotes(null, TEXT_LONG);
testShownotes(TEXT_LONG, null);
}
@@ -122,7 +119,7 @@ public class FeedItemTest {
* If `description` is reasonably longer than `content:encoded`, use `description`.
*/
@Test
- public void testShownotesLength() throws Exception {
+ public void testShownotesLength() {
testShownotes(TEXT_SHORT, TEXT_LONG);
testShownotes(TEXT_LONG, TEXT_SHORT);
}
@@ -133,12 +130,10 @@ public class FeedItemTest {
* @param description Description of the feed item
* @param contentEncoded `content:encoded` of the feed item
*/
- private void testShownotes(String description, String contentEncoded) throws Exception {
- try (MockedStatic<DBReader> ignore = Mockito.mockStatic(DBReader.class)) {
- FeedItem item = new FeedItem();
- item.setDescription(description);
- item.setContentEncoded(contentEncoded);
- assertEquals(TEXT_LONG, item.loadShownotes().call());
- }
+ private void testShownotes(String description, String contentEncoded) {
+ FeedItem item = new FeedItem();
+ item.setDescriptionIfLonger(description);
+ item.setDescriptionIfLonger(contentEncoded);
+ assertEquals(TEXT_LONG, item.getDescription());
}
} \ No newline at end of file
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaMother.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaMother.java
index d95b8787c..35a85af02 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaMother.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaMother.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+
class FeedMediaMother {
private static final String EPISODE_URL = "http://example.com/episode";
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaTest.java
index f27a54f84..43d855cc4 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMediaTest.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import org.junit.Before;
import org.junit.Test;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
index 991495a3f..b1f3fca27 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedMother.java
@@ -1,5 +1,7 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.Feed;
+
public class FeedMother {
public static final String IMAGE_URL = "http://example.com/image";
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
index 88b342850..c256fea11 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/FeedTest.java
@@ -1,9 +1,10 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.Feed;
import org.junit.Before;
import org.junit.Test;
-import de.danoeh.antennapod.core.util.SortOrder;
+import de.danoeh.antennapod.model.feed.SortOrder;
import static de.danoeh.antennapod.core.feed.FeedMother.anyFeed;
import static org.junit.Assert.assertEquals;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java
index 169146c07..eb56a1876 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/LocalFeedUpdaterTest.java
@@ -12,6 +12,8 @@ import androidx.documentfile.provider.DocumentFile;
import androidx.test.platform.app.InstrumentationRegistry;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java b/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java
index e933ce034..4241707bc 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/feed/VolumeAdaptionSettingTest.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.feed;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java b/core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java
index 8c7ecbc52..c9c941d38 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/service/download/DownloadRequestTest.java
@@ -9,9 +9,10 @@ import org.robolectric.RobolectricTestRunner;
import java.util.ArrayList;
-import de.danoeh.antennapod.core.feed.FeedFile;
+import de.danoeh.antennapod.model.feed.FeedFile;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
@RunWith(RobolectricTestRunner.class)
public class DownloadRequestTest {
@@ -34,6 +35,36 @@ public class DownloadRequestTest {
null, null, "usr2", "pass2");
}
+ @Test
+ public void downloadRequestTestEquals() {
+ String destStr = "file://location/media.mp3";
+ String username = "testUser";
+ String password = "testPassword";
+ FeedFile item = createFeedItem(1);
+ Bundle arg = new Bundle();
+ arg.putString("arg1", "value1");
+ DownloadRequest request1 = new DownloadRequest.Builder(destStr, item, true)
+ .deleteOnFailure(true)
+ .withAuthentication(username, password)
+ .withArguments(arg)
+ .build();
+
+ DownloadRequest request2 = new DownloadRequest.Builder(destStr, item, true)
+ .deleteOnFailure(true)
+ .withAuthentication(username, password)
+ .withArguments(arg)
+ .build();
+
+ DownloadRequest request3 = new DownloadRequest.Builder(destStr, item, true)
+ .deleteOnFailure(true)
+ .withAuthentication("diffUsername", "diffPassword")
+ .withArguments(arg)
+ .build();
+
+ assertEquals(request1, request2);
+ assertNotEquals(request1, request3);
+ }
+
// Test to ensure parcel using put/getParcelableArrayList() API work
// based on: https://stackoverflow.com/a/13507191
private void doTestParcelInArrayList(String message,
diff --git a/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java b/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java
index 22f67933f..4890c471a 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/service/playback/PlaybackVolumeUpdaterTest.java
@@ -1,11 +1,11 @@
package de.danoeh.antennapod.core.service.playback;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.FeedPreferences;
-import de.danoeh.antennapod.core.feed.VolumeAdaptionSetting;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedPreferences;
+import de.danoeh.antennapod.model.feed.VolumeAdaptionSetting;
+import de.danoeh.antennapod.model.playback.Playable;
import org.junit.Before;
import org.junit.Test;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbCleanupTests.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbCleanupTests.java
index 13d24adc1..1c4c92574 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbCleanupTests.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbCleanupTests.java
@@ -15,9 +15,9 @@ import androidx.test.platform.app.InstrumentationRegistry;
import de.danoeh.antennapod.core.ApplicationCallbacks;
import de.danoeh.antennapod.core.ClientConfig;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbNullCleanupAlgorithmTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbNullCleanupAlgorithmTest.java
index b4dbacb82..733318724 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbNullCleanupAlgorithmTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbNullCleanupAlgorithmTest.java
@@ -11,9 +11,9 @@ import java.util.Date;
import java.util.List;
import androidx.test.platform.app.InstrumentationRegistry;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import org.junit.After;
import org.junit.Before;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbQueueCleanupAlgorithmTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbQueueCleanupAlgorithmTest.java
index 6e53bd20e..7d851c322 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbQueueCleanupAlgorithmTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbQueueCleanupAlgorithmTest.java
@@ -5,8 +5,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import org.junit.Test;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java
index 212b94743..07d7242aa 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbReaderTest.java
@@ -8,9 +8,9 @@ import java.util.List;
import java.util.Random;
import androidx.test.platform.app.InstrumentationRegistry;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.LongList;
import org.junit.After;
@@ -322,8 +322,8 @@ public class DbReaderTest {
final int numFeeds = 10;
final int numItems = 10;
DbTestUtils.saveFeedlist(numFeeds, numItems, true);
- DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData();
- assertEquals(numFeeds, navDrawerData.feeds.size());
+ NavDrawerData navDrawerData = DBReader.getNavDrawerData();
+ assertEquals(numFeeds, navDrawerData.items.size());
assertEquals(0, navDrawerData.numNewItems);
assertEquals(0, navDrawerData.queueSize);
}
@@ -351,8 +351,8 @@ public class DbReaderTest {
adapter.close();
- DBReader.NavDrawerData navDrawerData = DBReader.getNavDrawerData();
- assertEquals(numFeeds, navDrawerData.feeds.size());
+ NavDrawerData navDrawerData = DBReader.getNavDrawerData();
+ assertEquals(numFeeds, navDrawerData.items.size());
assertEquals(numNew, navDrawerData.numNewItems);
assertEquals(numQueue, navDrawerData.queueSize);
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java
index be9f53cdb..9e3dca0f7 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbTasksTest.java
@@ -19,9 +19,9 @@ import java.util.List;
import de.danoeh.antennapod.core.ApplicationCallbacks;
import de.danoeh.antennapod.core.ClientConfig;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
@@ -172,7 +172,7 @@ public class DbTasksTest {
final Feed feedFromDB = DBReader.getFeed(newFeed.getId());
final FeedItem feedItemFromDB = feedFromDB.getItems().get(0);
- assertTrue("state: " + feedItemFromDB.getState(), feedItemFromDB.isNew());
+ assertTrue(feedItemFromDB.isNew());
}
@Test
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbTestUtils.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbTestUtils.java
index 400ddda36..6c1e8d4f9 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbTestUtils.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbTestUtils.java
@@ -5,10 +5,10 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
-import de.danoeh.antennapod.core.feed.Chapter;
-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.model.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.SimpleChapter;
import de.danoeh.antennapod.core.util.comparator.FeedItemPubdateComparator;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java
index 3efb2705f..f70ed6e29 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/DbWriterTest.java
@@ -27,9 +27,9 @@ import java.util.concurrent.TimeUnit;
import de.danoeh.antennapod.core.ApplicationCallbacks;
import de.danoeh.antennapod.core.ClientConfig;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.util.FeedItemUtil;
@@ -780,6 +780,34 @@ public class DbWriterTest {
}
@Test
+ public void testRemoveAllNewFlags() throws Exception {
+ final int numItems = 10;
+ Feed feed = new Feed("url", null, "title");
+ feed.setItems(new ArrayList<>());
+ for (int i = 0; i < numItems; i++) {
+ FeedItem item = new FeedItem(0, "title " + i, "id " + i, "link " + i,
+ new Date(), FeedItem.NEW, feed);
+ feed.getItems().add(item);
+ }
+
+ PodDBAdapter adapter = PodDBAdapter.getInstance();
+ adapter.open();
+ adapter.setCompleteFeed(feed);
+ adapter.close();
+
+ assertTrue(feed.getId() != 0);
+ for (FeedItem item : feed.getItems()) {
+ assertTrue(item.getId() != 0);
+ }
+
+ DBWriter.removeAllNewFlags().get();
+ List<FeedItem> loadedItems = DBReader.getFeedItemList(feed);
+ for (FeedItem item : loadedItems) {
+ assertFalse(item.isNew());
+ }
+ }
+
+ @Test
public void testMarkAllItemsReadSameFeed() throws Exception {
final int numItems = 10;
Feed feed = new Feed("url", null, "title");
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java
index 8c02391ca..2224cff4f 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/ExceptFavoriteCleanupAlgorithmTest.java
@@ -9,8 +9,8 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import static org.junit.Assert.assertFalse;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
index 6c5a9daf1..91693a084 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/ItemEnqueuePositionCalculatorTest.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.storage;
+import de.danoeh.antennapod.model.playback.RemoteMedia;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@@ -13,14 +14,12 @@ import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
-import de.danoeh.antennapod.core.feed.FeedComponent;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.model.feed.FeedComponent;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedMother;
-import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation;
-import de.danoeh.antennapod.core.util.playback.ExternalMedia;
-import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.model.playback.Playable;
import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.AFTER_CURRENTLY_PLAYING;
import static de.danoeh.antennapod.core.preferences.UserPreferences.EnqueueLocation.BACK;
@@ -105,7 +104,7 @@ public class ItemEnqueuePositionCalculatorTest {
{"case option after currently playing, no currentlyPlaying is null",
concat(TFI_ID, QUEUE_DEFAULT_IDS),
AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NULL},
- {"case option after currently playing, currentlyPlaying is externalMedia",
+ {"case option after currently playing, currentlyPlaying is not a feedMedia",
concat(TFI_ID, QUEUE_DEFAULT_IDS),
AFTER_CURRENTLY_PLAYING, QUEUE_DEFAULT, ID_CURRENTLY_PLAYING_NOT_FEEDMEDIA},
{"case empty queue, option after currently playing",
@@ -270,7 +269,7 @@ public class ItemEnqueuePositionCalculatorTest {
}
static Playable externalMedia() {
- return new ExternalMedia("http://example.com/episode.mp3", MediaType.AUDIO);
+ return new RemoteMedia(createFeedItem(0));
}
static final long ID_CURRENTLY_PLAYING_NULL = -1L;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java b/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java
index c779b6d55..8695f98b1 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/storage/mapper/FeedCursorMapperTest.java
@@ -12,7 +12,7 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
import de.danoeh.antennapod.core.storage.PodDBAdapter;
import static org.junit.Assert.assertEquals;
@@ -48,7 +48,7 @@ public class FeedCursorMapperTest {
assertEquals("feed custom title", feed.getCustomTitle());
assertEquals("feed link", feed.getLink());
assertEquals("feed description", feed.getDescription());
- assertEquals("feed payment link", feed.getPaymentLink());
+ assertEquals("feed payment link", feed.getPaymentLinks().get(0).url);
assertEquals("feed author", feed.getAuthor());
assertEquals("feed language", feed.getLanguage());
assertEquals("feed image url", feed.getImageUrl());
diff --git a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java
index 82f7fcfca..2acc73204 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/AtomParserTest.java
@@ -7,9 +7,9 @@ import org.robolectric.RobolectricTestRunner;
import java.io.File;
import java.util.Date;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -30,7 +30,7 @@ public class AtomParserTest {
assertEquals("http://example.com/feed", feed.getFeedIdentifier());
assertEquals("http://example.com", feed.getLink());
assertEquals("This is the description", feed.getDescription());
- assertEquals("http://example.com/payment", feed.getPaymentLink());
+ assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url);
assertEquals("http://example.com/picture", feed.getImageUrl());
assertEquals(10, feed.getItems().size());
for (int i = 0; i < feed.getItems().size(); i++) {
@@ -38,7 +38,6 @@ public class AtomParserTest {
assertEquals("http://example.com/item-" + i, item.getItemIdentifier());
assertEquals("item-" + i, item.getTitle());
assertNull(item.getDescription());
- assertNull(item.getContentEncoded());
assertEquals("http://example.com/items/" + i, item.getLink());
assertEquals(new Date(i * 60000), item.getPubDate());
assertNull(item.getPaymentLink());
@@ -63,7 +62,7 @@ public class AtomParserTest {
assertEquals("http://example.com/feed", feed.getFeedIdentifier());
assertEquals("http://example.com", feed.getLink());
assertEquals("This is the description", feed.getDescription());
- assertEquals("http://example.com/payment", feed.getPaymentLink());
+ assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url);
assertEquals("https://example.com/image.png", feed.getImageUrl());
assertEquals(0, feed.getItems().size());
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/FeedParserTestHelper.java b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/FeedParserTestHelper.java
index c02a7d209..b9318b377 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/FeedParserTestHelper.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/FeedParserTestHelper.java
@@ -4,7 +4,7 @@ import androidx.annotation.NonNull;
import java.io.File;
-import de.danoeh.antennapod.core.feed.Feed;
+import de.danoeh.antennapod.model.feed.Feed;
/**
* Tests for FeedHandler.
diff --git a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java
index 1195520a6..d95c8b3ab 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/syndication/handler/RssParserTest.java
@@ -1,5 +1,6 @@
package de.danoeh.antennapod.core.syndication.handler;
+import android.text.TextUtils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@@ -7,10 +8,10 @@ import org.robolectric.RobolectricTestRunner;
import java.io.File;
import java.util.Date;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
+import de.danoeh.antennapod.model.playback.MediaType;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@@ -31,7 +32,7 @@ public class RssParserTest {
assertEquals("en", feed.getLanguage());
assertEquals("http://example.com", feed.getLink());
assertEquals("This is the description", feed.getDescription());
- assertEquals("http://example.com/payment", feed.getPaymentLink());
+ assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url);
assertEquals("http://example.com/picture", feed.getImageUrl());
assertEquals(10, feed.getItems().size());
for (int i = 0; i < feed.getItems().size(); i++) {
@@ -39,7 +40,6 @@ public class RssParserTest {
assertEquals("http://example.com/item-" + i, item.getItemIdentifier());
assertEquals("item-" + i, item.getTitle());
assertNull(item.getDescription());
- assertNull(item.getContentEncoded());
assertEquals("http://example.com/items/" + i, item.getLink());
assertEquals(new Date(i * 60000), item.getPubDate());
assertNull(item.getPaymentLink());
@@ -63,7 +63,7 @@ public class RssParserTest {
assertEquals("title", feed.getTitle());
assertEquals("http://example.com", feed.getLink());
assertEquals("This is the description", feed.getDescription());
- assertEquals("http://example.com/payment", feed.getPaymentLink());
+ assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url);
assertEquals("https://example.com/image.png", feed.getImageUrl());
assertEquals(0, feed.getItems().size());
}
@@ -75,7 +75,7 @@ public class RssParserTest {
assertEquals("title", feed.getTitle());
assertEquals("http://example.com", feed.getLink());
assertEquals("This is the description", feed.getDescription());
- assertEquals("http://example.com/payment", feed.getPaymentLink());
+ assertEquals("http://example.com/payment", feed.getPaymentLinks().get(0).url);
assertNull(feed.getImageUrl());
assertEquals(1, feed.getItems().size());
FeedItem feedItem = feed.getItems().get(0);
@@ -83,4 +83,17 @@ public class RssParserTest {
assertEquals(MediaType.VIDEO, feedItem.getMedia().getMediaType());
assertEquals("https://www.example.com/file.mp4", feedItem.getMedia().getDownload_url());
}
+
+ @Test
+ public void testMultipleFundingTags() throws Exception {
+ File feedFile = FeedParserTestHelper.getFeedFile("feed-rss-testMultipleFundingTags.xml");
+ Feed feed = FeedParserTestHelper.runFeedParser(feedFile);
+ assertEquals(3, feed.getPaymentLinks().size());
+ assertEquals("Text 1", feed.getPaymentLinks().get(0).content);
+ assertEquals("https://example.com/funding1", feed.getPaymentLinks().get(0).url);
+ assertEquals("Text 2", feed.getPaymentLinks().get(1).content);
+ assertEquals("https://example.com/funding2", feed.getPaymentLinks().get(1).url);
+ assertTrue(TextUtils.isEmpty(feed.getPaymentLinks().get(2).content));
+ assertEquals("https://example.com/funding3", feed.getPaymentLinks().get(2).url);
+ }
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java
index ccaa77ae8..9c48f8df7 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemPermutorsTest.java
@@ -1,14 +1,15 @@
package de.danoeh.antennapod.core.util;
+import de.danoeh.antennapod.model.feed.SortOrder;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
-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.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.FeedMedia;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java
index a3744035b..6bc3c09a2 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/FeedItemUtilTest.java
@@ -5,12 +5,14 @@ import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.List;
-import de.danoeh.antennapod.core.feed.Feed;
-import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.model.feed.Feed;
+import de.danoeh.antennapod.model.feed.FeedItem;
import static org.junit.Assert.assertEquals;
@@ -26,14 +28,14 @@ public class FeedItemUtilTest {
@Parameters
public static Collection<Object[]> data() {
- return Arrays.asList(new Object[][] {
- { "average", FEED_LINK, ITEM_LINK, ITEM_LINK },
- { "null item link - fallback to feed", FEED_LINK, null, FEED_LINK},
- { "empty item link - same as null", FEED_LINK, "", FEED_LINK},
- { "blank item link - same as null", FEED_LINK, " ", FEED_LINK},
- { "fallback, but feed link is null too", null, null, null },
- { "fallback - but empty feed link - same as null", "", null, null},
- { "fallback - but blank feed link - same as null", " ", null, null}
+ return Arrays.asList(new Object[][]{
+ {"average", FEED_LINK, ITEM_LINK, ITEM_LINK},
+ {"null item link - fallback to feed", FEED_LINK, null, FEED_LINK},
+ {"empty item link - same as null", FEED_LINK, "", FEED_LINK},
+ {"blank item link - same as null", FEED_LINK, " ", FEED_LINK},
+ {"fallback, but feed link is null too", null, null, null},
+ {"fallback - but empty feed link - same as null", "", null, null},
+ {"fallback - but blank feed link - same as null", " ", null, null}
});
}
@@ -44,6 +46,47 @@ public class FeedItemUtilTest {
this.expected = expected;
}
+
+ // Test the getIds() method
+ @Test
+ public void testGetIds() {
+ List<FeedItem> feedItemsList = new ArrayList<FeedItem>(5);
+ List<Integer> idList = new ArrayList<Integer>();
+
+ idList.add(980);
+ idList.add(324);
+ idList.add(226);
+ idList.add(164);
+ idList.add(854);
+
+ for (int i = 0; i < 5; i++) {
+ FeedItem item = createFeedItem(feedLink, itemLink);
+ item.setId(idList.get(i));
+ feedItemsList.add(item);
+ }
+
+ long[] actual = FeedItemUtil.getIds(feedItemsList);
+
+ // covers edge case for getIds() method
+ List<FeedItem> emptyList = new ArrayList<FeedItem>();
+ long[] testEmptyList = FeedItemUtil.getIds(emptyList);
+ assertEquals(msg, 0, testEmptyList.length);
+ assertEquals(msg, 980, actual[0]);
+ assertEquals(msg, 324, actual[1]);
+ assertEquals(msg, 226, actual[2]);
+ assertEquals(msg, 164, actual[3]);
+ assertEquals(msg, 854, actual[4]);
+
+ }
+
+ // Tests the Null value for getLinkWithFallback() method
+ @Test
+ public void testLinkWithFallbackNullValue() {
+ String actual = FeedItemUtil.getLinkWithFallback(null);
+ assertEquals(msg, null, actual);
+ }
+
+
@Test
public void testLinkWithFallback() {
String actual = FeedItemUtil.getLinkWithFallback(createFeedItem(feedLink, itemLink));
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java
index ee4d43131..5e2e46e74 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/id3reader/ChapterReaderTest.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.core.util.id3reader;
-import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Chapter;
import de.danoeh.antennapod.core.feed.ID3Chapter;
import de.danoeh.antennapod.core.util.EmbeddedChapterImage;
import de.danoeh.antennapod.core.util.id3reader.model.FrameHeader;
@@ -162,4 +162,27 @@ public class ChapterReaderTest {
assertEquals(EmbeddedChapterImage.makeUrl(1771, 308), chapters.get(2).getImageUrl());
assertEquals(EmbeddedChapterImage.makeUrl(2259, 308), chapters.get(3).getImageUrl());
}
+
+ @Test
+ public void testRealFileHindenburgJournalistPro() throws IOException, ID3ReaderException {
+ CountingInputStream inputStream = new CountingInputStream(getClass().getClassLoader()
+ .getResource("media-parser/hindenburg-journalist-pro.mp3").openStream());
+ ChapterReader reader = new ChapterReader(inputStream);
+ reader.readInputStream();
+ List<Chapter> chapters = reader.getChapters();
+
+ assertEquals(2, chapters.size());
+
+ assertEquals(0, chapters.get(0).getStart());
+ assertEquals(5006, chapters.get(1).getStart());
+
+ assertEquals("Chapter Marker 1", chapters.get(0).getTitle());
+ assertEquals("Chapter Marker 2", chapters.get(1).getTitle());
+
+ assertEquals("https://example.com/chapter1url", chapters.get(0).getLink());
+ assertEquals("https://example.com/chapter2url", chapters.get(1).getLink());
+
+ assertEquals(EmbeddedChapterImage.makeUrl(5330, 4015), chapters.get(0).getImageUrl());
+ assertEquals(EmbeddedChapterImage.makeUrl(9498, 4364), chapters.get(1).getImageUrl());
+ }
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/playback/ExternalMediaTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/playback/ExternalMediaTest.java
deleted file mode 100644
index d5e63eeba..000000000
--- a/core/src/test/java/de/danoeh/antennapod/core/util/playback/ExternalMediaTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package de.danoeh.antennapod.core.util.playback;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.SharedPreferences;
-import androidx.preference.PreferenceManager;
-
-import androidx.test.platform.app.InstrumentationRegistry;
-import de.danoeh.antennapod.core.feed.MediaType;
-import org.junit.After;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.robolectric.RobolectricTestRunner;
-
-import static org.junit.Assert.assertEquals;
-
-/**
- * Tests for {@link ExternalMedia} entity.
- */
-@RunWith(RobolectricTestRunner.class)
-public class ExternalMediaTest {
-
- private static final int NOT_SET = -1;
- private static final int POSITION = 50;
- private static final int LAST_PLAYED_TIME = 1650;
-
- @After
- public void tearDown() {
- clearSharedPrefs();
- }
-
- @SuppressLint("CommitPrefEdits")
- private void clearSharedPrefs() {
- SharedPreferences prefs = getDefaultSharedPrefs();
- SharedPreferences.Editor editor = prefs.edit();
- editor.clear();
- editor.commit();
- }
-
- private SharedPreferences getDefaultSharedPrefs() {
- Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
- return PreferenceManager.getDefaultSharedPreferences(context);
- }
-
- @Test
- public void testSaveCurrentPositionUpdatesPreferences() {
- assertEquals(NOT_SET, getDefaultSharedPrefs().getInt(ExternalMedia.PREF_POSITION, NOT_SET));
- assertEquals(NOT_SET, getDefaultSharedPrefs().getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, NOT_SET));
-
- ExternalMedia media = new ExternalMedia("source", MediaType.AUDIO);
- media.saveCurrentPosition(getDefaultSharedPrefs(), POSITION, LAST_PLAYED_TIME);
-
- assertEquals(POSITION, getDefaultSharedPrefs().getInt(ExternalMedia.PREF_POSITION, NOT_SET));
- assertEquals(LAST_PLAYED_TIME, getDefaultSharedPrefs().getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, NOT_SET));
- }
-}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java
index 8927a41de..987a75981 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/playback/TimelineTest.java
@@ -7,13 +7,6 @@ import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
-
-import java.util.Date;
-import java.util.List;
-
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.storage.DBReader;
import org.junit.After;
@@ -51,25 +44,13 @@ public class TimelineTest {
dbReaderMock.close();
}
- @SuppressWarnings("SameParameterValue")
- private Playable newTestPlayable(List<Chapter> chapters, String shownotes, int duration) {
- FeedItem item = new FeedItem(0, "Item", "item-id", "http://example.com/item", new Date(), FeedItem.PLAYED, null);
- item.setChapters(chapters);
- item.setContentEncoded(shownotes);
- FeedMedia media = new FeedMedia(item, "http://example.com/episode", 100, "audio/mp3");
- media.setDuration(duration);
- item.setMedia(media);
- return media;
- }
-
@Test
public void testProcessShownotesAddTimecodeHhmmssNoChapters() {
final String timeStr = "10:11:12";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11 + 12 * 1000;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStr + " here.</p>", Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
+ Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -79,9 +60,8 @@ public class TimelineTest {
final String timeStr = "25:00:00";
final long time = 25 * 60 * 60 * 1000;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStr + " here.</p>", Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
+ Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -91,9 +71,8 @@ public class TimelineTest {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStr + " here.</p>", Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
+ Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -103,9 +82,8 @@ public class TimelineTest {
final String timeStr = "10:11";
final long time = 10 * 60 * 1000 + 11 * 1000;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStr + " here.</p>", 11 * 60 * 1000);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
+ Timeline t = new Timeline(context, shownotes, 11 * 60 * 1000);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -114,9 +92,9 @@ public class TimelineTest {
public void testProcessShownotesAddTimecodeHmmssNoChapters() {
final String timeStr = "2:11:12";
final long time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStr + " here.</p>", Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+
+ String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
+ Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -126,9 +104,8 @@ public class TimelineTest {
final String timeStr = "1:12";
final long time = 60 * 1000 + 12 * 1000;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStr + " here.</p>", 2 * 60 * 1000);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
+ Timeline t = new Timeline(context, shownotes, 2 * 60 * 1000);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -137,9 +114,9 @@ public class TimelineTest {
public void testProcessShownotesAddNoTimecodeDuration() {
final String timeStr = "2:11:12";
final int time = 2 * 60 * 60 * 1000 + 11 * 60 * 1000 + 12 * 1000;
- String originalText = "<p> Some test text with a timecode " + timeStr + " here.</p>";
- Playable p = newTestPlayable(null, originalText, time);
- Timeline t = new Timeline(context, p);
+
+ String shownotes = "<p> Some test text with a timecode " + timeStr + " here.</p>";
+ Timeline t = new Timeline(context, shownotes, time);
String res = t.processShownotes();
Document d = Jsoup.parse(res);
assertEquals("Should not parse time codes that equal duration", 0, d.body().getElementsByTag("a").size());
@@ -149,9 +126,9 @@ public class TimelineTest {
public void testProcessShownotesAddTimecodeMultipleFormatsNoChapters() {
final String[] timeStrings = new String[]{ "10:12", "1:10:12" };
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 2 * 60 * 60 * 1000);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode " + timeStrings[0]
+ + " here. Hey look another one " + timeStrings[1] + " here!</p>";
+ Timeline t = new Timeline(context, shownotes, 2 * 60 * 60 * 1000);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{10 * 60 * 1000 + 12 * 1000,
60 * 60 * 1000 + 10 * 60 * 1000 + 12 * 1000}, timeStrings);
@@ -163,9 +140,9 @@ public class TimelineTest {
// One of these timecodes fits as HH:MM and one does not so both should be parsed as MM:SS.
final String[] timeStrings = new String[]{ "10:12", "2:12" };
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode "
- + timeStrings[0] + " here. Hey look another one " + timeStrings[1] + " here!</p>", 3 * 60 * 60 * 1000);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode " + timeStrings[0]
+ + " here. Hey look another one " + timeStrings[1] + " here!</p>";
+ Timeline t = new Timeline(context, shownotes, 3 * 60 * 60 * 1000);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{10 * 60 * 1000 + 12 * 1000, 2 * 60 * 1000 + 12 * 1000}, timeStrings);
}
@@ -175,9 +152,8 @@ public class TimelineTest {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode ("
- + timeStr + ") here.</p>", Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode (" + timeStr + ") here.</p>";
+ Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -187,9 +163,8 @@ public class TimelineTest {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode ["
- + timeStr + "] here.</p>", Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode [" + timeStr + "] here.</p>";
+ Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -199,9 +174,8 @@ public class TimelineTest {
final String timeStr = "10:11";
final long time = 3600 * 1000 * 10 + 60 * 1000 * 11;
- Playable p = newTestPlayable(null, "<p> Some test text with a timecode <"
- + timeStr + "> here.</p>", Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+ String shownotes = "<p> Some test text with a timecode <" + timeStr + "> here.</p>";
+ Timeline t = new Timeline(context, shownotes, Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[]{time}, new String[]{timeStr});
}
@@ -216,8 +190,7 @@ public class TimelineTest {
}
shownotes.append("here.</p>");
- Playable p = newTestPlayable(null, shownotes.toString(), Integer.MAX_VALUE);
- Timeline t = new Timeline(context, p);
+ Timeline t = new Timeline(context, shownotes.toString(), Integer.MAX_VALUE);
String res = t.processShownotes();
checkLinkCorrect(res, new long[0], new String[0]);
}
diff --git a/core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java b/core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java
index cf9228292..40f8b381f 100644
--- a/core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java
+++ b/core/src/test/java/de/danoeh/antennapod/core/util/vorbiscommentreader/VorbisCommentChapterReaderTest.java
@@ -1,6 +1,6 @@
package de.danoeh.antennapod.core.util.vorbiscommentreader;
-import de.danoeh.antennapod.core.feed.Chapter;
+import de.danoeh.antennapod.model.feed.Chapter;
import org.junit.Test;
import java.io.IOException;
diff --git a/core/src/test/resources/feed-rss-testMultipleFundingTags.xml b/core/src/test/resources/feed-rss-testMultipleFundingTags.xml
new file mode 100644
index 000000000..2535bda32
--- /dev/null
+++ b/core/src/test/resources/feed-rss-testMultipleFundingTags.xml
@@ -0,0 +1,9 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<rss version="2.0" xmlns:podcast="https://podcastindex.org/namespace/1.0">
+ <channel>
+ <title>title</title>
+ </channel>
+ <podcast:funding url="https://example.com/funding1">Text 1</podcast:funding>
+ <podcast:funding url="https://example.com/funding2">Text 2</podcast:funding>
+ <podcast:funding url="https://example.com/funding3" />
+</rss>
diff --git a/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a b/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a
new file mode 100644
index 000000000..bd64dd9da
--- /dev/null
+++ b/core/src/test/resources/media-parser/hindenburg-journalist-pro.m4a
Binary files differ
diff --git a/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3 b/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3
new file mode 100644
index 000000000..d341b6045
--- /dev/null
+++ b/core/src/test/resources/media-parser/hindenburg-journalist-pro.mp3
Binary files differ