From 62a6288740c27618e514013302170967e8c10626 Mon Sep 17 00:00:00 2001 From: Xilin Jia <6257601+XilinJia@users.noreply.github.com> Date: Sun, 25 Feb 2024 11:28:34 +0100 Subject: [PATCH] drastic project restructuring --- 7_podcast.jpg | Bin 235363 -> 0 bytes README.md | 10 +- app/build.gradle | 84 +- .../java/ac/test/podcini/EspressoTestUtils.kt | 12 +- .../test/podcini/dialogs/ShareDialogTest.kt | 4 +- .../ac/test/podcini/playback/PlaybackTest.kt | 38 +- .../service/download/HttpDownloaderTest.kt | 29 +- .../playback/CancelablePSMPCallback.kt | 4 +- .../service/playback/DefaultPSMPCallback.kt | 4 +- .../PlaybackServiceMediaPlayerTest.kt | 6 +- .../PlaybackServiceTaskManagerTest.kt | 18 +- .../playback/SleepTimerPreferencesTest.kt | 2 +- .../test/podcini/storage/AutoDownloadTest.kt | 18 +- .../ac/test/podcini/ui/FeedSettingsTest.kt | 4 +- .../ac/test/podcini/ui/MainActivityTest.kt | 2 +- .../test/podcini/ui/NavigationDrawerTest.kt | 19 +- .../ac/test/podcini/ui/PreferencesTest.kt | 54 +- .../ac/test/podcini/ui/QueueFragmentTest.kt | 4 +- .../ac/test/podcini/ui/TextOnlyFeedsTest.kt | 2 +- .../java/ac/test/podcini/ui/UITestUtils.kt | 12 +- .../ac/test/podcini/ui/UITestUtilsTest.kt | 8 +- .../util/event/FeedItemEventListener.kt | 8 +- .../feedgenerator/FeedGenerator.kt | 2 +- .../feedgenerator/Rss2Generator.kt | 14 +- .../ac/mdiq/podcini/dialog/RatingDialog.kt | 2 +- .../podcini/net/ssl/SslProviderInstaller.kt | 0 .../playback/cast/CastEnabledActivity.kt | 0 .../ac/mdiq/podcini/playback/cast/CastPsmp.kt | 0 .../playback/cast/CastStateListener.kt | 11 + .../service/playback/WearMediaSession.kt | 17 + app/src/main/AndroidManifest.xml | 100 +- .../html-export-favorites-item-template.html | 0 .../assets/html-export-feed-template.html | 0 .../src/main/assets/html-export-template.html | 0 .../src/main/assets/shownotes-style.css | 0 .../main/java/ac/mdiq/podcini/PodciniApp.kt | 20 +- .../podcini/activity/BugReportActivity.kt | 125 -- .../ac/mdiq/podcini/activity/MainActivity.kt | 682 ------ .../activity/OnlineFeedViewActivity.kt | 706 ------ .../podcini/activity/OpmlImportActivity.kt | 270 --- .../activity/PlaybackSpeedDialogActivity.kt | 23 - .../podcini/activity/PreferenceActivity.kt | 203 -- .../activity/SelectSubscriptionActivity.kt | 158 -- .../mdiq/podcini/activity/SplashActivity.kt | 48 - .../podcini/activity/VideoplayerActivity.kt | 787 ------- .../podcini/activity/WidgetConfigActivity.kt | 141 -- .../podcini/adapter/ChaptersListAdapter.kt | 146 -- .../ac/mdiq/podcini/adapter/CoverLoader.kt | 122 -- .../mdiq/podcini/adapter/DataFolderAdapter.kt | 112 - .../podcini/adapter/DownloadLogAdapter.kt | 149 -- .../podcini/adapter/EpisodeItemListAdapter.kt | 213 -- .../podcini/adapter/FeedDiscoverAdapter.kt | 68 - .../adapter/FeedItemlistDescriptionAdapter.kt | 104 - .../adapter/HorizontalFeedListAdapter.kt | 125 -- .../adapter/HorizontalItemListAdapter.kt | 120 - .../ac/mdiq/podcini/adapter/NavListAdapter.kt | 376 ---- .../podcini/adapter/QueueRecyclerAdapter.kt | 91 - .../mdiq/podcini/adapter/SelectableAdapter.kt | 188 -- .../mdiq/podcini/adapter/SimpleChipAdapter.kt | 40 - .../podcini/adapter/SimpleIconListAdapter.kt | 41 - .../adapter/SubscriptionsRecyclerAdapter.kt | 272 --- .../CancelDownloadActionButton.kt | 32 - .../actionbutton/DeleteActionButton.kt | 32 - .../actionbutton/DownloadActionButton.kt | 60 - .../adapter/actionbutton/ItemActionButton.kt | 59 - .../actionbutton/MarkAsPlayedActionButton.kt | 25 - .../adapter/actionbutton/PauseActionButton.kt | 25 - .../adapter/actionbutton/PlayActionButton.kt | 33 - .../actionbutton/PlayLocalActionButton.kt | 29 - .../actionbutton/StreamActionButton.kt | 38 - .../actionbutton/VisitWebsiteActionButton.kt | 22 - .../podcini/adapter/itunes/ItunesAdapter.kt | 93 - .../asynctask/DocumentFileExportWorker.kt | 56 - .../ac/mdiq/podcini/asynctask/ExportWorker.kt | 57 - .../config/ApplicationCallbacksImpl.kt | 12 - .../podcini/dialog/AllEpisodesFilterDialog.kt | 22 - .../podcini/dialog/AuthenticationDialog.kt | 57 - .../podcini/dialog/ChooseDataFolderDialog.kt | 33 - .../dialog/DownloadLogDetailsDialog.kt | 60 - .../podcini/dialog/DrawerPreferencesDialog.kt | 47 - .../podcini/dialog/EditUrlSettingsDialog.kt | 82 - .../podcini/dialog/EpisodeFilterDialog.kt | 111 - .../podcini/dialog/FeedItemFilterDialog.kt | 25 - .../dialog/FeedPreferenceSkipDialog.kt | 44 - .../ac/mdiq/podcini/dialog/FeedSortDialog.kt | 34 - .../mdiq/podcini/dialog/ItemFilterDialog.kt | 116 - .../ac/mdiq/podcini/dialog/ItemSortDialog.kt | 100 - .../podcini/dialog/MediaPlayerErrorDialog.kt | 37 - .../podcini/dialog/PlaybackControlsDialog.kt | 69 - .../ac/mdiq/podcini/dialog/ProxyDialog.kt | 303 --- .../mdiq/podcini/dialog/RemoveFeedDialog.kt | 75 - .../mdiq/podcini/dialog/RenameItemDialog.kt | 74 - .../ac/mdiq/podcini/dialog/ShareDialog.kt | 99 - .../podcini/dialog/SkipPreferenceDialog.kt | 62 - .../mdiq/podcini/dialog/SleepTimerDialog.kt | 208 -- .../dialog/StreamingConfirmationDialog.kt | 34 - .../dialog/SubscriptionsFilterDialog.kt | 130 -- .../mdiq/podcini/dialog/SwipeActionsDialog.kt | 203 -- .../mdiq/podcini/dialog/TagSettingsDialog.kt | 142 -- .../ac/mdiq/podcini/dialog/TimeRangeDialog.kt | 179 -- .../podcini/dialog/VariableSpeedDialog.kt | 151 -- .../mdiq/podcini/error/CrashReportWriter.kt | 61 - .../podcini/error/RxJavaErrorHandlerSetup.kt | 31 - .../ac/mdiq/podcini/feed/ChapterMerger.kt | 62 + .../java/ac/mdiq/podcini/feed/FeedEvent.kt | 18 + .../mdiq/podcini/feed/FeedItemFilterGroup.kt | 28 + .../podcini/feed/FeedUrlNotFoundException.kt | 8 + .../ac/mdiq/podcini/feed/LocalFeedUpdater.kt | 285 +++ .../podcini/feed/SubscriptionsFilterGroup.kt | 24 + .../mdiq/podcini/feed/parser/FeedHandler.kt | 41 + .../podcini/feed/parser/FeedHandlerResult.kt | 8 + .../mdiq/podcini/feed/parser/HandlerState.kt | 81 + .../feed/parser/PodcastIndexChapterParser.kt | 27 + .../mdiq/podcini/feed/parser/SyndHandler.kt | 115 + .../parser/UnsupportedFeedtypeException.kt | 47 + .../podcini/feed/parser/element/AtomText.kt | 36 + .../feed/parser/element/SyndElement.kt | 6 + .../feed/parser/media/id3/ChapterReader.kt | 111 + .../feed/parser/media/id3/ID3Reader.kt | 222 ++ .../parser/media/id3/ID3ReaderException.kt | 7 + .../parser/media/id3/Id3MetadataReader.kt | 32 + .../parser/media/id3/model/FrameHeader.kt | 3 + .../feed/parser/media/id3/model/Header.kt | 7 + .../feed/parser/media/id3/model/TagHeader.kt | 7 + .../vorbis/VorbisCommentChapterReader.kt | 120 + .../media/vorbis/VorbisCommentHeader.kt | 7 + .../vorbis/VorbisCommentMetadataReader.kt | 25 + .../media/vorbis/VorbisCommentReader.kt | 192 ++ .../vorbis/VorbisCommentReaderException.kt | 11 + .../podcini/feed/parser/namespace/Atom.kt | 211 ++ .../podcini/feed/parser/namespace/Content.kt | 24 + .../feed/parser/namespace/DublinCore.kt | 32 + .../podcini/feed/parser/namespace/Itunes.kt | 78 + .../podcini/feed/parser/namespace/Media.kt | 125 ++ .../feed/parser/namespace/Namespace.kt | 16 + .../feed/parser/namespace/PodcastIndex.kt | 42 + .../podcini/feed/parser/namespace/Rss20.kt | 134 ++ .../feed/parser/namespace/SimpleChapters.kt | 48 + .../podcini/feed/parser/util/DateUtils.kt | 149 ++ .../feed/parser/util/DurationParser.kt | 42 + .../podcini/feed/parser/util/MimeTypeUtils.kt | 70 + .../feed/parser/util/SyndStringUtils.kt | 11 + .../podcini/feed/parser/util/TypeGetter.kt | 137 ++ .../podcini/feed/util/ImageResourceUtils.kt | 58 + .../podcini/feed/util/PlaybackSpeedUtils.kt | 48 + .../mdiq/podcini/fragment/AddFeedFragment.kt | 214 -- .../podcini/fragment/AllEpisodesFragment.kt | 139 -- .../podcini/fragment/AudioPlayerFragment.kt | 545 ----- .../mdiq/podcini/fragment/ChaptersFragment.kt | 198 -- .../fragment/CompletedDownloadsFragment.kt | 389 ---- .../ac/mdiq/podcini/fragment/CoverFragment.kt | 359 --- .../podcini/fragment/DiscoveryFragment.kt | 269 --- .../podcini/fragment/DownloadLogFragment.kt | 117 - .../podcini/fragment/EpisodesListFragment.kt | 436 ---- .../fragment/ExternalPlayerFragment.kt | 221 -- .../mdiq/podcini/fragment/FeedInfoFragment.kt | 348 --- .../podcini/fragment/FeedItemlistFragment.kt | 652 ------ .../podcini/fragment/FeedSettingsFragment.kt | 498 ----- .../ac/mdiq/podcini/fragment/InboxFragment.kt | 146 -- .../fragment/ItemDescriptionFragment.kt | 183 -- .../ac/mdiq/podcini/fragment/ItemFragment.kt | 437 ---- .../podcini/fragment/ItemPagerFragment.kt | 180 -- .../podcini/fragment/NavDrawerFragment.kt | 455 ---- .../podcini/fragment/OnlineSearchFragment.kt | 210 -- .../fragment/PlaybackHistoryFragment.kt | 93 - .../ac/mdiq/podcini/fragment/QueueFragment.kt | 598 ----- .../fragment/QuickFeedDiscoveryFragment.kt | 170 -- .../mdiq/podcini/fragment/SearchFragment.kt | 431 ---- .../podcini/fragment/SubscriptionFragment.kt | 360 --- .../mdiq/podcini/fragment/TransitionEffect.kt | 5 - .../EpisodeMultiSelectActionHandler.kt | 135 -- .../actions/FeedMultiSelectActionHandler.kt | 165 -- .../AutoDownloadPreferencesFragment.kt | 205 -- .../DownloadsPreferencesFragment.kt | 114 - .../ImportExportPreferencesFragment.kt | 323 --- .../preferences/MainPreferencesFragment.kt | 185 -- .../NotificationPreferencesFragment.kt | 29 - .../PlaybackPreferencesFragment.kt | 126 -- .../preferences/SwipePreferencesFragment.kt | 72 - .../UserInterfacePreferencesFragment.kt | 175 -- .../preferences/about/AboutFragment.kt | 62 - .../about/ContributorsPagerFragment.kt | 71 - .../preferences/about/DevelopersFragment.kt | 54 - .../preferences/about/LicensesFragment.kt | 108 - .../about/SpecialThanksFragment.kt | 53 - .../preferences/about/TranslatorsFragment.kt | 53 - .../dialog/PreferenceListDialog.kt | 39 - .../dialog/PreferenceSwitchDialog.kt | 44 - .../GpodderAuthenticationFragment.kt | 271 --- .../NextcloudAuthenticationFragment.kt | 104 - .../SynchronizationPreferencesFragment.kt | 216 -- .../swipeactions/AddToQueueSwipeAction.kt | 38 - .../swipeactions/DeleteSwipeAction.kt | 40 - .../swipeactions/MarkFavoriteSwipeAction.kt | 34 - .../RemoveFromHistorySwipeAction.kt | 48 - .../RemoveFromInboxSwipeAction.kt | 36 - .../RemoveFromQueueSwipeAction.kt | 51 - .../ShowFirstSwipeDialogAction.kt | 33 - .../swipeactions/StartDownloadSwipeAction.kt | 37 - .../fragment/swipeactions/SwipeAction.kt | 36 - .../fragment/swipeactions/SwipeActions.kt | 252 --- .../TogglePlaybackStateSwipeAction.kt | 39 - .../menuhandler/FeedItemMenuHandler.kt | 274 --- .../podcini/menuhandler/FeedMenuHandler.kt | 60 - .../ac/mdiq/podcini/net/common/UrlChecker.kt | 137 ++ .../podcini/net/discovery/CombinedSearcher.kt | 0 .../net/discovery/FyydPodcastSearcher.kt | 43 + .../net/discovery/GpodnetPodcastSearcher.kt | 44 + .../net/discovery/ItunesPodcastSearcher.kt | 105 + .../net/discovery/ItunesTopListLoader.kt | 111 + .../discovery/PodcastIndexPodcastSearcher.kt | 113 + .../net/discovery/PodcastSearchResult.kt | 0 .../podcini/net/discovery/PodcastSearcher.kt | 0 .../net/discovery/PodcastSearcherRegistry.kt | 0 .../net/download/ConnectionStateMonitor.kt | 41 + .../podcini/net/download/FeedUpdateManager.kt | 109 + .../podcini/net/download/MediaSizeLoader.kt | 77 + .../NetworkConnectionChangeHandler.kt | 34 + .../serviceinterface/DownloadRequest.kt | 212 ++ .../DownloadServiceInterface.kt | 58 + .../DownloadServiceInterfaceStub.kt | 19 + .../net/ssl/AntennaPodSslSocketFactory.kt | 0 .../mdiq/podcini/net/ssl/BackportCaCerts.kt | 0 .../podcini/net/ssl/BackportTrustManager.kt | 0 .../net/ssl/CompositeX509TrustManager.kt | 0 .../ac/mdiq/podcini/net/ssl/SslClientSetup.kt | 0 .../podcini/net/sync/EpisodeActionFilter.kt | 65 + .../ac/mdiq/podcini/net/sync/GuidValidator.kt | 10 + .../mdiq/podcini/net/sync/HostnameParser.kt | 0 .../podcini/net/sync/LockingAsyncExecutor.kt | 35 + .../ac/mdiq/podcini/net/sync/SyncService.kt | 354 +++ .../net/sync/SynchronizationCredentials.kt | 58 + .../sync/SynchronizationProviderViewData.kt | 29 + .../net/sync/SynchronizationSettings.kt | 74 + .../net/sync/gpoddernet/GpodnetService.kt | 555 +++++ .../GpodnetServiceAuthenticationException.kt | 0 .../GpodnetServiceBadStatusCodeException.kt | 0 .../gpoddernet/GpodnetServiceException.kt | 0 .../sync/gpoddernet/mapper/ResponseMapper.kt | 0 .../sync/gpoddernet/model/GpodnetDevice.kt | 0 .../model/GpodnetEpisodeActionPostResponse.kt | 0 .../sync/gpoddernet/model/GpodnetPodcast.kt | 0 .../model/GpodnetUploadChangesResponse.kt | 0 .../podcini/net/sync/model/EpisodeAction.kt | 230 ++ .../net/sync/model/EpisodeActionChanges.kt | 0 .../podcini/net/sync/model/ISyncService.kt | 0 .../net/sync/model/SubscriptionChanges.kt | 0 .../net/sync/model/SyncServiceException.kt | 0 .../net/sync/model/UploadChangesResponse.kt | 0 .../net/sync/nextcloud/NextcloudLoginFlow.kt | 0 .../sync/nextcloud/NextcloudSyncService.kt | 0 ...extcloudSynchronizationServiceException.kt | 0 .../sync/queue/SynchronizationQueueSink.kt | 82 + .../sync/queue/SynchronizationQueueStorage.kt | 148 ++ .../ac/mdiq/podcini/playback/PlayableUtils.kt | 36 + .../podcini/playback/PlaybackController.kt | 400 ++++ .../playback/PlaybackServiceStarter.kt | 45 + .../base/PlaybackServiceMediaPlayer.kt | 393 ++++ .../podcini/playback/base/PlayerStatus.kt | 0 .../playback/base/RewindAfterPauseUtils.kt | 0 .../playback/event/BufferUpdateEvent.kt | 27 + .../playback/event/PlaybackHistoryEvent.kt | 14 + .../playback/event/PlaybackPositionEvent.kt | 3 + .../playback/event/PlaybackServiceEvent.kt | 8 + .../playback/event/SleepTimerUpdatedEvent.kt | 35 + .../playback/event/SpeedChangedEvent.kt | 3 + .../preferences/MasterSwitchPreference.kt | 8 +- .../preferences/MaterialListPreference.kt | 4 +- .../MaterialMultiSelectListPreference.kt | 4 +- .../preferences/PlaybackPreferences.kt | 230 ++ .../podcini/preferences/PreferenceUpgrader.kt | 36 +- .../preferences/SleepTimerPreferences.kt | 117 + .../podcini/preferences/ThemePreference.kt | 5 +- .../mdiq/podcini/preferences/ThemeSwitcher.kt | 67 + .../podcini/preferences/UsageStatistics.kt | 64 + .../podcini/preferences/UserPreferences.kt | 889 ++++++++ .../receiver/ConnectivityActionReceiver.kt | 4 +- .../podcini/receiver/FeedUpdateReceiver.kt | 26 + .../podcini/receiver/MediaButtonReceiver.kt | 66 + .../ac/mdiq/podcini/receiver/PlayerWidget.kt | 95 + .../receiver/PowerConnectionReceiver.kt | 6 +- .../ac/mdiq/podcini/receiver/SPAReceiver.kt | 8 +- .../service/BasicAuthorizationInterceptor.kt | 83 + .../mdiq/podcini/service/FeedUpdateWorker.kt | 200 ++ .../service/QuickSettingsTileService.kt | 63 + .../podcini/service/UserAgentInterceptor.kt | 16 + .../download/DefaultDownloaderFactory.kt | 19 + .../download/DownloadRequestCreator.kt | 120 + .../download/DownloadServiceInterfaceImpl.kt | 91 + .../podcini/service/download/Downloader.kt | 63 + .../service/download/DownloaderFactory.kt | 7 + .../service/download/EpisodeDownloadWorker.kt | 275 +++ .../service/download/HttpCredentialEncoder.kt | 18 + .../service/download/HttpDownloader.kt | 304 +++ .../download/NewEpisodesNotification.kt | 157 ++ .../service/download/PodciniHttpClient.kt | 107 + .../download/handler/FeedParserTask.kt | 124 ++ .../service/download/handler/FeedSyncTask.kt | 33 + .../handler/MediaDownloadedHandler.kt | 98 + .../service/playback/ExoPlayerWrapper.kt | 361 +++ .../podcini/service/playback/LocalPSMP.kt | 755 +++++++ .../service/playback/PlaybackService.kt | 1935 ++++++++++++++++ .../playback/PlaybackServiceInterface.kt | 20 + .../PlaybackServiceNotificationBuilder.kt | 260 +++ .../playback/PlaybackServiceStateManager.kt | 58 + .../playback/PlaybackServiceTaskManager.kt | 372 ++++ .../service/playback/PlaybackVolumeUpdater.kt | 36 + .../podcini/service/playback/ShakeListener.kt | 59 + .../main/java/ac/mdiq/podcini/spa/SPAUtil.kt | 49 - .../podcini/storage/APCleanupAlgorithm.kt | 115 + .../podcini/storage/APNullCleanupAlgorithm.kt | 27 + .../storage/APQueueCleanupAlgorithm.kt | 90 + .../storage/AutomaticDownloadAlgorithm.kt | 105 + .../java/ac/mdiq/podcini/storage/DBReader.kt | 932 ++++++++ .../java/ac/mdiq/podcini/storage/DBTasks.kt | 434 ++++ .../java/ac/mdiq/podcini/storage/DBWriter.kt | 1014 +++++++++ .../podcini/storage/DatabaseTransporter.kt | 111 + .../storage/EpisodeCleanupAlgorithm.kt | 64 + .../storage/EpisodeCleanupAlgorithmFactory.kt | 20 + .../storage/ExceptFavoriteCleanupAlgorithm.kt | 96 + .../storage/FeedItemDuplicateGuesser.kt | 84 + .../ac/mdiq/podcini/storage/FeedSearcher.kt | 41 + .../storage/ItemEnqueuePositionCalculator.kt | 77 + .../ac/mdiq/podcini/storage/NavDrawerData.kt | 33 + .../ac/mdiq/podcini/storage/StatisticsItem.kt | 26 + .../asynctask/DocumentFileExportWorker.kt | 56 + .../podcini/storage/asynctask/ExportWorker.kt | 57 + .../podcini/storage/backup/OpmlBackupAgent.kt | 170 ++ .../podcini/storage/database/DBUpgrader.kt | 338 +++ .../podcini/storage/database/PodDBAdapter.kt | 1470 +++++++++++++ .../database/mapper/ChapterCursorMapper.kt | 31 + .../mapper/DownloadResultCursorMapper.kt | 38 + .../database/mapper/FeedCursorMapper.kt | 65 + .../database/mapper/FeedItemCursorMapper.kt | 46 + .../database/mapper/FeedItemFilterQuery.kt | 72 + .../database/mapper/FeedItemSortQuery.kt | 23 + .../database/mapper/FeedMediaCursorMapper.kt | 58 + .../mapper/FeedPreferencesCursorMapper.kt | 77 + .../podcini/storage/export/CommonSymbols.kt | 24 + .../podcini/storage/export/ExportWriter.kt | 26 + .../export/favorites/FavoritesWriter.kt | 117 + .../podcini/storage/export/html/HtmlWriter.kt | 49 + .../storage/export/opml/OpmlElement.kt | 10 + .../podcini/storage/export/opml/OpmlReader.kt | 78 + .../storage/export/opml/OpmlSymbols.kt | 15 + .../podcini/storage/export/opml/OpmlWriter.kt | 70 + .../model/MediaMetadataRetrieverCompat.kt | 17 + .../storage/model/download/DownloadError.kt | 44 + .../storage/model/download/DownloadResult.kt | 81 + .../storage/model/download/DownloadStatus.kt | 9 + .../storage/model/download/ProxyConfig.kt | 14 + .../podcini/storage/model/feed/Chapter.kt | 36 + .../model/feed/EmbeddedChapterImage.kt | 60 + .../mdiq/podcini/storage/model/feed/Feed.kt | 416 ++++ .../storage/model/feed/FeedComponent.kt | 47 + .../podcini/storage/model/feed/FeedCounter.kt | 20 + .../podcini/storage/model/feed/FeedFile.kt | 92 + .../podcini/storage/model/feed/FeedFilter.kt | 119 + .../podcini/storage/model/feed/FeedFunding.kt | 82 + .../podcini/storage/model/feed/FeedItem.kt | 348 +++ .../storage/model/feed/FeedItemFilter.kt | 119 + .../podcini/storage/model/feed/FeedMedia.kt | 458 ++++ .../storage/model/feed/FeedPreferences.kt | 118 + .../podcini/storage/model/feed/SortOrder.kt | 67 + .../storage/model/feed/SubscriptionsFilter.kt | 97 + .../model/feed/VolumeAdaptionSetting.kt | 26 + .../storage/model/playback/MediaType.kt | 31 + .../storage/model/playback/Playable.kt | 153 ++ .../storage/model/playback/RemoteMedia.kt | 283 +++ .../podcini/ui/activity/BugReportActivity.kt | 125 ++ .../mdiq/podcini/ui/activity/MainActivity.kt | 681 ++++++ .../ui/activity/OnlineFeedViewActivity.kt | 705 ++++++ .../podcini/ui/activity/OpmlImportActivity.kt | 270 +++ .../activity/PlaybackSpeedDialogActivity.kt | 23 + .../podcini/ui/activity/PreferenceActivity.kt | 203 ++ .../ui/activity/SelectSubscriptionActivity.kt | 158 ++ .../podcini/ui/activity/SplashActivity.kt | 48 + .../ui/activity/VideoplayerActivity.kt | 784 +++++++ .../ui/activity/WidgetConfigActivity.kt | 141 ++ .../podcini/ui/adapter/ChaptersListAdapter.kt | 144 ++ .../ac/mdiq/podcini/ui/adapter/CoverLoader.kt | 122 ++ .../podcini/ui/adapter/DataFolderAdapter.kt | 112 + .../podcini/ui/adapter/DownloadLogAdapter.kt | 149 ++ .../ui/adapter/EpisodeItemListAdapter.kt | 213 ++ .../podcini/ui/adapter/FeedDiscoverAdapter.kt | 68 + .../adapter/FeedItemlistDescriptionAdapter.kt | 104 + .../ui/adapter/HorizontalFeedListAdapter.kt | 125 ++ .../ui/adapter/HorizontalItemListAdapter.kt | 120 + .../mdiq/podcini/ui/adapter/NavListAdapter.kt | 378 ++++ .../ui/adapter/QueueRecyclerAdapter.kt | 91 + .../podcini/ui/adapter/SelectableAdapter.kt | 188 ++ .../podcini/ui/adapter/SimpleChipAdapter.kt | 40 + .../ui/adapter/SimpleIconListAdapter.kt | 41 + .../adapter/SubscriptionsRecyclerAdapter.kt | 256 +++ .../CancelDownloadActionButton.kt | 32 + .../actionbutton/DeleteActionButton.kt | 32 + .../actionbutton/DownloadActionButton.kt | 60 + .../adapter/actionbutton/ItemActionButton.kt | 59 + .../actionbutton/MarkAsPlayedActionButton.kt | 25 + .../adapter/actionbutton/PauseActionButton.kt | 25 + .../adapter/actionbutton/PlayActionButton.kt | 33 + .../actionbutton/PlayLocalActionButton.kt | 29 + .../actionbutton/StreamActionButton.kt | 38 + .../actionbutton/VisitWebsiteActionButton.kt | 22 + .../ui/adapter/itunes/ItunesAdapter.kt | 93 + .../ui/appstartintent/MainActivityStarter.kt | 86 + .../PlaybackSpeedActivityStarter.kt | 33 + .../VideoPlayerActivityStarter.kt | 33 + .../podcini/ui/common/CircularProgressBar.kt | 100 + .../podcini/ui/common/PagedToolbarFragment.kt | 0 .../ui/common/PlaybackSpeedIndicatorView.kt | 107 + .../mdiq/podcini/ui/common/SquareImageView.kt | 55 + .../ac/mdiq/podcini/ui/common/ThemeUtils.kt | 32 + .../podcini/ui/common/TriangleLabelView.kt | 200 ++ .../podcini/ui/common/WrappingGridView.kt | 27 + .../ui/dialog/AllEpisodesFilterDialog.kt | 22 + .../podcini/ui/dialog/AuthenticationDialog.kt | 56 + .../ui/dialog/ChooseDataFolderDialog.kt | 33 + .../podcini/ui/dialog/ConfirmationDialog.kt | 46 + .../ui/dialog/DownloadLogDetailsDialog.kt | 60 + .../ui/dialog/DrawerPreferencesDialog.kt | 50 + .../ui/dialog/EditUrlSettingsDialog.kt | 82 + .../podcini/ui/dialog/EpisodeFilterDialog.kt | 109 + .../podcini/ui/dialog/FeedItemFilterDialog.kt | 27 + .../ui/dialog/FeedPreferenceSkipDialog.kt | 43 + .../mdiq/podcini/ui/dialog/FeedSortDialog.kt | 34 + .../podcini/ui/dialog/ItemFilterDialog.kt | 115 + .../mdiq/podcini/ui/dialog/ItemSortDialog.kt | 102 + .../ui/dialog/MediaPlayerErrorDialog.kt | 40 + .../ui/dialog/PlaybackControlsDialog.kt | 69 + .../ac/mdiq/podcini/ui/dialog/ProxyDialog.kt | 292 +++ .../podcini/ui/dialog/RemoveFeedDialog.kt | 74 + .../podcini/ui/dialog/RenameItemDialog.kt | 78 + .../ac/mdiq/podcini/ui/dialog/ShareDialog.kt | 96 + .../podcini/ui/dialog/SkipPreferenceDialog.kt | 62 + .../podcini/ui/dialog/SleepTimerDialog.kt | 195 ++ .../ui/dialog/StreamingConfirmationDialog.kt | 34 + .../ui/dialog/SubscriptionsFilterDialog.kt | 127 ++ .../podcini/ui/dialog/SwipeActionsDialog.kt | 195 ++ .../podcini/ui/dialog/TagSettingsDialog.kt | 143 ++ .../mdiq/podcini/ui/dialog/TimeRangeDialog.kt | 179 ++ .../podcini/ui/dialog/VariableSpeedDialog.kt | 152 ++ .../podcini/ui/fragment/AddFeedFragment.kt | 214 ++ .../ui/fragment/AllEpisodesFragment.kt | 139 ++ .../ui/fragment/AudioPlayerFragment.kt | 539 +++++ .../podcini/ui/fragment/ChaptersFragment.kt | 182 ++ .../ui/fragment/CompletedDownloadsFragment.kt | 379 ++++ .../mdiq/podcini/ui/fragment/CoverFragment.kt | 347 +++ .../podcini/ui/fragment/DiscoveryFragment.kt | 270 +++ .../ui/fragment/DownloadLogFragment.kt | 114 + .../ui/fragment/EpisodesListFragment.kt | 437 ++++ .../ui/fragment/ExternalPlayerFragment.kt | 206 ++ .../podcini/ui/fragment/FeedInfoFragment.kt | 348 +++ .../ui/fragment/FeedItemlistFragment.kt | 652 ++++++ .../ui/fragment/FeedSettingsFragment.kt | 498 +++++ .../mdiq/podcini/ui/fragment/InboxFragment.kt | 146 ++ .../ui/fragment/ItemDescriptionFragment.kt | 169 ++ .../mdiq/podcini/ui/fragment/ItemFragment.kt | 418 ++++ .../podcini/ui/fragment/ItemPagerFragment.kt | 180 ++ .../podcini/ui/fragment/NavDrawerFragment.kt | 450 ++++ .../ui/fragment/OnlineSearchFragment.kt | 210 ++ .../ui/fragment/PlaybackHistoryFragment.kt | 93 + .../mdiq/podcini/ui/fragment/QueueFragment.kt | 596 +++++ .../ui/fragment/QuickFeedDiscoveryFragment.kt | 170 ++ .../podcini/ui/fragment/SearchFragment.kt | 431 ++++ .../ui/fragment/SubscriptionFragment.kt | 381 ++++ .../podcini/ui/fragment/TransitionEffect.kt | 5 + .../EpisodeMultiSelectActionHandler.kt | 135 ++ .../actions/FeedMultiSelectActionHandler.kt | 165 ++ .../AutoDownloadPreferencesFragment.kt | 204 ++ .../DownloadsPreferencesFragment.kt | 113 + .../ImportExportPreferencesFragment.kt | 322 +++ .../preferences/MainPreferencesFragment.kt | 185 ++ .../NotificationPreferencesFragment.kt | 29 + .../PlaybackPreferencesFragment.kt | 126 ++ .../preferences/SwipePreferencesFragment.kt | 72 + .../UserInterfacePreferencesFragment.kt | 175 ++ .../preferences/about/AboutFragment.kt | 62 + .../about/ContributorsPagerFragment.kt | 71 + .../preferences/about/DevelopersFragment.kt | 54 + .../preferences/about/LicensesFragment.kt | 108 + .../about/SpecialThanksFragment.kt | 53 + .../preferences/about/TranslatorsFragment.kt | 53 + .../dialog/PreferenceListDialog.kt | 39 + .../dialog/PreferenceSwitchDialog.kt | 44 + .../GpodderAuthenticationFragment.kt | 271 +++ .../NextcloudAuthenticationFragment.kt | 104 + .../SynchronizationPreferencesFragment.kt | 215 ++ .../swipeactions/AddToQueueSwipeAction.kt | 40 + .../swipeactions/DeleteSwipeAction.kt | 41 + .../swipeactions/MarkFavoriteSwipeAction.kt | 36 + .../RemoveFromHistorySwipeAction.kt | 50 + .../RemoveFromInboxSwipeAction.kt | 36 + .../RemoveFromQueueSwipeAction.kt | 53 + .../ShowFirstSwipeDialogAction.kt | 33 + .../swipeactions/StartDownloadSwipeAction.kt | 37 + .../ui/fragment/swipeactions/SwipeAction.kt | 36 + .../ui/fragment/swipeactions/SwipeActions.kt | 252 +++ .../TogglePlaybackStateSwipeAction.kt | 39 + .../ac/mdiq/podcini/ui/glide/ApGlideModule.kt | 49 + .../podcini/ui/glide/ApOkHttpUrlLoader.kt | 92 + .../podcini/ui/glide/AudioCoverFetcher.kt | 48 + .../ui/glide/ChapterImageModelLoader.kt | 90 + .../ui/glide/FastBlurTransformation.kt | 317 +++ .../GenerativePlaceholderImageModelLoader.kt | 122 ++ .../ui/glide/MetadataRetrieverLoader.kt | 37 + .../podcini/ui/glide/NoHttpStringLoader.kt | 34 + .../ui/glide/ResizingOkHttpStreamFetcher.kt | 0 .../ui/gui/MoreContentListFooterUtil.kt | 44 + .../mdiq/podcini/ui/gui/NotificationUtils.kt | 115 + .../podcini/ui/gui/PictureInPictureUtil.kt | 25 + .../mdiq/podcini/ui/gui/ShownotesCleaner.kt | 195 ++ .../ac/mdiq/podcini/ui/home/HomeFragment.kt | 192 -- .../ac/mdiq/podcini/ui/home/HomeSection.kt | 99 - .../ui/home/HomeSectionsSettingsDialog.kt | 40 - .../sections/AllowNotificationsSection.kt | 68 - .../ui/home/sections/DownloadsSection.kt | 127 -- .../podcini/ui/home/sections/EchoSection.kt | 80 - .../home/sections/EpisodesSurpriseSection.kt | 142 -- .../podcini/ui/home/sections/InboxSection.kt | 131 -- .../podcini/ui/home/sections/QueueSection.kt | 150 -- .../ui/home/sections/SubscriptionsSection.kt | 105 - .../ui/menuhandler/FeedItemMenuHandler.kt | 274 +++ .../podcini/ui/menuhandler/FeedMenuHandler.kt | 62 + .../podcini/ui/menuhandler/MenuItemUtils.kt | 28 + .../podcini/ui/statistics/PieChartView.kt | 132 ++ .../ui/statistics/StatisticsFragment.kt | 132 ++ .../ui/statistics/StatisticsListAdapter.kt | 100 + .../downloads/DownloadStatisticsFragment.kt | 85 + .../DownloadStatisticsListAdapter.kt | 48 + .../feed/FeedStatisticsDialogFragment.kt | 47 + .../statistics/feed/FeedStatisticsFragment.kt | 96 + .../PlaybackStatisticsListAdapter.kt | 66 + .../subscriptions/StatisticsFilterDialog.kt | 141 ++ .../SubscriptionStatisticsFragment.kt | 126 ++ .../ui/statistics/years/BarChartView.kt | 127 ++ .../years/YearStatisticsListAdapter.kt | 101 + .../years/YearsStatisticsFragment.kt | 91 + .../podcini/ui/view/AspectRatioVideoView.kt | 81 + .../ac/mdiq/podcini/ui/view/ChapterSeekBar.kt | 144 ++ .../mdiq/podcini/ui/view/EmptyViewHandler.kt | 139 ++ .../ui/view/EpisodeItemListRecyclerView.kt | 77 + .../podcini/ui/view/ItemOffsetDecoration.kt | 18 + .../podcini/ui/view/LiftOnScrollListener.kt | 54 + .../mdiq/podcini/ui/view/LocalDeleteModal.kt | 32 + .../ui/view/LockableBottomSheetBehavior.kt | 81 + .../podcini/ui/view/NestedScrollableHost.kt | 175 ++ .../podcini/ui/view/NoRelayoutTextView.kt | 32 + .../ac/mdiq/podcini/ui/view/PlayButton.kt | 47 + .../podcini/ui/view/PlaybackSpeedSeekBar.kt | 67 + .../mdiq/podcini/ui/view/ShownotesWebView.kt | 185 ++ .../ui/view/SimpleAdapterDataObserver.kt | 34 + .../podcini/ui/view/ToolbarIconTintManager.kt | 51 + .../viewholder/DownloadLogItemViewHolder.kt | 41 + .../view/viewholder/EpisodeItemViewHolder.kt | 281 +++ .../viewholder/HorizontalItemViewHolder.kt | 124 ++ .../mdiq/podcini/ui/widget/WidgetUpdater.kt | 211 ++ .../podcini/ui/widget/WidgetUpdaterWorker.kt | 48 + .../java/ac/mdiq/podcini/util/ChapterUtils.kt | 217 ++ .../java/ac/mdiq/podcini/util/Converter.kt | 122 ++ .../ac/mdiq/podcini/util/DateFormatter.kt | 42 + .../mdiq/podcini/util/DownloadErrorLabel.kt | 45 + .../ac/mdiq/podcini/util/FastDocumentFile.kt | 37 + .../ac/mdiq/podcini/util/FeedItemPermutors.kt | 176 ++ .../java/ac/mdiq/podcini/util/FeedItemUtil.kt | 73 + .../java/ac/mdiq/podcini/util/FeedUtil.kt | 14 + .../ac/mdiq/podcini/util/FileNameGenerator.kt | 73 + .../java/ac/mdiq/podcini/util/IntentUtils.kt | 46 + .../mdiq/podcini/util/InvalidFeedException.kt | 10 + .../java/ac/mdiq/podcini/util/LongList.kt | 262 +++ .../java/ac/mdiq/podcini/util/NetworkUtils.kt | 133 ++ .../java/ac/mdiq/podcini/util/Permutor.kt | 15 + .../ac/mdiq/podcini/util/PlaybackStatus.kt | 24 + .../java/ac/mdiq/podcini/util/PowerUtils.kt | 25 + .../main/java/ac/mdiq/podcini/util/SPAUtil.kt | 49 + .../java/ac/mdiq/podcini/util/ShareUtils.kt | 104 + .../java/ac/mdiq/podcini/util/StorageUtils.kt | 42 + .../mdiq/podcini/util/TimeSpeedConverter.kt | 17 + .../main/java/ac/mdiq/podcini/util/URIUtil.kt | 35 + .../comparator/ChapterStartTimeComparator.kt | 9 + .../comparator/DownloadResultComparator.kt | 10 + .../comparator/FeedItemPubdateComparator.kt | 22 + .../PlaybackCompletionDateComparator.kt | 12 + .../util/config/ApplicationCallbacks.kt | 13 + .../util/config/ApplicationCallbacksImpl.kt | 11 + .../mdiq/podcini/util/config/ClientConfig.kt | 15 + .../podcini/util/config/ClientConfigurator.kt | 46 + .../podcini/util/error/CrashReportWriter.kt | 61 + .../util/error/RxJavaErrorHandlerSetup.kt | 31 + .../util/event/DiscoveryDefaultUpdateEvent.kt | 3 + .../podcini/util/event/DownloadLogEvent.kt | 14 + .../util/event/EpisodeDownloadEvent.kt | 8 + .../mdiq/podcini/util/event/FavoritesEvent.kt | 3 + .../mdiq/podcini/util/event/FeedItemEvent.kt | 17 + .../podcini/util/event/FeedListUpdateEvent.kt | 25 + .../util/event/FeedUpdateRunningEvent.kt | 3 + .../mdiq/podcini/util/event/MessageEvent.kt | 9 + .../podcini/util/event/PlayerErrorEvent.kt | 3 + .../podcini/util/event/PlayerStatusEvent.kt | 3 + .../ac/mdiq/podcini/util/event/QueueEvent.kt | 72 + .../podcini/util/event/StatisticsEvent.kt | 3 + .../podcini/util/event/SyncServiceEvent.kt | 3 + .../util/event/UnreadItemsUpdateEvent.kt | 3 + .../settings/SkipIntroEndingChangedEvent.kt | 3 + .../event/settings/SpeedPresetChangedEvent.kt | 3 + .../settings/VolumeAdaptionChangedEvent.kt | 5 + .../util/syndication/FeedDiscoverer.kt | 75 + .../util/syndication/HtmlToPlainText.kt | 126 ++ .../mdiq/podcini/view/AspectRatioVideoView.kt | 81 - .../ac/mdiq/podcini/view/ChapterSeekBar.kt | 144 -- .../ac/mdiq/podcini/view/EmptyViewHandler.kt | 139 -- .../view/EpisodeItemListRecyclerView.kt | 77 - .../mdiq/podcini/view/ItemOffsetDecoration.kt | 18 - .../mdiq/podcini/view/LiftOnScrollListener.kt | 54 - .../ac/mdiq/podcini/view/LocalDeleteModal.kt | 32 - .../view/LockableBottomSheetBehavior.kt | 81 - .../mdiq/podcini/view/NestedScrollableHost.kt | 175 -- .../mdiq/podcini/view/NoRelayoutTextView.kt | 32 - .../java/ac/mdiq/podcini/view/PlayButton.kt | 47 - .../mdiq/podcini/view/PlaybackSpeedSeekBar.kt | 67 - .../ac/mdiq/podcini/view/ShownotesWebView.kt | 186 -- .../podcini/view/SimpleAdapterDataObserver.kt | 34 - .../podcini/view/ToolbarIconTintManager.kt | 51 - .../viewholder/DownloadLogItemViewHolder.kt | 41 - .../view/viewholder/EpisodeItemViewHolder.kt | 283 --- .../viewholder/HorizontalItemViewHolder.kt | 125 -- .../ViewPagerBottomSheetBehavior.kt | 2 +- .../src/main/res/color/button_bg_selector.xml | 0 .../drawable-anydpi-v26/ic_feed_shortcut.xml | 0 .../ic_playlist_shortcut.xml | 0 .../ic_refresh_shortcut.xml | 0 .../ic_subscriptions_shortcut.xml | 0 .../res/drawable-hdpi/ic_notification.png | Bin .../res/drawable-hdpi/ic_notification_new.png | Bin .../res/drawable-hdpi/ic_widget_preview.png | Bin .../res/drawable-mdpi/ic_notification.png | Bin .../res/drawable-mdpi/ic_notification_new.png | Bin .../src/main/res/drawable-nodpi/echo.png | Bin .../main/res/drawable-nodpi/gpodder_icon.png | Bin .../drawable-nodpi/launcher_animate_bg.png | Bin .../drawable-nodpi/launcher_animate_wave1.png | Bin .../drawable-nodpi/launcher_animate_wave2.png | Bin .../res/drawable-nodpi/logo_monochrome.png | Bin .../res/drawable-nodpi/nextcloud_logo.png | Bin .../src/main/res/drawable-nodpi/teaser.png | Bin .../res/drawable-xhdpi/ic_notification.png | Bin .../drawable-xhdpi/ic_notification_new.png | Bin .../res/drawable-xxhdpi/ic_notification.png | Bin .../drawable-xxhdpi/ic_notification_new.png | Bin .../res/drawable-xxxhdpi/ic_notification.png | Bin .../drawable-xxxhdpi/ic_notification_new.png | Bin .../main/res/drawable/bg_blue_gradient.xml | 0 .../src/main/res/drawable/bg_circle.xml | 0 .../src/main/res/drawable/bg_gradient.xml | 0 .../src/main/res/drawable/bg_pill.xml | 0 .../main/res/drawable/bg_pill_translucent.xml | 0 .../main/res/drawable/bg_rounded_corners.xml | 0 .../res/drawable/drawer_item_background.xml | 0 .../src/main/res/drawable/ic_add.xml | 0 .../src/main/res/drawable/ic_appearance.xml | 0 .../src/main/res/drawable/ic_arrow_down.xml | 0 .../res/drawable/ic_arrow_right_white.xml | 0 .../src/main/res/drawable/ic_bug.xml | 0 .../src/main/res/drawable/ic_cancel.xml | 0 .../src/main/res/drawable/ic_chapter_next.xml | 0 .../src/main/res/drawable/ic_chapter_prev.xml | 0 .../src/main/res/drawable/ic_chart_box.xml | 0 .../src/main/res/drawable/ic_chat.xml | 0 .../src/main/res/drawable/ic_check.xml | 0 .../res/drawable/ic_checkbox_background.xml | 0 .../src/main/res/drawable/ic_close_white.xml | 0 .../src/main/res/drawable/ic_cloud.xml | 0 .../src/main/res/drawable/ic_contribute.xml | 0 .../src/main/res/drawable/ic_curved_arrow.xml | 0 .../src/main/res/drawable/ic_delete.xml | 0 .../src/main/res/drawable/ic_delete_auto.xml | 0 .../src/main/res/drawable/ic_disc_alert.xml | 0 .../src/main/res/drawable/ic_download.xml | 0 .../main/res/drawable/ic_download_black.xml | 0 .../main/res/drawable/ic_drag_darktheme.xml | 0 .../main/res/drawable/ic_drag_lighttheme.xml | 0 .../src/main/res/drawable/ic_error.xml | 0 .../src/main/res/drawable/ic_fab_edit.xml | 0 .../src/main/res/drawable/ic_fast_forward.xml | 0 .../drawable/ic_fast_forward_video_white.xml | 0 .../src/main/res/drawable/ic_fast_rewind.xml | 0 .../drawable/ic_fast_rewind_video_white.xml | 0 .../src/main/res/drawable/ic_feed.xml | 0 .../src/main/res/drawable/ic_feed_black.xml | 0 .../main/res/drawable/ic_feed_shortcut.xml | 0 .../src/main/res/drawable/ic_filter.xml | 0 .../src/main/res/drawable/ic_filter_white.xml | 0 .../src/main/res/drawable/ic_folder.xml | 0 .../src/main/res/drawable/ic_history.xml | 0 .../main/res/drawable/ic_history_remove.xml | 0 .../src/main/res/drawable/ic_home.xml | 0 .../src/main/res/drawable/ic_inbox.xml | 0 .../src/main/res/drawable/ic_info.xml | 0 .../src/main/res/drawable/ic_info_white.xml | 0 .../src/main/res/drawable/ic_key.xml | 0 .../src/main/res/drawable/ic_load_more.xml | 0 .../src/main/res/drawable/ic_mark_played.xml | 0 .../main/res/drawable/ic_mark_unplayed.xml | 0 .../drawable/ic_notification_fast_forward.xml | 0 .../drawable/ic_notification_fast_rewind.xml | 0 .../drawable/ic_notification_next_chapter.xml | 0 .../res/drawable/ic_notification_pause.xml | 0 .../res/drawable/ic_notification_play.xml | 0 .../ic_notification_playback_speed.xml | 0 .../res/drawable/ic_notification_skip.xml | 0 .../res/drawable/ic_notification_stream.xml | 0 .../res/drawable/ic_notification_sync.xml | 0 .../drawable/ic_notification_sync_error.xml | 0 .../main/res/drawable/ic_notifications.xml | 0 .../src/main/res/drawable/ic_paperclip.xml | 0 .../src/main/res/drawable/ic_pause.xml | 0 .../res/drawable/ic_pause_video_white.xml | 0 .../src/main/res/drawable/ic_play_24dp.xml | 0 .../src/main/res/drawable/ic_play_48dp.xml | 0 .../main/res/drawable/ic_play_video_white.xml | 0 .../main/res/drawable/ic_playback_speed.xml | 0 .../main/res/drawable/ic_playlist_play.xml | 0 .../res/drawable/ic_playlist_play_black.xml | 0 .../main/res/drawable/ic_playlist_remove.xml | 0 .../res/drawable/ic_playlist_shortcut.xml | 0 .../src/main/res/drawable/ic_questionmark.xml | 0 .../src/main/res/drawable/ic_refresh.xml | 0 .../main/res/drawable/ic_refresh_black.xml | 0 .../main/res/drawable/ic_refresh_shortcut.xml | 0 .../src/main/res/drawable/ic_replay.xml | 0 .../res/drawable/ic_rounded_corner_left.xml | 0 .../res/drawable/ic_rounded_corner_right.xml | 0 .../src/main/res/drawable/ic_search.xml | 0 .../src/main/res/drawable/ic_select_all.xml | 0 .../src/main/res/drawable/ic_select_none.xml | 0 .../src/main/res/drawable/ic_settings.xml | 0 .../main/res/drawable/ic_settings_white.xml | 0 .../src/main/res/drawable/ic_share.xml | 0 .../res/drawable/ic_shortcut_background.xml | 0 .../src/main/res/drawable/ic_shuffle.xml | 0 .../src/main/res/drawable/ic_skip_24dp.xml | 0 .../src/main/res/drawable/ic_skip_48dp.xml | 0 .../src/main/res/drawable/ic_sleep.xml | 0 .../src/main/res/drawable/ic_sleep_off.xml | 0 .../src/main/res/drawable/ic_star.xml | 0 .../src/main/res/drawable/ic_star_border.xml | 0 .../src/main/res/drawable/ic_storage.xml | 0 .../src/main/res/drawable/ic_stream.xml | 0 .../main/res/drawable/ic_subscriptions.xml | 0 .../res/drawable/ic_subscriptions_black.xml | 0 .../drawable/ic_subscriptions_shortcut.xml | 0 .../src/main/res/drawable/ic_tag.xml | 0 .../src/main/res/drawable/ic_videocam.xml | 0 .../main/res/drawable/ic_volume_adaption.xml | 0 .../src/main/res/drawable/ic_web.xml | 0 .../res/drawable/ic_widget_fast_forward.xml | 0 .../res/drawable/ic_widget_fast_rewind.xml | 0 .../src/main/res/drawable/ic_widget_pause.xml | 0 .../src/main/res/drawable/ic_widget_play.xml | 0 .../res/drawable/ic_widget_playback_speed.xml | 0 .../src/main/res/drawable/ic_widget_skip.xml | 0 .../main/res/drawable/launcher_animate.xml | 0 .../drawable/progress_bar_horizontal_dark.xml | 0 .../progress_bar_horizontal_light.xml | 0 .../res/drawable/scrollbar_thumb_dark.xml | 0 .../res/drawable/scrollbar_thumb_default.xml | 0 .../res/drawable/scrollbar_thumb_light.xml | 0 .../drawable/scrollbar_thumb_pressed_dark.xml | 0 .../scrollbar_thumb_pressed_light.xml | 0 .../src/main/res/drawable/scrollbar_track.xml | 0 .../src/main/res/font/sarabun_regular.ttf | Bin .../src/main/res/font/sarabun_semi_bold.ttf | Bin app/src/main/res/layout-sw720dp/main.xml | 2 +- .../res/layout/activity_widget_config.xml | 2 +- app/src/main/res/layout/addfeed.xml | 2 +- .../main/res/layout/audioplayer_fragment.xml | 8 +- .../src/main/res/layout/echo_activity.xml | 0 .../res/layout/episodes_list_fragment.xml | 2 +- .../res/layout/external_player_fragment.xml | 2 +- .../res/layout/feed_item_list_fragment.xml | 2 +- .../src/main/res/layout/feed_statistics.xml | 0 .../res/layout/feed_statistics_dialog.xml | 0 app/src/main/res/layout/feeditem_fragment.xml | 6 +- .../res/layout/fragment_subscriptions.xml | 39 +- app/src/main/res/layout/home_fragment.xml | 89 - app/src/main/res/layout/home_section.xml | 108 - app/src/main/res/layout/home_section_echo.xml | 91 - .../res/layout/home_section_notification.xml | 66 - .../res/layout/item_description_fragment.xml | 6 +- app/src/main/res/layout/main.xml | 2 +- .../res/layout/more_content_list_footer.xml | 0 .../src/main/res/layout/pager_fragment.xml | 0 .../playback_speed_feed_setting_dialog.xml | 2 +- .../src/main/res/layout/player_widget.xml | 0 .../src/main/res/layout/popup_bubble_view.xml | 0 .../res/layout/preference_material_switch.xml | 0 app/src/main/res/layout/queue_fragment.xml | 2 +- app/src/main/res/layout/search_fragment.xml | 2 +- .../main/res/layout/simple_list_fragment.xml | 2 +- .../main/res/layout/speed_select_dialog.xml | 2 +- .../res/layout/statistics_filter_dialog.xml | 0 .../main/res/layout/statistics_fragment.xml | 0 .../main/res/layout/statistics_listitem.xml | 0 .../layout/statistics_listitem_barchart.xml | 0 .../res/layout/statistics_listitem_total.xml | 0 .../res/layout/statistics_year_listitem.xml | 0 app/src/main/res/layout/subscription_item.xml | 130 +- .../main/res/layout/videoplayer_activity.xml | 4 +- .../src/main/res/menu/statistics.xml | 0 app/src/main/res/menu/subscriptions.xml | 22 - .../res/mipmap-anydpi-v26/ic_launcher.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../mipmap-hdpi/ic_launcher_background.png | Bin .../mipmap-hdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../mipmap-mdpi/ic_launcher_background.png | Bin .../mipmap-mdpi/ic_launcher_foreground.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../mipmap-xhdpi/ic_launcher_background.png | Bin .../mipmap-xhdpi/ic_launcher_foreground.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../mipmap-xxhdpi/ic_launcher_background.png | Bin .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../mipmap-xxxhdpi/ic_launcher_background.png | Bin .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin .../mipmap-xxxhdpi/ic_launcher_monochrome.png | Bin .../src/main/res/values-ar/strings.xml | 0 .../src/main/res/values-ast/strings.xml | 0 .../src/main/res/values-br/strings.xml | 0 .../src/main/res/values-ca/strings.xml | 0 .../src/main/res/values-cs/strings.xml | 0 .../src/main/res/values-da/strings.xml | 0 .../src/main/res/values-de/strings.xml | 0 .../src/main/res/values-es/strings.xml | 0 .../src/main/res/values-et/strings.xml | 0 .../src/main/res/values-eu/strings.xml | 0 .../src/main/res/values-fa/strings.xml | 0 .../src/main/res/values-fi/strings.xml | 0 .../src/main/res/values-fr/strings.xml | 0 .../src/main/res/values-gl/strings.xml | 0 .../src/main/res/values-hu/strings.xml | 0 .../src/main/res/values-in/strings.xml | 0 .../src/main/res/values-it/strings.xml | 0 .../src/main/res/values-iw/strings.xml | 0 .../src/main/res/values-ja/strings.xml | 0 .../src/main/res/values-ko/strings.xml | 0 .../src/main/res/values-lt/strings.xml | 0 .../src/main/res/values-nb/strings.xml | 0 .../src/main/res/values-nl/strings.xml | 0 .../src/main/res/values-pl/strings.xml | 0 .../src/main/res/values-pt-rBR/strings.xml | 0 .../src/main/res/values-pt/strings.xml | 0 .../src/main/res/values-ro/strings.xml | 0 .../src/main/res/values-ru/strings.xml | 0 .../src/main/res/values-sk/strings.xml | 0 .../src/main/res/values-sl/strings.xml | 0 .../src/main/res/values-sv/strings.xml | 0 .../src/main/res/values-tr/strings.xml | 0 .../src/main/res/values-uk/strings.xml | 0 .../src/main/res/values-v23/styles.xml | 0 .../src/main/res/values-v27/styles.xml | 0 .../src/main/res/values-zh-rCN/strings.xml | 0 .../src/main/res/values-zh-rTW/strings.xml | 0 app/src/main/res/values/arrays.xml | 253 +++ app/src/main/res/values/attrs.xml | 29 +- {core => app}/src/main/res/values/colors.xml | 0 app/src/main/res/values/dimens.xml | 29 +- .../src/main/res/values/echo-strings.xml | 0 {core => app}/src/main/res/values/ids.xml | 0 app/src/main/res/values/integers.xml | 3 +- .../src/main/res/values/keycodes.xml | 0 .../src/main/res/values/pending_intent.xml | 0 app/src/main/res/values/strings.xml | 827 +++++++ .../src/main/res/values/styleable.xml | 0 {core => app}/src/main/res/values/styles.xml | 0 app/src/main/res/xml/player_widget_info.xml | 2 +- .../res/xml/preferences_user_interface.xml | 12 +- app/src/main/res/xml/shortcuts.xml | 8 +- .../ac/mdiq/podcini/dialog/RatingDialog.kt | 2 +- .../podcini/net/ssl/SslProviderInstaller.kt | 0 .../playback/cast/CastEnabledActivity.kt | 39 + .../playback/cast/CastOptionsProvider.kt | 0 .../ac/mdiq/podcini/playback/cast/CastPsmp.kt | 511 +++++ .../playback/cast/CastStateListener.kt | 66 + .../mdiq/podcini/playback/cast/CastUtils.kt | 172 ++ .../podcini/playback/cast/MediaInfoCreator.kt | 130 ++ .../service/playback/WearMediaSession.kt | 25 + .../src/play/res/menu/cast_button.xml | 0 .../src/test/assets/local-feed1/track1.mp3 | Bin .../src/test/assets/local-feed2/folder.png | Bin .../src/test/assets/local-feed2/track1.mp3 | Bin .../src/test/assets/local-feed2/track2.mp3 | Bin .../ac/mdiq/podcini/feed/FeedFilterTest.kt | 152 ++ .../ac/mdiq/podcini/feed/FeedItemMother.kt | 15 + .../java/ac/mdiq/podcini/feed/FeedItemTest.kt | 138 ++ .../ac/mdiq/podcini/feed/FeedMediaMother.kt | 14 + .../ac/mdiq/podcini/feed/FeedMediaTest.kt | 68 + .../java/ac/mdiq/podcini/feed/FeedMother.kt | 14 + .../java/ac/mdiq/podcini/feed/FeedTest.kt | 117 + .../mdiq/podcini/feed/LocalFeedUpdaterTest.kt | 307 +++ .../podcini/feed/VolumeAdaptionSettingTest.kt | 115 + .../mdiq/podcini/net/common/UrlCheckerTest.kt | 0 .../serviceinterface/DownloadRequestTest.kt | 118 + .../podcini/net/sync/HostnameParserTest.kt | 0 .../feed/element/element/AtomTextTest.kt | 37 + .../feed/element/namespace/AtomParserTest.kt | 92 + .../element/namespace/FeedParserTestHelper.kt | 32 + .../feed/element/namespace/RssParserTest.kt | 104 + .../parser/feed/element/util/DateUtilsTest.kt | 169 ++ .../feed/element/util/DurationParserTest.kt | 43 + .../parser/media/id3/ChapterReaderTest.kt | 211 ++ .../podcini/parser/media/id3/Id3ReaderTest.kt | 155 ++ .../parser/media/id3/MetadataReaderTest.kt | 54 + .../vorbis/VorbisCommentChapterReaderTest.kt | 45 + .../vorbis/VorbisCommentMetadataReaderTest.kt | 28 + .../playback/base/RewindAfterPauseUtilTest.kt | 0 .../playback/PlaybackVolumeUpdaterTest.kt | 225 ++ .../podcini/storage/APCleanupAlgorithmTest.kt | 17 + .../ac/mdiq/podcini/storage/DbCleanupTests.kt | 232 ++ .../storage/DbNullCleanupAlgorithmTest.kt | 117 + .../storage/DbQueueCleanupAlgorithmTest.kt | 47 + .../ac/mdiq/podcini/storage/DbReaderTest.kt | 491 +++++ .../ac/mdiq/podcini/storage/DbTasksTest.kt | 263 +++ .../ac/mdiq/podcini/storage/DbTestUtils.kt | 65 + .../ac/mdiq/podcini/storage/DbWriterTest.kt | 854 ++++++++ .../ExceptFavoriteCleanupAlgorithmTest.kt | 87 + .../storage/FeedItemDuplicateGuesserTest.kt | 70 + .../ItemEnqueuePositionCalculatorTest.kt | 153 ++ .../storage/mapper/FeedCursorMapperTest.kt | 96 + .../podcini/sync/EpisodeActionFilterTest.kt | 187 ++ .../ac/mdiq/podcini/sync/GuidValidatorTest.kt | 19 + .../mdiq/podcini/util/CollectionTestUtil.kt | 27 + .../ac/mdiq/podcini/util/ConverterTest.kt | 41 + .../podcini/util/FeedItemPermutorsTest.kt | 222 ++ .../ac/mdiq/podcini/util/FeedItemUtilTest.kt | 90 + .../podcini/util/FilenameGeneratorTest.kt | 92 + .../java/ac/mdiq/podcini/util/URIUtilTest.kt | 23 + .../podcini/util/gui/ShownotesCleanerTest.kt | 234 ++ .../util/syndication/FeedDiscovererTest.kt | 133 ++ .../src/test/java/android/text/TextUtils.kt | 0 .../src/test/java/android/util/Log.kt | 0 .../src/test/resources/auphonic.m4a | Bin .../src/test/resources/auphonic.mp3 | Bin .../src/test/resources/auphonic.ogg | Bin .../src/test/resources/auphonic.opus | Bin .../resources/feed-atom-testAtomBasic.xml | 0 .../resources/feed-atom-testEmptyRelLinks.xml | 0 .../feed-atom-testLogoWithWhitespace.xml | 0 .../feed-rss-testImageWithWhitespace.xml | 0 .../feed-rss-testMediaContentMime.xml | 0 .../feed-rss-testMultipleFundingTags.xml | 0 .../test/resources/feed-rss-testRss2Basic.xml | 0 .../feed-rss-testUnsupportedElements.xml | 0 .../resources/hindenburg-journalist-pro.m4a | Bin .../resources/hindenburg-journalist-pro.mp3 | Bin .../src/test/resources/mp3chaps-py.mp3 | Bin .../src/test/resources/ultraschall5.mp3 | Bin common.gradle | 2 +- config/spotbugs/exclude.xml | 26 +- core/.gitignore | 1 - core/build.gradle | 112 - core/lint.xml | 7 - .../core/service/playback/WearMediaSession.kt | 17 - core/src/main/AndroidManifest.xml | 73 - .../mdiq/podcini/core/ApplicationCallbacks.kt | 13 - .../java/ac/mdiq/podcini/core/ClientConfig.kt | 15 - .../mdiq/podcini/core/ClientConfigurator.kt | 47 - .../podcini/core/backup/OpmlBackupAgent.kt | 170 -- .../podcini/core/dialog/ConfirmationDialog.kt | 46 - .../podcini/core/event/DownloadLogEvent.kt | 14 - .../mdiq/podcini/core/export/CommonSymbols.kt | 24 - .../mdiq/podcini/core/export/ExportWriter.kt | 26 - .../core/export/favorites/FavoritesWriter.kt | 117 - .../podcini/core/export/html/HtmlWriter.kt | 49 - .../podcini/core/export/opml/OpmlElement.kt | 10 - .../podcini/core/export/opml/OpmlReader.kt | 78 - .../podcini/core/export/opml/OpmlSymbols.kt | 15 - .../podcini/core/export/opml/OpmlWriter.kt | 70 - .../mdiq/podcini/core/feed/ChapterMerger.kt | 63 - .../ac/mdiq/podcini/core/feed/FeedEvent.kt | 18 - .../podcini/core/feed/FeedItemFilterGroup.kt | 28 - .../core/feed/FeedUrlNotFoundException.kt | 8 - .../podcini/core/feed/LocalFeedUpdater.kt | 288 --- .../core/feed/SubscriptionsFilterGroup.kt | 24 - .../core/feed/util/ImageResourceUtils.kt | 58 - .../core/feed/util/PlaybackSpeedUtils.kt | 48 - .../podcini/core/menuhandler/MenuItemUtils.kt | 28 - .../core/preferences/PlaybackPreferences.kt | 230 -- .../core/preferences/SleepTimerPreferences.kt | 117 - .../podcini/core/preferences/ThemeSwitcher.kt | 68 - .../core/preferences/UsageStatistics.kt | 64 - .../core/receiver/FeedUpdateReceiver.kt | 26 - .../core/receiver/MediaButtonReceiver.kt | 66 - .../podcini/core/receiver/PlayerWidget.kt | 96 - .../service/BasicAuthorizationInterceptor.kt | 85 - .../podcini/core/service/FeedUpdateWorker.kt | 200 -- .../core/service/QuickSettingsTileService.kt | 63 - .../core/service/UserAgentInterceptor.kt | 16 - .../download/DefaultDownloaderFactory.kt | 19 - .../download/DownloadRequestCreator.kt | 120 - .../download/DownloadServiceInterfaceImpl.kt | 89 - .../core/service/download/Downloader.kt | 63 - .../service/download/DownloaderFactory.kt | 7 - .../service/download/EpisodeDownloadWorker.kt | 275 --- .../service/download/HttpCredentialEncoder.kt | 18 - .../core/service/download/HttpDownloader.kt | 304 --- .../download/NewEpisodesNotification.kt | 157 -- .../service/download/PodciniHttpClient.kt | 109 - .../download/handler/FeedParserTask.kt | 129 -- .../service/download/handler/FeedSyncTask.kt | 33 - .../handler/MediaDownloadedHandler.kt | 98 - .../core/service/playback/ExoPlayerWrapper.kt | 361 --- .../core/service/playback/LocalPSMP.kt | 756 ------- .../core/service/playback/PlaybackService.kt | 1939 ----------------- .../playback/PlaybackServiceInterface.kt | 20 - .../PlaybackServiceNotificationBuilder.kt | 261 --- .../playback/PlaybackServiceStateManager.kt | 58 - .../playback/PlaybackServiceTaskManager.kt | 372 ---- .../service/playback/PlaybackVolumeUpdater.kt | 36 - .../core/service/playback/ShakeListener.kt | 59 - .../core/storage/APCleanupAlgorithm.kt | 115 - .../core/storage/APNullCleanupAlgorithm.kt | 27 - .../core/storage/APQueueCleanupAlgorithm.kt | 90 - .../storage/AutomaticDownloadAlgorithm.kt | 105 - .../ac/mdiq/podcini/core/storage/DBReader.kt | 938 -------- .../ac/mdiq/podcini/core/storage/DBTasks.kt | 436 ---- .../ac/mdiq/podcini/core/storage/DBWriter.kt | 1015 --------- .../core/storage/DatabaseTransporter.kt | 111 - .../core/storage/EpisodeCleanupAlgorithm.kt | 64 - .../storage/EpisodeCleanupAlgorithmFactory.kt | 20 - .../storage/ExceptFavoriteCleanupAlgorithm.kt | 96 - .../core/storage/FeedItemDuplicateGuesser.kt | 84 - .../mdiq/podcini/core/storage/FeedSearcher.kt | 41 - .../storage/ItemEnqueuePositionCalculator.kt | 77 - .../podcini/core/storage/NavDrawerData.kt | 49 - .../podcini/core/storage/StatisticsItem.kt | 26 - .../podcini/core/sync/EpisodeActionFilter.kt | 65 - .../mdiq/podcini/core/sync/GuidValidator.kt | 10 - .../podcini/core/sync/LockingAsyncExecutor.kt | 35 - .../ac/mdiq/podcini/core/sync/SyncService.kt | 354 --- .../core/sync/SynchronizationCredentials.kt | 58 - .../sync/SynchronizationProviderViewData.kt | 28 - .../core/sync/SynchronizationSettings.kt | 74 - .../sync/queue/SynchronizationQueueSink.kt | 82 - .../sync/queue/SynchronizationQueueStorage.kt | 148 -- .../ac/mdiq/podcini/core/util/ChapterUtils.kt | 217 -- .../ac/mdiq/podcini/core/util/Converter.kt | 122 -- .../mdiq/podcini/core/util/DateFormatter.kt | 42 - .../podcini/core/util/DownloadErrorLabel.kt | 45 - .../podcini/core/util/FastDocumentFile.kt | 37 - .../podcini/core/util/FeedItemPermutors.kt | 176 -- .../ac/mdiq/podcini/core/util/FeedItemUtil.kt | 73 - .../ac/mdiq/podcini/core/util/FeedUtil.kt | 14 - .../podcini/core/util/FileNameGenerator.kt | 77 - .../ac/mdiq/podcini/core/util/IntentUtils.kt | 46 - .../podcini/core/util/InvalidFeedException.kt | 10 - .../ac/mdiq/podcini/core/util/LongList.kt | 262 --- .../ac/mdiq/podcini/core/util/NetworkUtils.kt | 134 -- .../ac/mdiq/podcini/core/util/Permutor.kt | 15 - .../mdiq/podcini/core/util/PlaybackStatus.kt | 24 - .../ac/mdiq/podcini/core/util/PowerUtils.kt | 25 - .../ac/mdiq/podcini/core/util/ShareUtils.kt | 104 - .../ac/mdiq/podcini/core/util/StorageUtils.kt | 42 - .../podcini/core/util/TimeSpeedConverter.kt | 17 - .../java/ac/mdiq/podcini/core/util/URIUtil.kt | 35 - .../comparator/ChapterStartTimeComparator.kt | 9 - .../comparator/DownloadResultComparator.kt | 10 - .../comparator/FeedItemPubdateComparator.kt | 22 - .../PlaybackCompletionDateComparator.kt | 12 - .../util/download/ConnectionStateMonitor.kt | 41 - .../core/util/download/FeedUpdateManager.kt | 109 - .../core/util/download/MediaSizeLoader.kt | 77 - .../NetworkConnectionChangeHandler.kt | 34 - .../util/gui/MoreContentListFooterUtil.kt | 44 - .../core/util/gui/NotificationUtils.kt | 115 - .../core/util/gui/PictureInPictureUtil.kt | 25 - .../podcini/core/util/gui/ShownotesCleaner.kt | 196 -- .../core/util/playback/PlayableUtils.kt | 36 - .../core/util/playback/PlaybackController.kt | 399 ---- .../util/playback/PlaybackServiceStarter.kt | 45 - .../core/util/syndication/FeedDiscoverer.kt | 75 - .../core/util/syndication/HtmlToPlainText.kt | 126 -- .../mdiq/podcini/core/widget/WidgetUpdater.kt | 210 -- .../core/widget/WidgetUpdaterWorker.kt | 48 - core/src/main/res/values/arrays.xml | 272 --- core/src/main/res/values/attrs.xml | 15 - core/src/main/res/values/dimens.xml | 30 - core/src/main/res/values/integers.xml | 3 - .../core/service/playback/WearMediaSession.kt | 25 - .../mdiq/podcini/core/feed/FeedFilterTest.kt | 152 -- .../mdiq/podcini/core/feed/FeedItemMother.kt | 15 - .../ac/mdiq/podcini/core/feed/FeedItemTest.kt | 138 -- .../mdiq/podcini/core/feed/FeedMediaMother.kt | 14 - .../mdiq/podcini/core/feed/FeedMediaTest.kt | 68 - .../ac/mdiq/podcini/core/feed/FeedMother.kt | 14 - .../ac/mdiq/podcini/core/feed/FeedTest.kt | 117 - .../podcini/core/feed/LocalFeedUpdaterTest.kt | 306 --- .../core/feed/VolumeAdaptionSettingTest.kt | 115 - .../playback/PlaybackVolumeUpdaterTest.kt | 225 -- .../core/storage/APCleanupAlgorithmTest.kt | 17 - .../podcini/core/storage/DbCleanupTests.kt | 234 -- .../storage/DbNullCleanupAlgorithmTest.kt | 116 - .../storage/DbQueueCleanupAlgorithmTest.kt | 47 - .../mdiq/podcini/core/storage/DbReaderTest.kt | 490 ----- .../mdiq/podcini/core/storage/DbTasksTest.kt | 262 --- .../mdiq/podcini/core/storage/DbTestUtils.kt | 65 - .../mdiq/podcini/core/storage/DbWriterTest.kt | 853 -------- .../ExceptFavoriteCleanupAlgorithmTest.kt | 87 - .../storage/FeedItemDuplicateGuesserTest.kt | 70 - .../ItemEnqueuePositionCalculatorTest.kt | 152 -- .../storage/mapper/FeedCursorMapperTest.kt | 96 - .../core/sync/EpisodeActionFilterTest.kt | 186 -- .../podcini/core/sync/GuidValidatorTest.kt | 19 - .../podcini/core/util/CollectionTestUtil.kt | 27 - .../mdiq/podcini/core/util/ConverterTest.kt | 41 - .../core/util/FeedItemPermutorsTest.kt | 222 -- .../podcini/core/util/FeedItemUtilTest.kt | 90 - .../core/util/FilenameGeneratorTest.kt | 93 - .../ac/mdiq/podcini/core/util/URIUtilTest.kt | 23 - .../core/util/gui/ShownotesCleanerTest.kt | 233 -- .../util/syndication/FeedDiscovererTest.kt | 133 -- event/build.gradle | 17 - .../event/DiscoveryDefaultUpdateEvent.kt | 3 - .../podcini/event/EpisodeDownloadEvent.kt | 8 - .../ac/mdiq/podcini/event/FavoritesEvent.kt | 3 - .../ac/mdiq/podcini/event/FeedItemEvent.kt | 17 - .../mdiq/podcini/event/FeedListUpdateEvent.kt | 25 - .../podcini/event/FeedUpdateRunningEvent.kt | 3 - .../ac/mdiq/podcini/event/MessageEvent.kt | 9 - .../ac/mdiq/podcini/event/PlayerErrorEvent.kt | 3 - .../mdiq/podcini/event/PlayerStatusEvent.kt | 3 - .../java/ac/mdiq/podcini/event/QueueEvent.kt | 51 - .../ac/mdiq/podcini/event/StatisticsEvent.kt | 3 - .../ac/mdiq/podcini/event/SyncServiceEvent.kt | 3 - .../podcini/event/UnreadItemsUpdateEvent.kt | 3 - .../event/playback/BufferUpdateEvent.kt | 27 - .../event/playback/PlaybackHistoryEvent.kt | 14 - .../event/playback/PlaybackPositionEvent.kt | 3 - .../event/playback/PlaybackServiceEvent.kt | 8 - .../event/playback/SleepTimerUpdatedEvent.kt | 35 - .../event/playback/SpeedChangedEvent.kt | 3 - .../settings/SkipIntroEndingChangedEvent.kt | 3 - .../event/settings/SpeedPresetChangedEvent.kt | 3 - .../settings/VolumeAdaptionChangedEvent.kt | 5 - .../images/phoneScreenshots/1_drawer.jpg | Bin 133309 -> 77366 bytes .../images/phoneScreenshots/3_setting.jpg | Bin 224608 -> 207971 bytes .../en-US/images/phoneScreenshots/4_home.jpg | Bin 383880 -> 0 bytes .../phoneScreenshots/4_subscriptions.jpg | Bin 0 -> 298601 bytes images/1_drawer.jpg | Bin 133309 -> 77366 bytes images/3_setting.jpg | Bin 224608 -> 207971 bytes images/4_home.jpg | Bin 383880 -> 0 bytes images/4_subscriptions.jpg | Bin 0 -> 298601 bytes model/README.md | 3 - model/build.gradle | 17 - .../model/MediaMetadataRetrieverCompat.kt | 17 - .../podcini/model/download/DownloadError.kt | 44 - .../podcini/model/download/DownloadResult.kt | 81 - .../podcini/model/download/DownloadStatus.kt | 9 - .../podcini/model/download/ProxyConfig.kt | 14 - .../ac/mdiq/podcini/model/feed/Chapter.kt | 36 - .../model/feed/EmbeddedChapterImage.kt | 59 - .../java/ac/mdiq/podcini/model/feed/Feed.kt | 416 ---- .../mdiq/podcini/model/feed/FeedComponent.kt | 47 - .../ac/mdiq/podcini/model/feed/FeedCounter.kt | 20 - .../ac/mdiq/podcini/model/feed/FeedFile.kt | 93 - .../ac/mdiq/podcini/model/feed/FeedFilter.kt | 119 - .../ac/mdiq/podcini/model/feed/FeedFunding.kt | 82 - .../ac/mdiq/podcini/model/feed/FeedItem.kt | 348 --- .../mdiq/podcini/model/feed/FeedItemFilter.kt | 119 - .../ac/mdiq/podcini/model/feed/FeedMedia.kt | 458 ---- .../podcini/model/feed/FeedPreferences.kt | 118 - .../ac/mdiq/podcini/model/feed/SortOrder.kt | 69 - .../podcini/model/feed/SubscriptionsFilter.kt | 97 - .../model/feed/VolumeAdaptionSetting.kt | 26 - .../mdiq/podcini/model/playback/MediaType.kt | 28 - .../mdiq/podcini/model/playback/Playable.kt | 153 -- .../podcini/model/playback/RemoteMedia.kt | 283 --- net/README.md | 3 - net/common/README.md | 3 - net/common/build.gradle | 20 - .../ac/mdiq/podcini/net/common/UrlChecker.kt | 138 -- net/discovery/README.md | 3 - net/discovery/build.gradle | 36 - .../net/discovery/FyydPodcastSearcher.kt | 43 - .../net/discovery/GpodnetPodcastSearcher.kt | 44 - .../net/discovery/ItunesPodcastSearcher.kt | 105 - .../net/discovery/ItunesTopListLoader.kt | 109 - .../discovery/PodcastIndexPodcastSearcher.kt | 112 - net/download/README.md | 3 - net/download/service-interface/README.md | 3 - net/download/service-interface/build.gradle | 27 - .../serviceinterface/DownloadRequest.kt | 213 -- .../DownloadServiceInterface.kt | 58 - .../DownloadServiceInterfaceStub.kt | 19 - .../serviceinterface/DownloadRequestTest.kt | 118 - net/ssl/README.md | 3 - net/ssl/build.gradle | 22 - net/sync/README.md | 3 - net/sync/gpoddernet/README.md | 3 - net/sync/gpoddernet/build.gradle | 27 - .../net/sync/gpoddernet/GpodnetService.kt | 554 ----- net/sync/model/README.md | 3 - net/sync/model/build.gradle | 16 - .../podcini/net/sync/model/EpisodeAction.kt | 233 -- parser/README.md | 3 - parser/feed/README.md | 3 - parser/feed/build.gradle | 25 - .../mdiq/podcini/parser/feed/FeedHandler.kt | 40 - .../podcini/parser/feed/FeedHandlerResult.kt | 8 - .../mdiq/podcini/parser/feed/HandlerState.kt | 81 - .../parser/feed/PodcastIndexChapterParser.kt | 27 - .../mdiq/podcini/parser/feed/SyndHandler.kt | 115 - .../feed/UnsupportedFeedtypeException.kt | 47 - .../podcini/parser/feed/element/AtomText.kt | 36 - .../parser/feed/element/SyndElement.kt | 6 - .../podcini/parser/feed/namespace/Atom.kt | 213 -- .../podcini/parser/feed/namespace/Content.kt | 24 - .../parser/feed/namespace/DublinCore.kt | 32 - .../podcini/parser/feed/namespace/Itunes.kt | 80 - .../podcini/parser/feed/namespace/Media.kt | 125 -- .../parser/feed/namespace/Namespace.kt | 16 - .../parser/feed/namespace/PodcastIndex.kt | 44 - .../podcini/parser/feed/namespace/Rss20.kt | 134 -- .../parser/feed/namespace/SimpleChapters.kt | 50 - .../podcini/parser/feed/util/DateUtils.kt | 149 -- .../parser/feed/util/DurationParser.kt | 42 - .../podcini/parser/feed/util/MimeTypeUtils.kt | 70 - .../parser/feed/util/SyndStringUtils.kt | 11 - .../podcini/parser/feed/util/TypeGetter.kt | 137 -- .../feed/element/element/AtomTextTest.kt | 37 - .../feed/element/namespace/AtomParserTest.kt | 92 - .../element/namespace/FeedParserTestHelper.kt | 32 - .../feed/element/namespace/RssParserTest.kt | 105 - .../parser/feed/element/util/DateUtilsTest.kt | 169 -- .../feed/element/util/DurationParserTest.kt | 43 - parser/media/README.md | 3 - parser/media/build.gradle | 23 - .../podcini/parser/media/id3/ChapterReader.kt | 112 - .../podcini/parser/media/id3/ID3Reader.kt | 222 -- .../parser/media/id3/ID3ReaderException.kt | 7 - .../parser/media/id3/Id3MetadataReader.kt | 32 - .../parser/media/id3/model/FrameHeader.kt | 3 - .../podcini/parser/media/id3/model/Header.kt | 7 - .../parser/media/id3/model/TagHeader.kt | 7 - .../vorbis/VorbisCommentChapterReader.kt | 120 - .../media/vorbis/VorbisCommentHeader.kt | 7 - .../vorbis/VorbisCommentMetadataReader.kt | 25 - .../media/vorbis/VorbisCommentReader.kt | 192 -- .../vorbis/VorbisCommentReaderException.kt | 11 - .../parser/media/id3/ChapterReaderTest.kt | 211 -- .../podcini/parser/media/id3/Id3ReaderTest.kt | 155 -- .../parser/media/id3/MetadataReaderTest.kt | 54 - .../vorbis/VorbisCommentChapterReaderTest.kt | 45 - .../vorbis/VorbisCommentMetadataReaderTest.kt | 28 - playback/README.md | 3 - playback/base/README.md | 3 - playback/base/build.gradle | 20 - .../base/PlaybackServiceMediaPlayer.kt | 393 ---- playback/cast/README.md | 3 - playback/cast/build.gradle | 36 - .../playback/cast/CastStateListener.kt | 11 - .../playback/cast/CastEnabledActivity.kt | 38 - .../ac/mdiq/podcini/playback/cast/CastPsmp.kt | 511 ----- .../playback/cast/CastStateListener.kt | 66 - .../mdiq/podcini/playback/cast/CastUtils.kt | 172 -- .../podcini/playback/cast/MediaInfoCreator.kt | 130 -- storage/README.md | 3 - storage/database/README.md | 3 - storage/database/build.gradle | 21 - .../podcini/storage/database/DBUpgrader.kt | 338 --- .../podcini/storage/database/PodDBAdapter.kt | 1470 ------------- .../database/mapper/ChapterCursorMapper.kt | 31 - .../mapper/DownloadResultCursorMapper.kt | 38 - .../database/mapper/FeedCursorMapper.kt | 65 - .../database/mapper/FeedItemCursorMapper.kt | 46 - .../database/mapper/FeedItemFilterQuery.kt | 72 - .../database/mapper/FeedItemSortQuery.kt | 23 - .../database/mapper/FeedMediaCursorMapper.kt | 58 - .../mapper/FeedPreferencesCursorMapper.kt | 78 - storage/preferences/README.md | 3 - storage/preferences/build.gradle | 34 - .../storage/preferences/UserPreferences.kt | 889 -------- ui/README.md | 3 - ui/app-start-intent/README.md | 3 - ui/app-start-intent/build.gradle | 14 - .../ui/appstartintent/MainActivityStarter.kt | 85 - .../PlaybackSpeedActivityStarter.kt | 32 - .../VideoPlayerActivityStarter.kt | 32 - ui/common/README.md | 3 - ui/common/build.gradle | 19 - .../podcini/ui/common/CircularProgressBar.kt | 98 - .../ui/common/PlaybackSpeedIndicatorView.kt | 105 - .../mdiq/podcini/ui/common/SquareImageView.kt | 53 - .../ac/mdiq/podcini/ui/common/ThemeUtils.kt | 29 - .../podcini/ui/common/TriangleLabelView.kt | 198 -- .../podcini/ui/common/WrappingGridView.kt | 27 - ui/common/src/main/res/values/attrs.xml | 18 - ui/echo/README.md | 3 - ui/echo/build.gradle | 29 - ui/echo/src/main/AndroidManifest.xml | 19 - .../ac/mdiq/podcini/ui/echo/EchoActivity.kt | 426 ---- .../ac/mdiq/podcini/ui/echo/EchoProgress.kt | 57 - .../podcini/ui/echo/screens/BaseScreen.kt | 73 - .../podcini/ui/echo/screens/BubbleScreen.kt | 34 - .../ui/echo/screens/FinalShareScreen.kt | 106 - .../ui/echo/screens/RotatingSquaresScreen.kt | 34 - .../podcini/ui/echo/screens/StripesScreen.kt | 36 - .../podcini/ui/echo/screens/WaveformScreen.kt | 38 - .../podcini/ui/echo/screens/WavesScreen.kt | 36 - .../src/main/res/values-de/echo-strings.xml | 46 - .../src/main/res/values-es/echo-strings.xml | 46 - .../src/main/res/values-fr/echo-strings.xml | 48 - .../src/main/res/values-it/echo-strings.xml | 46 - ui/glide/README.md | 3 - ui/glide/build.gradle | 25 - .../ac/mdiq/podcini/ui/glide/ApGlideModule.kt | 49 - .../podcini/ui/glide/ApOkHttpUrlLoader.kt | 93 - .../podcini/ui/glide/AudioCoverFetcher.kt | 48 - .../ui/glide/ChapterImageModelLoader.kt | 90 - .../ui/glide/FastBlurTransformation.kt | 317 --- .../GenerativePlaceholderImageModelLoader.kt | 122 -- .../ui/glide/MetadataRetrieverLoader.kt | 37 - .../podcini/ui/glide/NoHttpStringLoader.kt | 34 - ui/i18n/README.md | 3 - ui/i18n/build.gradle | 19 - ui/i18n/lint.xml | 10 - ui/i18n/src/main/res/values/strings.xml | 834 ------- ui/png-icons/README.md | 3 - ui/png-icons/build.gradle | 19 - ui/statistics/README.md | 3 - ui/statistics/build.gradle | 35 - .../podcini/ui/statistics/PieChartView.kt | 132 -- .../ui/statistics/StatisticsFragment.kt | 130 -- .../ui/statistics/StatisticsListAdapter.kt | 98 - .../downloads/DownloadStatisticsFragment.kt | 87 - .../DownloadStatisticsListAdapter.kt | 48 - .../feed/FeedStatisticsDialogFragment.kt | 46 - .../statistics/feed/FeedStatisticsFragment.kt | 96 - .../PlaybackStatisticsListAdapter.kt | 65 - .../subscriptions/StatisticsFilterDialog.kt | 140 -- .../SubscriptionStatisticsFragment.kt | 130 -- .../ui/statistics/years/BarChartView.kt | 126 -- .../years/YearStatisticsListAdapter.kt | 100 - .../years/YearsStatisticsFragment.kt | 95 - 1345 files changed, 57600 insertions(+), 61024 deletions(-) delete mode 100644 7_podcast.jpg rename {net/ssl => app}/src/free/java/ac/mdiq/podcini/net/ssl/SslProviderInstaller.kt (100%) rename {playback/cast => app}/src/free/java/ac/mdiq/podcini/playback/cast/CastEnabledActivity.kt (100%) rename {playback/cast => app}/src/free/java/ac/mdiq/podcini/playback/cast/CastPsmp.kt (100%) create mode 100644 app/src/free/java/ac/mdiq/podcini/playback/cast/CastStateListener.kt create mode 100644 app/src/free/java/ac/mdiq/podcini/service/playback/WearMediaSession.kt rename {core => app}/src/main/assets/html-export-favorites-item-template.html (100%) rename {core => app}/src/main/assets/html-export-feed-template.html (100%) rename {core => app}/src/main/assets/html-export-template.html (100%) rename {core => app}/src/main/assets/shownotes-style.css (100%) delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/BugReportActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/MainActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/OnlineFeedViewActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/OpmlImportActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/PlaybackSpeedDialogActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/PreferenceActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/SelectSubscriptionActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/SplashActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/VideoplayerActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/activity/WidgetConfigActivity.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/ChaptersListAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/CoverLoader.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/DataFolderAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/DownloadLogAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/EpisodeItemListAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/FeedDiscoverAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/FeedItemlistDescriptionAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/HorizontalFeedListAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/HorizontalItemListAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/NavListAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/QueueRecyclerAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/SelectableAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/SimpleChipAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/SimpleIconListAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/SubscriptionsRecyclerAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/CancelDownloadActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DeleteActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DownloadActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/ItemActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/MarkAsPlayedActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PauseActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayLocalActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/StreamActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/VisitWebsiteActionButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/adapter/itunes/ItunesAdapter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/asynctask/DocumentFileExportWorker.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/asynctask/ExportWorker.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/config/ApplicationCallbacksImpl.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/AllEpisodesFilterDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/AuthenticationDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/ChooseDataFolderDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/DownloadLogDetailsDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/DrawerPreferencesDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/EditUrlSettingsDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/EpisodeFilterDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/FeedItemFilterDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/FeedPreferenceSkipDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/FeedSortDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/ItemFilterDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/ItemSortDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/MediaPlayerErrorDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/PlaybackControlsDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/ProxyDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/RemoveFeedDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/RenameItemDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/ShareDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/SkipPreferenceDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/SleepTimerDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/StreamingConfirmationDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/SubscriptionsFilterDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/SwipeActionsDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/TagSettingsDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/TimeRangeDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/dialog/VariableSpeedDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/error/CrashReportWriter.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/error/RxJavaErrorHandlerSetup.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/ChapterMerger.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/FeedEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/FeedItemFilterGroup.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/FeedUrlNotFoundException.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/LocalFeedUpdater.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/SubscriptionsFilterGroup.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/FeedHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/FeedHandlerResult.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/HandlerState.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/PodcastIndexChapterParser.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/SyndHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/UnsupportedFeedtypeException.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/element/AtomText.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/element/SyndElement.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/id3/ChapterReader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/id3/ID3Reader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/id3/ID3ReaderException.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/id3/Id3MetadataReader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/id3/model/FrameHeader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/id3/model/Header.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/id3/model/TagHeader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/vorbis/VorbisCommentChapterReader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/vorbis/VorbisCommentHeader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/vorbis/VorbisCommentMetadataReader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/vorbis/VorbisCommentReader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/media/vorbis/VorbisCommentReaderException.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Atom.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Content.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/DublinCore.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Itunes.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Media.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Namespace.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/PodcastIndex.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/Rss20.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/namespace/SimpleChapters.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/util/DateUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/util/DurationParser.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/util/MimeTypeUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/util/SyndStringUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/parser/util/TypeGetter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/util/ImageResourceUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/feed/util/PlaybackSpeedUtils.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/AddFeedFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/AllEpisodesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/AudioPlayerFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/ChaptersFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/CompletedDownloadsFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/CoverFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/DiscoveryFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/DownloadLogFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/EpisodesListFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/ExternalPlayerFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/FeedInfoFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/FeedItemlistFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/FeedSettingsFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/InboxFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/ItemDescriptionFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/ItemFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/ItemPagerFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/NavDrawerFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/OnlineSearchFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/PlaybackHistoryFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/QueueFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/QuickFeedDiscoveryFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/SearchFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/SubscriptionFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/TransitionEffect.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/actions/EpisodeMultiSelectActionHandler.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/actions/FeedMultiSelectActionHandler.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/AutoDownloadPreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/DownloadsPreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/ImportExportPreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/MainPreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/NotificationPreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/PlaybackPreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/SwipePreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/UserInterfacePreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/about/AboutFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/about/ContributorsPagerFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/about/DevelopersFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/about/LicensesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/about/SpecialThanksFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/about/TranslatorsFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/dialog/PreferenceListDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/dialog/PreferenceSwitchDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/synchronization/GpodderAuthenticationFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/synchronization/NextcloudAuthenticationFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/preferences/synchronization/SynchronizationPreferencesFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/AddToQueueSwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/DeleteSwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/MarkFavoriteSwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/RemoveFromHistorySwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/RemoveFromInboxSwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/RemoveFromQueueSwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/ShowFirstSwipeDialogAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/StartDownloadSwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/SwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/SwipeActions.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/fragment/swipeactions/TogglePlaybackStateSwipeAction.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/menuhandler/FeedItemMenuHandler.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/menuhandler/FeedMenuHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/common/UrlChecker.kt rename {net/discovery => app}/src/main/java/ac/mdiq/podcini/net/discovery/CombinedSearcher.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/net/discovery/FyydPodcastSearcher.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/discovery/GpodnetPodcastSearcher.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/discovery/ItunesPodcastSearcher.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/discovery/ItunesTopListLoader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/discovery/PodcastIndexPodcastSearcher.kt rename {net/discovery => app}/src/main/java/ac/mdiq/podcini/net/discovery/PodcastSearchResult.kt (100%) rename {net/discovery => app}/src/main/java/ac/mdiq/podcini/net/discovery/PodcastSearcher.kt (100%) rename {net/discovery => app}/src/main/java/ac/mdiq/podcini/net/discovery/PodcastSearcherRegistry.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/net/download/ConnectionStateMonitor.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/download/FeedUpdateManager.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/download/MediaSizeLoader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/download/NetworkConnectionChangeHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadRequest.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadServiceInterface.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadServiceInterfaceStub.kt rename {net/ssl => app}/src/main/java/ac/mdiq/podcini/net/ssl/AntennaPodSslSocketFactory.kt (100%) rename {net/ssl => app}/src/main/java/ac/mdiq/podcini/net/ssl/BackportCaCerts.kt (100%) rename {net/ssl => app}/src/main/java/ac/mdiq/podcini/net/ssl/BackportTrustManager.kt (100%) rename {net/ssl => app}/src/main/java/ac/mdiq/podcini/net/ssl/CompositeX509TrustManager.kt (100%) rename {net/ssl => app}/src/main/java/ac/mdiq/podcini/net/ssl/SslClientSetup.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/EpisodeActionFilter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/GuidValidator.kt rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/HostnameParser.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/LockingAsyncExecutor.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/SyncService.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/SynchronizationCredentials.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/SynchronizationProviderViewData.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/SynchronizationSettings.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/GpodnetService.kt rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/GpodnetServiceAuthenticationException.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/GpodnetServiceBadStatusCodeException.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/GpodnetServiceException.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/mapper/ResponseMapper.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/model/GpodnetDevice.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/model/GpodnetEpisodeActionPostResponse.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/model/GpodnetPodcast.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/model/GpodnetUploadChangesResponse.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/model/EpisodeAction.kt rename {net/sync/model => app}/src/main/java/ac/mdiq/podcini/net/sync/model/EpisodeActionChanges.kt (100%) rename {net/sync/model => app}/src/main/java/ac/mdiq/podcini/net/sync/model/ISyncService.kt (100%) rename {net/sync/model => app}/src/main/java/ac/mdiq/podcini/net/sync/model/SubscriptionChanges.kt (100%) rename {net/sync/model => app}/src/main/java/ac/mdiq/podcini/net/sync/model/SyncServiceException.kt (100%) rename {net/sync/model => app}/src/main/java/ac/mdiq/podcini/net/sync/model/UploadChangesResponse.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/nextcloud/NextcloudLoginFlow.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/nextcloud/NextcloudSyncService.kt (100%) rename {net/sync/gpoddernet => app}/src/main/java/ac/mdiq/podcini/net/sync/nextcloud/NextcloudSynchronizationServiceException.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/queue/SynchronizationQueueSink.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/net/sync/queue/SynchronizationQueueStorage.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/PlayableUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/PlaybackController.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/PlaybackServiceStarter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/base/PlaybackServiceMediaPlayer.kt rename {playback/base => app}/src/main/java/ac/mdiq/podcini/playback/base/PlayerStatus.kt (100%) rename {playback/base => app}/src/main/java/ac/mdiq/podcini/playback/base/RewindAfterPauseUtils.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/event/BufferUpdateEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/event/PlaybackHistoryEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/event/PlaybackPositionEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/event/PlaybackServiceEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/event/SleepTimerUpdatedEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/playback/event/SpeedChangedEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/preferences/PlaybackPreferences.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/preferences/SleepTimerPreferences.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/preferences/ThemeSwitcher.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/preferences/UsageStatistics.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/preferences/UserPreferences.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/receiver/FeedUpdateReceiver.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/receiver/MediaButtonReceiver.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/receiver/PlayerWidget.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/BasicAuthorizationInterceptor.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/FeedUpdateWorker.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/QuickSettingsTileService.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/UserAgentInterceptor.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/DefaultDownloaderFactory.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/DownloadRequestCreator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/DownloadServiceInterfaceImpl.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/Downloader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/DownloaderFactory.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/EpisodeDownloadWorker.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/HttpCredentialEncoder.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/HttpDownloader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/NewEpisodesNotification.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/PodciniHttpClient.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/handler/FeedParserTask.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/handler/FeedSyncTask.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/download/handler/MediaDownloadedHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/ExoPlayerWrapper.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/LocalPSMP.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackService.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceInterface.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceNotificationBuilder.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceStateManager.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackServiceTaskManager.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/PlaybackVolumeUpdater.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/service/playback/ShakeListener.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/spa/SPAUtil.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/APCleanupAlgorithm.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/APNullCleanupAlgorithm.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/APQueueCleanupAlgorithm.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/AutomaticDownloadAlgorithm.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/DBReader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/DBTasks.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/DBWriter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/DatabaseTransporter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/EpisodeCleanupAlgorithm.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/EpisodeCleanupAlgorithmFactory.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithm.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/FeedItemDuplicateGuesser.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/FeedSearcher.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/ItemEnqueuePositionCalculator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/NavDrawerData.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/StatisticsItem.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/asynctask/DocumentFileExportWorker.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/asynctask/ExportWorker.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/backup/OpmlBackupAgent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/DBUpgrader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/PodDBAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/ChapterCursorMapper.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/DownloadResultCursorMapper.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedCursorMapper.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedItemCursorMapper.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedItemFilterQuery.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedItemSortQuery.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedMediaCursorMapper.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedPreferencesCursorMapper.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/CommonSymbols.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/ExportWriter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/favorites/FavoritesWriter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/html/HtmlWriter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/opml/OpmlElement.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/opml/OpmlReader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/opml/OpmlSymbols.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/export/opml/OpmlWriter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/MediaMetadataRetrieverCompat.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/download/DownloadError.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/download/DownloadResult.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/download/DownloadStatus.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/download/ProxyConfig.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/Chapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/EmbeddedChapterImage.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/Feed.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedComponent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedCounter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedFile.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedFilter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedFunding.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedItem.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedItemFilter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedMedia.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/FeedPreferences.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/SortOrder.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/SubscriptionsFilter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/feed/VolumeAdaptionSetting.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/playback/MediaType.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/playback/Playable.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/storage/model/playback/RemoteMedia.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/BugReportActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/MainActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/OnlineFeedViewActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/OpmlImportActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/PlaybackSpeedDialogActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/PreferenceActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/SelectSubscriptionActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/SplashActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/VideoplayerActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/activity/WidgetConfigActivity.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/ChaptersListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/CoverLoader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/DataFolderAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/DownloadLogAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/EpisodeItemListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/FeedDiscoverAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/FeedItemlistDescriptionAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/HorizontalFeedListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/HorizontalItemListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/NavListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/QueueRecyclerAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/SelectableAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/SimpleChipAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/SimpleIconListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/SubscriptionsRecyclerAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/CancelDownloadActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/DeleteActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/DownloadActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/ItemActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/MarkAsPlayedActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/PauseActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/PlayActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/PlayLocalActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/StreamActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/actionbutton/VisitWebsiteActionButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/adapter/itunes/ItunesAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/appstartintent/MainActivityStarter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/appstartintent/PlaybackSpeedActivityStarter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/appstartintent/VideoPlayerActivityStarter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/common/CircularProgressBar.kt rename {ui/common => app}/src/main/java/ac/mdiq/podcini/ui/common/PagedToolbarFragment.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/common/PlaybackSpeedIndicatorView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/common/SquareImageView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/common/ThemeUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/common/TriangleLabelView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/common/WrappingGridView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/AllEpisodesFilterDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/AuthenticationDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/ChooseDataFolderDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/ConfirmationDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/DownloadLogDetailsDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/DrawerPreferencesDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/EditUrlSettingsDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/EpisodeFilterDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/FeedItemFilterDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/FeedPreferenceSkipDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/FeedSortDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/ItemFilterDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/ItemSortDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/MediaPlayerErrorDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/PlaybackControlsDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/ProxyDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/RemoveFeedDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/RenameItemDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/ShareDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/SkipPreferenceDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/SleepTimerDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/StreamingConfirmationDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/SubscriptionsFilterDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/SwipeActionsDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/TagSettingsDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/TimeRangeDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/dialog/VariableSpeedDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/AddFeedFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/AllEpisodesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/AudioPlayerFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/ChaptersFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/CompletedDownloadsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/CoverFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/DiscoveryFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/DownloadLogFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/EpisodesListFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/ExternalPlayerFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedInfoFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedItemlistFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/FeedSettingsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/InboxFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/ItemDescriptionFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/ItemFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/ItemPagerFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/NavDrawerFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/OnlineSearchFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/PlaybackHistoryFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/QueueFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/QuickFeedDiscoveryFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/SearchFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/SubscriptionFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/TransitionEffect.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/actions/EpisodeMultiSelectActionHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/actions/FeedMultiSelectActionHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/AutoDownloadPreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/DownloadsPreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/ImportExportPreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/MainPreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/NotificationPreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/PlaybackPreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/SwipePreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/UserInterfacePreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/about/AboutFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/about/ContributorsPagerFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/about/DevelopersFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/about/LicensesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/about/SpecialThanksFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/about/TranslatorsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/dialog/PreferenceListDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/dialog/PreferenceSwitchDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/synchronization/GpodderAuthenticationFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/synchronization/NextcloudAuthenticationFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/preferences/synchronization/SynchronizationPreferencesFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/AddToQueueSwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/DeleteSwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/MarkFavoriteSwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/RemoveFromHistorySwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/RemoveFromInboxSwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/RemoveFromQueueSwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/ShowFirstSwipeDialogAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/StartDownloadSwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/SwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/SwipeActions.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/fragment/swipeactions/TogglePlaybackStateSwipeAction.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/ApGlideModule.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/ApOkHttpUrlLoader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/AudioCoverFetcher.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/ChapterImageModelLoader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/FastBlurTransformation.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/GenerativePlaceholderImageModelLoader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/MetadataRetrieverLoader.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/glide/NoHttpStringLoader.kt rename {ui/glide => app}/src/main/java/ac/mdiq/podcini/ui/glide/ResizingOkHttpStreamFetcher.kt (100%) create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/gui/MoreContentListFooterUtil.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/gui/NotificationUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/gui/PictureInPictureUtil.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/gui/ShownotesCleaner.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/HomeFragment.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/HomeSection.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/HomeSectionsSettingsDialog.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/sections/AllowNotificationsSection.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/sections/DownloadsSection.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/sections/EchoSection.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/sections/EpisodesSurpriseSection.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/sections/InboxSection.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/sections/QueueSection.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/ui/home/sections/SubscriptionsSection.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/menuhandler/FeedItemMenuHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/menuhandler/FeedMenuHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/menuhandler/MenuItemUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/PieChartView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/downloads/DownloadStatisticsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/downloads/DownloadStatisticsListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/feed/FeedStatisticsDialogFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/feed/FeedStatisticsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/PlaybackStatisticsListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/StatisticsFilterDialog.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/SubscriptionStatisticsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/years/BarChartView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearStatisticsListAdapter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearsStatisticsFragment.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/AspectRatioVideoView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/ChapterSeekBar.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/EmptyViewHandler.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/EpisodeItemListRecyclerView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/ItemOffsetDecoration.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/LiftOnScrollListener.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/LocalDeleteModal.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/LockableBottomSheetBehavior.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/NestedScrollableHost.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/NoRelayoutTextView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/PlayButton.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/PlaybackSpeedSeekBar.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/ShownotesWebView.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/SimpleAdapterDataObserver.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/ToolbarIconTintManager.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/DownloadLogItemViewHolder.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/EpisodeItemViewHolder.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/view/viewholder/HorizontalItemViewHolder.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdater.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/ui/widget/WidgetUpdaterWorker.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/ChapterUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/Converter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/DateFormatter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/DownloadErrorLabel.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/FastDocumentFile.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/FeedItemPermutors.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/FeedItemUtil.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/FeedUtil.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/FileNameGenerator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/IntentUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/InvalidFeedException.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/LongList.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/NetworkUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/Permutor.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/PlaybackStatus.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/PowerUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/SPAUtil.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/ShareUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/StorageUtils.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/TimeSpeedConverter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/URIUtil.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/comparator/ChapterStartTimeComparator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/comparator/DownloadResultComparator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/comparator/FeedItemPubdateComparator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/comparator/PlaybackCompletionDateComparator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/config/ApplicationCallbacks.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/config/ApplicationCallbacksImpl.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/config/ClientConfig.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/config/ClientConfigurator.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/error/CrashReportWriter.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/error/RxJavaErrorHandlerSetup.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/DiscoveryDefaultUpdateEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/DownloadLogEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/EpisodeDownloadEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/FavoritesEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/FeedItemEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/FeedListUpdateEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/FeedUpdateRunningEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/MessageEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/PlayerErrorEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/PlayerStatusEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/QueueEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/StatisticsEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/SyncServiceEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/UnreadItemsUpdateEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/settings/SkipIntroEndingChangedEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/settings/SpeedPresetChangedEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/event/settings/VolumeAdaptionChangedEvent.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/syndication/FeedDiscoverer.kt create mode 100644 app/src/main/java/ac/mdiq/podcini/util/syndication/HtmlToPlainText.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/AspectRatioVideoView.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/ChapterSeekBar.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/EmptyViewHandler.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/EpisodeItemListRecyclerView.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/ItemOffsetDecoration.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/LiftOnScrollListener.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/LocalDeleteModal.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/LockableBottomSheetBehavior.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/NestedScrollableHost.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/NoRelayoutTextView.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/PlayButton.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/PlaybackSpeedSeekBar.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/ShownotesWebView.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/SimpleAdapterDataObserver.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/ToolbarIconTintManager.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/viewholder/DownloadLogItemViewHolder.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/viewholder/EpisodeItemViewHolder.kt delete mode 100644 app/src/main/java/ac/mdiq/podcini/view/viewholder/HorizontalItemViewHolder.kt rename {core => app}/src/main/res/color/button_bg_selector.xml (100%) rename {core => app}/src/main/res/drawable-anydpi-v26/ic_feed_shortcut.xml (100%) rename {core => app}/src/main/res/drawable-anydpi-v26/ic_playlist_shortcut.xml (100%) rename {core => app}/src/main/res/drawable-anydpi-v26/ic_refresh_shortcut.xml (100%) rename {core => app}/src/main/res/drawable-anydpi-v26/ic_subscriptions_shortcut.xml (100%) rename {core => app}/src/main/res/drawable-hdpi/ic_notification.png (100%) rename {core => app}/src/main/res/drawable-hdpi/ic_notification_new.png (100%) rename {core => app}/src/main/res/drawable-hdpi/ic_widget_preview.png (100%) rename {core => app}/src/main/res/drawable-mdpi/ic_notification.png (100%) rename {core => app}/src/main/res/drawable-mdpi/ic_notification_new.png (100%) rename {ui/echo => app}/src/main/res/drawable-nodpi/echo.png (100%) rename {core => app}/src/main/res/drawable-nodpi/gpodder_icon.png (100%) rename {core => app}/src/main/res/drawable-nodpi/launcher_animate_bg.png (100%) rename {core => app}/src/main/res/drawable-nodpi/launcher_animate_wave1.png (100%) rename {core => app}/src/main/res/drawable-nodpi/launcher_animate_wave2.png (100%) rename {ui/echo => app}/src/main/res/drawable-nodpi/logo_monochrome.png (100%) rename {core => app}/src/main/res/drawable-nodpi/nextcloud_logo.png (100%) rename {core => app}/src/main/res/drawable-nodpi/teaser.png (100%) rename {core => app}/src/main/res/drawable-xhdpi/ic_notification.png (100%) rename {core => app}/src/main/res/drawable-xhdpi/ic_notification_new.png (100%) rename {core => app}/src/main/res/drawable-xxhdpi/ic_notification.png (100%) rename {core => app}/src/main/res/drawable-xxhdpi/ic_notification_new.png (100%) rename {core => app}/src/main/res/drawable-xxxhdpi/ic_notification.png (100%) rename {core => app}/src/main/res/drawable-xxxhdpi/ic_notification_new.png (100%) rename {core => app}/src/main/res/drawable/bg_blue_gradient.xml (100%) rename {core => app}/src/main/res/drawable/bg_circle.xml (100%) rename {core => app}/src/main/res/drawable/bg_gradient.xml (100%) rename {core => app}/src/main/res/drawable/bg_pill.xml (100%) rename {core => app}/src/main/res/drawable/bg_pill_translucent.xml (100%) rename {core => app}/src/main/res/drawable/bg_rounded_corners.xml (100%) rename {core => app}/src/main/res/drawable/drawer_item_background.xml (100%) rename {core => app}/src/main/res/drawable/ic_add.xml (100%) rename {core => app}/src/main/res/drawable/ic_appearance.xml (100%) rename {core => app}/src/main/res/drawable/ic_arrow_down.xml (100%) rename {core => app}/src/main/res/drawable/ic_arrow_right_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_bug.xml (100%) rename {core => app}/src/main/res/drawable/ic_cancel.xml (100%) rename {core => app}/src/main/res/drawable/ic_chapter_next.xml (100%) rename {core => app}/src/main/res/drawable/ic_chapter_prev.xml (100%) rename {core => app}/src/main/res/drawable/ic_chart_box.xml (100%) rename {core => app}/src/main/res/drawable/ic_chat.xml (100%) rename {core => app}/src/main/res/drawable/ic_check.xml (100%) rename {core => app}/src/main/res/drawable/ic_checkbox_background.xml (100%) rename {core => app}/src/main/res/drawable/ic_close_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_cloud.xml (100%) rename {core => app}/src/main/res/drawable/ic_contribute.xml (100%) rename {core => app}/src/main/res/drawable/ic_curved_arrow.xml (100%) rename {core => app}/src/main/res/drawable/ic_delete.xml (100%) rename {core => app}/src/main/res/drawable/ic_delete_auto.xml (100%) rename {core => app}/src/main/res/drawable/ic_disc_alert.xml (100%) rename {core => app}/src/main/res/drawable/ic_download.xml (100%) rename {core => app}/src/main/res/drawable/ic_download_black.xml (100%) rename {core => app}/src/main/res/drawable/ic_drag_darktheme.xml (100%) rename {core => app}/src/main/res/drawable/ic_drag_lighttheme.xml (100%) rename {core => app}/src/main/res/drawable/ic_error.xml (100%) rename {core => app}/src/main/res/drawable/ic_fab_edit.xml (100%) rename {core => app}/src/main/res/drawable/ic_fast_forward.xml (100%) rename {core => app}/src/main/res/drawable/ic_fast_forward_video_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_fast_rewind.xml (100%) rename {core => app}/src/main/res/drawable/ic_fast_rewind_video_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_feed.xml (100%) rename {core => app}/src/main/res/drawable/ic_feed_black.xml (100%) rename {core => app}/src/main/res/drawable/ic_feed_shortcut.xml (100%) rename {core => app}/src/main/res/drawable/ic_filter.xml (100%) rename {core => app}/src/main/res/drawable/ic_filter_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_folder.xml (100%) rename {core => app}/src/main/res/drawable/ic_history.xml (100%) rename {core => app}/src/main/res/drawable/ic_history_remove.xml (100%) rename {core => app}/src/main/res/drawable/ic_home.xml (100%) rename {core => app}/src/main/res/drawable/ic_inbox.xml (100%) rename {core => app}/src/main/res/drawable/ic_info.xml (100%) rename {core => app}/src/main/res/drawable/ic_info_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_key.xml (100%) rename {core => app}/src/main/res/drawable/ic_load_more.xml (100%) rename {core => app}/src/main/res/drawable/ic_mark_played.xml (100%) rename {core => app}/src/main/res/drawable/ic_mark_unplayed.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_fast_forward.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_fast_rewind.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_next_chapter.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_pause.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_play.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_playback_speed.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_skip.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_stream.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_sync.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_notification_sync_error.xml (100%) rename {core => app}/src/main/res/drawable/ic_notifications.xml (100%) rename {core => app}/src/main/res/drawable/ic_paperclip.xml (100%) rename {core => app}/src/main/res/drawable/ic_pause.xml (100%) rename {core => app}/src/main/res/drawable/ic_pause_video_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_play_24dp.xml (100%) rename {core => app}/src/main/res/drawable/ic_play_48dp.xml (100%) rename {core => app}/src/main/res/drawable/ic_play_video_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_playback_speed.xml (100%) rename {core => app}/src/main/res/drawable/ic_playlist_play.xml (100%) rename {core => app}/src/main/res/drawable/ic_playlist_play_black.xml (100%) rename {core => app}/src/main/res/drawable/ic_playlist_remove.xml (100%) rename {core => app}/src/main/res/drawable/ic_playlist_shortcut.xml (100%) rename {core => app}/src/main/res/drawable/ic_questionmark.xml (100%) rename {core => app}/src/main/res/drawable/ic_refresh.xml (100%) rename {core => app}/src/main/res/drawable/ic_refresh_black.xml (100%) rename {core => app}/src/main/res/drawable/ic_refresh_shortcut.xml (100%) rename {core => app}/src/main/res/drawable/ic_replay.xml (100%) rename {core => app}/src/main/res/drawable/ic_rounded_corner_left.xml (100%) rename {core => app}/src/main/res/drawable/ic_rounded_corner_right.xml (100%) rename {core => app}/src/main/res/drawable/ic_search.xml (100%) rename {core => app}/src/main/res/drawable/ic_select_all.xml (100%) rename {core => app}/src/main/res/drawable/ic_select_none.xml (100%) rename {core => app}/src/main/res/drawable/ic_settings.xml (100%) rename {core => app}/src/main/res/drawable/ic_settings_white.xml (100%) rename {core => app}/src/main/res/drawable/ic_share.xml (100%) rename {core => app}/src/main/res/drawable/ic_shortcut_background.xml (100%) rename {core => app}/src/main/res/drawable/ic_shuffle.xml (100%) rename {core => app}/src/main/res/drawable/ic_skip_24dp.xml (100%) rename {core => app}/src/main/res/drawable/ic_skip_48dp.xml (100%) rename {core => app}/src/main/res/drawable/ic_sleep.xml (100%) rename {core => app}/src/main/res/drawable/ic_sleep_off.xml (100%) rename {core => app}/src/main/res/drawable/ic_star.xml (100%) rename {core => app}/src/main/res/drawable/ic_star_border.xml (100%) rename {core => app}/src/main/res/drawable/ic_storage.xml (100%) rename {core => app}/src/main/res/drawable/ic_stream.xml (100%) rename {core => app}/src/main/res/drawable/ic_subscriptions.xml (100%) rename {core => app}/src/main/res/drawable/ic_subscriptions_black.xml (100%) rename {core => app}/src/main/res/drawable/ic_subscriptions_shortcut.xml (100%) rename {core => app}/src/main/res/drawable/ic_tag.xml (100%) rename {core => app}/src/main/res/drawable/ic_videocam.xml (100%) rename {core => app}/src/main/res/drawable/ic_volume_adaption.xml (100%) rename {core => app}/src/main/res/drawable/ic_web.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_widget_fast_forward.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_widget_fast_rewind.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_widget_pause.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_widget_play.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_widget_playback_speed.xml (100%) rename {ui/png-icons => app}/src/main/res/drawable/ic_widget_skip.xml (100%) rename {core => app}/src/main/res/drawable/launcher_animate.xml (100%) rename {core => app}/src/main/res/drawable/progress_bar_horizontal_dark.xml (100%) rename {core => app}/src/main/res/drawable/progress_bar_horizontal_light.xml (100%) rename {core => app}/src/main/res/drawable/scrollbar_thumb_dark.xml (100%) rename {core => app}/src/main/res/drawable/scrollbar_thumb_default.xml (100%) rename {core => app}/src/main/res/drawable/scrollbar_thumb_light.xml (100%) rename {core => app}/src/main/res/drawable/scrollbar_thumb_pressed_dark.xml (100%) rename {core => app}/src/main/res/drawable/scrollbar_thumb_pressed_light.xml (100%) rename {core => app}/src/main/res/drawable/scrollbar_track.xml (100%) rename {ui/echo => app}/src/main/res/font/sarabun_regular.ttf (100%) rename {ui/echo => app}/src/main/res/font/sarabun_semi_bold.ttf (100%) rename {ui/echo => app}/src/main/res/layout/echo_activity.xml (100%) rename {ui/statistics => app}/src/main/res/layout/feed_statistics.xml (100%) rename {ui/statistics => app}/src/main/res/layout/feed_statistics_dialog.xml (100%) delete mode 100644 app/src/main/res/layout/home_fragment.xml delete mode 100644 app/src/main/res/layout/home_section.xml delete mode 100644 app/src/main/res/layout/home_section_echo.xml delete mode 100644 app/src/main/res/layout/home_section_notification.xml rename {core => app}/src/main/res/layout/more_content_list_footer.xml (100%) rename {ui/common => app}/src/main/res/layout/pager_fragment.xml (100%) rename {core => app}/src/main/res/layout/player_widget.xml (100%) rename {core => app}/src/main/res/layout/popup_bubble_view.xml (100%) rename {core => app}/src/main/res/layout/preference_material_switch.xml (100%) rename {ui/statistics => app}/src/main/res/layout/statistics_filter_dialog.xml (100%) rename {ui/statistics => app}/src/main/res/layout/statistics_fragment.xml (100%) rename {ui/statistics => app}/src/main/res/layout/statistics_listitem.xml (100%) rename {ui/statistics => app}/src/main/res/layout/statistics_listitem_barchart.xml (100%) rename {ui/statistics => app}/src/main/res/layout/statistics_listitem_total.xml (100%) rename {ui/statistics => app}/src/main/res/layout/statistics_year_listitem.xml (100%) rename {ui/statistics => app}/src/main/res/menu/statistics.xml (100%) rename {core => app}/src/main/res/mipmap-anydpi-v26/ic_launcher.xml (100%) rename {core => app}/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {core => app}/src/main/res/mipmap-hdpi/ic_launcher_background.png (100%) rename {core => app}/src/main/res/mipmap-hdpi/ic_launcher_foreground.png (100%) rename {core => app}/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {core => app}/src/main/res/mipmap-mdpi/ic_launcher_background.png (100%) rename {core => app}/src/main/res/mipmap-mdpi/ic_launcher_foreground.png (100%) rename {core => app}/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {core => app}/src/main/res/mipmap-xhdpi/ic_launcher_background.png (100%) rename {core => app}/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png (100%) rename {core => app}/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {core => app}/src/main/res/mipmap-xxhdpi/ic_launcher_background.png (100%) rename {core => app}/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png (100%) rename {core => app}/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {core => app}/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png (100%) rename {core => app}/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png (100%) rename {core => app}/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png (100%) rename {ui/i18n => app}/src/main/res/values-ar/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-ast/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-br/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-ca/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-cs/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-da/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-de/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-es/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-et/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-eu/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-fa/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-fi/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-fr/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-gl/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-hu/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-in/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-it/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-iw/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-ja/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-ko/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-lt/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-nb/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-nl/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-pl/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-pt-rBR/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-pt/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-ro/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-ru/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-sk/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-sl/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-sv/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-tr/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-uk/strings.xml (100%) rename {core => app}/src/main/res/values-v23/styles.xml (100%) rename {core => app}/src/main/res/values-v27/styles.xml (100%) rename {ui/i18n => app}/src/main/res/values-zh-rCN/strings.xml (100%) rename {ui/i18n => app}/src/main/res/values-zh-rTW/strings.xml (100%) create mode 100644 app/src/main/res/values/arrays.xml rename {core => app}/src/main/res/values/colors.xml (100%) rename {ui/echo => app}/src/main/res/values/echo-strings.xml (100%) rename {core => app}/src/main/res/values/ids.xml (100%) rename {core => app}/src/main/res/values/keycodes.xml (100%) rename {ui/app-start-intent => app}/src/main/res/values/pending_intent.xml (100%) create mode 100644 app/src/main/res/values/strings.xml rename {ui/common => app}/src/main/res/values/styleable.xml (100%) rename {core => app}/src/main/res/values/styles.xml (100%) rename {net/ssl => app}/src/play/java/ac/mdiq/podcini/net/ssl/SslProviderInstaller.kt (100%) create mode 100644 app/src/play/java/ac/mdiq/podcini/playback/cast/CastEnabledActivity.kt rename {playback/cast => app}/src/play/java/ac/mdiq/podcini/playback/cast/CastOptionsProvider.kt (100%) create mode 100644 app/src/play/java/ac/mdiq/podcini/playback/cast/CastPsmp.kt create mode 100644 app/src/play/java/ac/mdiq/podcini/playback/cast/CastStateListener.kt create mode 100644 app/src/play/java/ac/mdiq/podcini/playback/cast/CastUtils.kt create mode 100644 app/src/play/java/ac/mdiq/podcini/playback/cast/MediaInfoCreator.kt create mode 100644 app/src/play/java/ac/mdiq/podcini/service/playback/WearMediaSession.kt rename {playback/cast => app}/src/play/res/menu/cast_button.xml (100%) rename {core => app}/src/test/assets/local-feed1/track1.mp3 (100%) rename {core => app}/src/test/assets/local-feed2/folder.png (100%) rename {core => app}/src/test/assets/local-feed2/track1.mp3 (100%) rename {core => app}/src/test/assets/local-feed2/track2.mp3 (100%) create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/FeedFilterTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/FeedItemMother.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/FeedItemTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/FeedMediaMother.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/FeedMediaTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/FeedMother.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/FeedTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/LocalFeedUpdaterTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/feed/VolumeAdaptionSettingTest.kt rename {net/common => app}/src/test/java/ac/mdiq/podcini/net/common/UrlCheckerTest.kt (100%) create mode 100644 app/src/test/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadRequestTest.kt rename {net/sync/gpoddernet => app}/src/test/java/ac/mdiq/podcini/net/sync/HostnameParserTest.kt (100%) create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/feed/element/element/AtomTextTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/feed/element/namespace/AtomParserTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/feed/element/namespace/FeedParserTestHelper.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/feed/element/namespace/RssParserTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/feed/element/util/DateUtilsTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/feed/element/util/DurationParserTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/media/id3/ChapterReaderTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/media/id3/Id3ReaderTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/media/id3/MetadataReaderTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentChapterReaderTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentMetadataReaderTest.kt rename {playback/base => app}/src/test/java/ac/mdiq/podcini/playback/base/RewindAfterPauseUtilTest.kt (100%) create mode 100644 app/src/test/java/ac/mdiq/podcini/service/playback/PlaybackVolumeUpdaterTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/APCleanupAlgorithmTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/DbCleanupTests.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/DbNullCleanupAlgorithmTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/DbQueueCleanupAlgorithmTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/DbReaderTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/DbTasksTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/DbTestUtils.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/DbWriterTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/ExceptFavoriteCleanupAlgorithmTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/FeedItemDuplicateGuesserTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/ItemEnqueuePositionCalculatorTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/storage/mapper/FeedCursorMapperTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/sync/EpisodeActionFilterTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/sync/GuidValidatorTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/CollectionTestUtil.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/ConverterTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/FeedItemPermutorsTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/FeedItemUtilTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/FilenameGeneratorTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/URIUtilTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/gui/ShownotesCleanerTest.kt create mode 100644 app/src/test/java/ac/mdiq/podcini/util/syndication/FeedDiscovererTest.kt rename {core => app}/src/test/java/android/text/TextUtils.kt (100%) rename {core => app}/src/test/java/android/util/Log.kt (100%) rename {parser/media => app}/src/test/resources/auphonic.m4a (100%) rename {parser/media => app}/src/test/resources/auphonic.mp3 (100%) rename {parser/media => app}/src/test/resources/auphonic.ogg (100%) rename {parser/media => app}/src/test/resources/auphonic.opus (100%) rename {parser/feed => app}/src/test/resources/feed-atom-testAtomBasic.xml (100%) rename {parser/feed => app}/src/test/resources/feed-atom-testEmptyRelLinks.xml (100%) rename {parser/feed => app}/src/test/resources/feed-atom-testLogoWithWhitespace.xml (100%) rename {parser/feed => app}/src/test/resources/feed-rss-testImageWithWhitespace.xml (100%) rename {parser/feed => app}/src/test/resources/feed-rss-testMediaContentMime.xml (100%) rename {parser/feed => app}/src/test/resources/feed-rss-testMultipleFundingTags.xml (100%) rename {parser/feed => app}/src/test/resources/feed-rss-testRss2Basic.xml (100%) rename {parser/feed => app}/src/test/resources/feed-rss-testUnsupportedElements.xml (100%) rename {parser/media => app}/src/test/resources/hindenburg-journalist-pro.m4a (100%) rename {parser/media => app}/src/test/resources/hindenburg-journalist-pro.mp3 (100%) rename {parser/media => app}/src/test/resources/mp3chaps-py.mp3 (100%) rename {parser/media => app}/src/test/resources/ultraschall5.mp3 (100%) delete mode 100644 core/.gitignore delete mode 100644 core/build.gradle delete mode 100644 core/lint.xml delete mode 100644 core/src/free/java/ac/mdiq/podcini/core/service/playback/WearMediaSession.kt delete mode 100644 core/src/main/AndroidManifest.xml delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/ApplicationCallbacks.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/ClientConfig.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/ClientConfigurator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/backup/OpmlBackupAgent.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/dialog/ConfirmationDialog.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/event/DownloadLogEvent.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/CommonSymbols.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/ExportWriter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/favorites/FavoritesWriter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/html/HtmlWriter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/opml/OpmlElement.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/opml/OpmlReader.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/opml/OpmlSymbols.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/export/opml/OpmlWriter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/ChapterMerger.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/FeedEvent.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/FeedItemFilterGroup.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/FeedUrlNotFoundException.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/LocalFeedUpdater.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/SubscriptionsFilterGroup.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/util/ImageResourceUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/feed/util/PlaybackSpeedUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/menuhandler/MenuItemUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/preferences/PlaybackPreferences.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/preferences/SleepTimerPreferences.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/preferences/ThemeSwitcher.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/preferences/UsageStatistics.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/receiver/FeedUpdateReceiver.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/receiver/MediaButtonReceiver.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/receiver/PlayerWidget.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/BasicAuthorizationInterceptor.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/FeedUpdateWorker.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/QuickSettingsTileService.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/UserAgentInterceptor.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/DefaultDownloaderFactory.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/DownloadRequestCreator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/DownloadServiceInterfaceImpl.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/Downloader.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/DownloaderFactory.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/EpisodeDownloadWorker.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/HttpCredentialEncoder.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/HttpDownloader.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/NewEpisodesNotification.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/PodciniHttpClient.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/handler/FeedParserTask.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/handler/FeedSyncTask.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/download/handler/MediaDownloadedHandler.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/ExoPlayerWrapper.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/LocalPSMP.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/PlaybackService.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/PlaybackServiceInterface.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/PlaybackServiceNotificationBuilder.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/PlaybackServiceStateManager.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/PlaybackServiceTaskManager.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/PlaybackVolumeUpdater.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/service/playback/ShakeListener.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/APCleanupAlgorithm.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/APNullCleanupAlgorithm.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/APQueueCleanupAlgorithm.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/AutomaticDownloadAlgorithm.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/DBReader.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/DBTasks.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/DBWriter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/DatabaseTransporter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/EpisodeCleanupAlgorithm.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/EpisodeCleanupAlgorithmFactory.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/ExceptFavoriteCleanupAlgorithm.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/FeedItemDuplicateGuesser.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/FeedSearcher.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/ItemEnqueuePositionCalculator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/NavDrawerData.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/storage/StatisticsItem.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/EpisodeActionFilter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/GuidValidator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/LockingAsyncExecutor.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/SyncService.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/SynchronizationCredentials.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/SynchronizationProviderViewData.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/SynchronizationSettings.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/queue/SynchronizationQueueSink.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/sync/queue/SynchronizationQueueStorage.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/ChapterUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/Converter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/DateFormatter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/DownloadErrorLabel.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/FastDocumentFile.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/FeedItemPermutors.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/FeedItemUtil.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/FeedUtil.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/FileNameGenerator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/IntentUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/InvalidFeedException.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/LongList.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/NetworkUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/Permutor.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/PlaybackStatus.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/PowerUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/ShareUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/StorageUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/TimeSpeedConverter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/URIUtil.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/comparator/ChapterStartTimeComparator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/comparator/DownloadResultComparator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/comparator/FeedItemPubdateComparator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/comparator/PlaybackCompletionDateComparator.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/download/ConnectionStateMonitor.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/download/FeedUpdateManager.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/download/MediaSizeLoader.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/download/NetworkConnectionChangeHandler.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/gui/MoreContentListFooterUtil.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/gui/NotificationUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/gui/PictureInPictureUtil.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/gui/ShownotesCleaner.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/playback/PlayableUtils.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/playback/PlaybackController.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/playback/PlaybackServiceStarter.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/syndication/FeedDiscoverer.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/util/syndication/HtmlToPlainText.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/widget/WidgetUpdater.kt delete mode 100644 core/src/main/java/ac/mdiq/podcini/core/widget/WidgetUpdaterWorker.kt delete mode 100644 core/src/main/res/values/arrays.xml delete mode 100644 core/src/main/res/values/attrs.xml delete mode 100644 core/src/main/res/values/dimens.xml delete mode 100644 core/src/main/res/values/integers.xml delete mode 100644 core/src/play/java/ac/mdiq/podcini/core/service/playback/WearMediaSession.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/FeedFilterTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/FeedItemMother.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/FeedItemTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/FeedMediaMother.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/FeedMediaTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/FeedMother.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/FeedTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/LocalFeedUpdaterTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/feed/VolumeAdaptionSettingTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/service/playback/PlaybackVolumeUpdaterTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/APCleanupAlgorithmTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/DbCleanupTests.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/DbNullCleanupAlgorithmTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/DbQueueCleanupAlgorithmTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/DbReaderTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/DbTasksTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/DbTestUtils.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/DbWriterTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/ExceptFavoriteCleanupAlgorithmTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/FeedItemDuplicateGuesserTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/ItemEnqueuePositionCalculatorTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/storage/mapper/FeedCursorMapperTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/sync/EpisodeActionFilterTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/sync/GuidValidatorTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/CollectionTestUtil.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/ConverterTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/FeedItemPermutorsTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/FeedItemUtilTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/FilenameGeneratorTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/URIUtilTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/gui/ShownotesCleanerTest.kt delete mode 100644 core/src/test/java/ac/mdiq/podcini/core/util/syndication/FeedDiscovererTest.kt delete mode 100644 event/build.gradle delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/DiscoveryDefaultUpdateEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/EpisodeDownloadEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/FavoritesEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/FeedItemEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/FeedListUpdateEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/FeedUpdateRunningEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/MessageEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/PlayerErrorEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/PlayerStatusEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/QueueEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/StatisticsEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/SyncServiceEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/UnreadItemsUpdateEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/playback/BufferUpdateEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/playback/PlaybackHistoryEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/playback/PlaybackPositionEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/playback/PlaybackServiceEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/playback/SleepTimerUpdatedEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/playback/SpeedChangedEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/settings/SkipIntroEndingChangedEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/settings/SpeedPresetChangedEvent.kt delete mode 100644 event/src/main/java/ac/mdiq/podcini/event/settings/VolumeAdaptionChangedEvent.kt delete mode 100644 fastlane/metadata/android/en-US/images/phoneScreenshots/4_home.jpg create mode 100644 fastlane/metadata/android/en-US/images/phoneScreenshots/4_subscriptions.jpg delete mode 100644 images/4_home.jpg create mode 100644 images/4_subscriptions.jpg delete mode 100644 model/README.md delete mode 100644 model/build.gradle delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/MediaMetadataRetrieverCompat.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/download/DownloadError.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/download/DownloadResult.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/download/DownloadStatus.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/download/ProxyConfig.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/Chapter.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/EmbeddedChapterImage.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/Feed.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedComponent.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedCounter.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedFile.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedFilter.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedFunding.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedItem.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedItemFilter.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedMedia.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/FeedPreferences.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/SortOrder.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/SubscriptionsFilter.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/feed/VolumeAdaptionSetting.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/playback/MediaType.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/playback/Playable.kt delete mode 100644 model/src/main/java/ac/mdiq/podcini/model/playback/RemoteMedia.kt delete mode 100644 net/README.md delete mode 100644 net/common/README.md delete mode 100644 net/common/build.gradle delete mode 100644 net/common/src/main/java/ac/mdiq/podcini/net/common/UrlChecker.kt delete mode 100644 net/discovery/README.md delete mode 100644 net/discovery/build.gradle delete mode 100644 net/discovery/src/main/java/ac/mdiq/podcini/net/discovery/FyydPodcastSearcher.kt delete mode 100644 net/discovery/src/main/java/ac/mdiq/podcini/net/discovery/GpodnetPodcastSearcher.kt delete mode 100644 net/discovery/src/main/java/ac/mdiq/podcini/net/discovery/ItunesPodcastSearcher.kt delete mode 100644 net/discovery/src/main/java/ac/mdiq/podcini/net/discovery/ItunesTopListLoader.kt delete mode 100644 net/discovery/src/main/java/ac/mdiq/podcini/net/discovery/PodcastIndexPodcastSearcher.kt delete mode 100644 net/download/README.md delete mode 100644 net/download/service-interface/README.md delete mode 100644 net/download/service-interface/build.gradle delete mode 100644 net/download/service-interface/src/main/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadRequest.kt delete mode 100644 net/download/service-interface/src/main/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadServiceInterface.kt delete mode 100644 net/download/service-interface/src/main/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadServiceInterfaceStub.kt delete mode 100644 net/download/service-interface/src/test/java/ac/mdiq/podcini/net/download/serviceinterface/DownloadRequestTest.kt delete mode 100644 net/ssl/README.md delete mode 100644 net/ssl/build.gradle delete mode 100644 net/sync/README.md delete mode 100644 net/sync/gpoddernet/README.md delete mode 100644 net/sync/gpoddernet/build.gradle delete mode 100644 net/sync/gpoddernet/src/main/java/ac/mdiq/podcini/net/sync/gpoddernet/GpodnetService.kt delete mode 100644 net/sync/model/README.md delete mode 100644 net/sync/model/build.gradle delete mode 100644 net/sync/model/src/main/java/ac/mdiq/podcini/net/sync/model/EpisodeAction.kt delete mode 100644 parser/README.md delete mode 100644 parser/feed/README.md delete mode 100644 parser/feed/build.gradle delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/FeedHandler.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/FeedHandlerResult.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/HandlerState.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/PodcastIndexChapterParser.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/SyndHandler.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/UnsupportedFeedtypeException.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/element/AtomText.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/element/SyndElement.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/Atom.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/Content.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/DublinCore.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/Itunes.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/Media.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/Namespace.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/PodcastIndex.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/Rss20.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/namespace/SimpleChapters.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/util/DateUtils.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/util/DurationParser.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/util/MimeTypeUtils.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/util/SyndStringUtils.kt delete mode 100644 parser/feed/src/main/java/ac/mdiq/podcini/parser/feed/util/TypeGetter.kt delete mode 100644 parser/feed/src/test/java/ac/mdiq/podcini/parser/feed/element/element/AtomTextTest.kt delete mode 100644 parser/feed/src/test/java/ac/mdiq/podcini/parser/feed/element/namespace/AtomParserTest.kt delete mode 100644 parser/feed/src/test/java/ac/mdiq/podcini/parser/feed/element/namespace/FeedParserTestHelper.kt delete mode 100644 parser/feed/src/test/java/ac/mdiq/podcini/parser/feed/element/namespace/RssParserTest.kt delete mode 100644 parser/feed/src/test/java/ac/mdiq/podcini/parser/feed/element/util/DateUtilsTest.kt delete mode 100644 parser/feed/src/test/java/ac/mdiq/podcini/parser/feed/element/util/DurationParserTest.kt delete mode 100644 parser/media/README.md delete mode 100644 parser/media/build.gradle delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/id3/ChapterReader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/id3/ID3Reader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/id3/ID3ReaderException.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/id3/Id3MetadataReader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/id3/model/FrameHeader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/id3/model/Header.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/id3/model/TagHeader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentChapterReader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentHeader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentMetadataReader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentReader.kt delete mode 100644 parser/media/src/main/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentReaderException.kt delete mode 100644 parser/media/src/test/java/ac/mdiq/podcini/parser/media/id3/ChapterReaderTest.kt delete mode 100644 parser/media/src/test/java/ac/mdiq/podcini/parser/media/id3/Id3ReaderTest.kt delete mode 100644 parser/media/src/test/java/ac/mdiq/podcini/parser/media/id3/MetadataReaderTest.kt delete mode 100644 parser/media/src/test/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentChapterReaderTest.kt delete mode 100644 parser/media/src/test/java/ac/mdiq/podcini/parser/media/vorbis/VorbisCommentMetadataReaderTest.kt delete mode 100644 playback/README.md delete mode 100644 playback/base/README.md delete mode 100644 playback/base/build.gradle delete mode 100644 playback/base/src/main/java/ac/mdiq/podcini/playback/base/PlaybackServiceMediaPlayer.kt delete mode 100644 playback/cast/README.md delete mode 100644 playback/cast/build.gradle delete mode 100644 playback/cast/src/free/java/ac/mdiq/podcini/playback/cast/CastStateListener.kt delete mode 100644 playback/cast/src/play/java/ac/mdiq/podcini/playback/cast/CastEnabledActivity.kt delete mode 100644 playback/cast/src/play/java/ac/mdiq/podcini/playback/cast/CastPsmp.kt delete mode 100644 playback/cast/src/play/java/ac/mdiq/podcini/playback/cast/CastStateListener.kt delete mode 100644 playback/cast/src/play/java/ac/mdiq/podcini/playback/cast/CastUtils.kt delete mode 100644 playback/cast/src/play/java/ac/mdiq/podcini/playback/cast/MediaInfoCreator.kt delete mode 100644 storage/README.md delete mode 100644 storage/database/README.md delete mode 100644 storage/database/build.gradle delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/DBUpgrader.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/PodDBAdapter.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/ChapterCursorMapper.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/DownloadResultCursorMapper.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedCursorMapper.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedItemCursorMapper.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedItemFilterQuery.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedItemSortQuery.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedMediaCursorMapper.kt delete mode 100644 storage/database/src/main/java/ac/mdiq/podcini/storage/database/mapper/FeedPreferencesCursorMapper.kt delete mode 100644 storage/preferences/README.md delete mode 100644 storage/preferences/build.gradle delete mode 100644 storage/preferences/src/main/java/ac/mdiq/podcini/storage/preferences/UserPreferences.kt delete mode 100644 ui/README.md delete mode 100644 ui/app-start-intent/README.md delete mode 100644 ui/app-start-intent/build.gradle delete mode 100644 ui/app-start-intent/src/main/java/ac/mdiq/podcini/ui/appstartintent/MainActivityStarter.kt delete mode 100644 ui/app-start-intent/src/main/java/ac/mdiq/podcini/ui/appstartintent/PlaybackSpeedActivityStarter.kt delete mode 100644 ui/app-start-intent/src/main/java/ac/mdiq/podcini/ui/appstartintent/VideoPlayerActivityStarter.kt delete mode 100644 ui/common/README.md delete mode 100644 ui/common/build.gradle delete mode 100644 ui/common/src/main/java/ac/mdiq/podcini/ui/common/CircularProgressBar.kt delete mode 100644 ui/common/src/main/java/ac/mdiq/podcini/ui/common/PlaybackSpeedIndicatorView.kt delete mode 100644 ui/common/src/main/java/ac/mdiq/podcini/ui/common/SquareImageView.kt delete mode 100644 ui/common/src/main/java/ac/mdiq/podcini/ui/common/ThemeUtils.kt delete mode 100644 ui/common/src/main/java/ac/mdiq/podcini/ui/common/TriangleLabelView.kt delete mode 100644 ui/common/src/main/java/ac/mdiq/podcini/ui/common/WrappingGridView.kt delete mode 100644 ui/common/src/main/res/values/attrs.xml delete mode 100644 ui/echo/README.md delete mode 100644 ui/echo/build.gradle delete mode 100644 ui/echo/src/main/AndroidManifest.xml delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/EchoActivity.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/EchoProgress.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/screens/BaseScreen.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/screens/BubbleScreen.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/screens/FinalShareScreen.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/screens/RotatingSquaresScreen.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/screens/StripesScreen.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/screens/WaveformScreen.kt delete mode 100644 ui/echo/src/main/java/ac/mdiq/podcini/ui/echo/screens/WavesScreen.kt delete mode 100644 ui/echo/src/main/res/values-de/echo-strings.xml delete mode 100644 ui/echo/src/main/res/values-es/echo-strings.xml delete mode 100644 ui/echo/src/main/res/values-fr/echo-strings.xml delete mode 100644 ui/echo/src/main/res/values-it/echo-strings.xml delete mode 100644 ui/glide/README.md delete mode 100644 ui/glide/build.gradle delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/ApGlideModule.kt delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/ApOkHttpUrlLoader.kt delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/AudioCoverFetcher.kt delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/ChapterImageModelLoader.kt delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/FastBlurTransformation.kt delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/GenerativePlaceholderImageModelLoader.kt delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/MetadataRetrieverLoader.kt delete mode 100644 ui/glide/src/main/java/ac/mdiq/podcini/ui/glide/NoHttpStringLoader.kt delete mode 100644 ui/i18n/README.md delete mode 100644 ui/i18n/build.gradle delete mode 100644 ui/i18n/lint.xml delete mode 100644 ui/i18n/src/main/res/values/strings.xml delete mode 100644 ui/png-icons/README.md delete mode 100644 ui/png-icons/build.gradle delete mode 100644 ui/statistics/README.md delete mode 100644 ui/statistics/build.gradle delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/PieChartView.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsFragment.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/StatisticsListAdapter.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/downloads/DownloadStatisticsFragment.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/downloads/DownloadStatisticsListAdapter.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/feed/FeedStatisticsDialogFragment.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/feed/FeedStatisticsFragment.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/PlaybackStatisticsListAdapter.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/StatisticsFilterDialog.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/subscriptions/SubscriptionStatisticsFragment.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/years/BarChartView.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearStatisticsListAdapter.kt delete mode 100644 ui/statistics/src/main/java/ac/mdiq/podcini/ui/statistics/years/YearsStatisticsFragment.kt diff --git a/7_podcast.jpg b/7_podcast.jpg deleted file mode 100644 index 36ba875cb6cafea2c56f62ef3f97de067e1bcf8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235363 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 o 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 diff --git a/README.md b/README.md index 33bcfd23..65260872 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,15 @@ This is based on a fork from the popular project AntennaPod ( - + @@ -28,4 +32,4 @@ Podcini, same as its forked project AntennaPod, is licensed under the GNU Genera New files and modifications in the project is copyrighted in 2024 by Xilin Jia. -Original files from the forked project maintains copyrights of the AntennaPod team. \ No newline at end of file +Original contents from the forked project maintains copyrights of the AntennaPod team. \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 14a66bd2..f137841f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -22,8 +22,8 @@ android { // Version code schema: // "1.2.3-beta4" -> 1020304 // "1.2.3" -> 1020395 - versionCode 3020100 - versionName "3.2.5" + versionCode 3020101 + versionName "4.0.1" def commit = "" try { @@ -37,11 +37,20 @@ android { } buildConfigField "String", "COMMIT_HASH", ('"' + (commit.isEmpty() ? "Unknown commit" : commit) + '"') - javaCompileOptions { - annotationProcessorOptions { - arguments = [eventBusIndex: 'ac.mdiq.podcini.ApEventBusIndex'] - } +// javaCompileOptions { +// annotationProcessorOptions { +// arguments = [eventBusIndex: 'ac.mdiq.podcini.ApEventBusIndex'] +// } +// } + + if (project.hasProperty("podcastindexApiKey")) { + buildConfigField "String", "PODCASTINDEX_API_KEY", '"' + podcastindexApiKey + '"' + buildConfigField "String", "PODCASTINDEX_API_SECRET", '"' + podcastindexApiSecret + '"' + } else { + buildConfigField "String", "PODCASTINDEX_API_KEY", '"XTMMQGA2YZ4WJUBYY4HK"' + buildConfigField "String", "PODCASTINDEX_API_SECRET", '"XAaAhk4^2YBsTE33vdbwbZNj82ZRLABDDqFdKe7x"' } + } signingConfigs { releaseConfig { @@ -91,46 +100,35 @@ dependencies { } } - implementation project(":core") - implementation project(":event") - implementation project(':model') - implementation project(':net:common') - implementation project(':net:discovery') - implementation project(':net:download:service-interface') - implementation project(':net:sync:gpoddernet') - implementation project(':net:sync:model') - implementation project(':parser:feed') - implementation project(':playback:base') - implementation project(':playback:cast') - implementation project(':storage:database') - implementation project(':storage:preferences') - implementation project(':ui:app-start-intent') - implementation project(':ui:common') - implementation project(':ui:echo') - implementation project(':ui:glide') - implementation project(':ui:i18n') - implementation project(':ui:statistics') + kapt "androidx.annotation:annotation:$annotationVersion" - kapt "androidx.annotation:annotation:1.7.1" - - implementation "androidx.appcompat:appcompat:1.6.1" + implementation "androidx.appcompat:appcompat:$appcompatVersion" implementation 'androidx.coordinatorlayout:coordinatorlayout:1.2.0' - implementation "androidx.core:core-ktx:$coreVersion" implementation "androidx.fragment:fragment-ktx:$fragmentVersion" implementation 'androidx.gridlayout:gridlayout:1.0.0' implementation "androidx.media:media:$mediaVersion" + implementation "androidx.media3:media3-exoplayer:$media3Version" + implementation "androidx.media3:media3-ui:$media3Version" + implementation "androidx.media3:media3-datasource-okhttp:$media3Version" implementation "androidx.media3:media3-common:$media3Version" implementation "androidx.palette:palette-ktx:$paletteVersion" implementation "androidx.preference:preference-ktx:$preferenceVersion" implementation "androidx.recyclerview:recyclerview:$recyclerViewVersion" implementation "androidx.viewpager2:viewpager2:$viewPager2Version" implementation "androidx.work:work-runtime:$workManagerVersion" + implementation "androidx.core:core-splashscreen:1.0.1" + implementation 'androidx.documentfile:documentfile:1.0.1' + implementation "com.google.android.material:material:$googleMaterialVersion" implementation "org.apache.commons:commons-lang3:$commonslangVersion" implementation "commons-io:commons-io:$commonsioVersion" implementation "org.jsoup:jsoup:$jsoupVersion" + implementation "com.github.bumptech.glide:glide:$glideVersion" + implementation "com.github.bumptech.glide:okhttp3-integration:$glideVersion@aar" + annotationProcessor "com.github.bumptech.glide:compiler:$glideVersion" + implementation "com.squareup.okhttp3:okhttp:$okhttpVersion" implementation "com.squareup.okhttp3:okhttp-urlconnection:$okhttpVersion" implementation "com.squareup.okio:okio:$okioVersion" @@ -152,7 +150,6 @@ dependencies { playImplementation 'com.google.android.play:core-ktx:1.8.0' compileOnly "com.google.android.wearable:wearable:$wearableSupportVersion" - androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion" androidTestImplementation 'com.nanohttpd:nanohttpd:2.1.1' androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" androidTestImplementation "androidx.test.espresso:espresso-contrib:$espressoVersion" @@ -160,6 +157,33 @@ dependencies { androidTestImplementation "androidx.test:runner:$runnerVersion" androidTestImplementation "androidx.test:rules:$rulesVersion" androidTestImplementation 'androidx.test.ext:junit:1.1.5' + androidTestImplementation "org.awaitility:awaitility:$awaitilityVersion" + + implementation "com.annimon:stream:$annimonStreamVersion" + implementation 'com.github.mfietz:fyydlin:v0.5.0' + + // Non-free dependencies: + + testImplementation "androidx.test:core:$testCoreVersion" + testImplementation "org.awaitility:awaitility:$awaitilityVersion" + testImplementation "junit:junit:$junitVersion" + testImplementation 'org.mockito:mockito-inline:5.2.0' + testImplementation "org.robolectric:robolectric:$robolectricVersion" + testImplementation 'javax.inject:javax.inject:1' + + + playImplementation 'com.google.android.gms:play-services-base:17.5.0' + freeImplementation 'org.conscrypt:conscrypt-android:2.5.2' + + playApi 'androidx.mediarouter:mediarouter:1.6.0' + playApi "com.google.android.support:wearable:$wearableSupportVersion" + playApi 'com.google.android.gms:play-services-cast-framework:21.2.0' +} + +kapt { + arguments { + arg('eventBusIndex', 'ac.mdiq.podcini.ApEventBusIndex') + } } if (project.hasProperty("podciniPlayPublisherCredentials")) { diff --git a/app/src/androidTest/java/ac/test/podcini/EspressoTestUtils.kt b/app/src/androidTest/java/ac/test/podcini/EspressoTestUtils.kt index 0725c1c8..4ef27b30 100644 --- a/app/src/androidTest/java/ac/test/podcini/EspressoTestUtils.kt +++ b/app/src/androidTest/java/ac/test/podcini/EspressoTestUtils.kt @@ -17,15 +17,15 @@ import androidx.test.espresso.util.HumanReadables import androidx.test.espresso.util.TreeIterables import androidx.test.platform.app.InstrumentationRegistry import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.core.service.playback.PlaybackService -import ac.mdiq.podcini.dialog.RatingDialog -import ac.mdiq.podcini.dialog.RatingDialog.saveRated -import ac.mdiq.podcini.fragment.NavDrawerFragment +import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.service.playback.PlaybackService +import ac.mdiq.podcini.ui.dialog.RatingDialog +import ac.mdiq.podcini.ui.dialog.RatingDialog.saveRated +import ac.mdiq.podcini.ui.fragment.NavDrawerFragment import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.deleteDatabase import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.getInstance import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.init -import ac.mdiq.podcini.storage.preferences.UserPreferences +import ac.mdiq.podcini.preferences.UserPreferences import junit.framework.AssertionFailedError import org.awaitility.Awaitility import org.awaitility.core.ConditionTimeoutException diff --git a/app/src/androidTest/java/ac/test/podcini/dialogs/ShareDialogTest.kt b/app/src/androidTest/java/ac/test/podcini/dialogs/ShareDialogTest.kt index 369df5de..4b88f6b9 100644 --- a/app/src/androidTest/java/ac/test/podcini/dialogs/ShareDialogTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/dialogs/ShareDialogTest.kt @@ -11,8 +11,8 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.fragment.AllEpisodesFragment +import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.ui.fragment.AllEpisodesFragment import de.test.podcini.EspressoTestUtils import de.test.podcini.NthMatcher import de.test.podcini.ui.UITestUtils diff --git a/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt b/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt index 9fb74e31..7a3181c4 100644 --- a/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/playback/PlaybackTest.kt @@ -13,19 +13,19 @@ import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry import androidx.test.rule.ActivityTestRule import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.core.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId -import ac.mdiq.podcini.core.receiver.MediaButtonReceiver.Companion.createIntent -import ac.mdiq.podcini.core.storage.DBReader.getEpisodes -import ac.mdiq.podcini.core.storage.DBReader.getFeedItem -import ac.mdiq.podcini.core.storage.DBReader.getQueue -import ac.mdiq.podcini.core.storage.DBReader.getQueueIDList -import ac.mdiq.podcini.core.storage.DBWriter.clearQueue -import ac.mdiq.podcini.core.util.playback.PlaybackController -import ac.mdiq.podcini.model.feed.FeedItemFilter.Companion.unfiltered -import ac.mdiq.podcini.model.feed.SortOrder +import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId +import ac.mdiq.podcini.receiver.MediaButtonReceiver.Companion.createIntent +import ac.mdiq.podcini.storage.DBReader.getEpisodes +import ac.mdiq.podcini.storage.DBReader.getFeedItem +import ac.mdiq.podcini.storage.DBReader.getQueue +import ac.mdiq.podcini.storage.DBReader.getQueueIDList +import ac.mdiq.podcini.storage.DBWriter.clearQueue +import ac.mdiq.podcini.playback.PlaybackController +import ac.mdiq.podcini.storage.model.feed.FeedItemFilter.Companion.unfiltered +import ac.mdiq.podcini.storage.model.feed.SortOrder import ac.mdiq.podcini.playback.base.PlayerStatus -import ac.mdiq.podcini.storage.preferences.UserPreferences +import ac.mdiq.podcini.preferences.UserPreferences import de.test.podcini.EspressoTestUtils import de.test.podcini.IgnoreOnCi import de.test.podcini.ui.UITestUtils @@ -44,7 +44,7 @@ class PlaybackTest { var activityTestRule: ActivityTestRule = ActivityTestRule(MainActivity::class.java, false, false) private var uiTestUtils: UITestUtils? = null - protected var context: Context? = null + protected lateinit var context: Context private var controller: PlaybackController? = null @Before @@ -54,7 +54,7 @@ class PlaybackTest { EspressoTestUtils.clearPreferences() EspressoTestUtils.clearDatabase() - uiTestUtils = UITestUtils(context!!) + uiTestUtils = UITestUtils(context) uiTestUtils!!.setup() } @@ -192,28 +192,28 @@ class PlaybackTest { } protected fun setContinuousPlaybackPreference(value: Boolean) { - val prefs = PreferenceManager.getDefaultSharedPreferences(context!!) + val prefs = PreferenceManager.getDefaultSharedPreferences(context) prefs.edit().putBoolean(UserPreferences.PREF_FOLLOW_QUEUE, value).commit() } protected fun setSkipKeepsEpisodePreference(value: Boolean) { - val prefs = PreferenceManager.getDefaultSharedPreferences(context!!) + val prefs = PreferenceManager.getDefaultSharedPreferences(context) prefs.edit().putBoolean(UserPreferences.PREF_SKIP_KEEPS_EPISODE, value).commit() } protected fun setSmartMarkAsPlayedPreference(smartMarkAsPlayedSecs: Int) { - val prefs = PreferenceManager.getDefaultSharedPreferences(context!!) + val prefs = PreferenceManager.getDefaultSharedPreferences(context) prefs.edit().putString(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS, smartMarkAsPlayedSecs.toString(10)) .commit() } private fun skipEpisode() { - context!!.sendBroadcast(createIntent(context, KeyEvent.KEYCODE_MEDIA_NEXT)) + context.sendBroadcast(createIntent(context, KeyEvent.KEYCODE_MEDIA_NEXT)) } protected fun pauseEpisode() { - context!!.sendBroadcast(createIntent(context, KeyEvent.KEYCODE_MEDIA_PAUSE)) + context.sendBroadcast(createIntent(context, KeyEvent.KEYCODE_MEDIA_PAUSE)) } protected fun startLocalPlayback() { diff --git a/app/src/androidTest/java/ac/test/podcini/service/download/HttpDownloaderTest.kt b/app/src/androidTest/java/ac/test/podcini/service/download/HttpDownloaderTest.kt index 984b75a5..660ead29 100644 --- a/app/src/androidTest/java/ac/test/podcini/service/download/HttpDownloaderTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/service/download/HttpDownloaderTest.kt @@ -3,12 +3,12 @@ package de.test.podcini.service.download import android.util.Log import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry -import ac.mdiq.podcini.core.service.download.Downloader -import ac.mdiq.podcini.core.service.download.HttpDownloader -import ac.mdiq.podcini.model.download.DownloadError -import ac.mdiq.podcini.model.feed.FeedFile +import ac.mdiq.podcini.service.download.Downloader +import ac.mdiq.podcini.service.download.HttpDownloader +import ac.mdiq.podcini.storage.model.download.DownloadError +import ac.mdiq.podcini.storage.model.feed.FeedFile import ac.mdiq.podcini.net.download.serviceinterface.DownloadRequest -import ac.mdiq.podcini.storage.preferences.UserPreferences.init +import ac.mdiq.podcini.preferences.UserPreferences.init import de.test.podcini.util.service.download.HTTPBin import org.junit.After import org.junit.Assert @@ -28,10 +28,11 @@ class HttpDownloaderTest { @Throws(Exception::class) fun tearDown() { val contents = destDir!!.listFiles() - for (f in contents) { - Assert.assertTrue(f.delete()) + if (contents != null) { + for (f in contents) { + Assert.assertTrue(f.delete()) + } } - httpServer!!.stop() } @@ -61,18 +62,18 @@ class HttpDownloaderTest { private fun download(url: String?, title: String, expectedResult: Boolean, deleteExisting: Boolean = true, username: String? = null, password: String? = null - ): Downloader { + ): ac.mdiq.podcini.service.download.Downloader { val feedFile: FeedFile = setupFeedFile(url, title, deleteExisting) val request = DownloadRequest( feedFile.getFile_url()!!, url!!, title, 0, feedFile.getTypeAsInt(), username, password, null, false) - val downloader: Downloader = HttpDownloader(request) + val downloader: ac.mdiq.podcini.service.download.Downloader = HttpDownloader(request) downloader.call() val status = downloader.result Assert.assertNotNull(status) Assert.assertEquals(expectedResult, status.isSuccessful) // the file should not exist if the download has failed and deleteExisting was true - Assert.assertTrue(!deleteExisting || File(feedFile.getFile_url()).exists() == expectedResult) + Assert.assertTrue(!deleteExisting || File(feedFile.getFile_url()!!).exists() == expectedResult) return downloader } @@ -100,7 +101,7 @@ class HttpDownloaderTest { fun testCancel() { val url = httpServer!!.baseUrl + "/delay/3" val feedFile = setupFeedFile(url, "delay", true) - val downloader: Downloader = HttpDownloader(DownloadRequest( + val downloader: ac.mdiq.podcini.service.download.Downloader = HttpDownloader(DownloadRequest( feedFile.getFile_url()!!, url, "delay", 0, feedFile.getTypeAsInt(), null, null, null, false)) val t: Thread = object : Thread() { @@ -122,7 +123,7 @@ class HttpDownloaderTest { @Test fun testDeleteOnFailShouldDelete() { val downloader = download(url404, "testDeleteOnFailShouldDelete", false, true, null, null) - Assert.assertFalse(File(downloader.downloadRequest.destination).exists()) + Assert.assertFalse(File(downloader.downloadRequest.destination!!).exists()) } @Test @@ -133,7 +134,7 @@ class HttpDownloaderTest { dest.delete() Assert.assertTrue(dest.createNewFile()) val downloader = download(url404, filename, false, false, null, null) - Assert.assertTrue(File(downloader.downloadRequest.destination).exists()) + Assert.assertTrue(File(downloader.downloadRequest.destination!!).exists()) } @Test diff --git a/app/src/androidTest/java/ac/test/podcini/service/playback/CancelablePSMPCallback.kt b/app/src/androidTest/java/ac/test/podcini/service/playback/CancelablePSMPCallback.kt index 2ad07073..09c38193 100644 --- a/app/src/androidTest/java/ac/test/podcini/service/playback/CancelablePSMPCallback.kt +++ b/app/src/androidTest/java/ac/test/podcini/service/playback/CancelablePSMPCallback.kt @@ -1,7 +1,7 @@ package de.test.podcini.service.playback -import ac.mdiq.podcini.model.playback.MediaType -import ac.mdiq.podcini.model.playback.Playable +import ac.mdiq.podcini.storage.model.playback.MediaType +import ac.mdiq.podcini.storage.model.playback.Playable import ac.mdiq.podcini.playback.base.PlaybackServiceMediaPlayer.PSMPCallback import ac.mdiq.podcini.playback.base.PlaybackServiceMediaPlayer.PSMPInfo diff --git a/app/src/androidTest/java/ac/test/podcini/service/playback/DefaultPSMPCallback.kt b/app/src/androidTest/java/ac/test/podcini/service/playback/DefaultPSMPCallback.kt index ca1b62e6..86ad1cda 100644 --- a/app/src/androidTest/java/ac/test/podcini/service/playback/DefaultPSMPCallback.kt +++ b/app/src/androidTest/java/ac/test/podcini/service/playback/DefaultPSMPCallback.kt @@ -1,7 +1,7 @@ package de.test.podcini.service.playback -import ac.mdiq.podcini.model.playback.MediaType -import ac.mdiq.podcini.model.playback.Playable +import ac.mdiq.podcini.storage.model.playback.MediaType +import ac.mdiq.podcini.storage.model.playback.Playable import ac.mdiq.podcini.playback.base.PlaybackServiceMediaPlayer.PSMPCallback import ac.mdiq.podcini.playback.base.PlaybackServiceMediaPlayer.PSMPInfo diff --git a/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceMediaPlayerTest.kt b/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceMediaPlayerTest.kt index d0eb9276..2494d61f 100644 --- a/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceMediaPlayerTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceMediaPlayerTest.kt @@ -3,9 +3,9 @@ package de.test.podcini.service.playback import androidx.test.annotation.UiThreadTest import androidx.test.filters.MediumTest import androidx.test.platform.app.InstrumentationRegistry -import ac.mdiq.podcini.core.service.playback.LocalPSMP -import ac.mdiq.podcini.model.feed.* -import ac.mdiq.podcini.model.playback.Playable +import ac.mdiq.podcini.service.playback.LocalPSMP +import ac.mdiq.podcini.storage.model.feed.* +import ac.mdiq.podcini.storage.model.playback.Playable import ac.mdiq.podcini.playback.base.PlaybackServiceMediaPlayer import ac.mdiq.podcini.playback.base.PlaybackServiceMediaPlayer.PSMPInfo import ac.mdiq.podcini.playback.base.PlayerStatus diff --git a/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceTaskManagerTest.kt b/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceTaskManagerTest.kt index 32caf647..b7b2ae7d 100644 --- a/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceTaskManagerTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/service/playback/PlaybackServiceTaskManagerTest.kt @@ -3,15 +3,15 @@ package de.test.podcini.service.playback import androidx.test.annotation.UiThreadTest import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry -import ac.mdiq.podcini.core.preferences.SleepTimerPreferences.setShakeToReset -import ac.mdiq.podcini.core.preferences.SleepTimerPreferences.setVibrate -import ac.mdiq.podcini.core.service.playback.PlaybackServiceTaskManager -import ac.mdiq.podcini.core.service.playback.PlaybackServiceTaskManager.PSTMCallback -import ac.mdiq.podcini.core.widget.WidgetUpdater.WidgetState -import ac.mdiq.podcini.event.playback.SleepTimerUpdatedEvent -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.playback.Playable +import ac.mdiq.podcini.preferences.SleepTimerPreferences.setShakeToReset +import ac.mdiq.podcini.preferences.SleepTimerPreferences.setVibrate +import ac.mdiq.podcini.service.playback.PlaybackServiceTaskManager +import ac.mdiq.podcini.service.playback.PlaybackServiceTaskManager.PSTMCallback +import ac.mdiq.podcini.ui.widget.WidgetUpdater.WidgetState +import ac.mdiq.podcini.playback.event.SleepTimerUpdatedEvent +import ac.mdiq.podcini.storage.model.feed.Feed +import ac.mdiq.podcini.storage.model.feed.FeedItem +import ac.mdiq.podcini.storage.model.playback.Playable import ac.mdiq.podcini.playback.base.PlayerStatus import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.deleteDatabase import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.getInstance diff --git a/app/src/androidTest/java/ac/test/podcini/service/playback/SleepTimerPreferencesTest.kt b/app/src/androidTest/java/ac/test/podcini/service/playback/SleepTimerPreferencesTest.kt index 4c95f072..8dd62b0f 100644 --- a/app/src/androidTest/java/ac/test/podcini/service/playback/SleepTimerPreferencesTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/service/playback/SleepTimerPreferencesTest.kt @@ -1,6 +1,6 @@ package de.test.podcini.service.playback -import ac.mdiq.podcini.core.preferences.SleepTimerPreferences.isInTimeRange +import ac.mdiq.podcini.preferences.SleepTimerPreferences.isInTimeRange import org.junit.Assert import org.junit.Test diff --git a/app/src/androidTest/java/ac/test/podcini/storage/AutoDownloadTest.kt b/app/src/androidTest/java/ac/test/podcini/storage/AutoDownloadTest.kt index 78cd6efa..0f2f0946 100644 --- a/app/src/androidTest/java/ac/test/podcini/storage/AutoDownloadTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/storage/AutoDownloadTest.kt @@ -2,14 +2,14 @@ package de.test.podcini.storage import android.content.Context import androidx.test.core.app.ApplicationProvider -import ac.mdiq.podcini.core.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId -import ac.mdiq.podcini.core.storage.AutomaticDownloadAlgorithm -import ac.mdiq.podcini.core.storage.DBReader.getQueue -import ac.mdiq.podcini.core.storage.DBTasks.setDownloadAlgorithm -import ac.mdiq.podcini.core.util.playback.PlaybackServiceStarter -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.storage.preferences.UserPreferences.isAllowMobileStreaming -import ac.mdiq.podcini.storage.preferences.UserPreferences.isFollowQueue +import ac.mdiq.podcini.preferences.PlaybackPreferences.Companion.currentlyPlayingFeedMediaId +import ac.mdiq.podcini.storage.AutomaticDownloadAlgorithm +import ac.mdiq.podcini.storage.DBReader.getQueue +import ac.mdiq.podcini.storage.DBTasks.setDownloadAlgorithm +import ac.mdiq.podcini.playback.PlaybackServiceStarter +import ac.mdiq.podcini.storage.model.feed.FeedItem +import ac.mdiq.podcini.preferences.UserPreferences.isAllowMobileStreaming +import ac.mdiq.podcini.preferences.UserPreferences.isFollowQueue import de.test.podcini.EspressoTestUtils import de.test.podcini.ui.UITestUtils import org.awaitility.Awaitility @@ -106,7 +106,7 @@ class AutoDownloadTest { var currentlyPlayingAtDownload: Long = -1 private set - override fun autoDownloadUndownloadedItems(context: Context?): Runnable? { + override fun autoDownloadUndownloadedItems(context: Context): Runnable? { return Runnable { if (currentlyPlayingAtDownload == -1L) { currentlyPlayingAtDownload = currentlyPlayingFeedMediaId diff --git a/app/src/androidTest/java/ac/test/podcini/ui/FeedSettingsTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/FeedSettingsTest.kt index c25ae273..9e2ca0e6 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/FeedSettingsTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/FeedSettingsTest.kt @@ -8,8 +8,8 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.model.feed.Feed +import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.storage.model.feed.Feed import de.test.podcini.EspressoTestUtils import org.hamcrest.Matchers import org.junit.After diff --git a/app/src/androidTest/java/ac/test/podcini/ui/MainActivityTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/MainActivityTest.kt index 31ebc286..c1aa6cf0 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/MainActivityTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/MainActivityTest.kt @@ -8,7 +8,7 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity +import ac.mdiq.podcini.ui.activity.MainActivity import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.deleteDatabase import de.test.podcini.EspressoTestUtils import org.junit.After diff --git a/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt index 3806cf08..19caf9f4 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/NavigationDrawerTest.kt @@ -11,10 +11,10 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.activity.PreferenceActivity -import ac.mdiq.podcini.fragment.* -import ac.mdiq.podcini.storage.preferences.UserPreferences.hiddenDrawerItems +import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.ui.activity.PreferenceActivity +import ac.mdiq.podcini.ui.fragment.* +import ac.mdiq.podcini.preferences.UserPreferences.hiddenDrawerItems import de.test.podcini.EspressoTestUtils import de.test.podcini.NthMatcher import org.hamcrest.Matchers @@ -63,13 +63,6 @@ class NavigationDrawerTest { hiddenDrawerItems = ArrayList() activityRule.launchActivity(Intent()) - // home - openNavDrawer() - EspressoTestUtils.onDrawerItem(ViewMatchers.withText(R.string.home_label)).perform(ViewActions.click()) - Espresso.onView(ViewMatchers.isRoot()) - .perform(EspressoTestUtils.waitForView(Matchers.allOf(ViewMatchers.isDescendantOfA(ViewMatchers.withId(R.id.toolbar)), - ViewMatchers.withText(R.string.home_label)), 1000)) - // queue openNavDrawer() EspressoTestUtils.onDrawerItem(ViewMatchers.withText(R.string.queue_label)).perform(ViewActions.click()) @@ -159,7 +152,7 @@ class NavigationDrawerTest { @Test fun testDrawerPreferencesUnhideSomeElements() { - var hidden = listOf(PlaybackHistoryFragment.TAG, CompletedDownloadsFragment.TAG) + var hidden = listOf(PlaybackHistoryFragment.TAG, CompletedDownloadsFragment.TAG) hiddenDrawerItems = hidden activityRule.launchActivity(Intent()) openNavDrawer() @@ -170,7 +163,7 @@ class NavigationDrawerTest { Espresso.onView(ViewMatchers.withText(R.string.downloads_label)).perform(ViewActions.click()) Espresso.onView(ViewMatchers.withText(R.string.confirm_label)).perform(ViewActions.click()) - hidden = hiddenDrawerItems as List + hidden = hiddenDrawerItems?.filterNotNull()?: listOf() Assert.assertEquals(2, hidden.size.toLong()) Assert.assertTrue(hidden.contains(QueueFragment.TAG)) Assert.assertTrue(hidden.contains(PlaybackHistoryFragment.TAG)) diff --git a/app/src/androidTest/java/ac/test/podcini/ui/PreferencesTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/PreferencesTest.kt index 7ff5f647..2fcab376 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/PreferencesTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/PreferencesTest.kt @@ -11,33 +11,33 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.filters.LargeTest import androidx.test.rule.ActivityTestRule import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.PreferenceActivity -import ac.mdiq.podcini.core.storage.APCleanupAlgorithm -import ac.mdiq.podcini.core.storage.APNullCleanupAlgorithm -import ac.mdiq.podcini.core.storage.APQueueCleanupAlgorithm -import ac.mdiq.podcini.core.storage.EpisodeCleanupAlgorithmFactory.build -import ac.mdiq.podcini.core.storage.ExceptFavoriteCleanupAlgorithm -import ac.mdiq.podcini.storage.preferences.UserPreferences -import ac.mdiq.podcini.storage.preferences.UserPreferences.EnqueueLocation -import ac.mdiq.podcini.storage.preferences.UserPreferences.enqueueLocation -import ac.mdiq.podcini.storage.preferences.UserPreferences.episodeCacheSize -import ac.mdiq.podcini.storage.preferences.UserPreferences.fastForwardSecs -import ac.mdiq.podcini.storage.preferences.UserPreferences.init -import ac.mdiq.podcini.storage.preferences.UserPreferences.isAutoDelete -import ac.mdiq.podcini.storage.preferences.UserPreferences.isAutoDeleteLocal -import ac.mdiq.podcini.storage.preferences.UserPreferences.isEnableAutodownload -import ac.mdiq.podcini.storage.preferences.UserPreferences.isEnableAutodownloadOnBattery -import ac.mdiq.podcini.storage.preferences.UserPreferences.isFollowQueue -import ac.mdiq.podcini.storage.preferences.UserPreferences.isPauseOnHeadsetDisconnect -import ac.mdiq.podcini.storage.preferences.UserPreferences.isPersistNotify -import ac.mdiq.podcini.storage.preferences.UserPreferences.isUnpauseOnBluetoothReconnect -import ac.mdiq.podcini.storage.preferences.UserPreferences.isUnpauseOnHeadsetReconnect -import ac.mdiq.podcini.storage.preferences.UserPreferences.rewindSecs -import ac.mdiq.podcini.storage.preferences.UserPreferences.shouldDeleteRemoveFromQueue -import ac.mdiq.podcini.storage.preferences.UserPreferences.shouldPauseForFocusLoss -import ac.mdiq.podcini.storage.preferences.UserPreferences.showNextChapterOnFullNotification -import ac.mdiq.podcini.storage.preferences.UserPreferences.showPlaybackSpeedOnFullNotification -import ac.mdiq.podcini.storage.preferences.UserPreferences.showSkipOnFullNotification +import ac.mdiq.podcini.ui.activity.PreferenceActivity +import ac.mdiq.podcini.storage.APCleanupAlgorithm +import ac.mdiq.podcini.storage.APNullCleanupAlgorithm +import ac.mdiq.podcini.storage.APQueueCleanupAlgorithm +import ac.mdiq.podcini.storage.EpisodeCleanupAlgorithmFactory.build +import ac.mdiq.podcini.storage.ExceptFavoriteCleanupAlgorithm +import ac.mdiq.podcini.preferences.UserPreferences +import ac.mdiq.podcini.preferences.UserPreferences.EnqueueLocation +import ac.mdiq.podcini.preferences.UserPreferences.enqueueLocation +import ac.mdiq.podcini.preferences.UserPreferences.episodeCacheSize +import ac.mdiq.podcini.preferences.UserPreferences.fastForwardSecs +import ac.mdiq.podcini.preferences.UserPreferences.init +import ac.mdiq.podcini.preferences.UserPreferences.isAutoDelete +import ac.mdiq.podcini.preferences.UserPreferences.isAutoDeleteLocal +import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownload +import ac.mdiq.podcini.preferences.UserPreferences.isEnableAutodownloadOnBattery +import ac.mdiq.podcini.preferences.UserPreferences.isFollowQueue +import ac.mdiq.podcini.preferences.UserPreferences.isPauseOnHeadsetDisconnect +import ac.mdiq.podcini.preferences.UserPreferences.isPersistNotify +import ac.mdiq.podcini.preferences.UserPreferences.isUnpauseOnBluetoothReconnect +import ac.mdiq.podcini.preferences.UserPreferences.isUnpauseOnHeadsetReconnect +import ac.mdiq.podcini.preferences.UserPreferences.rewindSecs +import ac.mdiq.podcini.preferences.UserPreferences.shouldDeleteRemoveFromQueue +import ac.mdiq.podcini.preferences.UserPreferences.shouldPauseForFocusLoss +import ac.mdiq.podcini.preferences.UserPreferences.showNextChapterOnFullNotification +import ac.mdiq.podcini.preferences.UserPreferences.showPlaybackSpeedOnFullNotification +import ac.mdiq.podcini.preferences.UserPreferences.showSkipOnFullNotification import de.test.podcini.EspressoTestUtils import org.awaitility.Awaitility import org.junit.Assert diff --git a/app/src/androidTest/java/ac/test/podcini/ui/QueueFragmentTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/QueueFragmentTest.kt index f8d41d9c..c8d44a08 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/QueueFragmentTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/QueueFragmentTest.kt @@ -7,8 +7,8 @@ import androidx.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.fragment.QueueFragment +import ac.mdiq.podcini.ui.activity.MainActivity +import ac.mdiq.podcini.ui.fragment.QueueFragment import de.test.podcini.EspressoTestUtils import de.test.podcini.NthMatcher import org.hamcrest.CoreMatchers diff --git a/app/src/androidTest/java/ac/test/podcini/ui/TextOnlyFeedsTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/TextOnlyFeedsTest.kt index c9cac468..e0744156 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/TextOnlyFeedsTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/TextOnlyFeedsTest.kt @@ -8,7 +8,7 @@ import androidx.test.espresso.matcher.ViewMatchers import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity +import ac.mdiq.podcini.ui.activity.MainActivity import de.test.podcini.EspressoTestUtils import org.hamcrest.CoreMatchers import org.junit.After diff --git a/app/src/androidTest/java/ac/test/podcini/ui/UITestUtils.kt b/app/src/androidTest/java/ac/test/podcini/ui/UITestUtils.kt index ba72be17..479a633f 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/UITestUtils.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/UITestUtils.kt @@ -3,11 +3,11 @@ package de.test.podcini.ui import android.content.Context import android.util.Log import androidx.test.platform.app.InstrumentationRegistry -import ac.mdiq.podcini.event.FeedListUpdateEvent -import ac.mdiq.podcini.event.QueueEvent.Companion.setQueue -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.feed.FeedMedia +import ac.mdiq.podcini.util.event.FeedListUpdateEvent +import ac.mdiq.podcini.util.event.QueueEvent.Companion.setQueue +import ac.mdiq.podcini.storage.model.feed.Feed +import ac.mdiq.podcini.storage.model.feed.FeedItem +import ac.mdiq.podcini.storage.model.feed.FeedMedia import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.deleteDatabase import ac.mdiq.podcini.storage.database.PodDBAdapter.Companion.getInstance import de.test.podcini.util.service.download.HTTPBin @@ -193,7 +193,7 @@ class UITestUtils(private val context: Context) { adapter.setCompleteFeed(*hostedFeeds.toTypedArray()) adapter.setQueue(queue) adapter.close() - EventBus.getDefault().post(FeedListUpdateEvent(hostedFeeds)) + EventBus.getDefault().post(ac.mdiq.podcini.util.event.FeedListUpdateEvent(hostedFeeds)) EventBus.getDefault().post(setQueue(queue)) } diff --git a/app/src/androidTest/java/ac/test/podcini/ui/UITestUtilsTest.kt b/app/src/androidTest/java/ac/test/podcini/ui/UITestUtilsTest.kt index f3e3a0c8..ba2239b5 100644 --- a/app/src/androidTest/java/ac/test/podcini/ui/UITestUtilsTest.kt +++ b/app/src/androidTest/java/ac/test/podcini/ui/UITestUtilsTest.kt @@ -2,7 +2,7 @@ package de.test.podcini.ui import androidx.test.filters.MediumTest import androidx.test.platform.app.InstrumentationRegistry -import ac.mdiq.podcini.model.feed.Feed +import ac.mdiq.podcini.storage.model.feed.Feed import org.junit.After import org.junit.Assert import org.junit.Before @@ -41,7 +41,7 @@ class UITestUtilsTest { for (feed in feeds) { testUrlReachable(feed.download_url) - for (item in feed.items!!) { + for (item in feed.items) { if (item.hasMedia()) { testUrlReachable(item.media!!.download_url) } @@ -68,14 +68,14 @@ class UITestUtilsTest { for (feed in uiTestUtils!!.hostedFeeds) { Assert.assertTrue(feed.id != 0L) - for (item in feed.items!!) { + for (item in feed.items) { Assert.assertTrue(item.id != 0L) if (item.hasMedia()) { Assert.assertTrue(item.media!!.id != 0L) if (downloadEpisodes) { Assert.assertTrue(item.media!!.isDownloaded()) Assert.assertNotNull(item.media!!.getFile_url()) - val file = File(item.media!!.getFile_url()) + val file = File(item.media!!.getFile_url()!!) Assert.assertTrue(file.exists()) } } diff --git a/app/src/androidTest/java/ac/test/podcini/util/event/FeedItemEventListener.kt b/app/src/androidTest/java/ac/test/podcini/util/event/FeedItemEventListener.kt index a0e4d3ba..f5ea50e0 100644 --- a/app/src/androidTest/java/ac/test/podcini/util/event/FeedItemEventListener.kt +++ b/app/src/androidTest/java/ac/test/podcini/util/event/FeedItemEventListener.kt @@ -1,6 +1,6 @@ package de.test.podcini.util.event -import ac.mdiq.podcini.event.FeedItemEvent +import ac.mdiq.podcini.util.event.FeedItemEvent import io.reactivex.functions.Consumer import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.Subscribe @@ -10,14 +10,14 @@ import org.greenrobot.eventbus.Subscribe * */ class FeedItemEventListener { - private val events: MutableList = ArrayList() + private val events: MutableList = ArrayList() @Subscribe - fun onEvent(event: FeedItemEvent) { + fun onEvent(event: ac.mdiq.podcini.util.event.FeedItemEvent) { events.add(event) } - fun getEvents(): List { + fun getEvents(): List { return events } diff --git a/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/FeedGenerator.kt b/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/FeedGenerator.kt index 8391488a..687d3502 100644 --- a/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/FeedGenerator.kt +++ b/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/FeedGenerator.kt @@ -1,6 +1,6 @@ package de.test.podcini.util.syndication.feedgenerator -import ac.mdiq.podcini.model.feed.Feed +import ac.mdiq.podcini.storage.model.feed.Feed import java.io.IOException import java.io.OutputStream diff --git a/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/Rss2Generator.kt b/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/Rss2Generator.kt index 65d8fd54..4318807b 100644 --- a/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/Rss2Generator.kt +++ b/app/src/androidTest/java/ac/test/podcini/util/syndication/feedgenerator/Rss2Generator.kt @@ -1,9 +1,9 @@ package de.test.podcini.util.syndication.feedgenerator import android.util.Xml -import ac.mdiq.podcini.core.util.DateFormatter.formatRfc822Date -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.parser.feed.namespace.PodcastIndex +import ac.mdiq.podcini.util.DateFormatter.formatRfc822Date +import ac.mdiq.podcini.storage.model.feed.Feed +import ac.mdiq.podcini.feed.parser.namespace.PodcastIndex import de.test.podcini.util.syndication.feedgenerator.GeneratorUtil.addPaymentLink import java.io.IOException import java.io.OutputStream @@ -56,15 +56,15 @@ class Rss2Generator : FeedGenerator { } val fundingList = feed.paymentLinks - if (fundingList != null) { + if (fundingList.isNotEmpty()) { for (funding in fundingList) { addPaymentLink(xml, funding.url, true) } } // Write FeedItem data - if (feed.items != null) { - for (item in feed.items!!) { + if (feed.items.isNotEmpty()) { + for (item in feed.items) { xml.startTag(null, "item") if (item.title != null) { @@ -99,7 +99,7 @@ class Rss2Generator : FeedGenerator { xml.attribute(null, "type", item.media!!.mime_type) xml.endTag(null, "enclosure") } - if (fundingList != null) { + if (fundingList.isNotEmpty()) { for (funding in fundingList) { xml.startTag(PodcastIndex.NSTAG, "funding") xml.attribute(PodcastIndex.NSTAG, "url", funding.url) diff --git a/app/src/free/java/ac/mdiq/podcini/dialog/RatingDialog.kt b/app/src/free/java/ac/mdiq/podcini/dialog/RatingDialog.kt index c6e17800..372025df 100644 --- a/app/src/free/java/ac/mdiq/podcini/dialog/RatingDialog.kt +++ b/app/src/free/java/ac/mdiq/podcini/dialog/RatingDialog.kt @@ -1,4 +1,4 @@ -package ac.mdiq.podcini.dialog +package ac.mdiq.podcini.ui.dialog import android.content.Context import androidx.annotation.VisibleForTesting diff --git a/net/ssl/src/free/java/ac/mdiq/podcini/net/ssl/SslProviderInstaller.kt b/app/src/free/java/ac/mdiq/podcini/net/ssl/SslProviderInstaller.kt similarity index 100% rename from net/ssl/src/free/java/ac/mdiq/podcini/net/ssl/SslProviderInstaller.kt rename to app/src/free/java/ac/mdiq/podcini/net/ssl/SslProviderInstaller.kt diff --git a/playback/cast/src/free/java/ac/mdiq/podcini/playback/cast/CastEnabledActivity.kt b/app/src/free/java/ac/mdiq/podcini/playback/cast/CastEnabledActivity.kt similarity index 100% rename from playback/cast/src/free/java/ac/mdiq/podcini/playback/cast/CastEnabledActivity.kt rename to app/src/free/java/ac/mdiq/podcini/playback/cast/CastEnabledActivity.kt diff --git a/playback/cast/src/free/java/ac/mdiq/podcini/playback/cast/CastPsmp.kt b/app/src/free/java/ac/mdiq/podcini/playback/cast/CastPsmp.kt similarity index 100% rename from playback/cast/src/free/java/ac/mdiq/podcini/playback/cast/CastPsmp.kt rename to app/src/free/java/ac/mdiq/podcini/playback/cast/CastPsmp.kt diff --git a/app/src/free/java/ac/mdiq/podcini/playback/cast/CastStateListener.kt b/app/src/free/java/ac/mdiq/podcini/playback/cast/CastStateListener.kt new file mode 100644 index 00000000..e3972e1a --- /dev/null +++ b/app/src/free/java/ac/mdiq/podcini/playback/cast/CastStateListener.kt @@ -0,0 +1,11 @@ +package ac.mdiq.podcini.playback.cast + +import android.content.Context + +open class CastStateListener(context: Context) { + fun destroy() { + } + + open fun onSessionStartedOrEnded() { + } +} diff --git a/app/src/free/java/ac/mdiq/podcini/service/playback/WearMediaSession.kt b/app/src/free/java/ac/mdiq/podcini/service/playback/WearMediaSession.kt new file mode 100644 index 00000000..4db068f7 --- /dev/null +++ b/app/src/free/java/ac/mdiq/podcini/service/playback/WearMediaSession.kt @@ -0,0 +1,17 @@ +package ac.mdiq.podcini.service.playback + +import android.support.v4.media.session.MediaSessionCompat +import android.support.v4.media.session.PlaybackStateCompat + +internal object WearMediaSession { + /** + * Take a custom action builder and add no extras, because this is not the Play version of the app. + */ + fun addWearExtrasToAction(actionBuilder: PlaybackStateCompat.CustomAction.Builder?) { + // no-op + } + + fun mediaSessionSetExtraForWear(mediaSession: MediaSessionCompat?) { + // no-op + } +} diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index feb448d3..116456ac 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,19 @@ xmlns:tools="http://schemas.android.com/tools" android:installLocation="auto"> + + + + + + + + + + + + @@ -32,7 +45,7 @@ android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher" android:label="@string/app_name" - android:backupAgent=".core.backup.OpmlBackupAgent" + android:backupAgent=".storage.backup.OpmlBackupAgent" android:restoreAnyVersion="true" android:theme="@style/Theme.Podcini.Splash" android:usesCleartextTraffic="true" @@ -42,15 +55,51 @@ android:allowAudioPlaybackCapture="true" android:networkSecurityConfig="@xml/network_security_config"> - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -100,7 +149,7 @@ + android:value="ac.mdiq.podcini.ui.activity.MainActivity"/> @@ -157,7 +206,7 @@ @@ -170,7 +219,7 @@ @@ -196,22 +245,22 @@ + android:value="ac.mdiq.podcini.ui.activity.PreferenceActivity"/> + android:value="ac.mdiq.podcini.ui.activity.MainActivity"/> @@ -219,14 +268,14 @@ + android:value="ac.mdiq.podcini.ui.activity.MainActivity"/> @@ -336,7 +385,7 @@ - + + + + + + + diff --git a/core/src/main/assets/html-export-favorites-item-template.html b/app/src/main/assets/html-export-favorites-item-template.html similarity index 100% rename from core/src/main/assets/html-export-favorites-item-template.html rename to app/src/main/assets/html-export-favorites-item-template.html diff --git a/core/src/main/assets/html-export-feed-template.html b/app/src/main/assets/html-export-feed-template.html similarity index 100% rename from core/src/main/assets/html-export-feed-template.html rename to app/src/main/assets/html-export-feed-template.html diff --git a/core/src/main/assets/html-export-template.html b/app/src/main/assets/html-export-template.html similarity index 100% rename from core/src/main/assets/html-export-template.html rename to app/src/main/assets/html-export-template.html diff --git a/core/src/main/assets/shownotes-style.css b/app/src/main/assets/shownotes-style.css similarity index 100% rename from core/src/main/assets/shownotes-style.css rename to app/src/main/assets/shownotes-style.css diff --git a/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt b/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt index b5412054..4e3d5ad2 100644 --- a/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt +++ b/app/src/main/java/ac/mdiq/podcini/PodciniApp.kt @@ -9,15 +9,14 @@ import com.google.android.material.color.DynamicColors import com.joanzapata.iconify.Iconify import com.joanzapata.iconify.fonts.FontAwesomeModule import com.joanzapata.iconify.fonts.MaterialModule -import ac.mdiq.podcini.activity.SplashActivity -import ac.mdiq.podcini.config.ApplicationCallbacksImpl -import ac.mdiq.podcini.core.ApCoreEventBusIndex -import ac.mdiq.podcini.core.ClientConfig -import ac.mdiq.podcini.core.ClientConfigurator -import ac.mdiq.podcini.error.CrashReportWriter -import ac.mdiq.podcini.error.RxJavaErrorHandlerSetup +import ac.mdiq.podcini.ui.activity.SplashActivity +import ac.mdiq.podcini.util.config.ApplicationCallbacksImpl +import ac.mdiq.podcini.util.config.ClientConfig +import ac.mdiq.podcini.util.config.ClientConfigurator +import ac.mdiq.podcini.util.error.CrashReportWriter +import ac.mdiq.podcini.util.error.RxJavaErrorHandlerSetup import ac.mdiq.podcini.preferences.PreferenceUpgrader -import ac.mdiq.podcini.spa.SPAUtil +import ac.mdiq.podcini.util.SPAUtil import org.greenrobot.eventbus.EventBus /** Main application class. */ @@ -26,7 +25,8 @@ class PodciniApp : Application() { override fun onCreate() { super.onCreate() ClientConfig.USER_AGENT = "Podcini/" + BuildConfig.VERSION_NAME - ClientConfig.applicationCallbacks = ApplicationCallbacksImpl() + ClientConfig.applicationCallbacks = + ApplicationCallbacksImpl() Thread.setDefaultUncaughtExceptionHandler(CrashReportWriter()) RxJavaErrorHandlerSetup.setupRxJavaErrorHandler() @@ -53,7 +53,7 @@ class PodciniApp : Application() { SPAUtil.sendSPAppsQueryFeedsIntent(this) EventBus.builder() .addIndex(ApEventBusIndex()) - .addIndex(ApCoreEventBusIndex()) +// .addIndex(ApCoreEventBusIndex()) .logNoSubscriberMessages(false) .sendNoSubscriberEvent(false) .installDefaultEventBus() diff --git a/app/src/main/java/ac/mdiq/podcini/activity/BugReportActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/BugReportActivity.kt deleted file mode 100644 index 9826da00..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/BugReportActivity.kt +++ /dev/null @@ -1,125 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.DialogInterface -import android.os.Build -import android.os.Bundle -import android.util.Log -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import androidx.core.app.ShareCompat.IntentBuilder -import androidx.core.content.FileProvider -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.snackbar.Snackbar -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.preferences.ThemeSwitcher.getTheme -import ac.mdiq.podcini.core.util.IntentUtils.openInBrowser -import ac.mdiq.podcini.error.CrashReportWriter -import ac.mdiq.podcini.storage.preferences.UserPreferences.getDataFolder -import org.apache.commons.io.IOUtils -import java.io.File -import java.io.FileInputStream -import java.io.IOException -import java.nio.charset.Charset - -/** - * Displays the 'crash report' screen - */ -class BugReportActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - setTheme(getTheme(this)) - super.onCreate(savedInstanceState) - supportActionBar!!.setDisplayShowHomeEnabled(true) - setContentView(R.layout.bug_report) - - var stacktrace = "No crash report recorded" - try { - val crashFile = CrashReportWriter.file - if (crashFile.exists()) { - stacktrace = IOUtils.toString(FileInputStream(crashFile), Charset.forName("UTF-8")) - } else { - Log.d(TAG, stacktrace) - } - } catch (e: IOException) { - e.printStackTrace() - } - - val crashDetailsTextView = findViewById(R.id.crash_report_logs) - crashDetailsTextView.text = """ - ${CrashReportWriter.systemInfo} - - $stacktrace - """.trimIndent() - - findViewById(R.id.btn_open_bug_tracker).setOnClickListener { v: View? -> - openInBrowser( - this@BugReportActivity, "https://github.com/XilinJia/Podcini/issues") - } - - findViewById(R.id.btn_copy_log).setOnClickListener { v: View? -> - val clipboard = getSystemService(CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsTextView.text) - clipboard.setPrimaryClip(clip) - if (Build.VERSION.SDK_INT < 32) { - Snackbar.make(findViewById(android.R.id.content), R.string.copied_to_clipboard, - Snackbar.LENGTH_SHORT).show() - } - } - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - menuInflater.inflate(R.menu.bug_report_options, menu) - return super.onCreateOptionsMenu(menu) - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == R.id.export_logcat) { - val alertBuilder = MaterialAlertDialogBuilder(this) - alertBuilder.setMessage(R.string.confirm_export_log_dialog_message) - alertBuilder.setPositiveButton(R.string.confirm_label) { dialog: DialogInterface, which: Int -> - exportLog() - dialog.dismiss() - } - alertBuilder.setNegativeButton(R.string.cancel_label, null) - alertBuilder.show() - return true - } - return super.onOptionsItemSelected(item) - } - - private fun exportLog() { - try { - val filename = File(getDataFolder(null), "full-logs.txt") - val cmd = "logcat -d -f " + filename.absolutePath - Runtime.getRuntime().exec(cmd) - //share file - try { - val authority = getString(R.string.provider_authority) - val fileUri = FileProvider.getUriForFile(this, authority, filename) - - IntentBuilder(this) - .setType("text/*") - .addStream(fileUri) - .setChooserTitle(R.string.share_file_label) - .startChooser() - } catch (e: Exception) { - e.printStackTrace() - val strResId = R.string.log_file_share_exception - Snackbar.make(findViewById(android.R.id.content), strResId, Snackbar.LENGTH_LONG) - .show() - } - } catch (e: IOException) { - e.printStackTrace() - Snackbar.make(findViewById(android.R.id.content), e.message!!, Snackbar.LENGTH_LONG).show() - } - } - - - companion object { - private const val TAG = "BugReportActivity" - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/MainActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/MainActivity.kt deleted file mode 100644 index d6ebe7ea..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/MainActivity.kt +++ /dev/null @@ -1,682 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.annotation.SuppressLint -import android.content.Context -import android.content.Intent -import android.content.res.Configuration -import android.media.AudioManager -import android.net.Uri -import android.os.Build -import android.os.Bundle -import android.util.DisplayMetrics -import android.util.Log -import android.view.KeyEvent -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup.MarginLayoutParams -import android.widget.EditText -import androidx.appcompat.app.ActionBarDrawerToggle -import androidx.core.graphics.Insets -import androidx.core.view.ViewCompat -import androidx.core.view.WindowCompat -import androidx.core.view.WindowInsetsCompat -import androidx.drawerlayout.widget.DrawerLayout -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentContainerView -import androidx.media3.common.util.UnstableApi -import androidx.recyclerview.widget.RecyclerView -import androidx.work.WorkInfo -import androidx.work.WorkManager -import com.bumptech.glide.Glide -import com.google.android.material.appbar.MaterialToolbar -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback -import com.google.android.material.snackbar.Snackbar -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.preferences.ThemeSwitcher.getNoTitleTheme -import ac.mdiq.podcini.core.receiver.MediaButtonReceiver.Companion.createIntent -import ac.mdiq.podcini.core.storage.DBReader -import ac.mdiq.podcini.core.sync.queue.SynchronizationQueueSink -import ac.mdiq.podcini.core.util.download.FeedUpdateManager -import ac.mdiq.podcini.core.util.download.FeedUpdateManager.restartUpdateAlarm -import ac.mdiq.podcini.core.util.download.FeedUpdateManager.runOnceOrAsk -import ac.mdiq.podcini.dialog.RatingDialog -import ac.mdiq.podcini.event.EpisodeDownloadEvent -import ac.mdiq.podcini.event.FeedUpdateRunningEvent -import ac.mdiq.podcini.event.MessageEvent -import ac.mdiq.podcini.fragment.* -import ac.mdiq.podcini.model.download.DownloadStatus -import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface -import ac.mdiq.podcini.playback.cast.CastEnabledActivity -import ac.mdiq.podcini.storage.preferences.UserPreferences -import ac.mdiq.podcini.storage.preferences.UserPreferences.backButtonOpensDrawer -import ac.mdiq.podcini.storage.preferences.UserPreferences.defaultPage -import ac.mdiq.podcini.storage.preferences.UserPreferences.hiddenDrawerItems -import ac.mdiq.podcini.ui.appstartintent.MainActivityStarter -import ac.mdiq.podcini.ui.common.ThemeUtils.getDrawableFromAttr -import ac.mdiq.podcini.ui.home.HomeFragment -import ac.mdiq.podcini.view.LockableBottomSheetBehavior -import org.apache.commons.lang3.ArrayUtils -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode -import kotlin.math.min - -/** - * The activity that is shown when the user launches the app. - */ -@UnstableApi -class MainActivity : CastEnabledActivity() { -// some device doesn't have a drawer - private var drawerLayout: DrawerLayout? = null - - private lateinit var navDrawer: View - private lateinit var dummyView : View - lateinit var bottomSheet: LockableBottomSheetBehavior<*> - private set - - private var drawerToggle: ActionBarDrawerToggle? = null - - @JvmField - val recycledViewPool: RecyclerView.RecycledViewPool = RecyclerView.RecycledViewPool() - private var lastTheme = 0 - private var navigationBarInsets = Insets.NONE - - @UnstableApi public override fun onCreate(savedInstanceState: Bundle?) { - lastTheme = getNoTitleTheme(this) - setTheme(lastTheme) - - DBReader.updateFeedList() - - if (savedInstanceState != null) { - ensureGeneratedViewIdGreaterThan(savedInstanceState.getInt(KEY_GENERATED_VIEW_ID, 0)) - } - WindowCompat.setDecorFitsSystemWindows(window, false) - super.onCreate(savedInstanceState) - setContentView(R.layout.main) - recycledViewPool.setMaxRecycledViews(R.id.view_type_episode_item, 25) - - dummyView = object : View(this) {} - - drawerLayout = findViewById(R.id.drawer_layout) - navDrawer = findViewById(R.id.navDrawerFragment) - setNavDrawerSize() - - // Consume navigation bar insets - we apply them in setPlayerVisible() - ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main_view)) { v: View?, insets: WindowInsetsCompat -> - navigationBarInsets = insets.getInsets(WindowInsetsCompat.Type.navigationBars()) - updateInsets() - WindowInsetsCompat.Builder(insets) - .setInsets(WindowInsetsCompat.Type.navigationBars(), Insets.NONE) - .build() - } - - val fm = supportFragmentManager - if (fm.findFragmentByTag(MAIN_FRAGMENT_TAG) == null) { - if (UserPreferences.DEFAULT_PAGE_REMEMBER != defaultPage) { - loadFragment(defaultPage, null) - } else { - val lastFragment = NavDrawerFragment.getLastNavFragment(this) - if (ArrayUtils.contains(NavDrawerFragment.NAV_DRAWER_TAGS, lastFragment)) { - loadFragment(lastFragment, null) - } else { - try { - loadFeedFragmentById(lastFragment.toInt().toLong(), null) - } catch (e: NumberFormatException) { - // it's not a number, this happens if we removed - // a label from the NAV_DRAWER_TAGS - // give them a nice default... - loadFragment(HomeFragment.TAG, null) - } - } - } - } - - val transaction = fm.beginTransaction() - val navDrawerFragment = NavDrawerFragment() - transaction.replace(R.id.navDrawerFragment, navDrawerFragment, NavDrawerFragment.TAG) - val audioPlayerFragment = AudioPlayerFragment() - transaction.replace(R.id.audioplayerFragment, audioPlayerFragment, AudioPlayerFragment.TAG) - transaction.commit() - - checkFirstLaunch() - val bottomSheet = findViewById(R.id.audioplayerFragment) - this.bottomSheet = BottomSheetBehavior.from(bottomSheet) as LockableBottomSheetBehavior<*> - this.bottomSheet.isHideable = false - this.bottomSheet.setBottomSheetCallback(bottomSheetCallback) - - restartUpdateAlarm(this, false) - SynchronizationQueueSink.syncNowIfNotSyncedRecently() - - WorkManager.getInstance(this) - .getWorkInfosByTagLiveData(FeedUpdateManager.WORK_TAG_FEED_UPDATE) - .observe(this) { workInfos: List -> - var isRefreshingFeeds = false - for (workInfo in workInfos) { - if (workInfo.state == WorkInfo.State.RUNNING) { - isRefreshingFeeds = true - } else if (workInfo.state == WorkInfo.State.ENQUEUED) { - isRefreshingFeeds = true - } - } - EventBus.getDefault().postSticky(FeedUpdateRunningEvent(isRefreshingFeeds)) - } - WorkManager.getInstance(this) - .getWorkInfosByTagLiveData(DownloadServiceInterface.WORK_TAG) - .observe(this) { workInfos: List -> - val updatedEpisodes: MutableMap = HashMap() - for (workInfo in workInfos) { - var downloadUrl: String? = null - for (tag in workInfo.tags) { - if (tag.startsWith(DownloadServiceInterface.WORK_TAG_EPISODE_URL)) { - downloadUrl = tag.substring(DownloadServiceInterface.WORK_TAG_EPISODE_URL.length) - } - } - if (downloadUrl == null) { - continue - } - var status: Int - status = when (workInfo.state) { - WorkInfo.State.RUNNING -> { - DownloadStatus.STATE_RUNNING - } - WorkInfo.State.ENQUEUED, WorkInfo.State.BLOCKED -> { - DownloadStatus.STATE_QUEUED - } - else -> { - DownloadStatus.STATE_COMPLETED - } - } - var progress = workInfo.progress.getInt(DownloadServiceInterface.WORK_DATA_PROGRESS, -1) - if (progress == -1 && status != DownloadStatus.STATE_COMPLETED) { - status = DownloadStatus.STATE_QUEUED - progress = 0 - } - updatedEpisodes[downloadUrl] = DownloadStatus(status, progress) - } - DownloadServiceInterface.get()?.setCurrentDownloads(updatedEpisodes) - EventBus.getDefault().postSticky(EpisodeDownloadEvent(updatedEpisodes)) - } - } - - override fun onAttachedToWindow() { - super.onAttachedToWindow() - updateInsets() - } - - /** - * View.generateViewId stores the current ID in a static variable. - * When the process is killed, the variable gets reset. - * This makes sure that we do not get ID collisions - * and therefore errors when trying to restore state from another view. - */ - private fun ensureGeneratedViewIdGreaterThan(minimum: Int) { - while (View.generateViewId() <= minimum) { - // Generate new IDs - } - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putInt(KEY_GENERATED_VIEW_ID, View.generateViewId()) - } - - private val bottomSheetCallback: BottomSheetCallback = @UnstableApi object : BottomSheetCallback() { - override fun onStateChanged(view: View, state: Int) { - if (state == BottomSheetBehavior.STATE_COLLAPSED) { - onSlide(view,0.0f) - } else if (state == BottomSheetBehavior.STATE_EXPANDED) { - onSlide(view, 1.0f) - } - } - override fun onSlide(view: View, slideOffset: Float) { - val audioPlayer = supportFragmentManager - .findFragmentByTag(AudioPlayerFragment.TAG) as AudioPlayerFragment? - if (audioPlayer == null) { - return - } - - if (slideOffset == 0.0f) { //STATE_COLLAPSED - audioPlayer.scrollToPage(AudioPlayerFragment.POS_COVER) - } - - audioPlayer.fadePlayerToToolbar(slideOffset) - } - } - - fun setupToolbarToggle(toolbar: MaterialToolbar, displayUpArrow: Boolean) { - if (drawerLayout != null) { // Tablet layout does not have a drawer - if (drawerToggle != null) { - drawerLayout!!.removeDrawerListener(drawerToggle!!) - } - drawerToggle = ActionBarDrawerToggle(this, drawerLayout, toolbar, - R.string.drawer_open, R.string.drawer_close) - drawerLayout!!.addDrawerListener(drawerToggle!!) - drawerToggle!!.syncState() - drawerToggle!!.isDrawerIndicatorEnabled = !displayUpArrow - drawerToggle!!.toolbarNavigationClickListener = View.OnClickListener { v: View? -> supportFragmentManager.popBackStack() } - } else if (!displayUpArrow) { - toolbar.navigationIcon = null - } else { - toolbar.setNavigationIcon(getDrawableFromAttr(this, R.attr.homeAsUpIndicator)) - toolbar.setNavigationOnClickListener { v: View? -> supportFragmentManager.popBackStack() } - } - } - - override fun onDestroy() { - super.onDestroy() - drawerLayout?.removeDrawerListener(drawerToggle!!) - } - - private fun checkFirstLaunch() { - val prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE) - if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) { - restartUpdateAlarm(this, true) - - val edit = prefs.edit() - edit.putBoolean(PREF_IS_FIRST_LAUNCH, false) - edit.apply() - } - } - - val isDrawerOpen: Boolean - get() = drawerLayout?.isDrawerOpen(navDrawer)?:false - - private fun updateInsets() { - setPlayerVisible(findViewById(R.id.audioplayerFragment).visibility == View.VISIBLE) - val playerHeight = resources.getDimension(R.dimen.external_player_height).toInt() - bottomSheet.peekHeight = playerHeight + navigationBarInsets.bottom - } - - fun setPlayerVisible(visible: Boolean) { - bottomSheet.setLocked(!visible) - if (visible) { - bottomSheetCallback.onStateChanged(dummyView, bottomSheet.state) // Update toolbar visibility - } else { - bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED) - } - val mainView = findViewById(R.id.main_view) - val params = mainView.layoutParams as MarginLayoutParams - val externalPlayerHeight = resources.getDimension(R.dimen.external_player_height).toInt() - params.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, - navigationBarInsets.bottom + (if (visible) externalPlayerHeight else 0)) - mainView.layoutParams = params - val playerView = findViewById(R.id.playerFragment) - val playerParams = playerView.layoutParams as MarginLayoutParams - playerParams.setMargins(navigationBarInsets.left, 0, navigationBarInsets.right, 0) - playerView.layoutParams = playerParams - findViewById(R.id.audioplayerFragment).visibility = if (visible) View.VISIBLE else View.GONE - } - - fun loadFragment(tag: String?, args: Bundle?) { - var tag = tag - var args = args - Log.d(TAG, "loadFragment(tag: $tag, args: $args)") - val fragment: Fragment - when (tag) { - HomeFragment.TAG -> fragment = HomeFragment() - QueueFragment.TAG -> fragment = QueueFragment() - InboxFragment.TAG -> fragment = InboxFragment() - AllEpisodesFragment.TAG -> fragment = AllEpisodesFragment() - CompletedDownloadsFragment.TAG -> fragment = CompletedDownloadsFragment() - PlaybackHistoryFragment.TAG -> fragment = PlaybackHistoryFragment() - AddFeedFragment.TAG -> fragment = AddFeedFragment() - SubscriptionFragment.TAG -> fragment = SubscriptionFragment() - else -> { - // default to home screen - fragment = HomeFragment() - tag = HomeFragment.TAG - args = null - } - } - if (args != null) { - fragment.arguments = args - } - NavDrawerFragment.saveLastNavFragment(this, tag) - loadFragment(fragment) - } - - fun loadFeedFragmentById(feedId: Long, args: Bundle?) { - val fragment: Fragment = FeedItemlistFragment.newInstance(feedId) - if (args != null) { - fragment.arguments = args - } - NavDrawerFragment.saveLastNavFragment(this, feedId.toString()) - loadFragment(fragment) - } - - private fun loadFragment(fragment: Fragment) { - val fragmentManager = supportFragmentManager - // clear back stack - for (i in 0 until fragmentManager.backStackEntryCount) { - fragmentManager.popBackStack() - } - val t = fragmentManager.beginTransaction() - t.replace(R.id.main_view, fragment, MAIN_FRAGMENT_TAG) - fragmentManager.popBackStack() - // TODO: we have to allow state loss here - // since this function can get called from an AsyncTask which - // could be finishing after our app has already committed state - // and is about to get shutdown. What we *should* do is - // not commit anything in an AsyncTask, but that's a bigger - // change than we want now. - t.commitAllowingStateLoss() - - // Tablet layout does not have a drawer - drawerLayout?.closeDrawer(navDrawer) - } - - @JvmOverloads - fun loadChildFragment(fragment: Fragment, transition: TransitionEffect? = TransitionEffect.NONE) { - val transaction = supportFragmentManager.beginTransaction() - - when (transition) { - TransitionEffect.FADE -> transaction.setCustomAnimations(R.anim.fade_in, R.anim.fade_out) - TransitionEffect.SLIDE -> transaction.setCustomAnimations( - R.anim.slide_right_in, - R.anim.slide_left_out, - R.anim.slide_left_in, - R.anim.slide_right_out) - TransitionEffect.NONE -> {} - null -> {} - } - transaction - .hide(supportFragmentManager.findFragmentByTag(MAIN_FRAGMENT_TAG)!!) - .add(R.id.main_view, fragment, MAIN_FRAGMENT_TAG) - .addToBackStack(null) - .commit() - } - - override fun onPostCreate(savedInstanceState: Bundle?) { - super.onPostCreate(savedInstanceState) - if (drawerToggle != null) { // Tablet layout does not have a drawer - drawerToggle!!.syncState() - } - } - - override fun onConfigurationChanged(newConfig: Configuration) { - super.onConfigurationChanged(newConfig) - if (drawerToggle != null) { // Tablet layout does not have a drawer - drawerToggle!!.onConfigurationChanged(newConfig) - } - setNavDrawerSize() - } - - private fun setNavDrawerSize() { - if (drawerToggle == null) { // Tablet layout does not have a drawer - return - } - val screenPercent = resources.getInteger(R.integer.nav_drawer_screen_size_percent) * 0.01f - val width = (screenWidth * screenPercent).toInt() - val maxWidth = resources.getDimension(R.dimen.nav_drawer_max_screen_size).toInt() - - navDrawer.layoutParams.width = min(width.toDouble(), maxWidth.toDouble()).toInt() - } - - private val screenWidth: Int - get() { - val displayMetrics = DisplayMetrics() - windowManager.defaultDisplay.getMetrics(displayMetrics) - return displayMetrics.widthPixels - } - - override fun onRestoreInstanceState(savedInstanceState: Bundle) { - super.onRestoreInstanceState(savedInstanceState) - - if (bottomSheet.state == BottomSheetBehavior.STATE_EXPANDED) { - bottomSheetCallback.onSlide(dummyView, 1.0f) - } - } - - public override fun onStart() { - super.onStart() - EventBus.getDefault().register(this) - RatingDialog.init(this) - } - - override fun onResume() { - super.onResume() - handleNavIntent() - RatingDialog.check() - - if (lastTheme != getNoTitleTheme(this)) { - finish() - startActivity(Intent(this, MainActivity::class.java)) - } - if (hiddenDrawerItems!!.contains(NavDrawerFragment.getLastNavFragment(this))) { - loadFragment(defaultPage, null) - } - } - - @Deprecated("Deprecated in Java") - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - super.onActivityResult(requestCode, resultCode, data) - lastTheme = getNoTitleTheme(this) // Don't recreate activity when a result is pending - } - - override fun onStop() { - super.onStop() - EventBus.getDefault().unregister(this) - } - - override fun onTrimMemory(level: Int) { - super.onTrimMemory(level) - Glide.get(this).trimMemory(level) - } - - override fun onLowMemory() { - super.onLowMemory() - Glide.get(this).clearMemory() - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (drawerToggle != null && drawerToggle!!.onOptionsItemSelected(item)) { // Tablet layout does not have a drawer - return true - } else if (item.itemId == android.R.id.home) { - if (supportFragmentManager.backStackEntryCount > 0) { - supportFragmentManager.popBackStack() - } - return true - } else { - return super.onOptionsItemSelected(item) - } - } - - @Deprecated("Deprecated in Java") - override fun onBackPressed() { - if (isDrawerOpen) { - drawerLayout?.closeDrawer(navDrawer) - } else if (bottomSheet.state == BottomSheetBehavior.STATE_EXPANDED) { - bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED) - } else if (supportFragmentManager.backStackEntryCount != 0) { - super.onBackPressed() - } else { - val toPage = defaultPage - if (NavDrawerFragment.getLastNavFragment(this) == toPage || UserPreferences.DEFAULT_PAGE_REMEMBER == toPage) { - if (backButtonOpensDrawer()) { - drawerLayout?.openDrawer(navDrawer) - } else { - super.onBackPressed() - } - } else { - loadFragment(toPage, null) - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(event: MessageEvent) { - Log.d(TAG, "onEvent($event)") - - val snackbar = showSnackbarAbovePlayer(event.message, Snackbar.LENGTH_LONG) - if (event.action != null) { - snackbar.setAction(event.actionText) { v: View? -> event.action!!.accept(this) } - } - } - - private fun handleNavIntent() { - Log.d(TAG, "handleNavIntent()") - val intent = intent - if (intent.hasExtra(EXTRA_FEED_ID)) { - val feedId = intent.getLongExtra(EXTRA_FEED_ID, 0) - val args = intent.getBundleExtra(MainActivityStarter.EXTRA_FRAGMENT_ARGS) - if (feedId > 0) { - val startedFromSearch = intent.getBooleanExtra(EXTRA_STARTED_FROM_SEARCH, false) - val addToBackStack = intent.getBooleanExtra(EXTRA_ADD_TO_BACK_STACK, false) - if (startedFromSearch || addToBackStack) { - loadChildFragment(FeedItemlistFragment.newInstance(feedId)) - } else { - loadFeedFragmentById(feedId, args) - } - } - bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED) - } else if (intent.hasExtra(MainActivityStarter.EXTRA_FRAGMENT_TAG)) { - val tag = intent.getStringExtra(MainActivityStarter.EXTRA_FRAGMENT_TAG) - val args = intent.getBundleExtra(MainActivityStarter.EXTRA_FRAGMENT_ARGS) - if (tag != null) { - loadFragment(tag, args) - } - bottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED) - } else if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_PLAYER, false)) { - bottomSheet.state = BottomSheetBehavior.STATE_EXPANDED - bottomSheetCallback.onSlide(dummyView, 1.0f) - } else { - handleDeeplink(intent.data) - } - - if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_DRAWER, false)) { - drawerLayout?.open() - } - if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_DOWNLOAD_LOGS, false)) { - DownloadLogFragment().show(supportFragmentManager, null) - } - if (intent.getBooleanExtra(EXTRA_REFRESH_ON_START, false)) { - runOnceOrAsk(this) - } - // to avoid handling the intent twice when the configuration changes - setIntent(Intent(this@MainActivity, MainActivity::class.java)) - } - - @SuppressLint("MissingSuperCall") - override fun onNewIntent(intent: Intent) { - super.onNewIntent(intent) - setIntent(intent) - handleNavIntent() - } - - fun showSnackbarAbovePlayer(text: CharSequence?, duration: Int): Snackbar { - val s: Snackbar - if (bottomSheet.state == BottomSheetBehavior.STATE_COLLAPSED) { - s = Snackbar.make(findViewById(R.id.main_view), text!!, duration) - if (findViewById(R.id.audioplayerFragment).visibility == View.VISIBLE) { - s.setAnchorView(findViewById(R.id.audioplayerFragment)) - } - } else { - s = Snackbar.make(findViewById(android.R.id.content), text!!, duration) - } - s.show() - return s - } - - fun showSnackbarAbovePlayer(text: Int, duration: Int): Snackbar { - return showSnackbarAbovePlayer(resources.getText(text), duration) - } - - /** - * Handles the deep link incoming via App Actions. - * Performs an in-app search or opens the relevant feature of the app - * depending on the query. - * - * @param uri incoming deep link - */ - private fun handleDeeplink(uri: Uri?) { - if (uri == null || uri.path == null) { - return - } - Log.d(TAG, "Handling deeplink: $uri") - when (uri.path) { - "/deeplink/search" -> { - val query = uri.getQueryParameter("query") ?: return - - this.loadChildFragment(SearchFragment.newInstance(query)) - } - "/deeplink/main" -> { - val feature = uri.getQueryParameter("page") ?: return - when (feature) { - "DOWNLOADS" -> loadFragment(CompletedDownloadsFragment.TAG, null) - "HISTORY" -> loadFragment(PlaybackHistoryFragment.TAG, null) - "EPISODES" -> loadFragment(AllEpisodesFragment.TAG, null) - "QUEUE" -> loadFragment(QueueFragment.TAG, null) - "SUBSCRIPTIONS" -> loadFragment(SubscriptionFragment.TAG, null) - else -> { - showSnackbarAbovePlayer(getString(R.string.app_action_not_found)+feature, Snackbar.LENGTH_LONG) - return - } - } - } - else -> {} - } - } - - //Hardware keyboard support - override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { - val currentFocus = currentFocus - if (currentFocus is EditText) { - return super.onKeyUp(keyCode, event) - } - - val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager - var customKeyCode: Int? = null - EventBus.getDefault().post(event) - - when (keyCode) { - KeyEvent.KEYCODE_P -> customKeyCode = KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE - KeyEvent.KEYCODE_J, KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_COMMA -> customKeyCode = - KeyEvent.KEYCODE_MEDIA_REWIND - KeyEvent.KEYCODE_K, KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_PERIOD -> customKeyCode = - KeyEvent.KEYCODE_MEDIA_FAST_FORWARD - KeyEvent.KEYCODE_PLUS, KeyEvent.KEYCODE_W -> { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI) - return true - } - KeyEvent.KEYCODE_MINUS, KeyEvent.KEYCODE_S -> { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI) - return true - } - KeyEvent.KEYCODE_M -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_TOGGLE_MUTE, AudioManager.FLAG_SHOW_UI) - return true - } - } - if (customKeyCode != null) { - sendBroadcast(createIntent(this, customKeyCode)) - return true - } - return super.onKeyUp(keyCode, event) - } - - companion object { - private const val TAG = "MainActivity" - const val MAIN_FRAGMENT_TAG: String = "main" - - const val PREF_NAME: String = "MainActivityPrefs" - const val PREF_IS_FIRST_LAUNCH: String = "prefMainActivityIsFirstLaunch" - - const val EXTRA_FEED_ID: String = "fragment_feed_id" - const val EXTRA_REFRESH_ON_START: String = "refresh_on_start" - const val EXTRA_STARTED_FROM_SEARCH: String = "started_from_search" - const val EXTRA_ADD_TO_BACK_STACK: String = "add_to_back_stack" - const val KEY_GENERATED_VIEW_ID: String = "generated_view_id" - - @JvmStatic - fun getIntentToOpenFeed(context: Context, feedId: Long): Intent { - val intent = Intent(context.applicationContext, MainActivity::class.java) - intent.putExtra(EXTRA_FEED_ID, feedId) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - return intent - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/OnlineFeedViewActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/OnlineFeedViewActivity.kt deleted file mode 100644 index 74a3c19f..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/OnlineFeedViewActivity.kt +++ /dev/null @@ -1,706 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.app.Dialog -import android.content.Context -import android.content.DialogInterface -import android.content.Intent -import android.graphics.LightingColorFilter -import android.os.Bundle -import android.text.Spannable -import android.text.SpannableString -import android.text.TextUtils -import android.text.style.ForegroundColorSpan -import android.util.Log -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.ArrayAdapter -import android.widget.Toast -import androidx.annotation.UiThread -import androidx.appcompat.app.AppCompatActivity -import androidx.core.app.NavUtils -import androidx.media3.common.util.UnstableApi -import com.bumptech.glide.Glide -import com.bumptech.glide.request.RequestOptions -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.snackbar.Snackbar -import ac.mdiq.podcini.R -import ac.mdiq.podcini.adapter.FeedItemlistDescriptionAdapter -import ac.mdiq.podcini.core.feed.FeedUrlNotFoundException -import ac.mdiq.podcini.core.preferences.PlaybackPreferences.Companion.currentlyPlayingMediaType -import ac.mdiq.podcini.core.preferences.PlaybackPreferences.Companion.writeNoMediaPlaying -import ac.mdiq.podcini.core.preferences.ThemeSwitcher.getTranslucentTheme -import ac.mdiq.podcini.core.service.download.DownloadRequestCreator.create -import ac.mdiq.podcini.core.service.download.Downloader -import ac.mdiq.podcini.core.service.download.HttpDownloader -import ac.mdiq.podcini.core.service.playback.PlaybackServiceInterface -import ac.mdiq.podcini.core.storage.DBReader -import ac.mdiq.podcini.core.storage.DBTasks -import ac.mdiq.podcini.core.storage.DBWriter -import ac.mdiq.podcini.core.util.DownloadErrorLabel.from -import ac.mdiq.podcini.core.util.IntentUtils.sendLocalBroadcast -import ac.mdiq.podcini.core.util.syndication.FeedDiscoverer -import ac.mdiq.podcini.core.util.syndication.HtmlToPlainText -import ac.mdiq.podcini.databinding.EditTextDialogBinding -import ac.mdiq.podcini.databinding.OnlinefeedviewActivityBinding -import ac.mdiq.podcini.databinding.OnlinefeedviewHeaderBinding -import ac.mdiq.podcini.dialog.AuthenticationDialog -import ac.mdiq.podcini.event.EpisodeDownloadEvent -import ac.mdiq.podcini.event.FeedListUpdateEvent -import ac.mdiq.podcini.event.PlayerStatusEvent -import ac.mdiq.podcini.model.download.DownloadError -import ac.mdiq.podcini.model.download.DownloadResult -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.model.playback.RemoteMedia -import ac.mdiq.podcini.net.common.UrlChecker.prepareUrl -import ac.mdiq.podcini.net.discovery.CombinedSearcher -import ac.mdiq.podcini.net.discovery.PodcastSearcherRegistry -import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface -import ac.mdiq.podcini.parser.feed.FeedHandler -import ac.mdiq.podcini.parser.feed.FeedHandlerResult -import ac.mdiq.podcini.parser.feed.UnsupportedFeedtypeException -import ac.mdiq.podcini.storage.preferences.UserPreferences.isEnableAutodownload -import ac.mdiq.podcini.ui.common.ThemeUtils.getColorFromAttr -import ac.mdiq.podcini.ui.glide.FastBlurTransformation -import io.reactivex.Maybe -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.observers.DisposableMaybeObserver -import io.reactivex.schedulers.Schedulers -import org.apache.commons.lang3.StringUtils -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode -import java.io.File -import java.io.IOException -import kotlin.concurrent.Volatile - -/** - * Downloads a feed from a feed URL and parses it. Subclasses can display the - * feed object that was parsed. This activity MUST be started with a given URL - * or an Exception will be thrown. - * - * - * If the feed cannot be downloaded or parsed, an error dialog will be displayed - * and the activity will finish as soon as the error dialog is closed. - */ -class OnlineFeedViewActivity : AppCompatActivity() { - @Volatile - private var feeds: List? = null - private var selectedDownloadUrl: String? = null - private var downloader: Downloader? = null - private var username: String? = null - private var password: String? = null - - private var isPaused = false - private var didPressSubscribe = false - private var isFeedFoundBySearch = false - - private var dialog: Dialog? = null - - private var download: Disposable? = null - private var parser: Disposable? = null - private var updater: Disposable? = null - - private lateinit var headerBinding: OnlinefeedviewHeaderBinding - private lateinit var viewBinding: OnlinefeedviewActivityBinding - - override fun onCreate(savedInstanceState: Bundle?) { - setTheme(getTranslucentTheme(this)) - super.onCreate(savedInstanceState) - - viewBinding = OnlinefeedviewActivityBinding.inflate(layoutInflater) - setContentView(viewBinding.root) - - viewBinding.transparentBackground.setOnClickListener { v: View? -> finish() } - viewBinding.closeButton.setOnClickListener { view: View? -> finish() } - viewBinding.card.setOnClickListener(null) - viewBinding.card.setCardBackgroundColor(getColorFromAttr(this, R.attr.colorSurface)) - headerBinding = OnlinefeedviewHeaderBinding.inflate(layoutInflater) - - var feedUrl: String? = null - if (intent.hasExtra(ARG_FEEDURL)) { - feedUrl = intent.getStringExtra(ARG_FEEDURL) - } else if (TextUtils.equals(intent.action, Intent.ACTION_SEND)) { - feedUrl = intent.getStringExtra(Intent.EXTRA_TEXT) - } else if (TextUtils.equals(intent.action, Intent.ACTION_VIEW)) { - feedUrl = intent.dataString - } - - if (feedUrl == null) { - Log.e(TAG, "feedUrl is null.") - showNoPodcastFoundError() - } else { - Log.d(TAG, "Activity was started with url $feedUrl") - setLoadingLayout() - // Remove subscribeonandroid.com from feed URL in order to subscribe to the actual feed URL - if (feedUrl.contains("subscribeonandroid.com")) { - feedUrl = feedUrl.replaceFirst("((www.)?(subscribeonandroid.com/))".toRegex(), "") - } - if (savedInstanceState != null) { - username = savedInstanceState.getString("username") - password = savedInstanceState.getString("password") - } - lookupUrlAndDownload(feedUrl) - } - } - - private fun showNoPodcastFoundError() { - runOnUiThread { - MaterialAlertDialogBuilder(this@OnlineFeedViewActivity) - .setNeutralButton(android.R.string.ok) { dialog: DialogInterface?, which: Int -> finish() } - .setTitle(R.string.error_label) - .setMessage(R.string.null_value_podcast_error) - .setOnDismissListener { dialog1: DialogInterface? -> - setResult(RESULT_ERROR) - finish() - } - .show() - } - } - - /** - * Displays a progress indicator. - */ - private fun setLoadingLayout() { - viewBinding.progressBar.visibility = View.VISIBLE - viewBinding.feedDisplayContainer.visibility = View.GONE - } - - override fun onStart() { - super.onStart() - isPaused = false - EventBus.getDefault().register(this) - } - - override fun onStop() { - super.onStop() - isPaused = true - EventBus.getDefault().unregister(this) - if (downloader != null && !downloader!!.isFinished) { - downloader!!.cancel() - } - if (dialog != null && dialog!!.isShowing) { - dialog!!.dismiss() - } - } - - public override fun onDestroy() { - super.onDestroy() - updater?.dispose() - download?.dispose() - parser?.dispose() - } - - override fun onSaveInstanceState(outState: Bundle) { - super.onSaveInstanceState(outState) - outState.putString("username", username) - outState.putString("password", password) - } - - private fun resetIntent(url: String) { - val intent = Intent() - intent.putExtra(ARG_FEEDURL, url) - setIntent(intent) - } - - override fun finish() { - super.finish() - overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out) - } - - @UnstableApi override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - val destIntent = Intent(this, MainActivity::class.java) - if (NavUtils.shouldUpRecreateTask(this, destIntent)) { - startActivity(destIntent) - } else { - NavUtils.navigateUpFromSameTask(this) - } - return true - } - return super.onOptionsItemSelected(item) - } - - private fun lookupUrlAndDownload(url: String) { - download = PodcastSearcherRegistry.lookupUrl(url) - ?.subscribeOn(Schedulers.io()) - ?.observeOn(Schedulers.io()) - ?.subscribe({ url1: String -> this.startFeedDownload(url1) }, - { error: Throwable? -> - if (error is FeedUrlNotFoundException) { - tryToRetrieveFeedUrlBySearch(error) - } else { - showNoPodcastFoundError() - Log.e(TAG, Log.getStackTraceString(error)) - } - }) - } - - private fun tryToRetrieveFeedUrlBySearch(error: FeedUrlNotFoundException) { - Log.d(TAG, "Unable to retrieve feed url, trying to retrieve feed url from search") - val url = searchFeedUrlByTrackName(error.trackName, error.artistName) - if (url != null) { - Log.d(TAG, "Successfully retrieve feed url") - isFeedFoundBySearch = true - startFeedDownload(url) - } else { - showNoPodcastFoundError() - Log.d(TAG, "Failed to retrieve feed url") - } - } - - private fun searchFeedUrlByTrackName(trackName: String, artistName: String): String? { - val searcher = CombinedSearcher() - val query = "$trackName $artistName" - val results = searcher.search(query)?.blockingGet() - if (results.isNullOrEmpty()) return null - for (result in results) { - if (result?.feedUrl != null && result.author != null && - result.author.equals(artistName, ignoreCase = true) && - result.title.equals(trackName, ignoreCase = true)) { - return result.feedUrl - } - } - return null - } - - private fun startFeedDownload(url: String) { - Log.d(TAG, "Starting feed download") - selectedDownloadUrl = prepareUrl(url) - val request = create(Feed(selectedDownloadUrl, null)) - .withAuthentication(username, password) - .withInitiatedByUser(true) - .build() - - download = Observable.fromCallable { - feeds = DBReader.getFeedList() - downloader = HttpDownloader(request) - downloader?.call() - downloader?.result - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ status: DownloadResult? -> if (request.destination != null) checkDownloadResult(status, request.destination!!) }, - { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) }) - } - - private fun checkDownloadResult(status: DownloadResult?, destination: String) { - if (status == null) return - if (status.isSuccessful) { - parseFeed(destination) - } else if (status.reason == DownloadError.ERROR_UNAUTHORIZED) { - if (!isFinishing && !isPaused) { - if (username != null && password != null) { - Toast.makeText(this, R.string.download_error_unauthorized, Toast.LENGTH_LONG).show() - } - if (downloader?.downloadRequest?.source != null) { - dialog = FeedViewAuthenticationDialog(this@OnlineFeedViewActivity, - R.string.authentication_notification_title, downloader!!.downloadRequest.source!!).create() - dialog?.show() - } - } - } else { - showErrorDialog(getString(from(status.reason)), status.reasonDetailed) - } - } - - @UnstableApi @Subscribe - fun onFeedListChanged(event: FeedListUpdateEvent?) { - updater = Observable.fromCallable { DBReader.getFeedList() } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { feeds: List? -> - this@OnlineFeedViewActivity.feeds = feeds - handleUpdatedFeedStatus() - }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) } - ) - } - - @UnstableApi @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) - fun onEventMainThread(event: EpisodeDownloadEvent?) { - handleUpdatedFeedStatus() - } - - private fun parseFeed(destination: String) { - Log.d(TAG, "Parsing feed") - parser = Maybe.fromCallable { doParseFeed(destination) } - .subscribeOn(Schedulers.computation()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribeWith(object : DisposableMaybeObserver() { - - @UnstableApi override fun onSuccess(result: FeedHandlerResult) { - showFeedInformation(result.feed, result.alternateFeedUrls) - } - - override fun onComplete() { - // Ignore null result: We showed the discovery dialog. - } - - override fun onError(error: Throwable) { - showErrorDialog(error.message, "") - Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error)) - } - }) - } - - /** - * Try to parse the feed. - * @return The FeedHandlerResult if successful. - * Null if unsuccessful but we started another attempt. - * @throws Exception If unsuccessful but we do not know a resolution. - */ - @Throws(Exception::class) - private fun doParseFeed(destination: String): FeedHandlerResult? { - val handler = FeedHandler() - val feed = Feed(selectedDownloadUrl, null) - feed.file_url = destination - val destinationFile = File(destination) - return try { - handler.parseFeed(feed) - } catch (e: UnsupportedFeedtypeException) { - Log.d(TAG, "Unsupported feed type detected") - if ("html".equals(e.rootElement, ignoreCase = true)) { - if (selectedDownloadUrl != null) { - val dialogShown = showFeedDiscoveryDialog(destinationFile, selectedDownloadUrl!!) - if (dialogShown) { - null // Should not display an error message - } else { - throw UnsupportedFeedtypeException(getString(R.string.download_error_unsupported_type_html)) - } - } else null - } else { - throw e - } - } catch (e: Exception) { - Log.e(TAG, Log.getStackTraceString(e)) - throw e - } finally { - val rc = destinationFile.delete() - Log.d(TAG, "Deleted feed source file. Result: $rc") - } - } - - /** - * Called when feed parsed successfully. - * This method is executed on the GUI thread. - */ - @UnstableApi private fun showFeedInformation(feed: Feed, alternateFeedUrls: Map) { - viewBinding.progressBar.visibility = View.GONE - viewBinding.feedDisplayContainer.visibility = View.VISIBLE - if (isFeedFoundBySearch) { - val resId = R.string.no_feed_url_podcast_found_by_search - Snackbar.make(findViewById(android.R.id.content), resId, Snackbar.LENGTH_LONG).show() - } - - viewBinding.backgroundImage.colorFilter = LightingColorFilter(-0x7d7d7e, 0x000000) - - viewBinding.listView.addHeaderView(headerBinding.root) - viewBinding.listView.setSelector(android.R.color.transparent) - viewBinding.listView.adapter = FeedItemlistDescriptionAdapter(this, 0, feed.items) - - if (StringUtils.isNotBlank(feed.imageUrl)) { - Glide.with(this) - .load(feed.imageUrl) - .apply(RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .fitCenter() - .dontAnimate()) - .into(viewBinding.coverImage) - Glide.with(this) - .load(feed.imageUrl) - .apply(RequestOptions() - .placeholder(R.color.image_readability_tint) - .error(R.color.image_readability_tint) - .transform(FastBlurTransformation()) - .dontAnimate()) - .into(viewBinding.backgroundImage) - } - - viewBinding.titleLabel.text = feed.title - viewBinding.authorLabel.text = feed.author - headerBinding.txtvDescription.text = HtmlToPlainText.getPlainText(feed.description?:"") - - viewBinding.subscribeButton.setOnClickListener { v: View? -> - if (feedInFeedlist()) { - openFeed() - } else { - DBTasks.updateFeed(this, feed, false) - didPressSubscribe = true - handleUpdatedFeedStatus() - } - } - - viewBinding.stopPreviewButton.setOnClickListener { v: View? -> - writeNoMediaPlaying() - sendLocalBroadcast(this, PlaybackServiceInterface.ACTION_SHUTDOWN_PLAYBACK_SERVICE) - } - - if (isEnableAutodownload) { - val preferences = getSharedPreferences(PREFS, MODE_PRIVATE) - viewBinding.autoDownloadCheckBox.isChecked = preferences.getBoolean(PREF_LAST_AUTO_DOWNLOAD, true) - } - - headerBinding.txtvDescription.maxLines = DESCRIPTION_MAX_LINES_COLLAPSED - headerBinding.txtvDescription.setOnClickListener { v: View? -> - if (headerBinding.txtvDescription.maxLines > DESCRIPTION_MAX_LINES_COLLAPSED) { - headerBinding.txtvDescription.maxLines = DESCRIPTION_MAX_LINES_COLLAPSED - } else { - headerBinding.txtvDescription.maxLines = 2000 - } - } - - if (alternateFeedUrls.isEmpty()) { - viewBinding.alternateUrlsSpinner.visibility = View.GONE - } else { - viewBinding.alternateUrlsSpinner.visibility = View.VISIBLE - - val alternateUrlsList: MutableList = ArrayList() - val alternateUrlsTitleList: MutableList = ArrayList() - - if (feed.download_url != null) alternateUrlsList.add(feed.download_url!!) - alternateUrlsTitleList.add(feed.title) - - - alternateUrlsList.addAll(alternateFeedUrls.keys) - for (url in alternateFeedUrls.keys) { - alternateUrlsTitleList.add(alternateFeedUrls[url]) - } - - val adapter: ArrayAdapter = object : ArrayAdapter(this, - R.layout.alternate_urls_item, alternateUrlsTitleList) { - override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { - // reusing the old view causes a visual bug on Android <= 10 - return super.getDropDownView(position, null, parent) - } - } - - adapter.setDropDownViewResource(R.layout.alternate_urls_dropdown_item) - viewBinding.alternateUrlsSpinner.adapter = adapter - viewBinding.alternateUrlsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>?, view: View, position: Int, id: Long) { - selectedDownloadUrl = alternateUrlsList[position] - } - - override fun onNothingSelected(parent: AdapterView<*>?) { - } - } - } - handleUpdatedFeedStatus() - } - - @UnstableApi private fun openFeed() { - // feed.getId() is always 0, we have to retrieve the id from the feed list from - // the database - val intent = MainActivity.getIntentToOpenFeed(this, feedId) - intent.putExtra(MainActivity.EXTRA_STARTED_FROM_SEARCH, - getIntent().getBooleanExtra(MainActivity.EXTRA_STARTED_FROM_SEARCH, false)) - finish() - startActivity(intent) - } - - @UnstableApi private fun handleUpdatedFeedStatus() { - val dli = DownloadServiceInterface.get() - if (dli == null || selectedDownloadUrl == null) return - - if (dli.isDownloadingEpisode(selectedDownloadUrl!!)) { - viewBinding.subscribeButton.isEnabled = false - viewBinding.subscribeButton.setText(R.string.subscribing_label) - } else if (feedInFeedlist()) { - viewBinding.subscribeButton.isEnabled = true - viewBinding.subscribeButton.setText(R.string.open_podcast) - if (didPressSubscribe) { - didPressSubscribe = false - - val feed1 = DBReader.getFeed(feedId)?: return - val feedPreferences = feed1.preferences - if (feedPreferences != null) { - if (isEnableAutodownload) { - val autoDownload = viewBinding.autoDownloadCheckBox.isChecked - feedPreferences.autoDownload = autoDownload - - val preferences = getSharedPreferences(PREFS, MODE_PRIVATE) - val editor = preferences.edit() - editor.putBoolean(PREF_LAST_AUTO_DOWNLOAD, autoDownload) - editor.apply() - } - if (username != null) { - feedPreferences.username = username - feedPreferences.password = password - } - DBWriter.setFeedPreferences(feedPreferences) - } - openFeed() - } - } else { - viewBinding.subscribeButton.isEnabled = true - viewBinding.subscribeButton.setText(R.string.subscribe_label) - if (isEnableAutodownload) { - viewBinding.autoDownloadCheckBox.visibility = View.VISIBLE - } - } - } - - private fun feedInFeedlist(): Boolean { - return feedId != 0L - } - - private val feedId: Long - get() { - if (feeds == null) { - return 0 - } - for (f in feeds!!) { - if (f.download_url == selectedDownloadUrl) { - return f.id - } - } - return 0 - } - - @UiThread - private fun showErrorDialog(errorMsg: String?, details: String) { - if (!isFinishing && !isPaused) { - val builder = MaterialAlertDialogBuilder(this) - builder.setTitle(R.string.error_label) - if (errorMsg != null) { - val total = """ - $errorMsg - - $details - """.trimIndent() - val errorMessage = SpannableString(total) - errorMessage.setSpan(ForegroundColorSpan(-0x77777778), - errorMsg.length, total.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - builder.setMessage(errorMessage) - } else { - builder.setMessage(R.string.download_error_error_unknown) - } - builder.setPositiveButton(android.R.string.ok) { dialog: DialogInterface, which: Int -> dialog.cancel() } - if (intent.getBooleanExtra(ARG_WAS_MANUAL_URL, false)) { - builder.setNeutralButton(R.string.edit_url_menu) { dialog: DialogInterface?, which: Int -> editUrl() } - } - builder.setOnCancelListener { dialog: DialogInterface? -> - setResult(RESULT_ERROR) - finish() - } - if (dialog != null && dialog!!.isShowing) { - dialog!!.dismiss() - } - dialog = builder.show() - } - } - - private fun editUrl() { - val builder = MaterialAlertDialogBuilder(this) - builder.setTitle(R.string.edit_url_menu) - val dialogBinding = EditTextDialogBinding.inflate(layoutInflater) - if (downloader != null) { - dialogBinding.urlEditText.setText(downloader!!.downloadRequest.source) - } - builder.setView(dialogBinding.root) - builder.setPositiveButton(R.string.confirm_label) { dialog: DialogInterface?, which: Int -> - setLoadingLayout() - lookupUrlAndDownload(dialogBinding.urlEditText.text.toString()) - } - builder.setNegativeButton(R.string.cancel_label) { dialog1: DialogInterface, which: Int -> dialog1.cancel() } - builder.setOnCancelListener { dialog1: DialogInterface? -> - setResult(RESULT_ERROR) - finish() - } - builder.show() - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun playbackStateChanged(event: PlayerStatusEvent?) { - val isPlayingPreview = currentlyPlayingMediaType == RemoteMedia.PLAYABLE_TYPE_REMOTE_MEDIA.toLong() - viewBinding.stopPreviewButton.visibility = if (isPlayingPreview) View.VISIBLE else View.GONE - } - - /** - * - * @return true if a FeedDiscoveryDialog is shown, false otherwise (e.g., due to no feed found). - */ - private fun showFeedDiscoveryDialog(feedFile: File, baseUrl: String): Boolean { - val fd = FeedDiscoverer() - val urlsMap: Map - try { - urlsMap = fd.findLinks(feedFile, baseUrl) - if (urlsMap.isEmpty()) { - return false - } - } catch (e: IOException) { - e.printStackTrace() - return false - } - - if (isPaused || isFinishing) { - return false - } - - val titles: MutableList = ArrayList() - - val urls: List = ArrayList(urlsMap.keys) - for (url in urls) { - titles.add(urlsMap[url]) - } - - if (urls.size == 1) { - // Skip dialog and display the item directly - resetIntent(urls[0]) - startFeedDownload(urls[0]) - return true - } - - val adapter = ArrayAdapter(this@OnlineFeedViewActivity, - R.layout.ellipsize_start_listitem, R.id.txtvTitle, titles) - val onClickListener = DialogInterface.OnClickListener { dialog: DialogInterface, which: Int -> - val selectedUrl = urls[which] - dialog.dismiss() - resetIntent(selectedUrl) - startFeedDownload(selectedUrl) - } - - val ab = MaterialAlertDialogBuilder(this@OnlineFeedViewActivity) - .setTitle(R.string.feeds_label) - .setCancelable(true) - .setOnCancelListener { dialog: DialogInterface? -> finish() } - .setAdapter(adapter, onClickListener) - - runOnUiThread { - if (dialog != null && dialog!!.isShowing) { - dialog!!.dismiss() - } - dialog = ab.show() - } - return true - } - - private inner class FeedViewAuthenticationDialog(context: Context?, titleRes: Int, private val feedUrl: String) : - AuthenticationDialog(context, titleRes, true, username, password) { - override fun onCancelled() { - super.onCancelled() - finish() - } - - override fun onConfirmed(username: String, password: String) { - this@OnlineFeedViewActivity.username = username - this@OnlineFeedViewActivity.password = password - startFeedDownload(feedUrl) - } - } - - companion object { - const val ARG_FEEDURL: String = "arg.feedurl" - const val ARG_WAS_MANUAL_URL: String = "manual_url" - private const val RESULT_ERROR = 2 - private const val TAG = "OnlineFeedViewActivity" - private const val PREFS = "OnlineFeedViewActivityPreferences" - private const val PREF_LAST_AUTO_DOWNLOAD = "lastAutoDownload" - private const val DESCRIPTION_MAX_LINES_COLLAPSED = 4 - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/OpmlImportActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/OpmlImportActivity.kt deleted file mode 100644 index 8ab8d028..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/OpmlImportActivity.kt +++ /dev/null @@ -1,270 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.Manifest -import android.content.DialogInterface -import android.content.Intent -import android.content.pm.PackageManager -import android.net.Uri -import android.os.Build -import android.os.Bundle -import android.text.Spannable -import android.text.SpannableString -import android.text.style.ForegroundColorSpan -import android.util.Log -import android.view.Menu -import android.view.MenuItem -import android.view.View -import android.widget.AdapterView -import android.widget.AdapterView.OnItemClickListener -import android.widget.ArrayAdapter -import android.widget.ListView -import android.widget.Toast -import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AppCompatActivity -import androidx.core.app.ActivityCompat -import androidx.media3.common.util.UnstableApi -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.export.opml.OpmlElement -import ac.mdiq.podcini.core.export.opml.OpmlReader -import ac.mdiq.podcini.core.preferences.ThemeSwitcher.getTheme -import ac.mdiq.podcini.core.storage.DBTasks -import ac.mdiq.podcini.core.util.download.FeedUpdateManager.runOnce -import ac.mdiq.podcini.databinding.OpmlSelectionBinding -import ac.mdiq.podcini.model.feed.Feed -import io.reactivex.Completable -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers -import org.apache.commons.io.input.BOMInputStream -import java.io.InputStreamReader -import java.io.Reader - -/** - * Activity for Opml Import. - */ -class OpmlImportActivity : AppCompatActivity() { - private var uri: Uri? = null - private lateinit var viewBinding: OpmlSelectionBinding - private lateinit var selectAll: MenuItem - private lateinit var deselectAll: MenuItem - - private var listAdapter: ArrayAdapter? = null - private var readElements: ArrayList? = null - - @UnstableApi override fun onCreate(savedInstanceState: Bundle?) { - setTheme(getTheme(this)) - super.onCreate(savedInstanceState) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - viewBinding = OpmlSelectionBinding.inflate(layoutInflater) - setContentView(viewBinding.root) - - viewBinding.feedlist.choiceMode = ListView.CHOICE_MODE_MULTIPLE - viewBinding.feedlist.onItemClickListener = - OnItemClickListener { parent: AdapterView<*>?, view: View?, position: Int, id: Long -> - val checked = viewBinding.feedlist.checkedItemPositions - var checkedCount = 0 - for (i in 0 until checked.size()) { - if (checked.valueAt(i)) { - checkedCount++ - } - } - if (listAdapter != null) { - if (checkedCount == listAdapter!!.count) { - selectAll.setVisible(false) - deselectAll.setVisible(true) - } else { - deselectAll.setVisible(false) - selectAll.setVisible(true) - } - } - } - viewBinding.butCancel.setOnClickListener { v: View? -> - setResult(RESULT_CANCELED) - finish() - } - viewBinding.butConfirm.setOnClickListener { v: View? -> - viewBinding.progressBar.visibility = View.VISIBLE - Completable.fromAction { - val checked = viewBinding.feedlist.checkedItemPositions - for (i in 0 until checked.size()) { - if (!checked.valueAt(i)) { - continue - } - if (!readElements.isNullOrEmpty()) { - val element = readElements!![checked.keyAt(i)] - val feed = Feed(element.xmlUrl, null, - if (element.text != null) element.text else "Unknown podcast") - feed.items = mutableListOf() - DBTasks.updateFeed(this, feed, false) - } - } - runOnce(this) - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - viewBinding.progressBar.visibility = View.GONE - val intent = Intent(this@OpmlImportActivity, MainActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - startActivity(intent) - finish() - }, { e: Throwable -> - e.printStackTrace() - viewBinding.progressBar.visibility = View.GONE - Toast.makeText(this, e.message, Toast.LENGTH_LONG).show() - }) - } - - var uri = intent.data - if (uri != null && uri.toString().startsWith("/")) { - uri = Uri.parse("file://$uri") - } else { - val extraText = intent.getStringExtra(Intent.EXTRA_TEXT) - if (extraText != null) { - uri = Uri.parse(extraText) - } - } - importUri(uri) - } - - fun importUri(uri: Uri?) { - if (uri == null) { - MaterialAlertDialogBuilder(this) - .setMessage(R.string.opml_import_error_no_file) - .setPositiveButton(android.R.string.ok, null) - .show() - return - } - this.uri = uri - startImport() - } - - private val titleList: List - get() { - val result: MutableList = ArrayList() - if (!readElements.isNullOrEmpty()) { - for (element in readElements!!) { - if (element.text != null) result.add(element.text!!) - } - } - return result - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - super.onCreateOptionsMenu(menu) - val inflater = menuInflater - inflater.inflate(R.menu.opml_selection_options, menu) - selectAll = menu.findItem(R.id.select_all_item) - deselectAll = menu.findItem(R.id.deselect_all_item) - deselectAll.setVisible(false) - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - val itemId = item.itemId - when (itemId) { - R.id.select_all_item -> { - selectAll.setVisible(false) - selectAllItems(true) - deselectAll.setVisible(true) - return true - } - R.id.deselect_all_item -> { - deselectAll.setVisible(false) - selectAllItems(false) - selectAll.setVisible(true) - return true - } - android.R.id.home -> { - finish() - } - } - return false - } - - private fun selectAllItems(b: Boolean) { - for (i in 0 until viewBinding.feedlist.count) { - viewBinding.feedlist.setItemChecked(i, b) - } - } - - private fun requestPermission() { - requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE) - } - - private val requestPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -> - if (isGranted) { - startImport() - } else { - MaterialAlertDialogBuilder(this) - .setMessage(R.string.opml_import_ask_read_permission) - .setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, which: Int -> requestPermission() } - .setNegativeButton(R.string.cancel_label) { dialog: DialogInterface?, which: Int -> finish() } - .show() - } - } - - /** Starts the import process. */ - private fun startImport() { - viewBinding.progressBar.visibility = View.VISIBLE - - Observable.fromCallable { - val opmlFileStream = contentResolver.openInputStream(uri!!) - val bomInputStream = BOMInputStream(opmlFileStream) - val bom = bomInputStream.bom - val charsetName = if (bom == null) "UTF-8" else bom.charsetName - val reader: Reader = InputStreamReader(bomInputStream, charsetName) - val opmlReader = OpmlReader() - val result = opmlReader.readDocument(reader) - reader.close() - result - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { result: ArrayList? -> - viewBinding.progressBar.visibility = View.GONE - Log.d(TAG, "Parsing was successful") - readElements = result - listAdapter = ArrayAdapter(this@OpmlImportActivity, - android.R.layout.simple_list_item_multiple_choice, - titleList) - viewBinding.feedlist.adapter = listAdapter - }, { e: Throwable -> - Log.d(TAG, Log.getStackTraceString(e)) - val message = if (e.message == null) "" else e.message!! - if (message.lowercase().contains("permission") - && Build.VERSION.SDK_INT >= 23) { - val permission = ActivityCompat.checkSelfPermission(this, - Manifest.permission.READ_EXTERNAL_STORAGE) - if (permission != PackageManager.PERMISSION_GRANTED) { - requestPermission() - return@subscribe - } - } - viewBinding.progressBar.visibility = View.GONE - val alert = MaterialAlertDialogBuilder(this) - alert.setTitle(R.string.error_label) - val userReadable = getString(R.string.opml_reader_error) - val details = e.message - val total = """ - $userReadable - - $details - """.trimIndent() - val errorMessage = SpannableString(total) - errorMessage.setSpan(ForegroundColorSpan(-0x77777778), - userReadable.length, total.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - alert.setMessage(errorMessage) - alert.setPositiveButton(android.R.string.ok) { dialog: DialogInterface?, which: Int -> finish() } - alert.show() - }) - } - - companion object { - private const val TAG = "OpmlImportBaseActivity" - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/PlaybackSpeedDialogActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/PlaybackSpeedDialogActivity.kt deleted file mode 100644 index 61007cfb..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/PlaybackSpeedDialogActivity.kt +++ /dev/null @@ -1,23 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.content.DialogInterface -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import ac.mdiq.podcini.core.preferences.ThemeSwitcher.getTranslucentTheme -import ac.mdiq.podcini.dialog.VariableSpeedDialog - -class PlaybackSpeedDialogActivity : AppCompatActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - setTheme(getTranslucentTheme(this)) - super.onCreate(savedInstanceState) - val speedDialog: VariableSpeedDialog = InnerVariableSpeedDialog() - speedDialog.show(supportFragmentManager, null) - } - - class InnerVariableSpeedDialog : VariableSpeedDialog() { - override fun onDismiss(dialog: DialogInterface) { - super.onDismiss(dialog) - requireActivity().finish() - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/PreferenceActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/PreferenceActivity.kt deleted file mode 100644 index f8dd98a3..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/PreferenceActivity.kt +++ /dev/null @@ -1,203 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.annotation.SuppressLint -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.provider.Settings -import android.util.Log -import android.view.MenuItem -import android.view.View -import android.view.inputmethod.InputMethodManager -import androidx.appcompat.app.AppCompatActivity -import androidx.preference.PreferenceFragmentCompat -import com.bytehamster.lib.preferencesearch.SearchPreferenceResult -import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import com.google.android.material.snackbar.Snackbar -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.preferences.ThemeSwitcher.getTheme -import ac.mdiq.podcini.databinding.SettingsActivityBinding -import ac.mdiq.podcini.event.MessageEvent -import ac.mdiq.podcini.fragment.preferences.* -import ac.mdiq.podcini.fragment.preferences.synchronization.SynchronizationPreferencesFragment -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode - -/** - * PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see - * PreferenceController. - */ -class PreferenceActivity : AppCompatActivity(), SearchPreferenceResultListener { - private lateinit var binding: SettingsActivityBinding - - @SuppressLint("CommitTransaction") - override fun onCreate(savedInstanceState: Bundle?) { - setTheme(getTheme(this)) - super.onCreate(savedInstanceState) - - val ab = supportActionBar - ab?.setDisplayHomeAsUpEnabled(true) - - binding = SettingsActivityBinding.inflate(layoutInflater) - setContentView(binding.root) - - if (supportFragmentManager.findFragmentByTag(FRAGMENT_TAG) == null) { - supportFragmentManager.beginTransaction() - .replace(binding.settingsContainer.id, MainPreferencesFragment(), FRAGMENT_TAG) - .commit() - } - val intent = intent - if (intent.getBooleanExtra(OPEN_AUTO_DOWNLOAD_SETTINGS, false)) { - openScreen(R.xml.preferences_autodownload) - } - } - - private fun getPreferenceScreen(screen: Int): PreferenceFragmentCompat? { - var prefFragment: PreferenceFragmentCompat? = null - - when (screen) { - R.xml.preferences_user_interface -> { - prefFragment = UserInterfacePreferencesFragment() - } - R.xml.preferences_downloads -> { - prefFragment = DownloadsPreferencesFragment() - } - R.xml.preferences_import_export -> { - prefFragment = ImportExportPreferencesFragment() - } - R.xml.preferences_autodownload -> { - prefFragment = AutoDownloadPreferencesFragment() - } - R.xml.preferences_synchronization -> { - prefFragment = SynchronizationPreferencesFragment() - } - R.xml.preferences_playback -> { - prefFragment = PlaybackPreferencesFragment() - } - R.xml.preferences_notifications -> { - prefFragment = NotificationPreferencesFragment() - } - R.xml.preferences_swipe -> { - prefFragment = SwipePreferencesFragment() - } - } - return prefFragment - } - - @SuppressLint("CommitTransaction") - fun openScreen(screen: Int): PreferenceFragmentCompat? { - val fragment = getPreferenceScreen(screen) - if (screen == R.xml.preferences_notifications && Build.VERSION.SDK_INT >= 26) { - val intent = Intent() - intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS) - intent.putExtra(Settings.EXTRA_APP_PACKAGE, packageName) - startActivity(intent) - } else { - supportFragmentManager.beginTransaction() - .replace(binding.settingsContainer.id, fragment!!) - .addToBackStack(getString(getTitleOfPage(screen))) - .commit() - } - - - return fragment - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - if (item.itemId == android.R.id.home) { - if (supportFragmentManager.backStackEntryCount == 0) { - finish() - } else { - val imm = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager - var view = currentFocus - //If no view currently has focus, create a new one, just so we can grab a window token from it - if (view == null) { - view = View(this) - } - imm.hideSoftInputFromWindow(view.windowToken, 0) - supportFragmentManager.popBackStack() - } - return true - } - return false - } - - override fun onSearchResultClicked(result: SearchPreferenceResult) { - when (val screen = result.resourceFile) { - R.xml.feed_settings -> { - val builder = MaterialAlertDialogBuilder(this) - builder.setTitle(R.string.feed_settings_label) - builder.setMessage(R.string.pref_feed_settings_dialog_msg) - builder.setPositiveButton(android.R.string.ok, null) - builder.show() - } - R.xml.preferences_notifications -> { - openScreen(screen) - } - else -> { - val fragment = openScreen(result.resourceFile) - result.highlight(fragment) - } - } - } - - override fun onStart() { - super.onStart() - EventBus.getDefault().register(this) - } - - override fun onStop() { - super.onStop() - EventBus.getDefault().unregister(this) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(event: MessageEvent) { - Log.d(FRAGMENT_TAG, "onEvent($event)") - val s = Snackbar.make(binding.root, event.message, Snackbar.LENGTH_LONG) - if (event.action != null) { - s.setAction(event.actionText) { v: View? -> event.action!!.accept(this) } - } - s.show() - } - - companion object { - private const val FRAGMENT_TAG = "tag_preferences" - const val OPEN_AUTO_DOWNLOAD_SETTINGS: String = "OpenAutoDownloadSettings" - @JvmStatic - fun getTitleOfPage(preferences: Int): Int { - when (preferences) { - R.xml.preferences_downloads -> { - return R.string.downloads_pref - } - R.xml.preferences_autodownload -> { - return R.string.pref_automatic_download_title - } - R.xml.preferences_playback -> { - return R.string.playback_pref - } - R.xml.preferences_import_export -> { - return R.string.import_export_pref - } - R.xml.preferences_user_interface -> { - return R.string.user_interface_label - } - R.xml.preferences_synchronization -> { - return R.string.synchronization_pref - } - R.xml.preferences_notifications -> { - return R.string.notification_pref_fragment - } - R.xml.feed_settings -> { - return R.string.feed_settings_label - } - R.xml.preferences_swipe -> { - return R.string.swipeactions_label - } - else -> return R.string.settings_label - } - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/SelectSubscriptionActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/SelectSubscriptionActivity.kt deleted file mode 100644 index d88a1ab7..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/SelectSubscriptionActivity.kt +++ /dev/null @@ -1,158 +0,0 @@ -package ac.mdiq.podcini.activity - -import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity.Companion.EXTRA_FEED_ID -import ac.mdiq.podcini.core.preferences.ThemeSwitcher -import ac.mdiq.podcini.core.storage.DBReader -import ac.mdiq.podcini.core.storage.NavDrawerData -import ac.mdiq.podcini.databinding.SubscriptionSelectionActivityBinding -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.storage.preferences.UserPreferences -import android.app.Activity -import android.content.Intent -import android.graphics.Bitmap -import android.os.Bundle -import android.util.Log -import android.view.View -import android.widget.AdapterView -import android.widget.ArrayAdapter -import android.widget.ListView -import androidx.appcompat.app.AppCompatActivity -import androidx.core.content.pm.ShortcutInfoCompat -import androidx.core.content.pm.ShortcutManagerCompat -import androidx.core.graphics.drawable.IconCompat -import androidx.media3.common.util.UnstableApi -import com.bumptech.glide.Glide -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.request.target.Target -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers - -class SelectSubscriptionActivity : AppCompatActivity() { - private var disposable: Disposable? = null - - @Volatile - private var listItems: List? = null - - private lateinit var viewBinding: SubscriptionSelectionActivityBinding - - override fun onCreate(savedInstanceState: Bundle?) { - setTheme(ThemeSwitcher.getTranslucentTheme(this)) - super.onCreate(savedInstanceState) - - viewBinding = SubscriptionSelectionActivityBinding.inflate(layoutInflater) - setContentView(viewBinding.root) - setSupportActionBar(viewBinding.toolbar) - setTitle(R.string.shortcut_select_subscription) - - viewBinding.transparentBackground.setOnClickListener { v: View? -> finish() } - viewBinding.card.setOnClickListener(null) - - loadSubscriptions() - - val checkedPosition = arrayOfNulls(1) - viewBinding.list.choiceMode = ListView.CHOICE_MODE_SINGLE - viewBinding.list.onItemClickListener = - AdapterView.OnItemClickListener { listView: AdapterView<*>?, view1: View?, position: Int, rowId: Long -> - checkedPosition[0] = position - } - viewBinding.shortcutBtn.setOnClickListener { view: View? -> - if (checkedPosition[0] != null && Intent.ACTION_CREATE_SHORTCUT == intent.action) { - getBitmapFromUrl(listItems!![checkedPosition[0]!!]) - } - } - } - - fun getFeedItems(items: List, result: MutableList): List { - for (item in items) { - if (item == null) continue - if (item.type == NavDrawerData.DrawerItem.Type.TAG) { - getFeedItems((item as NavDrawerData.TagDrawerItem).children, result) - } else { - val feed: Feed = (item as NavDrawerData.FeedDrawerItem).feed - if (!result.contains(feed)) { - result.add(feed) - } - } - } - return result - } - - @UnstableApi private fun addShortcut(feed: Feed, bitmap: Bitmap?) { - val intent = Intent(this, MainActivity::class.java) - intent.setAction(Intent.ACTION_MAIN) - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) - intent.putExtra(EXTRA_FEED_ID, feed.id) - val id = "subscription-" + feed.id - - val icon: IconCompat = if (bitmap != null) { - IconCompat.createWithAdaptiveBitmap(bitmap) - } else { - IconCompat.createWithResource(this, R.drawable.ic_subscriptions_shortcut) - } - - val shortcut: ShortcutInfoCompat = ShortcutInfoCompat.Builder(this, id) - .setShortLabel(feed.title?:"") - .setLongLabel(feed.feedTitle?:"") - .setIntent(intent) - .setIcon(icon) - .build() - - setResult(Activity.RESULT_OK, ShortcutManagerCompat.createShortcutResultIntent(this, shortcut)) - finish() - } - - private fun getBitmapFromUrl(feed: Feed) { - val iconSize = (128 * resources.displayMetrics.density).toInt() - Glide.with(this) - .asBitmap() - .load(feed.imageUrl) - .apply(RequestOptions.overrideOf(iconSize, iconSize)) - .listener(object : RequestListener { - @UnstableApi override fun onLoadFailed(e: GlideException?, model: Any?, - target: Target, isFirstResource: Boolean - ): Boolean { - addShortcut(feed, null) - return true - } - - @UnstableApi override fun onResourceReady(resource: Bitmap, model: Any, - target: Target, dataSource: DataSource, isFirstResource: Boolean - ): Boolean { - addShortcut(feed, resource) - return true - } - }).submit() - } - - private fun loadSubscriptions() { - disposable?.dispose() - - disposable = Observable.fromCallable { - val data: NavDrawerData = DBReader.getNavDrawerData(UserPreferences.subscriptionsFilter) - getFeedItems(data.items, ArrayList()) - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { result: List -> - listItems = result - val titles = ArrayList() - for (feed in result) { - if (feed.title != null) titles.add(feed.title!!) - } - val adapter: ArrayAdapter = ArrayAdapter(this, - R.layout.simple_list_item_multiple_choice_on_start, titles) - viewBinding.list.adapter = adapter - }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) }) - } - - companion object { - private const val TAG = "SelectSubscription" - } -} \ No newline at end of file diff --git a/app/src/main/java/ac/mdiq/podcini/activity/SplashActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/SplashActivity.kt deleted file mode 100644 index 17262236..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/SplashActivity.kt +++ /dev/null @@ -1,48 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.annotation.SuppressLint -import android.app.Activity -import android.content.Intent -import android.os.Bundle -import android.view.View -import android.widget.Toast -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.error.CrashReportWriter -import ac.mdiq.podcini.storage.database.PodDBAdapter -import io.reactivex.Completable -import io.reactivex.CompletableEmitter -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.schedulers.Schedulers - -/** - * Shows the Podcini logo while waiting for the main activity to start. - */ -@SuppressLint("CustomSplashScreen") -class SplashActivity : Activity() { - @UnstableApi override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val content = findViewById(android.R.id.content) - content.viewTreeObserver.addOnPreDrawListener { false } // Keep splash screen active - - Completable.create { subscriber: CompletableEmitter -> - // Trigger schema updates - PodDBAdapter.getInstance()?.open() - PodDBAdapter.getInstance()?.close() - subscriber.onComplete() - } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - val intent = Intent(this@SplashActivity, MainActivity::class.java) - startActivity(intent) - overridePendingTransition(0, 0) - finish() - }, { error: Throwable -> - error.printStackTrace() - CrashReportWriter.write(error) - Toast.makeText(this, error.localizedMessage, Toast.LENGTH_LONG).show() - finish() - }) - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/VideoplayerActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/VideoplayerActivity.kt deleted file mode 100644 index 16d7cf25..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/VideoplayerActivity.kt +++ /dev/null @@ -1,787 +0,0 @@ -package ac.mdiq.podcini.activity - -import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.core.service.playback.PlaybackService.Companion.getPlayerActivityIntent -import ac.mdiq.podcini.core.service.playback.PlaybackService.Companion.isCasting -import ac.mdiq.podcini.core.storage.DBReader -import ac.mdiq.podcini.core.storage.DBWriter -import ac.mdiq.podcini.core.util.Converter.getDurationStringLong -import ac.mdiq.podcini.core.util.FeedItemUtil.getLinkWithFallback -import ac.mdiq.podcini.core.util.IntentUtils.openInBrowser -import ac.mdiq.podcini.core.util.ShareUtils.hasLinkToShare -import ac.mdiq.podcini.core.util.TimeSpeedConverter -import ac.mdiq.podcini.core.util.gui.PictureInPictureUtil -import ac.mdiq.podcini.core.util.playback.PlaybackController -import ac.mdiq.podcini.databinding.VideoplayerActivityBinding -import ac.mdiq.podcini.dialog.* -import ac.mdiq.podcini.event.MessageEvent -import ac.mdiq.podcini.event.PlayerErrorEvent -import ac.mdiq.podcini.event.playback.BufferUpdateEvent -import ac.mdiq.podcini.event.playback.PlaybackPositionEvent -import ac.mdiq.podcini.event.playback.PlaybackServiceEvent -import ac.mdiq.podcini.event.playback.SleepTimerUpdatedEvent -import ac.mdiq.podcini.fragment.ChaptersFragment -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.feed.FeedMedia -import ac.mdiq.podcini.model.playback.Playable -import ac.mdiq.podcini.playback.base.PlayerStatus -import ac.mdiq.podcini.playback.cast.CastEnabledActivity -import ac.mdiq.podcini.storage.preferences.UserPreferences.fastForwardSecs -import ac.mdiq.podcini.storage.preferences.UserPreferences.rewindSecs -import ac.mdiq.podcini.storage.preferences.UserPreferences.setShowRemainTimeSetting -import ac.mdiq.podcini.storage.preferences.UserPreferences.shouldShowRemainingTime -import ac.mdiq.podcini.ui.appstartintent.MainActivityStarter -import android.content.DialogInterface -import android.content.Intent -import android.graphics.PixelFormat -import android.graphics.drawable.ColorDrawable -import android.media.AudioManager -import android.os.Build -import android.os.Bundle -import android.os.Handler -import android.os.Looper -import android.util.Log -import android.view.* -import android.view.View.OnTouchListener -import android.view.animation.* -import android.widget.EditText -import android.widget.FrameLayout -import android.widget.SeekBar -import android.widget.SeekBar.OnSeekBarChangeListener -import androidx.interpolator.view.animation.FastOutSlowInInterpolator -import androidx.media3.common.util.UnstableApi -import com.bumptech.glide.Glide -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import io.reactivex.Observable -import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.Disposable -import io.reactivex.schedulers.Schedulers -import org.greenrobot.eventbus.EventBus -import org.greenrobot.eventbus.Subscribe -import org.greenrobot.eventbus.ThreadMode - -/** - * Activity for playing video files. - */ -@UnstableApi -class VideoplayerActivity : CastEnabledActivity(), OnSeekBarChangeListener { - - private lateinit var viewBinding: VideoplayerActivityBinding - - /** - * True if video controls are currently visible. - */ - private var videoControlsShowing = true - private var videoSurfaceCreated = false - private var destroyingDueToReload = false - private var lastScreenTap: Long = 0 - private val videoControlsHider = Handler(Looper.getMainLooper()) - private var controller: PlaybackController? = null - private var showTimeLeft = false - private var isFavorite = false - private var switchToAudioOnly = false - private var disposable: Disposable? = null - private var prog = 0f - - override fun onCreate(savedInstanceState: Bundle?) { - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN) - // has to be called before setting layout content - supportRequestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY) - setTheme(R.style.Theme_Podcini_VideoPlayer) - super.onCreate(savedInstanceState) - - Log.d(TAG, "onCreate()") - - window.setFormat(PixelFormat.TRANSPARENT) - viewBinding = VideoplayerActivityBinding.inflate(LayoutInflater.from(this)) - setContentView(viewBinding.root) - setupView() - supportActionBar?.setBackgroundDrawable(ColorDrawable(-0x80000000)) - supportActionBar?.setDisplayHomeAsUpEnabled(true) - } - - @UnstableApi - override fun onResume() { - super.onResume() - switchToAudioOnly = false - if (isCasting) { - val intent = getPlayerActivityIntent(this) - if (intent.component!!.className != VideoplayerActivity::class.java.name) { - destroyingDueToReload = true - finish() - startActivity(intent) - } - } - } - - @UnstableApi - override fun onStop() { - controller?.release() - controller = null // prevent leak - disposable?.dispose() - - EventBus.getDefault().unregister(this) - super.onStop() - if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { - videoControlsHider.removeCallbacks(hideVideoControls) - } - // Controller released; we will not receive buffering updates - viewBinding.progressBar.visibility = View.GONE - } - - public override fun onUserLeaveHint() { - if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { - compatEnterPictureInPicture() - } - } - - @UnstableApi - override fun onStart() { - super.onStart() - controller = newPlaybackController() - controller!!.init() - loadMediaInfo() - onPositionObserverUpdate() - EventBus.getDefault().register(this) - } - - @UnstableApi - override fun onPause() { - if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { - if (controller != null && controller!!.status == PlayerStatus.PLAYING) { - controller!!.pause() - } - } - super.onPause() - } - - override fun onTrimMemory(level: Int) { - super.onTrimMemory(level) - Glide.get(this).trimMemory(level) - } - - override fun onLowMemory() { - super.onLowMemory() - Glide.get(this).clearMemory() - } - - @UnstableApi - private fun newPlaybackController(): PlaybackController { - return object : PlaybackController(this@VideoplayerActivity) { - override fun updatePlayButtonShowsPlay(showPlay: Boolean) { - viewBinding.playButton.setIsShowPlay(showPlay) - if (showPlay) { - window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - } else { - window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) - setupVideoAspectRatio() - if (videoSurfaceCreated && controller != null) { - Log.d(TAG, "Videosurface already created, setting videosurface now") - controller!!.setVideoSurface(viewBinding.videoView.holder) - } - } - } - - override fun loadMediaInfo() { - this@VideoplayerActivity.loadMediaInfo() - } - - override fun onPlaybackEnd() { - finish() - } - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - @Suppress("unused") - fun bufferUpdate(event: BufferUpdateEvent) { - if (event.hasStarted()) { - viewBinding.progressBar.visibility = View.VISIBLE - } else if (event.hasEnded()) { - viewBinding.progressBar.visibility = View.INVISIBLE - } else { - viewBinding.sbPosition.secondaryProgress = (event.progress * viewBinding.sbPosition.max).toInt() - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - @Suppress("unused") - fun sleepTimerUpdate(event: SleepTimerUpdatedEvent) { - if (event.isCancelled || event.wasJustEnabled()) { - supportInvalidateOptionsMenu() - } - } - - @UnstableApi - private fun loadMediaInfo() { - Log.d(TAG, "loadMediaInfo()") - if (controller?.getMedia() == null) { - return - } - if (controller!!.status == PlayerStatus.PLAYING && !controller!!.isPlayingVideoLocally) { - Log.d(TAG, "Closing, no longer video") - destroyingDueToReload = true - finish() - MainActivityStarter(this).withOpenPlayer().start() - return - } - showTimeLeft = shouldShowRemainingTime() - onPositionObserverUpdate() - checkFavorite() - val media = controller!!.getMedia() - if (media != null) { - supportActionBar!!.subtitle = media.getEpisodeTitle() - supportActionBar!!.title = media.getFeedTitle() - } - } - - @UnstableApi - private fun setupView() { - showTimeLeft = shouldShowRemainingTime() - Log.d("timeleft", if (showTimeLeft) "true" else "false") - viewBinding.durationLabel.setOnClickListener { v: View? -> - showTimeLeft = !showTimeLeft - val media = controller?.getMedia() ?: return@setOnClickListener - - val converter = TimeSpeedConverter(controller!!.currentPlaybackSpeedMultiplier) - val length: String - if (showTimeLeft) { - val remainingTime = converter.convert(media.getDuration() - media.getPosition()) - length = "-" + getDurationStringLong(remainingTime) - } else { - val duration = converter.convert(media.getDuration()) - length = getDurationStringLong(duration) - } - viewBinding.durationLabel.text = length - - setShowRemainTimeSetting(showTimeLeft) - Log.d("timeleft on click", if (showTimeLeft) "true" else "false") - } - - viewBinding.sbPosition.setOnSeekBarChangeListener(this) - viewBinding.rewindButton.setOnClickListener { v: View? -> onRewind() } - viewBinding.rewindButton.setOnLongClickListener { v: View? -> - SkipPreferenceDialog.showSkipPreference(this@VideoplayerActivity, - SkipPreferenceDialog.SkipDirection.SKIP_REWIND, null) - true - } - viewBinding.playButton.setIsVideoScreen(true) - viewBinding.playButton.setOnClickListener { v: View? -> onPlayPause() } - viewBinding.fastForwardButton.setOnClickListener { v: View? -> onFastForward() } - viewBinding.fastForwardButton.setOnLongClickListener { v: View? -> - SkipPreferenceDialog.showSkipPreference(this@VideoplayerActivity, - SkipPreferenceDialog.SkipDirection.SKIP_FORWARD, null) - false - } - // To suppress touches directly below the slider - viewBinding.bottomControlsContainer.setOnTouchListener { view: View?, motionEvent: MotionEvent? -> true } - viewBinding.bottomControlsContainer.fitsSystemWindows = true - viewBinding.videoView.holder.addCallback(surfaceHolderCallback) - viewBinding.videoView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION - - setupVideoControlsToggler() - window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, - WindowManager.LayoutParams.FLAG_FULLSCREEN) - - viewBinding.videoPlayerContainer.setOnTouchListener(onVideoviewTouched) - viewBinding.videoPlayerContainer.viewTreeObserver.addOnGlobalLayoutListener { - viewBinding.videoView.setAvailableSize( - viewBinding.videoPlayerContainer.width.toFloat(), viewBinding.videoPlayerContainer.height.toFloat()) - } - } - - private val hideVideoControls = Runnable { - if (videoControlsShowing) { - Log.d(TAG, "Hiding video controls") - supportActionBar?.hide() - hideVideoControls(true) - videoControlsShowing = false - } - } - - private val onVideoviewTouched = OnTouchListener { v: View, event: MotionEvent -> - if (event.action != MotionEvent.ACTION_DOWN) { - return@OnTouchListener false - } - if (PictureInPictureUtil.isInPictureInPictureMode(this)) { - return@OnTouchListener true - } - videoControlsHider.removeCallbacks(hideVideoControls) - - if (System.currentTimeMillis() - lastScreenTap < 300) { - if (event.x > v.measuredWidth / 2.0f) { - onFastForward() - showSkipAnimation(true) - } else { - onRewind() - showSkipAnimation(false) - } - if (videoControlsShowing) { - supportActionBar?.hide() - hideVideoControls(false) - videoControlsShowing = false - } - return@OnTouchListener true - } - - toggleVideoControlsVisibility() - if (videoControlsShowing) { - setupVideoControlsToggler() - } - - lastScreenTap = System.currentTimeMillis() - true - } - - private fun showSkipAnimation(isForward: Boolean) { - val skipAnimation = AnimationSet(true) - skipAnimation.addAnimation(ScaleAnimation(1f, 2f, 1f, 2f, - Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f)) - skipAnimation.addAnimation(AlphaAnimation(1f, 0f)) - skipAnimation.fillAfter = false - skipAnimation.duration = 800 - - val params = viewBinding.skipAnimationImage.layoutParams as FrameLayout.LayoutParams - if (isForward) { - viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_forward_video_white) - params.gravity = Gravity.RIGHT or Gravity.CENTER_VERTICAL - } else { - viewBinding.skipAnimationImage.setImageResource(R.drawable.ic_fast_rewind_video_white) - params.gravity = Gravity.LEFT or Gravity.CENTER_VERTICAL - } - - viewBinding.skipAnimationImage.visibility = View.VISIBLE - viewBinding.skipAnimationImage.layoutParams = params - viewBinding.skipAnimationImage.startAnimation(skipAnimation) - skipAnimation.setAnimationListener(object : Animation.AnimationListener { - override fun onAnimationStart(animation: Animation) { - } - - override fun onAnimationEnd(animation: Animation) { - viewBinding.skipAnimationImage.visibility = View.GONE - } - - override fun onAnimationRepeat(animation: Animation) { - } - }) - } - - private fun setupVideoControlsToggler() { - videoControlsHider.removeCallbacks(hideVideoControls) - videoControlsHider.postDelayed(hideVideoControls, 2500) - } - - @UnstableApi - private fun setupVideoAspectRatio() { - if (videoSurfaceCreated && controller != null) { - val videoSize = controller!!.videoSize - if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) { - Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second) - viewBinding.videoView.setVideoSize(videoSize.first, videoSize.second) - } else { - Log.e(TAG, "Could not determine video size") - } - } - } - - private fun toggleVideoControlsVisibility() { - if (videoControlsShowing) { - supportActionBar?.hide() - hideVideoControls(true) - } else { - supportActionBar?.show() - showVideoControls() - } - videoControlsShowing = !videoControlsShowing - } - - @UnstableApi - fun onRewind() { - if (controller == null) { - return - } - val curr = controller!!.position - controller!!.seekTo(curr - rewindSecs * 1000) - setupVideoControlsToggler() - } - - @UnstableApi - fun onPlayPause() { - if (controller == null) { - return - } - controller!!.playPause() - setupVideoControlsToggler() - } - - @UnstableApi - fun onFastForward() { - if (controller == null) { - return - } - val curr = controller!!.position - controller!!.seekTo(curr + fastForwardSecs * 1000) - setupVideoControlsToggler() - } - - private val surfaceHolderCallback: SurfaceHolder.Callback = object : SurfaceHolder.Callback { - override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) { - holder.setFixedSize(width, height) - } - - @UnstableApi - override fun surfaceCreated(holder: SurfaceHolder) { - Log.d(TAG, "Videoview holder created") - videoSurfaceCreated = true - if (controller?.status == PlayerStatus.PLAYING) { - controller!!.setVideoSurface(holder) - } - setupVideoAspectRatio() - } - - override fun surfaceDestroyed(holder: SurfaceHolder) { - Log.d(TAG, "Videosurface was destroyed") - videoSurfaceCreated = false - if (controller != null && !destroyingDueToReload && !switchToAudioOnly) { - controller!!.notifyVideoSurfaceAbandoned() - } - } - } - - private fun showVideoControls() { - viewBinding.bottomControlsContainer.visibility = View.VISIBLE - viewBinding.controlsContainer.visibility = View.VISIBLE - val animation = AnimationUtils.loadAnimation(this, R.anim.fade_in) - if (animation != null) { - viewBinding.bottomControlsContainer.startAnimation(animation) - viewBinding.controlsContainer.startAnimation(animation) - } - viewBinding.videoView.systemUiVisibility = View.SYSTEM_UI_FLAG_VISIBLE - } - - private fun hideVideoControls(showAnimation: Boolean) { - if (showAnimation) { - val animation = AnimationUtils.loadAnimation(this, R.anim.fade_out) - if (animation != null) { - viewBinding.bottomControlsContainer.startAnimation(animation) - viewBinding.controlsContainer.startAnimation(animation) - } - } - window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LOW_PROFILE - or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION - or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) - viewBinding.bottomControlsContainer.fitsSystemWindows = true - - viewBinding.bottomControlsContainer.visibility = View.GONE - viewBinding.controlsContainer.visibility = View.GONE - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(event: PlaybackPositionEvent?) { - onPositionObserverUpdate() - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onPlaybackServiceChanged(event: PlaybackServiceEvent) { - if (event.action == PlaybackServiceEvent.Action.SERVICE_SHUT_DOWN) { - finish() - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onMediaPlayerError(event: PlayerErrorEvent) { - MediaPlayerErrorDialog.show(this, event) - } - - @Subscribe(threadMode = ThreadMode.MAIN) - fun onEventMainThread(event: MessageEvent) { - Log.d(TAG, "onEvent($event)") - val errorDialog = MaterialAlertDialogBuilder(this) - errorDialog.setMessage(event.message) - errorDialog.setPositiveButton(event.actionText) { dialog: DialogInterface?, which: Int -> - event.action?.accept(this) - } - - errorDialog.show() - } - - override fun onCreateOptionsMenu(menu: Menu): Boolean { - super.onCreateOptionsMenu(menu) - requestCastButton(menu) - val inflater = menuInflater - inflater.inflate(R.menu.mediaplayer, menu) - return true - } - - @UnstableApi - override fun onPrepareOptionsMenu(menu: Menu): Boolean { - super.onPrepareOptionsMenu(menu) - if (controller == null) { - return false - } - val media = controller!!.getMedia() - val isFeedMedia = (media is FeedMedia) - - menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia) // FeedMedia implies it belongs to a Feed - - val hasWebsiteLink = getWebsiteLinkWithFallback(media) != null - menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink) - - val isItemAndHasLink = isFeedMedia && hasLinkToShare((media as FeedMedia).getItem()) - val isItemHasDownloadLink = isFeedMedia && (media as FeedMedia?)?.download_url != null - menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink) - - menu.findItem(R.id.add_to_favorites_item).setVisible(false) - menu.findItem(R.id.remove_from_favorites_item).setVisible(false) - if (isFeedMedia) { - menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite) - menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite) - } - - menu.findItem(R.id.set_sleeptimer_item).setVisible(!controller!!.sleepTimerActive()) - menu.findItem(R.id.disable_sleeptimer_item).setVisible(controller!!.sleepTimerActive()) - - menu.findItem(R.id.player_switch_to_audio_only).setVisible(true) - - menu.findItem(R.id.audio_controls).setVisible(controller!!.audioTracks.size >= 2) - menu.findItem(R.id.playback_speed).setVisible(true) - menu.findItem(R.id.player_show_chapters).setVisible(true) - return true - } - - override fun onOptionsItemSelected(item: MenuItem): Boolean { - // some options option requires FeedItem - when { - item.itemId == R.id.player_switch_to_audio_only -> { - switchToAudioOnly = true - finish() - return true - } - item.itemId == android.R.id.home -> { - val intent = Intent(this@VideoplayerActivity, MainActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK) - startActivity(intent) - finish() - return true - } - item.itemId == R.id.player_show_chapters -> { - ChaptersFragment().show(supportFragmentManager, ChaptersFragment.TAG) - return true - } - controller == null -> { - return false - } - else -> { - val media = controller?.getMedia() ?: return false - val feedItem = getFeedItem(media) // some options option requires FeedItem - if (item.itemId == R.id.add_to_favorites_item && feedItem != null) { - DBWriter.addFavoriteItem(feedItem) - isFavorite = true - invalidateOptionsMenu() - } else if (item.itemId == R.id.remove_from_favorites_item && feedItem != null) { - DBWriter.removeFavoriteItem(feedItem) - isFavorite = false - invalidateOptionsMenu() - } else if (item.itemId == R.id.disable_sleeptimer_item - || item.itemId == R.id.set_sleeptimer_item) { - SleepTimerDialog().show(supportFragmentManager, "SleepTimerDialog") - } else if (item.itemId == R.id.audio_controls) { - val dialog = PlaybackControlsDialog.newInstance() - dialog.show(supportFragmentManager, "playback_controls") - } else if (item.itemId == R.id.open_feed_item && feedItem != null) { - val intent = MainActivity.getIntentToOpenFeed(this, feedItem.feedId) - startActivity(intent) - } else if (item.itemId == R.id.visit_website_item) { - val url = getWebsiteLinkWithFallback(media) - if (url != null) openInBrowser(this@VideoplayerActivity, url) - } else if (item.itemId == R.id.share_item && feedItem != null) { - val shareDialog = ShareDialog.newInstance(feedItem) - shareDialog.show(supportFragmentManager, "ShareEpisodeDialog") - } else if (item.itemId == R.id.playback_speed) { - VariableSpeedDialog().show(supportFragmentManager, null) - } else { - return false - } - return true - } - } - } - - fun onPositionObserverUpdate() { - if (controller == null) { - return - } - - val converter = TimeSpeedConverter(controller!!.currentPlaybackSpeedMultiplier) - val currentPosition = converter.convert(controller!!.position) - val duration = converter.convert(controller!!.duration) - val remainingTime = converter.convert( - controller!!.duration - controller!!.position) - // Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition)); - if (currentPosition == Playable.INVALID_TIME - || duration == Playable.INVALID_TIME) { - Log.w(TAG, "Could not react to position observer update because of invalid time") - return - } - viewBinding.positionLabel.text = getDurationStringLong(currentPosition) - if (showTimeLeft) { - viewBinding.durationLabel.text = "-" + getDurationStringLong(remainingTime) - } else { - viewBinding.durationLabel.text = getDurationStringLong(duration) - } - updateProgressbarPosition(currentPosition, duration) - } - - private fun updateProgressbarPosition(position: Int, duration: Int) { - Log.d(TAG, "updateProgressbarPosition($position, $duration)") - val progress = (position.toFloat()) / duration - viewBinding.sbPosition.progress = (progress * viewBinding.sbPosition.max).toInt() - } - - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (controller == null) { - return - } - if (fromUser) { - prog = progress / (seekBar.max.toFloat()) - val converter = TimeSpeedConverter(controller!!.currentPlaybackSpeedMultiplier) - val position = converter.convert((prog * controller!!.duration).toInt()) - viewBinding.seekPositionLabel.text = getDurationStringLong(position) - } - } - - override fun onStartTrackingTouch(seekBar: SeekBar) { - viewBinding.seekCardView.scaleX = .8f - viewBinding.seekCardView.scaleY = .8f - viewBinding.seekCardView.animate() - .setInterpolator(FastOutSlowInInterpolator()) - .alpha(1f).scaleX(1f).scaleY(1f) - .setDuration(200) - .start() - videoControlsHider.removeCallbacks(hideVideoControls) - } - - override fun onStopTrackingTouch(seekBar: SeekBar) { - if (controller != null) { - controller!!.seekTo((prog * controller!!.duration).toInt()) - } - viewBinding.seekCardView.scaleX = 1f - viewBinding.seekCardView.scaleY = 1f - viewBinding.seekCardView.animate() - .setInterpolator(FastOutSlowInInterpolator()) - .alpha(0f).scaleX(.8f).scaleY(.8f) - .setDuration(200) - .start() - setupVideoControlsToggler() - } - - private fun checkFavorite() { - val feedItem = getFeedItem(controller?.getMedia()) ?: return - disposable?.dispose() - - disposable = Observable.fromCallable { DBReader.getFeedItem(feedItem.id) } - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { item: FeedItem? -> - if (item != null) { - val isFav = item.isTagged(FeedItem.TAG_FAVORITE) - if (isFavorite != isFav) { - isFavorite = isFav - invalidateOptionsMenu() - } - } - }, { error: Throwable? -> Log.e(TAG, Log.getStackTraceString(error)) }) - } - - private fun compatEnterPictureInPicture() { - if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - supportActionBar?.hide() - hideVideoControls(false) - enterPictureInPictureMode() - } - } - - //Hardware keyboard support - override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { - val currentFocus = currentFocus - if (currentFocus is EditText) { - return super.onKeyUp(keyCode, event) - } - - val audioManager = getSystemService(AUDIO_SERVICE) as AudioManager - - when (keyCode) { - KeyEvent.KEYCODE_P, KeyEvent.KEYCODE_SPACE -> { - onPlayPause() - toggleVideoControlsVisibility() - return true - } - KeyEvent.KEYCODE_J, KeyEvent.KEYCODE_A, KeyEvent.KEYCODE_COMMA -> { - onRewind() - showSkipAnimation(false) - return true - } - KeyEvent.KEYCODE_K, KeyEvent.KEYCODE_D, KeyEvent.KEYCODE_PERIOD -> { - onFastForward() - showSkipAnimation(true) - return true - } - KeyEvent.KEYCODE_F, KeyEvent.KEYCODE_ESCAPE -> { - //Exit fullscreen mode - onBackPressed() - return true - } - KeyEvent.KEYCODE_I -> { - compatEnterPictureInPicture() - return true - } - KeyEvent.KEYCODE_PLUS, KeyEvent.KEYCODE_W -> { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_RAISE, AudioManager.FLAG_SHOW_UI) - return true - } - KeyEvent.KEYCODE_MINUS, KeyEvent.KEYCODE_S -> { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI) - return true - } - KeyEvent.KEYCODE_M -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, - AudioManager.ADJUST_TOGGLE_MUTE, AudioManager.FLAG_SHOW_UI) - return true - } - } - //Go to x% of video: - if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { - controller?.seekTo((0.1f * (keyCode - KeyEvent.KEYCODE_0) * controller!!.duration).toInt()) - return true - } - return super.onKeyUp(keyCode, event) - } - - companion object { - private const val TAG = "VideoplayerActivity" - - private fun getWebsiteLinkWithFallback(media: Playable?): String? { - if (media == null) { - return null - } else if (!media.getWebsiteLink().isNullOrBlank()) { - return media.getWebsiteLink() - } else if (media is FeedMedia) { - return getLinkWithFallback(media.getItem()) - } - return null - } - - private fun getFeedItem(playable: Playable?): FeedItem? { - return if (playable is FeedMedia) { - playable.getItem() - } else { - null - } - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/activity/WidgetConfigActivity.kt b/app/src/main/java/ac/mdiq/podcini/activity/WidgetConfigActivity.kt deleted file mode 100644 index f135f6d3..00000000 --- a/app/src/main/java/ac/mdiq/podcini/activity/WidgetConfigActivity.kt +++ /dev/null @@ -1,141 +0,0 @@ -package ac.mdiq.podcini.activity - -import android.appwidget.AppWidgetManager -import android.content.Intent -import android.graphics.Color -import android.os.Build -import android.os.Bundle -import android.view.View -import android.widget.CheckBox -import android.widget.SeekBar -import android.widget.SeekBar.OnSeekBarChangeListener -import android.widget.TextView -import androidx.appcompat.app.AppCompatActivity -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.preferences.ThemeSwitcher.getTheme -import ac.mdiq.podcini.core.receiver.PlayerWidget -import ac.mdiq.podcini.core.widget.WidgetUpdaterWorker - -class WidgetConfigActivity : AppCompatActivity() { - private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID - - private lateinit var widgetPreview: View - private lateinit var opacitySeekBar: SeekBar - private lateinit var opacityTextView: TextView - private lateinit var ckPlaybackSpeed: CheckBox - private lateinit var ckRewind: CheckBox - private lateinit var ckFastForward: CheckBox - private lateinit var ckSkip: CheckBox - - override fun onCreate(savedInstanceState: Bundle?) { - setTheme(getTheme(this)) - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_widget_config) - - val configIntent = intent - val extras = configIntent.extras - if (extras != null) { - appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID) - } - - val resultValue = Intent() - resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) - setResult(RESULT_CANCELED, resultValue) - if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { - finish() - } - - opacityTextView = findViewById(R.id.widget_opacity_textView) - opacitySeekBar = findViewById(R.id.widget_opacity_seekBar) - widgetPreview = findViewById(R.id.widgetLayout) - findViewById(R.id.butConfirm).setOnClickListener { v: View? -> confirmCreateWidget() } - opacitySeekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener { - override fun onProgressChanged(seekBar: SeekBar, i: Int, b: Boolean) { - opacityTextView.text = seekBar.progress.toString() + "%" - val color = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.progress) - widgetPreview.setBackgroundColor(color) - } - - override fun onStartTrackingTouch(seekBar: SeekBar) { - } - - override fun onStopTrackingTouch(seekBar: SeekBar) { - } - }) - - widgetPreview.findViewById(R.id.txtNoPlaying).visibility = View.GONE - val title = widgetPreview.findViewById(R.id.txtvTitle) - title.visibility = View.VISIBLE - title.setText(R.string.app_name) - val progress = widgetPreview.findViewById(R.id.txtvProgress) - progress.visibility = View.VISIBLE - progress.setText(R.string.position_default_label) - - ckPlaybackSpeed = findViewById(R.id.ckPlaybackSpeed) - ckPlaybackSpeed.setOnClickListener { v: View? -> displayPreviewPanel() } - ckRewind = findViewById(R.id.ckRewind) - ckRewind.setOnClickListener { v: View? -> displayPreviewPanel() } - ckFastForward = findViewById(R.id.ckFastForward) - ckFastForward.setOnClickListener { v: View? -> displayPreviewPanel() } - ckSkip = findViewById(R.id.ckSkip) - ckSkip.setOnClickListener { v: View? -> displayPreviewPanel() } - - setInitialState() - } - - private fun setInitialState() { - val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE) - ckPlaybackSpeed.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, false) - ckRewind.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, false) - ckFastForward.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, false) - ckSkip.isChecked = prefs.getBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, false) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val color = prefs.getInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, PlayerWidget.DEFAULT_COLOR) - val opacity = Color.alpha(color) * 100 / 0xFF - - opacitySeekBar.setProgress(opacity, false) - } - displayPreviewPanel() - } - - private fun displayPreviewPanel() { - val showExtendedPreview = - ckPlaybackSpeed.isChecked || ckRewind.isChecked || ckFastForward.isChecked || ckSkip.isChecked - widgetPreview.findViewById(R.id.extendedButtonsContainer).visibility = - if (showExtendedPreview) View.VISIBLE else View.GONE - widgetPreview.findViewById(R.id.butPlay).visibility = - if (showExtendedPreview) View.GONE else View.VISIBLE - widgetPreview.findViewById(R.id.butPlaybackSpeed).visibility = - if (ckPlaybackSpeed.isChecked) View.VISIBLE else View.GONE - widgetPreview.findViewById(R.id.butFastForward).visibility = - if (ckFastForward.isChecked) View.VISIBLE else View.GONE - widgetPreview.findViewById(R.id.butSkip).visibility = - if (ckSkip.isChecked) View.VISIBLE else View.GONE - widgetPreview.findViewById(R.id.butRew).visibility = - if (ckRewind.isChecked) View.VISIBLE else View.GONE - } - - private fun confirmCreateWidget() { - val backgroundColor = getColorWithAlpha(PlayerWidget.DEFAULT_COLOR, opacitySeekBar.progress) - - val prefs = getSharedPreferences(PlayerWidget.PREFS_NAME, MODE_PRIVATE) - val editor = prefs.edit() - editor.putInt(PlayerWidget.KEY_WIDGET_COLOR + appWidgetId, backgroundColor) - editor.putBoolean(PlayerWidget.KEY_WIDGET_PLAYBACK_SPEED + appWidgetId, ckPlaybackSpeed.isChecked) - editor.putBoolean(PlayerWidget.KEY_WIDGET_SKIP + appWidgetId, ckSkip.isChecked) - editor.putBoolean(PlayerWidget.KEY_WIDGET_REWIND + appWidgetId, ckRewind.isChecked) - editor.putBoolean(PlayerWidget.KEY_WIDGET_FAST_FORWARD + appWidgetId, ckFastForward.isChecked) - editor.apply() - - val resultValue = Intent() - resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) - setResult(RESULT_OK, resultValue) - finish() - WidgetUpdaterWorker.enqueueWork(this) - } - - private fun getColorWithAlpha(color: Int, opacity: Int): Int { - return Math.round(0xFF * (0.01 * opacity)).toInt() * 0x1000000 + (color and 0xffffff) - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/ChaptersListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/ChaptersListAdapter.kt deleted file mode 100644 index e342bde7..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/ChaptersListAdapter.kt +++ /dev/null @@ -1,146 +0,0 @@ -package ac.mdiq.podcini.adapter - -import android.content.Context -import android.text.TextUtils -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import androidx.core.content.ContextCompat -import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.bitmap.FitCenter -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions -import com.google.android.material.elevation.SurfaceColors -import ac.mdiq.podcini.R -import ac.mdiq.podcini.adapter.ChaptersListAdapter.ChapterHolder -import ac.mdiq.podcini.core.util.Converter.getDurationStringLocalized -import ac.mdiq.podcini.core.util.Converter.getDurationStringLong -import ac.mdiq.podcini.core.util.IntentUtils.openInBrowser -import ac.mdiq.podcini.model.feed.Chapter -import ac.mdiq.podcini.model.feed.EmbeddedChapterImage -import ac.mdiq.podcini.model.playback.Playable -import ac.mdiq.podcini.ui.common.CircularProgressBar -import kotlin.math.max -import kotlin.math.min - -class ChaptersListAdapter(private val context: Context, private val callback: Callback?) : RecyclerView.Adapter() { - - private var media: Playable? = null - private var currentChapterIndex = -1 - private var currentChapterPosition: Long = -1 - private var hasImages = false - - fun setMedia(media: Playable) { - this.media = media - hasImages = false - for (chapter in media.getChapters()) { - if (!TextUtils.isEmpty(chapter.imageUrl)) { - hasImages = true - } - } - notifyDataSetChanged() - } - - override fun onBindViewHolder(holder: ChapterHolder, position: Int) { - val sc = getItem(position)?: return - holder.title.text = sc.title - holder.start.text = getDurationStringLong(sc.start.toInt()) - val duration = if (position + 1 < itemCount) { - media!!.getChapters()[position + 1].start - sc.start - } else { - (media?.getDuration()?:0) - sc.start - } - holder.duration.text = context.getString(R.string.chapter_duration, - getDurationStringLocalized(context, duration.toInt().toLong())) - - if (TextUtils.isEmpty(sc.link)) { - holder.link.visibility = View.GONE - } else { - holder.link.visibility = View.VISIBLE - holder.link.text = sc.link - holder.link.setOnClickListener { v: View? -> - if (sc.link!=null) openInBrowser(context, sc.link!!) - } - } - holder.secondaryActionIcon.setImageResource(R.drawable.ic_play_48dp) - holder.secondaryActionButton.contentDescription = context.getString(R.string.play_chapter) - holder.secondaryActionButton.setOnClickListener { v: View? -> - callback?.onPlayChapterButtonClicked(position) - } - - if (position == currentChapterIndex) { - val density = context.resources.displayMetrics.density - holder.itemView.setBackgroundColor(SurfaceColors.getColorForElevation(context, 32 * density)) - var progress = ((currentChapterPosition - sc.start).toFloat()) / duration - progress = max(progress.toDouble(), CircularProgressBar.MINIMUM_PERCENTAGE.toDouble()).toFloat() - progress = min(progress.toDouble(), CircularProgressBar.MAXIMUM_PERCENTAGE.toDouble()).toFloat() - holder.progressBar.setPercentage(progress, position) - holder.secondaryActionIcon.setImageResource(R.drawable.ic_replay) - } else { - holder.itemView.setBackgroundColor(ContextCompat.getColor(context, android.R.color.transparent)) - holder.progressBar.setPercentage(0f, null) - } - - if (hasImages) { - holder.image.visibility = View.VISIBLE - if (TextUtils.isEmpty(sc.imageUrl)) { - Glide.with(context).clear(holder.image) - } else { - if (media != null) Glide.with(context) - .load(EmbeddedChapterImage.getModelFor(media!!, position)) - .apply(RequestOptions() - .dontAnimate() - .transform(FitCenter(), RoundedCorners((4 * context.resources.displayMetrics.density).toInt()))) - .into(holder.image) - } - } else { - holder.image.visibility = View.GONE - } - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ChapterHolder { - val inflater = LayoutInflater.from(context) - return ChapterHolder(inflater.inflate(R.layout.simplechapter_item, parent, false)) - } - - override fun getItemCount(): Int { - return media?.getChapters()?.size?:0 - } - - class ChapterHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - val title: TextView = itemView.findViewById(R.id.txtvTitle) - val start: TextView = itemView.findViewById(R.id.txtvStart) - val link: TextView = itemView.findViewById(R.id.txtvLink) - val duration: TextView = itemView.findViewById(R.id.txtvDuration) - val image: ImageView = itemView.findViewById(R.id.imgvCover) - val secondaryActionButton: View = itemView.findViewById(R.id.secondaryActionButton) - val secondaryActionIcon: ImageView = itemView.findViewById(R.id.secondaryActionIcon) - val progressBar: CircularProgressBar = itemView.findViewById(R.id.secondaryActionProgress) - } - - fun notifyChapterChanged(newChapterIndex: Int) { - currentChapterIndex = newChapterIndex - currentChapterPosition = getItem(newChapterIndex)?.start?:0 - notifyDataSetChanged() - } - - fun notifyTimeChanged(timeMs: Long) { - currentChapterPosition = timeMs - // Passing an argument prevents flickering. - // See EpisodeItemListAdapter.notifyItemChangedCompat. - notifyItemChanged(currentChapterIndex, "foo") - } - - fun getItem(position: Int): Chapter? { - val chapters = media?.getChapters()?: return null - if (position < 0 || position >= chapters.size) return null - return chapters[position] - } - - interface Callback { - fun onPlayChapterButtonClicked(position: Int) - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/CoverLoader.kt b/app/src/main/java/ac/mdiq/podcini/adapter/CoverLoader.kt deleted file mode 100644 index 9394bc42..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/CoverLoader.kt +++ /dev/null @@ -1,122 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.activity.MainActivity -import android.graphics.drawable.Drawable -import android.view.View -import android.widget.ImageView -import android.widget.TextView -import com.bumptech.glide.Glide -import com.bumptech.glide.RequestBuilder -import com.bumptech.glide.request.RequestOptions -import com.bumptech.glide.request.target.CustomViewTarget -import com.bumptech.glide.request.transition.Transition -import java.lang.ref.WeakReference - -class CoverLoader(activity: MainActivity) { - private var resource = 0 - private var uri: String? = null - private var fallbackUri: String? = null - private var imgvCover: ImageView? = null - private var textAndImageCombined = false - private var fallbackTitle: TextView? = null - - fun withUri(uri: String?): CoverLoader { - this.uri = uri - return this - } - - fun withResource(resource: Int): CoverLoader { - this.resource = resource - return this - } - - fun withFallbackUri(uri: String?): CoverLoader { - fallbackUri = uri - return this - } - - fun withCoverView(coverView: ImageView): CoverLoader { - imgvCover = coverView - return this - } - - fun withPlaceholderView(title: TextView): CoverLoader { - this.fallbackTitle = title - return this - } - - /** - * Set cover text and if it should be shown even if there is a cover image. - * @param fallbackTitle Fallback title text - * @param textAndImageCombined Show cover text even if there is a cover image? - */ - fun withPlaceholderView(fallbackTitle: TextView?, textAndImageCombined: Boolean): CoverLoader { - this.fallbackTitle = fallbackTitle - this.textAndImageCombined = textAndImageCombined - return this - } - - fun load() { - if (imgvCover == null) return - - val coverTarget = CoverTarget(fallbackTitle, imgvCover!!, textAndImageCombined) - - if (resource != 0) { - Glide.with(imgvCover!!).clear(coverTarget) - imgvCover!!.setImageResource(resource) - CoverTarget.setTitleVisibility(fallbackTitle, textAndImageCombined) - return - } - - val options: RequestOptions = RequestOptions() - .fitCenter() - .dontAnimate() - - var builder: RequestBuilder = Glide.with(imgvCover!!) - .`as`(Drawable::class.java) - .load(uri) - .apply(options) - - if (fallbackUri != null) { - builder = builder.error(Glide.with(imgvCover!!) - .`as`(Drawable::class.java) - .load(fallbackUri) - .apply(options)) - } - - builder.into(coverTarget) - } - - internal class CoverTarget(fallbackTitle: TextView?, - coverImage: ImageView, - private val textAndImageCombined: Boolean - ) : CustomViewTarget(coverImage) { - - private val fallbackTitle: WeakReference = WeakReference(fallbackTitle) - private val cover: WeakReference = WeakReference(coverImage) - - override fun onLoadFailed(errorDrawable: Drawable?) { - setTitleVisibility(fallbackTitle.get(), true) - } - - override fun onResourceReady(resource: Drawable, - transition: Transition? - ) { - val ivCover = cover.get() - ivCover!!.setImageDrawable(resource) - setTitleVisibility(fallbackTitle.get(), textAndImageCombined) - } - - override fun onResourceCleared(placeholder: Drawable?) { - val ivCover = cover.get() - ivCover!!.setImageDrawable(placeholder) - setTitleVisibility(fallbackTitle.get(), textAndImageCombined) - } - - companion object { - fun setTitleVisibility(fallbackTitle: TextView?, textAndImageCombined: Boolean) { - fallbackTitle?.visibility = if (textAndImageCombined) View.VISIBLE else View.GONE - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/DataFolderAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/DataFolderAdapter.kt deleted file mode 100644 index 6ea6972e..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/DataFolderAdapter.kt +++ /dev/null @@ -1,112 +0,0 @@ -package ac.mdiq.podcini.adapter - -import android.content.Context -import android.text.format.Formatter -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ProgressBar -import android.widget.RadioButton -import android.widget.TextView -import androidx.core.util.Consumer -import androidx.recyclerview.widget.RecyclerView -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.util.StorageUtils.getFreeSpaceAvailable -import ac.mdiq.podcini.core.util.StorageUtils.getTotalSpaceAvailable -import ac.mdiq.podcini.storage.preferences.UserPreferences.getDataFolder -import java.io.File - -class DataFolderAdapter(context: Context, selectionHandler: Consumer) : RecyclerView.Adapter() { - - private val selectionHandler: Consumer - private val currentPath: String? - private val entries: List - private val freeSpaceString: String - - init { - this.entries = getStorageEntries(context) - this.currentPath = getCurrentPath() - this.selectionHandler = selectionHandler - this.freeSpaceString = context.getString(R.string.choose_data_directory_available_space) - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val inflater = LayoutInflater.from(parent.context) - val entryView = inflater.inflate(R.layout.choose_data_folder_dialog_entry, parent, false) - return ViewHolder(entryView) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val storagePath = entries[position] - val context = holder.root.context - val freeSpace = Formatter.formatShortFileSize(context, storagePath.availableSpace) - val totalSpace = Formatter.formatShortFileSize(context, storagePath.totalSpace) - - holder.path.text = storagePath.shortPath - holder.size.text = String.format(freeSpaceString, freeSpace, totalSpace) - holder.progressBar.progress = storagePath.usagePercentage - val selectListener = View.OnClickListener { v: View? -> - selectionHandler.accept( - storagePath.fullPath) - } - holder.root.setOnClickListener(selectListener) - holder.radioButton.setOnClickListener(selectListener) - - if (storagePath.fullPath == currentPath) { - holder.radioButton.toggle() - } - } - - override fun getItemCount(): Int { - return entries.size - } - - private fun getCurrentPath(): String? { - val dataFolder = getDataFolder(null) - return dataFolder?.absolutePath - } - - private fun getStorageEntries(context: Context): List { - val mediaDirs = context.getExternalFilesDirs(null) - val entries: MutableList = ArrayList(mediaDirs.size) - for (dir in mediaDirs) { - if (!isWritable(dir)) { - continue - } - entries.add(StoragePath(dir.absolutePath)) - } - if (entries.isEmpty() && isWritable(context.filesDir)) { - entries.add(StoragePath(context.filesDir.absolutePath)) - } - return entries - } - - private fun isWritable(dir: File?): Boolean { - return dir != null && dir.exists() && dir.canRead() && dir.canWrite() - } - - class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - val root: View = itemView.findViewById(R.id.root) - val path: TextView = itemView.findViewById(R.id.path) - val size: TextView = itemView.findViewById(R.id.size) - val radioButton: RadioButton = itemView.findViewById(R.id.radio_button) - val progressBar: ProgressBar = itemView.findViewById(R.id.used_space) - } - - internal class StoragePath(val fullPath: String) { - val shortPath: String - get() { - val prefixIndex = fullPath.indexOf("Android") - return if ((prefixIndex > 0)) fullPath.substring(0, prefixIndex) else fullPath - } - - val availableSpace: Long - get() = getFreeSpaceAvailable(fullPath) - - val totalSpace: Long - get() = getTotalSpaceAvailable(fullPath) - - val usagePercentage: Int - get() = 100 - (100 * availableSpace / totalSpace.toFloat()).toInt() - } -} \ No newline at end of file diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/DownloadLogAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/DownloadLogAdapter.kt deleted file mode 100644 index 71046047..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/DownloadLogAdapter.kt +++ /dev/null @@ -1,149 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.activity.MainActivity -import android.app.Activity -import android.text.format.DateUtils -import android.util.Log -import android.view.View -import android.view.ViewGroup -import android.widget.BaseAdapter -import android.widget.Toast -import ac.mdiq.podcini.R -import ac.mdiq.podcini.adapter.actionbutton.DownloadActionButton -import ac.mdiq.podcini.core.storage.DBReader -import ac.mdiq.podcini.core.util.DownloadErrorLabel -import ac.mdiq.podcini.core.util.download.FeedUpdateManager -import ac.mdiq.podcini.model.download.DownloadError -import ac.mdiq.podcini.model.download.DownloadResult -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.model.feed.FeedMedia -import ac.mdiq.podcini.ui.common.ThemeUtils -import ac.mdiq.podcini.view.viewholder.DownloadLogItemViewHolder -import androidx.media3.common.util.UnstableApi - -/** - * Displays a list of DownloadStatus entries. - */ -class DownloadLogAdapter(private val context: Activity) : BaseAdapter() { - private var downloadLog: List = ArrayList() - - fun setDownloadLog(downloadLog: List) { - this.downloadLog = downloadLog - notifyDataSetChanged() - } - - @UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - val holder: DownloadLogItemViewHolder - if (convertView == null) { - holder = DownloadLogItemViewHolder(context, parent) - holder.itemView.tag = holder - } else { - holder = convertView.tag as DownloadLogItemViewHolder - } - val item = getItem(position) - if (item != null) bind(holder, item, position) - return holder.itemView - } - - @UnstableApi private fun bind(holder: DownloadLogItemViewHolder, status: DownloadResult, position: Int) { - var statusText: String? = "" - if (status.feedfileType == Feed.FEEDFILETYPE_FEED) { - statusText += context.getString(R.string.download_type_feed) - } else if (status.feedfileType == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { - statusText += context.getString(R.string.download_type_media) - } - statusText += " · " - statusText += DateUtils.getRelativeTimeSpanString(status.getCompletionDate().time, - System.currentTimeMillis(), DateUtils.MINUTE_IN_MILLIS, 0) - holder.status.text = statusText - - if (status.title.isNotEmpty()) { - holder.title.text = status.title - } else { - holder.title.setText(R.string.download_log_title_unknown) - } - - if (status.isSuccessful) { - holder.icon.setTextColor(ThemeUtils.getColorFromAttr(context, R.attr.icon_green)) - holder.icon.text = "{fa-check-circle}" - holder.icon.setContentDescription(context.getString(R.string.download_successful)) - holder.secondaryActionButton.visibility = View.INVISIBLE - holder.reason.visibility = View.GONE - holder.tapForDetails.visibility = View.GONE - } else { - if (status.reason == DownloadError.ERROR_PARSER_EXCEPTION_DUPLICATE) { - holder.icon.setTextColor(ThemeUtils.getColorFromAttr(context, R.attr.icon_yellow)) - holder.icon.text = "{fa-exclamation-circle}" - } else { - holder.icon.setTextColor(ThemeUtils.getColorFromAttr(context, R.attr.icon_red)) - holder.icon.text = "{fa-times-circle}" - } - holder.icon.setContentDescription(context.getString(R.string.error_label)) - holder.reason.setText(DownloadErrorLabel.from(status.reason)) - holder.reason.visibility = View.VISIBLE - holder.tapForDetails.visibility = View.VISIBLE - - if (newerWasSuccessful(position, status.feedfileType, status.feedfileId)) { - holder.secondaryActionButton.visibility = View.INVISIBLE - holder.secondaryActionButton.setOnClickListener(null) - holder.secondaryActionButton.tag = null - } else { - holder.secondaryActionIcon.setImageResource(R.drawable.ic_refresh) - holder.secondaryActionButton.visibility = View.VISIBLE - - if (status.feedfileType == Feed.FEEDFILETYPE_FEED) { - holder.secondaryActionButton.setOnClickListener(View.OnClickListener setOnClickListener@{ v: View? -> - holder.secondaryActionButton.visibility = View.INVISIBLE - val feed: Feed? = DBReader.getFeed(status.feedfileId) - if (feed == null) { - Log.e(TAG, "Could not find feed for feed id: " + status.feedfileId) - return@setOnClickListener - } - FeedUpdateManager.runOnce(context, feed) - }) - } else if (status.feedfileType == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { - holder.secondaryActionButton.setOnClickListener(View.OnClickListener { v: View? -> - holder.secondaryActionButton.visibility = View.INVISIBLE - val media: FeedMedia? = DBReader.getFeedMedia(status.feedfileId) - if (media == null) { - Log.e(TAG, "Could not find feed media for feed id: " + status.feedfileId) - return@OnClickListener - } - if (media.getItem() != null) DownloadActionButton(media.getItem()!!).onClick(context) - (context as MainActivity).showSnackbarAbovePlayer( - R.string.status_downloading_label, Toast.LENGTH_SHORT) - }) - } - } - } - } - - private fun newerWasSuccessful(downloadStatusIndex: Int, feedTypeId: Int, id: Long): Boolean { - for (i in 0 until downloadStatusIndex) { - val status: DownloadResult = downloadLog[i] - if (status.feedfileType == feedTypeId && status.feedfileId == id && status.isSuccessful) { - return true - } - } - return false - } - - override fun getCount(): Int { - return downloadLog.size - } - - override fun getItem(position: Int): DownloadResult? { - if (position in downloadLog.indices) { - return downloadLog[position] - } - return null - } - - override fun getItemId(position: Int): Long { - return position.toLong() - } - - companion object { - private const val TAG = "DownloadLogAdapter" - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/EpisodeItemListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/EpisodeItemListAdapter.kt deleted file mode 100644 index 032bb9ce..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/EpisodeItemListAdapter.kt +++ /dev/null @@ -1,213 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.MainActivity -import ac.mdiq.podcini.core.util.FeedItemUtil -import ac.mdiq.podcini.fragment.ItemPagerFragment -import ac.mdiq.podcini.menuhandler.FeedItemMenuHandler -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.ui.common.ThemeUtils -import ac.mdiq.podcini.view.viewholder.EpisodeItemViewHolder -import android.R.color -import android.app.Activity -import android.os.Build -import android.view.* -import androidx.media3.common.util.UnstableApi -import androidx.recyclerview.widget.RecyclerView -import org.apache.commons.lang3.ArrayUtils -import java.lang.ref.WeakReference - - -/** - * List adapter for the list of new episodes. - */ -open class EpisodeItemListAdapter(mainActivity: MainActivity) : SelectableAdapter(mainActivity), - View.OnCreateContextMenuListener { - - private val mainActivityRef: WeakReference = WeakReference(mainActivity) - private var episodes: List = ArrayList() - var longPressedItem: FeedItem? = null - var longPressedPosition: Int = 0 // used to init actionMode - private var dummyViews = 0 - - init { - setHasStableIds(true) - } - - fun setDummyViews(dummyViews: Int) { - this.dummyViews = dummyViews - notifyDataSetChanged() - } - - fun updateItems(items: List) { - episodes = items - notifyDataSetChanged() - updateTitle() - } - - override fun getItemViewType(position: Int): Int { - return R.id.view_type_episode_item - } - - @UnstableApi override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EpisodeItemViewHolder { - return EpisodeItemViewHolder(mainActivityRef.get()!!, parent) - } - - @UnstableApi override fun onBindViewHolder(holder: EpisodeItemViewHolder, pos: Int) { - if (pos >= episodes.size || pos < 0) { - beforeBindViewHolder(holder, pos) - holder.bindDummy() - afterBindViewHolder(holder, pos) - holder.hideSeparatorIfNecessary() - return - } - - // Reset state of recycled views - holder.coverHolder.visibility = View.VISIBLE - holder.dragHandle.setVisibility(View.GONE) - - beforeBindViewHolder(holder, pos) - - val item: FeedItem = episodes[pos] - holder.bind(item) - - holder.itemView.setOnClickListener { v: View? -> - val activity: MainActivity? = mainActivityRef.get() - if (!inActionMode()) { - val ids: LongArray = FeedItemUtil.getIds(episodes) - val position = ArrayUtils.indexOf(ids, item.id) - activity?.loadChildFragment(ItemPagerFragment.newInstance(ids, position)) - } else { - toggleSelection(holder.bindingAdapterPosition) - } - } - holder.itemView.setOnCreateContextMenuListener(this) - holder.itemView.setOnLongClickListener { v: View? -> - longPressedItem = item - longPressedPosition = holder.bindingAdapterPosition - false - } - holder.itemView.setOnTouchListener(View.OnTouchListener { v: View?, e: MotionEvent -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (e.isFromSource(InputDevice.SOURCE_MOUSE) && e.buttonState == MotionEvent.BUTTON_SECONDARY) { - longPressedItem = item - longPressedPosition = holder.bindingAdapterPosition - return@OnTouchListener false - } - } - false - }) - - if (inActionMode()) { - holder.secondaryActionButton.setOnClickListener(null) - if (isSelected(pos)) { - holder.itemView.setBackgroundColor(-0x78000000 - + (0xffffff and ThemeUtils.getColorFromAttr(mainActivityRef.get()!!, R.attr.colorAccent))) - } else { - holder.itemView.setBackgroundResource(color.transparent) - } - } - - afterBindViewHolder(holder, pos) - holder.hideSeparatorIfNecessary() - } - - protected open fun beforeBindViewHolder(holder: EpisodeItemViewHolder, pos: Int) { - } - - protected open fun afterBindViewHolder(holder: EpisodeItemViewHolder, pos: Int) { - } - - @UnstableApi override fun onViewRecycled(holder: EpisodeItemViewHolder) { - super.onViewRecycled(holder) - // Set all listeners to null. This is required to prevent leaking fragments that have set a listener. - // Activity -> recycledViewPool -> EpisodeItemViewHolder -> Listener -> Fragment (can not be garbage collected) - holder.itemView.setOnClickListener(null) - holder.itemView.setOnCreateContextMenuListener(null) - holder.itemView.setOnLongClickListener(null) - holder.itemView.setOnTouchListener(null) - holder.secondaryActionButton.setOnClickListener(null) - holder.dragHandle.setOnTouchListener(null) - holder.coverHolder.setOnTouchListener(null) - } - - /** - * [.notifyItemChanged] is final, so we can not override. - * Calling [.notifyItemChanged] may bind the item to a new ViewHolder and execute a transition. - * This causes flickering and breaks the download animation that stores the old progress in the View. - * Instead, we tell the adapter to use partial binding by calling [.notifyItemChanged]. - * We actually ignore the payload and always do a full bind but calling the partial bind method ensures - * that ViewHolders are always re-used. - * - * @param position Position of the item that has changed - */ - fun notifyItemChangedCompat(position: Int) { - notifyItemChanged(position, "foo") - } - - override fun getItemId(position: Int): Long { -// if (position >= episodes.size) { -// return RecyclerView.NO_ID // Dummy views -// } -// val item = episodes[position] -// return item.id ?: RecyclerView.NO_POSITION.toLong() - return getItem(position)?.id ?: RecyclerView.NO_ID - } - - override fun getItemCount(): Int { - return dummyViews + episodes.size - } - - protected fun getItem(index: Int): FeedItem? { -// return episodes[index] - return if (index in episodes.indices) episodes[index] else null - } - - protected val activity: Activity? - get() = mainActivityRef.get() - - @UnstableApi override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) { - val inflater: MenuInflater = activity!!.menuInflater - if (inActionMode()) { - inflater.inflate(R.menu.multi_select_context_popup, menu) - } else { - if (longPressedItem == null) { - return - } - inflater.inflate(R.menu.feeditemlist_context, menu) - menu.setHeaderTitle(longPressedItem!!.title) - FeedItemMenuHandler.onPrepareMenu(menu, longPressedItem, R.id.skip_episode_item) - } - } - - fun onContextItemSelected(item: MenuItem): Boolean { - when (item.itemId) { - R.id.multi_select -> { - startSelectMode(longPressedPosition) - return true - } - R.id.select_all_above -> { - setSelected(0, longPressedPosition, true) - return true - } - R.id.select_all_below -> { - shouldSelectLazyLoadedItems = true - setSelected(longPressedPosition + 1, itemCount, true) - return true - } - else -> return false - } - } - - val selectedItems: List - get() { - val items: MutableList = ArrayList() - for (i in 0 until itemCount) { - if (i < episodes.size && isSelected(i)) { - val item = getItem(i) - if (item != null) items.add(item) - } - } - return items - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/FeedDiscoverAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/FeedDiscoverAdapter.kt deleted file mode 100644 index 5ceb3be7..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/FeedDiscoverAdapter.kt +++ /dev/null @@ -1,68 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.activity.MainActivity -import android.view.View -import android.view.ViewGroup -import android.widget.BaseAdapter -import android.widget.ImageView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.bitmap.FitCenter -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions -import ac.mdiq.podcini.R -import ac.mdiq.podcini.net.discovery.PodcastSearchResult -import java.lang.ref.WeakReference - -class FeedDiscoverAdapter(mainActivity: MainActivity) : BaseAdapter() { - private val mainActivityRef: WeakReference = WeakReference(mainActivity) - private val data: MutableList = ArrayList() - - fun updateData(newData: List) { - data.clear() - data.addAll(newData) - notifyDataSetChanged() - } - - override fun getCount(): Int { - return data.size - } - - override fun getItem(position: Int): PodcastSearchResult? { - return if (position in data.indices) data[position] else null - } - - override fun getItemId(position: Int): Long { - return 0 - } - - override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - var convertView = convertView - val holder: Holder - - if (convertView == null) { - convertView = View.inflate(mainActivityRef.get(), R.layout.quick_feed_discovery_item, null) - holder = Holder() - holder.imageView = convertView.findViewById(R.id.discovery_cover) - convertView.tag = holder - } else { - holder = convertView.tag as Holder - } - - val podcast: PodcastSearchResult? = getItem(position) - holder.imageView!!.contentDescription = podcast?.title - - Glide.with(mainActivityRef.get()!!) - .load(podcast?.imageUrl) - .apply(RequestOptions() - .placeholder(R.color.light_gray) - .transform(FitCenter(), RoundedCorners((8 * mainActivityRef.get()!!.resources.displayMetrics.density).toInt())) - .dontAnimate()) - .into(holder.imageView!!) - - return convertView!! - } - - internal class Holder { - var imageView: ImageView? = null - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/FeedItemlistDescriptionAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/FeedItemlistDescriptionAdapter.kt deleted file mode 100644 index a679dc4f..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/FeedItemlistDescriptionAdapter.kt +++ /dev/null @@ -1,104 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.service.playback.PlaybackService.Companion.getPlayerActivityIntent -import ac.mdiq.podcini.core.util.DateFormatter.formatAbbrev -import ac.mdiq.podcini.core.util.NetworkUtils.isStreamingAllowed -import ac.mdiq.podcini.core.util.playback.PlaybackServiceStarter -import ac.mdiq.podcini.core.util.syndication.HtmlToPlainText -import ac.mdiq.podcini.dialog.StreamingConfirmationDialog -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.playback.MediaType -import ac.mdiq.podcini.model.playback.Playable -import ac.mdiq.podcini.model.playback.RemoteMedia -import android.content.Context -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.Button -import android.widget.TextView -import androidx.media3.common.util.UnstableApi - -/** - * List adapter for showing a list of FeedItems with their title and description. - */ -class FeedItemlistDescriptionAdapter(context: Context, resource: Int, objects: List?) : - ArrayAdapter(context, resource, objects!!) { - @UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - var convertView = convertView - val holder: Holder - - val item = getItem(position) - - // Inflate layout - if (convertView == null) { - holder = Holder() - val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater - convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false) - holder.title = convertView.findViewById(R.id.txtvTitle) - holder.pubDate = convertView.findViewById(R.id.txtvPubDate) - holder.description = convertView.findViewById(R.id.txtvDescription) - holder.preview = convertView.findViewById(R.id.butPreview) - - convertView.tag = holder - } else { - holder = convertView.tag as Holder - } - - holder.title!!.text = item!!.title - holder.pubDate!!.text = formatAbbrev(context, item.pubDate) - if (item.description != null) { - val description = HtmlToPlainText.getPlainText(item.description!!) - .replace("\n".toRegex(), " ") - .replace("\\s+".toRegex(), " ") - .trim { it <= ' ' } - holder.description!!.text = description - holder.description!!.maxLines = MAX_LINES_COLLAPSED - } - holder.description!!.tag = false - holder.preview!!.visibility = View.GONE - holder.preview!!.setOnClickListener { v: View? -> - if (item.media == null) { - return@setOnClickListener - } - val playable: Playable = RemoteMedia(item) - if (!isStreamingAllowed) { - StreamingConfirmationDialog(context, playable).show() - return@setOnClickListener - } - - PlaybackServiceStarter(context, playable) - .callEvenIfRunning(true) - .start() - if (playable.getMediaType() == MediaType.VIDEO) { - context.startActivity(getPlayerActivityIntent(context, playable)) - } - } - convertView!!.setOnClickListener { v: View? -> - if (holder.description!!.tag == true) { - holder.description!!.maxLines = MAX_LINES_COLLAPSED - holder.preview!!.visibility = View.GONE - holder.description!!.tag = false - } else { - holder.description!!.maxLines = 30 - holder.description!!.tag = true - - holder.preview!!.visibility = if (item.media != null) View.VISIBLE else View.GONE - holder.preview!!.setText(R.string.preview_episode) - } - } - return convertView - } - - internal class Holder { - var title: TextView? = null - var pubDate: TextView? = null - var description: TextView? = null - var preview: Button? = null - } - - companion object { - private const val MAX_LINES_COLLAPSED = 2 - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/HorizontalFeedListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/HorizontalFeedListAdapter.kt deleted file mode 100644 index f1cba636..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/HorizontalFeedListAdapter.kt +++ /dev/null @@ -1,125 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.activity.MainActivity -import android.view.ContextMenu -import android.view.MenuInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import androidx.annotation.StringRes -import androidx.cardview.widget.CardView -import androidx.media3.common.util.UnstableApi -import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.Glide -import com.bumptech.glide.request.RequestOptions -import ac.mdiq.podcini.R -import ac.mdiq.podcini.fragment.FeedItemlistFragment -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.ui.common.SquareImageView -import java.lang.ref.WeakReference - -open class HorizontalFeedListAdapter(mainActivity: MainActivity) : - RecyclerView.Adapter(), View.OnCreateContextMenuListener { - - private val mainActivityRef: WeakReference = WeakReference(mainActivity) - private val data: MutableList = ArrayList() - private var dummyViews = 0 - var longPressedItem: Feed? = null - - @StringRes - private var endButtonText = 0 - private var endButtonAction: Runnable? = null - - fun setDummyViews(dummyViews: Int) { - this.dummyViews = dummyViews - } - - fun updateData(newData: List?) { - data.clear() - data.addAll(newData!!) - notifyDataSetChanged() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { - val convertView = View.inflate(mainActivityRef.get(), R.layout.horizontal_feed_item, null) - return Holder(convertView) - } - - @UnstableApi override fun onBindViewHolder(holder: Holder, position: Int) { - if (position == itemCount - 1 && endButtonAction != null) { - holder.cardView.visibility = View.GONE - holder.actionButton.visibility = View.VISIBLE - holder.actionButton.setText(endButtonText) - holder.actionButton.setOnClickListener { v: View? -> endButtonAction!!.run() } - return - } - holder.cardView.visibility = View.VISIBLE - holder.actionButton.visibility = View.GONE - if (position >= data.size) { - holder.itemView.alpha = 0.1f - Glide.with(mainActivityRef.get()!!).clear(holder.imageView) - holder.imageView.setImageResource(R.color.medium_gray) - return - } - - holder.itemView.alpha = 1.0f - val podcast: Feed = data[position] - holder.imageView.setContentDescription(podcast.title) - holder.imageView.setOnClickListener { v: View? -> - mainActivityRef.get()?.loadChildFragment(FeedItemlistFragment.newInstance(podcast.id)) - } - - holder.imageView.setOnCreateContextMenuListener(this) - holder.imageView.setOnLongClickListener { v: View? -> - val currentItemPosition = holder.bindingAdapterPosition - longPressedItem = data[currentItemPosition] - false - } - - Glide.with(mainActivityRef.get()!!) - .load(podcast.imageUrl) - .apply(RequestOptions() - .placeholder(R.color.light_gray) - .fitCenter() - .dontAnimate()) - .into(holder.imageView) - } - - override fun getItemId(position: Int): Long { - if (position >= data.size) { - return RecyclerView.NO_ID // Dummy views - } - return data[position].id - } - - override fun getItemCount(): Int { - return dummyViews + data.size + (if ((endButtonAction == null)) 0 else 1) - } - - override fun onCreateContextMenu(contextMenu: ContextMenu, view: View, contextMenuInfo: ContextMenu.ContextMenuInfo?) { - val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater - if (longPressedItem == null) { - return - } - inflater.inflate(R.menu.nav_feed_context, contextMenu) - contextMenu.setHeaderTitle(longPressedItem!!.title) - } - - fun setEndButton(@StringRes text: Int, action: Runnable?) { - endButtonAction = action - endButtonText = text - notifyDataSetChanged() - } - - class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) { - var imageView: SquareImageView = itemView.findViewById(R.id.discovery_cover) - var cardView: CardView - var actionButton: Button - - init { - imageView.setDirection(SquareImageView.DIRECTION_HEIGHT) - actionButton = itemView.findViewById(R.id.actionButton) - cardView = itemView.findViewById(R.id.cardView) - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/HorizontalItemListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/HorizontalItemListAdapter.kt deleted file mode 100644 index 8dfcdf7e..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/HorizontalItemListAdapter.kt +++ /dev/null @@ -1,120 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.activity.MainActivity -import android.view.ContextMenu -import android.view.MenuInflater -import android.view.View -import android.view.ViewGroup -import androidx.media3.common.util.UnstableApi -import androidx.recyclerview.widget.RecyclerView -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.util.FeedItemUtil -import ac.mdiq.podcini.fragment.ItemPagerFragment -import ac.mdiq.podcini.menuhandler.FeedItemMenuHandler -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.view.viewholder.HorizontalItemViewHolder -import org.apache.commons.lang3.ArrayUtils -import java.lang.ref.WeakReference - -open class HorizontalItemListAdapter(mainActivity: MainActivity) : RecyclerView.Adapter(), - View.OnCreateContextMenuListener { - - private val mainActivityRef: WeakReference = WeakReference(mainActivity) - private var data: List = ArrayList() - var longPressedItem: FeedItem? = null - private var dummyViews = 0 - - init { - setHasStableIds(true) - } - - fun setDummyViews(dummyViews: Int) { - this.dummyViews = dummyViews - } - - fun updateData(newData: List) { - data = newData - notifyDataSetChanged() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): HorizontalItemViewHolder { - return HorizontalItemViewHolder(mainActivityRef.get()!!, parent) - } - - @UnstableApi override fun onBindViewHolder(holder: HorizontalItemViewHolder, position: Int) { - if (position >= data.size) { - holder.bindDummy() - return - } - - val item: FeedItem = data[position] - holder.bind(item) - - holder.card.setOnCreateContextMenuListener(this) - holder.card.setOnLongClickListener { v: View? -> - longPressedItem = item - false - } - holder.secondaryActionIcon.setOnCreateContextMenuListener(this) - holder.secondaryActionIcon.setOnLongClickListener { v: View? -> - longPressedItem = item - false - } - holder.card.setOnClickListener { v: View? -> - val activity: MainActivity? = mainActivityRef.get() - if (activity != null) { - val ids: LongArray = FeedItemUtil.getIds(data) - val clickPosition = ArrayUtils.indexOf(ids, item.id) - activity.loadChildFragment(ItemPagerFragment.newInstance(ids, clickPosition)) - } - } - } - - override fun getItemId(position: Int): Long { - if (position in data.indices) { - val item: FeedItem = data[position] - return item.id - } - return RecyclerView.NO_ID // Dummy views - } - - override fun getItemCount(): Int { - return dummyViews + data.size - } - - override fun onViewRecycled(holder: HorizontalItemViewHolder) { - super.onViewRecycled(holder) - // Set all listeners to null. This is required to prevent leaking fragments that have set a listener. - // Activity -> recycledViewPool -> ViewHolder -> Listener -> Fragment (can not be garbage collected) - holder.card.setOnClickListener(null) - holder.card.setOnCreateContextMenuListener(null) - holder.card.setOnLongClickListener(null) - holder.secondaryActionIcon.setOnClickListener(null) - holder.secondaryActionIcon.setOnCreateContextMenuListener(null) - holder.secondaryActionIcon.setOnLongClickListener(null) - } - - /** - * [.notifyItemChanged] is final, so we can not override. - * Calling [.notifyItemChanged] may bind the item to a new ViewHolder and execute a transition. - * This causes flickering and breaks the download animation that stores the old progress in the View. - * Instead, we tell the adapter to use partial binding by calling [.notifyItemChanged]. - * We actually ignore the payload and always do a full bind but calling the partial bind method ensures - * that ViewHolders are always re-used. - * - * @param position Position of the item that has changed - */ - fun notifyItemChangedCompat(position: Int) { - notifyItemChanged(position, "foo") - } - - @UnstableApi override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) { - val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater - if (longPressedItem == null) return - - menu.clear() - inflater.inflate(R.menu.feeditemlist_context, menu) - menu.setHeaderTitle(longPressedItem!!.title) - FeedItemMenuHandler.onPrepareMenu(menu, longPressedItem, R.id.skip_episode_item) - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/NavListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/NavListAdapter.kt deleted file mode 100644 index f869de4d..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/NavListAdapter.kt +++ /dev/null @@ -1,376 +0,0 @@ -package ac.mdiq.podcini.adapter - -import android.app.Activity -import android.content.DialogInterface -import android.content.Intent -import android.content.SharedPreferences -import android.content.SharedPreferences.OnSharedPreferenceChangeListener -import android.os.Build -import android.view.* -import android.view.ContextMenu.ContextMenuInfo -import android.view.View.OnCreateContextMenuListener -import android.widget.ImageView -import android.widget.LinearLayout -import android.widget.RelativeLayout -import android.widget.TextView -import androidx.annotation.DrawableRes -import androidx.preference.PreferenceManager -import androidx.recyclerview.widget.RecyclerView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.resource.bitmap.FitCenter -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.activity.PreferenceActivity -import ac.mdiq.podcini.core.storage.NavDrawerData.* -import ac.mdiq.podcini.fragment.* -import ac.mdiq.podcini.storage.preferences.UserPreferences -import ac.mdiq.podcini.storage.preferences.UserPreferences.episodeCacheSize -import ac.mdiq.podcini.storage.preferences.UserPreferences.hiddenDrawerItems -import ac.mdiq.podcini.storage.preferences.UserPreferences.isEnableAutodownload -import ac.mdiq.podcini.storage.preferences.UserPreferences.subscriptionsFilter -import ac.mdiq.podcini.ui.home.HomeFragment -import androidx.media3.common.util.UnstableApi -import org.apache.commons.lang3.ArrayUtils -import java.lang.ref.WeakReference -import java.text.NumberFormat -import java.util.* -import kotlin.math.abs - -/** - * BaseAdapter for the navigation drawer - */ -class NavListAdapter(private val itemAccess: ItemAccess, context: Activity) : - RecyclerView.Adapter(), OnSharedPreferenceChangeListener { - - private val fragmentTags: MutableList = ArrayList() - private val titles: Array = context.resources.getStringArray(R.array.nav_drawer_titles) - private val activity = WeakReference(context) - @JvmField - var showSubscriptionList: Boolean = true - - init { - loadItems() - - val prefs = PreferenceManager.getDefaultSharedPreferences(context) - prefs.registerOnSharedPreferenceChangeListener(this) - } - - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String?) { - if (UserPreferences.PREF_HIDDEN_DRAWER_ITEMS == key) { - loadItems() - } - } - - private fun loadItems() { - val newTags: MutableList = ArrayList(listOf(*NavDrawerFragment.NAV_DRAWER_TAGS)) - val hiddenFragments = hiddenDrawerItems - newTags.removeAll(hiddenFragments!!) - - if (newTags.contains(SUBSCRIPTION_LIST_TAG)) { - // we never want SUBSCRIPTION_LIST_TAG to be in 'tags' - // since it doesn't actually correspond to a position in the list, but is - // a placeholder that indicates if we should show the subscription list in the - // nav drawer at all. - showSubscriptionList = true - newTags.remove(SUBSCRIPTION_LIST_TAG) - } else { - showSubscriptionList = false - } - - fragmentTags.clear() - fragmentTags.addAll(newTags) - notifyDataSetChanged() - } - - fun getLabel(tag: String?): String { - val index = ArrayUtils.indexOf(NavDrawerFragment.NAV_DRAWER_TAGS, tag) - return titles[index] - } - - @UnstableApi @DrawableRes - private fun getDrawable(tag: String?): Int { - return when (tag) { - HomeFragment.TAG -> R.drawable.ic_home - QueueFragment.TAG -> R.drawable.ic_playlist_play - InboxFragment.TAG -> R.drawable.ic_inbox - AllEpisodesFragment.TAG -> R.drawable.ic_feed - CompletedDownloadsFragment.TAG -> R.drawable.ic_download - PlaybackHistoryFragment.TAG -> R.drawable.ic_history - SubscriptionFragment.TAG -> R.drawable.ic_subscriptions - AddFeedFragment.TAG -> R.drawable.ic_add - else -> 0 - } - } - - fun getFragmentTags(): List { - return Collections.unmodifiableList(fragmentTags) - } - - override fun getItemCount(): Int { - var baseCount = subscriptionOffset - if (showSubscriptionList) { - baseCount += itemAccess.count - } - return baseCount - } - - override fun getItemId(position: Int): Long { - val viewType = getItemViewType(position) - return when (viewType) { - VIEW_TYPE_SUBSCRIPTION -> { - itemAccess.getItem(position - subscriptionOffset)?.id?:0 - } - VIEW_TYPE_NAV -> { - (-abs(fragmentTags[position].hashCode().toLong().toDouble()) - 1).toLong() // Folder IDs are >0 - } - else -> { - 0 - } - } - } - - override fun getItemViewType(position: Int): Int { - return if (0 <= position && position < fragmentTags.size) { - VIEW_TYPE_NAV - } else if (position < subscriptionOffset) { - VIEW_TYPE_SECTION_DIVIDER - } else { - VIEW_TYPE_SUBSCRIPTION - } - } - - val subscriptionOffset: Int - get() = if (fragmentTags.size > 0) fragmentTags.size + 1 else 0 - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder { - val inflater = LayoutInflater.from(activity.get()) - return when (viewType) { - VIEW_TYPE_NAV -> { - NavHolder(inflater.inflate(R.layout.nav_listitem, parent, false)) - } - VIEW_TYPE_SECTION_DIVIDER -> { - DividerHolder(inflater.inflate(R.layout.nav_section_item, parent, false)) - } - else -> { - FeedHolder(inflater.inflate(R.layout.nav_listitem, parent, false)) - } - } - } - - @UnstableApi override fun onBindViewHolder(holder: Holder, position: Int) { - val viewType = getItemViewType(position) - - holder.itemView.setOnCreateContextMenuListener(null) - when (viewType) { - VIEW_TYPE_NAV -> { - bindNavView(getLabel(fragmentTags[position]), position, holder as NavHolder) - } - VIEW_TYPE_SECTION_DIVIDER -> { - bindSectionDivider(holder as DividerHolder) - } - else -> { - val itemPos = position - subscriptionOffset - val item = itemAccess.getItem(itemPos) - if (item != null) { - bindListItem(item, holder as FeedHolder) - if (item.type == DrawerItem.Type.FEED) { - bindFeedView(item as FeedDrawerItem, holder) - } else { - bindTagView(item as TagDrawerItem, holder) - } - } - holder.itemView.setOnCreateContextMenuListener(itemAccess) - } - } - if (viewType != VIEW_TYPE_SECTION_DIVIDER) { - holder.itemView.isSelected = itemAccess.isSelected(position) - holder.itemView.setOnClickListener { v: View? -> itemAccess.onItemClick(position) } - holder.itemView.setOnLongClickListener { v: View? -> itemAccess.onItemLongClick(position) } - holder.itemView.setOnTouchListener { v: View?, e: MotionEvent -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (e.isFromSource(InputDevice.SOURCE_MOUSE) - && e.buttonState == MotionEvent.BUTTON_SECONDARY) { - itemAccess.onItemLongClick(position) - return@setOnTouchListener false - } - } - false - } - } - } - - @UnstableApi private fun bindNavView(title: String, position: Int, holder: NavHolder) { - val context = activity.get() ?: return - holder.title.text = title - - // reset for re-use - holder.count.visibility = View.GONE - holder.count.setOnClickListener(null) - holder.count.isClickable = false - - val tag = fragmentTags[position] - when { - tag == QueueFragment.TAG -> { - val queueSize = itemAccess.queueSize - if (queueSize > 0) { - holder.count.text = NumberFormat.getInstance().format(queueSize.toLong()) - holder.count.visibility = View.VISIBLE - } - } - tag == InboxFragment.TAG -> { - val unreadItems = itemAccess.numberOfNewItems - if (unreadItems > 0) { - holder.count.text = NumberFormat.getInstance().format(unreadItems.toLong()) - holder.count.visibility = View.VISIBLE - } - } - tag == SubscriptionFragment.TAG -> { - val sum = itemAccess.feedCounterSum - if (sum > 0) { - holder.count.text = NumberFormat.getInstance().format(sum.toLong()) - holder.count.visibility = View.VISIBLE - } - } - tag == CompletedDownloadsFragment.TAG && isEnableAutodownload -> { - val epCacheSize = episodeCacheSize - // don't count episodes that can be reclaimed - val spaceUsed = (itemAccess.numberOfDownloadedItems - - itemAccess.reclaimableItems) - if (epCacheSize in 1..spaceUsed) { - holder.count.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, R.drawable.ic_disc_alert, 0) - holder.count.visibility = View.VISIBLE - holder.count.setOnClickListener { v: View? -> - MaterialAlertDialogBuilder(context) - .setTitle(R.string.episode_cache_full_title) - .setMessage(R.string.episode_cache_full_message) - .setPositiveButton(android.R.string.ok, null) - .setNeutralButton(R.string.open_autodownload_settings) { dialog: DialogInterface?, which: Int -> - val intent = Intent(context, PreferenceActivity::class.java) - intent.putExtra(PreferenceActivity.OPEN_AUTO_DOWNLOAD_SETTINGS, true) - context.startActivity(intent) - } - .show() - } - } - } - } - - holder.image.setImageResource(getDrawable(fragmentTags[position])) - } - - private fun bindSectionDivider(holder: DividerHolder) { - val context = activity.get() ?: return - - if (subscriptionsFilter.isEnabled && showSubscriptionList) { - holder.itemView.isEnabled = true - holder.feedsFilteredMsg.visibility = View.VISIBLE - } else { - holder.itemView.isEnabled = false - holder.feedsFilteredMsg.visibility = View.GONE - } - } - - private fun bindListItem(item: DrawerItem, holder: FeedHolder) { - if (item.counter > 0) { - holder.count.visibility = View.VISIBLE - holder.count.text = NumberFormat.getInstance().format(item.counter.toLong()) - } else { - holder.count.visibility = View.GONE - } - holder.title.text = item.title - val padding = (activity.get()!!.resources.getDimension(R.dimen.thumbnail_length_navlist) / 2).toInt() - holder.itemView.setPadding(item.layer * padding, 0, 0, 0) - } - - private fun bindFeedView(drawerItem: FeedDrawerItem, holder: FeedHolder) { - val feed = drawerItem.feed - val context = activity.get() ?: return - - Glide.with(context) - .load(feed.imageUrl) - .apply(RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .transform(FitCenter(), - RoundedCorners((4 * context.resources.displayMetrics.density).toInt())) - .dontAnimate()) - .into(holder.image) - - if (feed.hasLastUpdateFailed()) { - val p = holder.title.layoutParams as RelativeLayout.LayoutParams - p.addRule(RelativeLayout.LEFT_OF, R.id.itxtvFailure) - holder.failure.visibility = View.VISIBLE - } else { - val p = holder.title.layoutParams as RelativeLayout.LayoutParams - p.addRule(RelativeLayout.LEFT_OF, R.id.txtvCount) - holder.failure.visibility = View.GONE - } - } - - private fun bindTagView(tag: TagDrawerItem, holder: FeedHolder) { - val context = activity.get() ?: return - if (tag.isOpen) { - holder.count.visibility = View.GONE - } - Glide.with(context).clear(holder.image) - holder.image.setImageResource(R.drawable.ic_tag) - holder.failure.visibility = View.GONE - } - - open class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) - - internal class DividerHolder(itemView: View) : Holder(itemView) { - val feedsFilteredMsg: LinearLayout = itemView.findViewById(R.id.nav_feeds_filtered_message) - } - - internal class NavHolder(itemView: View) : Holder(itemView) { - val image: ImageView = itemView.findViewById(R.id.imgvCover) - val title: TextView = itemView.findViewById(R.id.txtvTitle) - val count: TextView = itemView.findViewById(R.id.txtvCount) - } - - internal class FeedHolder(itemView: View) : Holder(itemView) { - val image: ImageView = itemView.findViewById(R.id.imgvCover) - val title: TextView = itemView.findViewById(R.id.txtvTitle) - val failure: ImageView = itemView.findViewById(R.id.itxtvFailure) - val count: TextView = itemView.findViewById(R.id.txtvCount) - } - - interface ItemAccess : OnCreateContextMenuListener { - val count: Int - - fun getItem(position: Int): DrawerItem? - - fun isSelected(position: Int): Boolean - - val queueSize: Int - - val numberOfNewItems: Int - - val numberOfDownloadedItems: Int - - val reclaimableItems: Int - - val feedCounterSum: Int - - fun onItemClick(position: Int) - - fun onItemLongClick(position: Int): Boolean - - override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenuInfo?) - } - - companion object { - const val VIEW_TYPE_NAV: Int = 0 - const val VIEW_TYPE_SECTION_DIVIDER: Int = 1 - private const val VIEW_TYPE_SUBSCRIPTION = 2 - - /** - * a tag used as a placeholder to indicate if the subscription list should be displayed or not - * This tag doesn't correspond to any specific activity. - */ - const val SUBSCRIPTION_LIST_TAG: String = "SubscriptionList" - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/QueueRecyclerAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/QueueRecyclerAdapter.kt deleted file mode 100644 index 26ac335d..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/QueueRecyclerAdapter.kt +++ /dev/null @@ -1,91 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.activity.MainActivity -import android.annotation.SuppressLint -import android.util.Log -import android.view.ContextMenu -import android.view.MenuInflater -import android.view.MotionEvent -import android.view.View -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.R -import ac.mdiq.podcini.fragment.swipeactions.SwipeActions -import ac.mdiq.podcini.storage.preferences.UserPreferences -import ac.mdiq.podcini.view.viewholder.EpisodeItemViewHolder - -/** - * List adapter for the queue. - */ -open class QueueRecyclerAdapter(mainActivity: MainActivity, private val swipeActions: SwipeActions) : EpisodeItemListAdapter(mainActivity) { - private var dragDropEnabled: Boolean - - init { - dragDropEnabled = !(UserPreferences.isQueueKeepSorted || UserPreferences.isQueueLocked) - } - - fun updateDragDropEnabled() { - dragDropEnabled = !(UserPreferences.isQueueKeepSorted || UserPreferences.isQueueLocked) - notifyDataSetChanged() - } - - @UnstableApi @SuppressLint("ClickableViewAccessibility") - override fun afterBindViewHolder(holder: EpisodeItemViewHolder, pos: Int) { - if (!dragDropEnabled) { - holder.dragHandle.setVisibility(View.GONE) - holder.dragHandle.setOnTouchListener(null) - holder.coverHolder.setOnTouchListener(null) - } else { - holder.dragHandle.setVisibility(View.VISIBLE) - holder.dragHandle.setOnTouchListener { v1: View?, event: MotionEvent -> - if (event.actionMasked == MotionEvent.ACTION_DOWN) { - Log.d(TAG, "startDrag()") - swipeActions.startDrag(holder) - } - false - } - holder.coverHolder.setOnTouchListener { v1, event -> - if (event.actionMasked == MotionEvent.ACTION_DOWN) { - val isLtr = holder.itemView.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR - val factor = (if (isLtr) 1 else -1).toFloat() - if (factor * event.x < factor * 0.5 * v1.width) { - Log.d(TAG, "startDrag()") - swipeActions.startDrag(holder) - } else { - Log.d(TAG, "Ignoring drag in right half of the image") - } - } - false - } - } - if (inActionMode()) { - holder.dragHandle.setOnTouchListener(null) - holder.coverHolder.setOnTouchListener(null) - } - - holder.isInQueue.setVisibility(View.GONE) - } - - @UnstableApi override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) { - val inflater: MenuInflater = activity!!.getMenuInflater() - inflater.inflate(R.menu.queue_context, menu) - super.onCreateContextMenu(menu, v, menuInfo) - - if (!inActionMode()) { - menu.findItem(R.id.multi_select).setVisible(true) - val keepSorted: Boolean = UserPreferences.isQueueKeepSorted - if (getItem(0)?.id === longPressedItem?.id || keepSorted) { - menu.findItem(R.id.move_to_top_item).setVisible(false) - } - if (getItem(itemCount - 1)?.id === longPressedItem?.id || keepSorted) { - menu.findItem(R.id.move_to_bottom_item).setVisible(false) - } - } else { - menu.findItem(R.id.move_to_top_item).setVisible(false) - menu.findItem(R.id.move_to_bottom_item).setVisible(false) - } - } - - companion object { - private const val TAG = "QueueRecyclerAdapter" - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/SelectableAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/SelectableAdapter.kt deleted file mode 100644 index 8809eb93..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/SelectableAdapter.kt +++ /dev/null @@ -1,188 +0,0 @@ -package ac.mdiq.podcini.adapter - -import android.app.Activity -import android.view.ActionMode -import android.view.Menu -import android.view.MenuItem -import androidx.recyclerview.widget.RecyclerView -import ac.mdiq.podcini.R - -/** - * Used by Recyclerviews that need to provide ability to select items. - */ -abstract class SelectableAdapter(private val activity: Activity) : - RecyclerView.Adapter() { - - private var actionMode: ActionMode? = null - private val selectedIds = HashSet() - private var onSelectModeListener: OnSelectModeListener? = null - var shouldSelectLazyLoadedItems: Boolean = false - private var totalNumberOfItems = COUNT_AUTOMATICALLY - - fun startSelectMode(pos: Int) { - if (inActionMode()) { - endSelectMode() - } - onSelectModeListener?.onStartSelectMode() - - shouldSelectLazyLoadedItems = false - selectedIds.clear() - selectedIds.add(getItemId(pos)) - notifyDataSetChanged() - - actionMode = activity.startActionMode(object : ActionMode.Callback { - override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean { - val inflater = mode.menuInflater - inflater.inflate(R.menu.multi_select_options, menu) - return true - } - - override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean { - updateTitle() - toggleSelectAllIcon(menu.findItem(R.id.select_toggle), false) - return false - } - - override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean { - if (item.itemId == R.id.select_toggle) { - val selectAll = selectedIds.size != itemCount - shouldSelectLazyLoadedItems = selectAll - setSelected(0, itemCount, selectAll) - toggleSelectAllIcon(item, selectAll) - updateTitle() - return true - } - return false - } - - override fun onDestroyActionMode(mode: ActionMode) { - callOnEndSelectMode() - actionMode = null - shouldSelectLazyLoadedItems = false - selectedIds.clear() - notifyDataSetChanged() - } - }) - updateTitle() - } - - /** - * End action mode if currently in select mode, otherwise do nothing - */ - fun endSelectMode() { - if (inActionMode()) { - callOnEndSelectMode() - actionMode?.finish() - } - } - - fun isSelected(pos: Int): Boolean { - return selectedIds.contains(getItemId(pos)) - } - - /** - * Set the selected state of item at given position - * - * @param pos the position to select - * @param selected true for selected state and false for unselected - */ - open fun setSelected(pos: Int, selected: Boolean) { - if (selected) { - selectedIds.add(getItemId(pos)) - } else { - selectedIds.remove(getItemId(pos)) - } - updateTitle() - } - - /** - * Set the selected state of item for a given range - * - * @param startPos start position of range, inclusive - * @param endPos end position of range, inclusive - * @param selected indicates the selection state - * @throws IllegalArgumentException if start and end positions are not valid - */ - @Throws(IllegalArgumentException::class) - fun setSelected(startPos: Int, endPos: Int, selected: Boolean) { - var i = startPos - while (i < endPos && i < itemCount) { - setSelected(i, selected) - i++ - } - notifyItemRangeChanged(startPos, (endPos - startPos)) - } - - protected fun toggleSelection(pos: Int) { - setSelected(pos, !isSelected(pos)) - notifyItemChanged(pos) - - if (selectedIds.size == 0) { - endSelectMode() - } - } - - fun inActionMode(): Boolean { - return actionMode != null - } - - val selectedCount: Int - get() = selectedIds.size - - private fun toggleSelectAllIcon(selectAllItem: MenuItem, allSelected: Boolean) { - if (allSelected) { - selectAllItem.setIcon(R.drawable.ic_select_none) - selectAllItem.setTitle(R.string.deselect_all_label) - } else { - selectAllItem.setIcon(R.drawable.ic_select_all) - selectAllItem.setTitle(R.string.select_all_label) - } - } - - fun updateTitle() { - if (actionMode == null) { - return - } - var totalCount = itemCount - var selectedCount = selectedIds.size - if (totalNumberOfItems != COUNT_AUTOMATICALLY) { - totalCount = totalNumberOfItems - if (shouldSelectLazyLoadedItems) { - selectedCount += (totalNumberOfItems - itemCount) - } - } - actionMode!!.title = activity.resources - .getQuantityString(R.plurals.num_selected_label, selectedIds.size, - selectedCount, totalCount) - } - - fun setOnSelectModeListener(onSelectModeListener: OnSelectModeListener?) { - this.onSelectModeListener = onSelectModeListener - } - - private fun callOnEndSelectMode() { - onSelectModeListener?.onEndSelectMode() - } - - fun shouldSelectLazyLoadedItems(): Boolean { - return shouldSelectLazyLoadedItems - } - - /** - * Sets the total number of items that could be lazy-loaded. - * Can also be set to [.COUNT_AUTOMATICALLY] to simply use [.getItemCount] - */ - fun setTotalNumberOfItems(totalNumberOfItems: Int) { - this.totalNumberOfItems = totalNumberOfItems - } - - interface OnSelectModeListener { - fun onStartSelectMode() - - fun onEndSelectMode() - } - - companion object { - const val COUNT_AUTOMATICALLY: Int = -1 - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/SimpleChipAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/SimpleChipAdapter.kt deleted file mode 100644 index 008808a4..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/SimpleChipAdapter.kt +++ /dev/null @@ -1,40 +0,0 @@ -package ac.mdiq.podcini.adapter - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.chip.Chip -import ac.mdiq.podcini.R - -abstract class SimpleChipAdapter(private val context: Context) : RecyclerView.Adapter() { - init { - setHasStableIds(true) - } - - protected abstract fun getChips(): List - - protected abstract fun onRemoveClicked(position: Int) - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - val chip = Chip(context) - chip.isCloseIconVisible = true - chip.setCloseIconResource(R.drawable.ic_delete) - return ViewHolder(chip) - } - - override fun onBindViewHolder(holder: ViewHolder, position: Int) { - holder.chip.text = getChips()[position] - holder.chip.setOnCloseIconClickListener { v: View? -> onRemoveClicked(position) } - } - - override fun getItemCount(): Int { - return getChips().size - } - - override fun getItemId(position: Int): Long { - return getChips()[position].hashCode().toLong() - } - - class ViewHolder internal constructor(var chip: Chip) : RecyclerView.ViewHolder(chip) -} \ No newline at end of file diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/SimpleIconListAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/SimpleIconListAdapter.kt deleted file mode 100644 index 520c129f..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/SimpleIconListAdapter.kt +++ /dev/null @@ -1,41 +0,0 @@ -package ac.mdiq.podcini.adapter - -import android.content.Context -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.ImageView -import android.widget.TextView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.request.RequestOptions -import ac.mdiq.podcini.R - -/** - * Displays a list of items that have a subtitle and an icon. - */ -class SimpleIconListAdapter(private val context: Context, - private val listItems: List -) : ArrayAdapter(context, R.layout.simple_icon_list_item, listItems) { - - override fun getView(position: Int, view: View?, parent: ViewGroup): View { - var view = view - if (view == null) { - view = View.inflate(context, R.layout.simple_icon_list_item, null) - } - - val item: ListItem = listItems[position] - (view!!.findViewById(R.id.title) as TextView).text = item.title - (view.findViewById(R.id.subtitle) as TextView).text = item.subtitle - Glide.with(context) - .load(item.imageUrl) - .apply(RequestOptions() - .diskCacheStrategy(DiskCacheStrategy.NONE) - .fitCenter() - .dontAnimate()) - .into(((view.findViewById(R.id.icon) as ImageView))) - return view - } - - open class ListItem(val title: String, val subtitle: String, val imageUrl: String) -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/SubscriptionsRecyclerAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/SubscriptionsRecyclerAdapter.kt deleted file mode 100644 index fdd36973..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/SubscriptionsRecyclerAdapter.kt +++ /dev/null @@ -1,272 +0,0 @@ -package ac.mdiq.podcini.adapter - -import ac.mdiq.podcini.activity.MainActivity -import android.content.Context -import android.graphics.Rect -import android.graphics.drawable.Drawable -import android.os.Build -import android.view.* -import android.widget.* -import androidx.appcompat.content.res.AppCompatResources -import androidx.cardview.widget.CardView -import androidx.fragment.app.Fragment -import androidx.media3.common.util.UnstableApi -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.elevation.SurfaceColors -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.storage.NavDrawerData -import ac.mdiq.podcini.fragment.FeedItemlistFragment -import ac.mdiq.podcini.fragment.SubscriptionFragment -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.storage.preferences.UserPreferences -import java.lang.ref.WeakReference -import java.text.NumberFormat - -/** - * Adapter for subscriptions - */ -open class SubscriptionsRecyclerAdapter(mainActivity: MainActivity) : - SelectableAdapter(mainActivity), - View.OnCreateContextMenuListener { - - private val mainActivityRef: WeakReference = WeakReference(mainActivity) - private var listItems: List - private var selectedItem: NavDrawerData.DrawerItem? = null - var longPressedPosition: Int = 0 // used to init actionMode - private var columnCount = 3 - - init { - this.listItems = ArrayList() - setHasStableIds(true) - } - - fun setColumnCount(columnCount: Int) { - this.columnCount = columnCount - } - - fun getItem(position: Int): Any { - return listItems[position] - } - - fun getSelectedItem(): NavDrawerData.DrawerItem? { - return selectedItem - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SubscriptionViewHolder { - val itemView: View = - LayoutInflater.from(mainActivityRef.get()).inflate(R.layout.subscription_item, parent, false) - itemView.findViewById(R.id.titleLabel).visibility = if (viewType == COVER_WITH_TITLE) View.VISIBLE else View.GONE - return SubscriptionViewHolder(itemView) - } - - @UnstableApi override fun onBindViewHolder(holder: SubscriptionViewHolder, position: Int) { - val drawerItem: NavDrawerData.DrawerItem = listItems[position] - val isFeed = drawerItem.type == NavDrawerData.DrawerItem.Type.FEED - holder.bind(drawerItem) - holder.itemView.setOnCreateContextMenuListener(this) - if (inActionMode()) { - if (isFeed) { - holder.selectCheckbox.visibility = View.VISIBLE - holder.selectView.visibility = View.VISIBLE - } - holder.selectCheckbox.setChecked((isSelected(position))) - holder.selectCheckbox.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean -> - setSelected(holder.bindingAdapterPosition, - isChecked) - } - if (holder.coverImage != null) holder.coverImage.alpha = 0.6f - holder.count.visibility = View.GONE - } else { - holder.selectView.visibility = View.GONE - if (holder.coverImage != null) holder.coverImage.alpha = 1.0f - } - - holder.itemView.setOnLongClickListener { v: View? -> - if (!inActionMode()) { - if (isFeed) { - longPressedPosition = holder.bindingAdapterPosition - } - selectedItem = drawerItem - } - false - } - - holder.itemView.setOnTouchListener { v: View?, e: MotionEvent -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (e.isFromSource(InputDevice.SOURCE_MOUSE) - && e.buttonState == MotionEvent.BUTTON_SECONDARY) { - if (!inActionMode()) { - if (isFeed) { - longPressedPosition = holder.bindingAdapterPosition - } - selectedItem = drawerItem - } - } - } - false - } - holder.itemView.setOnClickListener { v: View? -> - if (isFeed) { - if (inActionMode()) { - holder.selectCheckbox.setChecked(!isSelected(holder.bindingAdapterPosition)) - } else { - val fragment: Fragment = FeedItemlistFragment - .newInstance((drawerItem as NavDrawerData.FeedDrawerItem).feed.id) - mainActivityRef.get()?.loadChildFragment(fragment) - } - } else if (!inActionMode()) { - val fragment: Fragment = SubscriptionFragment.newInstance(drawerItem.title) - mainActivityRef.get()?.loadChildFragment(fragment) - } - } - } - - override fun getItemCount(): Int { - return listItems.size - } - - override fun getItemId(position: Int): Long { - if (position >= listItems.size) { - return RecyclerView.NO_ID // Dummy views - } - return listItems[position].id - } - - override fun onCreateContextMenu(menu: ContextMenu, v: View, menuInfo: ContextMenu.ContextMenuInfo?) { - if (inActionMode() || selectedItem == null) { - return - } - val inflater: MenuInflater = mainActivityRef.get()!!.menuInflater - if (selectedItem?.type == NavDrawerData.DrawerItem.Type.FEED) { - inflater.inflate(R.menu.nav_feed_context, menu) - menu.findItem(R.id.multi_select).setVisible(true) - } else { - inflater.inflate(R.menu.nav_folder_context, menu) - } - menu.setHeaderTitle(selectedItem?.title) - } - - fun onContextItemSelected(item: MenuItem): Boolean { - if (item.itemId == R.id.multi_select) { - startSelectMode(longPressedPosition) - return true - } - return false - } - - val selectedItems: List - get() { - val items = ArrayList() - for (i in 0 until itemCount) { - if (isSelected(i)) { - val drawerItem: NavDrawerData.DrawerItem = listItems[i] - if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { - val feed: Feed = (drawerItem as NavDrawerData.FeedDrawerItem).feed - items.add(feed) - } - } - } - return items - } - - fun setItems(listItems: List) { - this.listItems = listItems - notifyDataSetChanged() - } - - override fun setSelected(pos: Int, selected: Boolean) { - val drawerItem: NavDrawerData.DrawerItem = listItems[pos] - if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { - super.setSelected(pos, selected) - } - } - - override fun getItemViewType(position: Int): Int { - return if (UserPreferences.shouldShowSubscriptionTitle()) COVER_WITH_TITLE else 0 - } - - inner class SubscriptionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { - private val title = itemView.findViewById(R.id.titleLabel) - val coverImage: ImageView? = itemView.findViewById(R.id.coverImage) - val count: TextView = itemView.findViewById(R.id.countViewPill) - private val fallbackTitle: TextView = itemView.findViewById(R.id.fallbackTitleLabel) - val selectView: FrameLayout = itemView.findViewById(R.id.selectContainer) - val selectCheckbox: CheckBox = itemView.findViewById(R.id.selectCheckBox) - private val card: CardView = itemView.findViewById(R.id.outerContainer) - private val errorIcon: View = itemView.findViewById(R.id.errorIcon) - - fun bind(drawerItem: NavDrawerData.DrawerItem) { - val drawable: Drawable? = AppCompatResources.getDrawable(selectView.context, - R.drawable.ic_checkbox_background) - selectView.background = drawable // Setting this in XML crashes API <= 21 - title.text = drawerItem.title - fallbackTitle.text = drawerItem.title - if (coverImage != null) coverImage.contentDescription = drawerItem.title - if (drawerItem.counter > 0) { - count.text = NumberFormat.getInstance().format(drawerItem.counter.toLong()) - count.visibility = View.VISIBLE - } else { - count.visibility = View.GONE - } - - val coverLoader = CoverLoader(mainActivityRef.get()!!) - val textAndImageCombined: Boolean - if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { - val feed: Feed = (drawerItem as NavDrawerData.FeedDrawerItem).feed - textAndImageCombined = feed.isLocalFeed && feed.imageUrl != null && feed.imageUrl!!.startsWith(Feed.PREFIX_GENERATIVE_COVER) - coverLoader.withUri(feed.imageUrl) - errorIcon.visibility = if (feed.hasLastUpdateFailed()) View.VISIBLE else View.GONE - } else { - textAndImageCombined = true - coverLoader.withResource(R.drawable.ic_tag) - errorIcon.visibility = View.GONE - } - if (UserPreferences.shouldShowSubscriptionTitle()) { - // No need for fallback title when already showing title - fallbackTitle.visibility = View.GONE - } else { - coverLoader.withPlaceholderView(fallbackTitle, textAndImageCombined) - } - if (coverImage != null) coverLoader.withCoverView(coverImage) - coverLoader.load() - - val density: Float = mainActivityRef.get()!!.resources.displayMetrics.density - card.setCardBackgroundColor(SurfaceColors.getColorForElevation(mainActivityRef.get()!!, 1 * density)) - - val textPadding = if (columnCount <= 3) 16 else 8 - title.setPadding(textPadding, textPadding, textPadding, textPadding) - fallbackTitle.setPadding(textPadding, textPadding, textPadding, textPadding) - - var textSize = 14 - if (columnCount == 3) { - textSize = 15 - } else if (columnCount == 2) { - textSize = 16 - } - title.textSize = textSize.toFloat() - fallbackTitle.textSize = textSize.toFloat() - } - } - - class GridDividerItemDecorator : RecyclerView.ItemDecoration() { - - override fun getItemOffsets(outRect: Rect, - view: View, - parent: RecyclerView, - state: RecyclerView.State - ) { - super.getItemOffsets(outRect, view, parent, state) - val context = parent.context - val insetOffset = convertDpToPixel(context, 1f).toInt() - outRect[insetOffset, insetOffset, insetOffset] = insetOffset - } - } - - companion object { - private const val COVER_WITH_TITLE = 1 - - fun convertDpToPixel(context: Context, dp: Float): Float { - return dp * context.resources.displayMetrics.density - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/CancelDownloadActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/CancelDownloadActionButton.kt deleted file mode 100644 index d3715345..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/CancelDownloadActionButton.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import androidx.annotation.DrawableRes -import androidx.annotation.StringRes -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.storage.DBWriter -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface -import ac.mdiq.podcini.storage.preferences.UserPreferences.isEnableAutodownload - -class CancelDownloadActionButton(item: FeedItem) : ItemActionButton(item) { - @StringRes - override fun getLabel(): Int { - return R.string.cancel_download_label - } - - @DrawableRes - override fun getDrawable(): Int { - return R.drawable.ic_cancel - } - - @UnstableApi override fun onClick(context: Context) { - val media = item.media - if (media != null) DownloadServiceInterface.get()?.cancel(context, media) - if (isEnableAutodownload) { - item.disableAutoDownload() - DBWriter.setFeedItem(item) - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DeleteActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DeleteActionButton.kt deleted file mode 100644 index 21e9fc9b..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DeleteActionButton.kt +++ /dev/null @@ -1,32 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import android.view.View -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.storage.DBWriter -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.view.LocalDeleteModal.showLocalFeedDeleteWarningIfNecessary -import androidx.media3.common.util.UnstableApi - -class DeleteActionButton(item: FeedItem) : ItemActionButton(item) { - override fun getLabel(): Int { - return R.string.delete_label - } - override fun getDrawable(): Int { - return R.drawable.ic_delete - } - @UnstableApi override fun onClick(context: Context) { - val media = item.media ?: return - - showLocalFeedDeleteWarningIfNecessary(context, listOf(item)) { DBWriter.deleteFeedMediaOfItem(context, media.id) } - } - - override val visibility: Int - get() { - if (item.media != null && (item.media!!.isDownloaded() || item.feed?.isLocalFeed == true)) { - return View.VISIBLE - } - - return View.INVISIBLE - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DownloadActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DownloadActionButton.kt deleted file mode 100644 index 6ff94d43..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/DownloadActionButton.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import android.content.DialogInterface -import android.view.View -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.preferences.UsageStatistics -import ac.mdiq.podcini.core.preferences.UsageStatistics.logAction -import ac.mdiq.podcini.core.util.NetworkUtils.isEpisodeDownloadAllowed -import ac.mdiq.podcini.core.util.NetworkUtils.isNetworkRestricted -import ac.mdiq.podcini.core.util.NetworkUtils.isVpnOverWifi -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.feed.FeedMedia -import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface - -class DownloadActionButton(item: FeedItem) : ItemActionButton(item) { - override fun getLabel(): Int { - return R.string.download_label - } - override fun getDrawable(): Int { - return R.drawable.ic_download - } - override val visibility: Int - get() = if (item.feed?.isLocalFeed == true) View.INVISIBLE else View.VISIBLE - - override fun onClick(context: Context) { - val media = item.media - if (media == null || shouldNotDownload(media)) { - return - } - - logAction(UsageStatistics.ACTION_DOWNLOAD) - - if (isEpisodeDownloadAllowed) { - DownloadServiceInterface.get()?.downloadNow(context, item, false) - } else { - val builder = MaterialAlertDialogBuilder(context) - .setTitle(R.string.confirm_mobile_download_dialog_title) - .setPositiveButton(R.string.confirm_mobile_download_dialog_download_later - ) { d: DialogInterface?, w: Int -> DownloadServiceInterface.get()?.downloadNow(context, item, false) } - .setNeutralButton(R.string.confirm_mobile_download_dialog_allow_this_time - ) { d: DialogInterface?, w: Int -> DownloadServiceInterface.get()?.downloadNow(context, item, true) } - .setNegativeButton(R.string.cancel_label, null) - if (isNetworkRestricted && isVpnOverWifi) { - builder.setMessage(R.string.confirm_mobile_download_dialog_message_vpn) - } else { - builder.setMessage(R.string.confirm_mobile_download_dialog_message) - } - - builder.show() - } - } - - private fun shouldNotDownload(media: FeedMedia): Boolean { - if (media.download_url == null) return true - val isDownloading = DownloadServiceInterface.get()?.isDownloadingEpisode(media.download_url!!)?:false - return isDownloading || media.isDownloaded() - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/ItemActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/ItemActionButton.kt deleted file mode 100644 index 0f2f3442..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/ItemActionButton.kt +++ /dev/null @@ -1,59 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import android.view.View -import android.widget.ImageView -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.core.util.PlaybackStatus.isCurrentlyPlaying -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.net.download.serviceinterface.DownloadServiceInterface -import ac.mdiq.podcini.storage.preferences.UserPreferences.isStreamOverDownload - -abstract class ItemActionButton internal constructor(@JvmField var item: FeedItem) { - abstract fun getLabel(): Int - - abstract fun getDrawable(): Int - - abstract fun onClick(context: Context) - - open val visibility: Int - get() = View.VISIBLE - - fun configure(button: View, icon: ImageView, context: Context) { - button.visibility = visibility - button.contentDescription = context.getString(getLabel()) - button.setOnClickListener { view: View? -> onClick(context) } - icon.setImageResource(getDrawable()) - } - - @UnstableApi companion object { - fun forItem(item: FeedItem): ItemActionButton { - val media = item.media ?: return MarkAsPlayedActionButton(item) - - val isDownloadingMedia = when (media.download_url) { - null -> false - else -> DownloadServiceInterface.get()?.isDownloadingEpisode(media.download_url!!)?:false - } - return when { - isCurrentlyPlaying(media) -> { - PauseActionButton(item) - } - item.feed != null && item.feed!!.isLocalFeed -> { - PlayLocalActionButton(item) - } - media.isDownloaded() -> { - PlayActionButton(item) - } - isDownloadingMedia -> { - CancelDownloadActionButton(item) - } - isStreamOverDownload -> { - StreamActionButton(item) - } - else -> { - DownloadActionButton(item) - } - } - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/MarkAsPlayedActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/MarkAsPlayedActionButton.kt deleted file mode 100644 index ecfda4f5..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/MarkAsPlayedActionButton.kt +++ /dev/null @@ -1,25 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import android.view.View -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.storage.DBWriter -import ac.mdiq.podcini.model.feed.FeedItem -import androidx.media3.common.util.UnstableApi - -class MarkAsPlayedActionButton(item: FeedItem) : ItemActionButton(item) { - override fun getLabel(): Int { - return (if (item.hasMedia()) R.string.mark_read_label else R.string.mark_read_no_media_label) - } - override fun getDrawable(): Int { - return R.drawable.ic_check - } - @UnstableApi override fun onClick(context: Context) { - if (!item.isPlayed()) { - DBWriter.markItemPlayed(item, FeedItem.PLAYED, true) - } - } - - override val visibility: Int - get() = if (item.isPlayed()) View.INVISIBLE else View.VISIBLE -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PauseActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PauseActionButton.kt deleted file mode 100644 index 5860b11d..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PauseActionButton.kt +++ /dev/null @@ -1,25 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import android.view.KeyEvent -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.receiver.MediaButtonReceiver.Companion.createIntent -import ac.mdiq.podcini.core.util.PlaybackStatus.isCurrentlyPlaying -import ac.mdiq.podcini.model.feed.FeedItem - -class PauseActionButton(item: FeedItem) : ItemActionButton(item) { - override fun getLabel(): Int { - return R.string.pause_label - } - override fun getDrawable(): Int { - return R.drawable.ic_pause - } - @UnstableApi override fun onClick(context: Context) { - val media = item.media ?: return - - if (isCurrentlyPlaying(media)) { - context.sendBroadcast(createIntent(context, KeyEvent.KEYCODE_MEDIA_PAUSE)) - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayActionButton.kt deleted file mode 100644 index 74300d04..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayActionButton.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.service.playback.PlaybackService.Companion.getPlayerActivityIntent -import ac.mdiq.podcini.core.storage.DBTasks -import ac.mdiq.podcini.core.util.playback.PlaybackServiceStarter -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.playback.MediaType - -class PlayActionButton(item: FeedItem) : ItemActionButton(item) { - override fun getLabel(): Int { - return R.string.play_label - } - override fun getDrawable(): Int { - return R.drawable.ic_play_24dp - } - @UnstableApi override fun onClick(context: Context) { - val media = item.media ?: return - if (!media.fileExists()) { - DBTasks.notifyMissingFeedMediaFile(context, media) - return - } - PlaybackServiceStarter(context, media) - .callEvenIfRunning(true) - .start() - - if (media.getMediaType() == MediaType.VIDEO) { - context.startActivity(getPlayerActivityIntent(context, media)) - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayLocalActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayLocalActionButton.kt deleted file mode 100644 index abba22c8..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/PlayLocalActionButton.kt +++ /dev/null @@ -1,29 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.service.playback.PlaybackService.Companion.getPlayerActivityIntent -import ac.mdiq.podcini.core.util.playback.PlaybackServiceStarter -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.playback.MediaType - -class PlayLocalActionButton(item: FeedItem?) : ItemActionButton(item!!) { - override fun getLabel(): Int { - return R.string.play_label - } - override fun getDrawable(): Int { - return R.drawable.ic_play_24dp - } - @UnstableApi override fun onClick(context: Context) { - val media = item.media ?: return - - PlaybackServiceStarter(context, media) - .callEvenIfRunning(true) - .start() - - if (media.getMediaType() == MediaType.VIDEO) { - context.startActivity(getPlayerActivityIntent(context, media)) - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/StreamActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/StreamActionButton.kt deleted file mode 100644 index bce7bfd9..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/StreamActionButton.kt +++ /dev/null @@ -1,38 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import androidx.media3.common.util.UnstableApi -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.preferences.UsageStatistics -import ac.mdiq.podcini.core.preferences.UsageStatistics.logAction -import ac.mdiq.podcini.core.service.playback.PlaybackService.Companion.getPlayerActivityIntent -import ac.mdiq.podcini.core.util.NetworkUtils.isStreamingAllowed -import ac.mdiq.podcini.core.util.playback.PlaybackServiceStarter -import ac.mdiq.podcini.dialog.StreamingConfirmationDialog -import ac.mdiq.podcini.model.feed.FeedItem -import ac.mdiq.podcini.model.playback.MediaType - -class StreamActionButton(item: FeedItem) : ItemActionButton(item) { - override fun getLabel(): Int { - return R.string.stream_label - } - override fun getDrawable(): Int { - return R.drawable.ic_stream - } - @UnstableApi override fun onClick(context: Context) { - val media = item.media ?: return - logAction(UsageStatistics.ACTION_STREAM) - - if (!isStreamingAllowed) { - StreamingConfirmationDialog(context, media).show() - return - } - PlaybackServiceStarter(context, media) - .callEvenIfRunning(true) - .start() - - if (media.getMediaType() == MediaType.VIDEO) { - context.startActivity(getPlayerActivityIntent(context, media)) - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/VisitWebsiteActionButton.kt b/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/VisitWebsiteActionButton.kt deleted file mode 100644 index 9705297d..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/actionbutton/VisitWebsiteActionButton.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ac.mdiq.podcini.adapter.actionbutton - -import android.content.Context -import android.view.View -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.util.IntentUtils.openInBrowser -import ac.mdiq.podcini.model.feed.FeedItem - -class VisitWebsiteActionButton(item: FeedItem) : ItemActionButton(item) { - override fun getLabel(): Int { - return R.string.visit_website_label - } - override fun getDrawable(): Int { - return R.drawable.ic_web - } - override fun onClick(context: Context) { - if (item.link!= null) openInBrowser(context, item.link!!) - } - - override val visibility: Int - get() = if (item.link == null) View.INVISIBLE else View.VISIBLE -} diff --git a/app/src/main/java/ac/mdiq/podcini/adapter/itunes/ItunesAdapter.kt b/app/src/main/java/ac/mdiq/podcini/adapter/itunes/ItunesAdapter.kt deleted file mode 100644 index 88a986f0..00000000 --- a/app/src/main/java/ac/mdiq/podcini/adapter/itunes/ItunesAdapter.kt +++ /dev/null @@ -1,93 +0,0 @@ -package ac.mdiq.podcini.adapter.itunes - -import ac.mdiq.podcini.activity.MainActivity -import android.content.Context -import android.view.View -import android.view.ViewGroup -import android.widget.ArrayAdapter -import android.widget.ImageView -import android.widget.TextView -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy -import com.bumptech.glide.load.resource.bitmap.FitCenter -import com.bumptech.glide.load.resource.bitmap.RoundedCorners -import com.bumptech.glide.request.RequestOptions -import ac.mdiq.podcini.R -import ac.mdiq.podcini.net.discovery.PodcastSearchResult -import androidx.media3.common.util.UnstableApi - -class ItunesAdapter( - /** - * Related Context - */ - private val context: Context, objects: List -) : ArrayAdapter(context, 0, objects) { - /** - * List holding the podcasts found in the search - */ - private val data: List = objects - - @UnstableApi override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { - //Current podcast - val podcast: PodcastSearchResult = data[position] - - //ViewHolder - val viewHolder: PodcastViewHolder - - //Resulting view - val view: View - - //Handle view holder stuff - if (convertView == null) { - view = (context as MainActivity).layoutInflater.inflate(R.layout.itunes_podcast_listitem, parent, false) - viewHolder = PodcastViewHolder(view) - view.tag = viewHolder - } else { - view = convertView - viewHolder = view.tag as PodcastViewHolder - } - - // Set the title - viewHolder.titleView.text = podcast.title - if (podcast.author != null && podcast.author!!.trim { it <= ' ' }.isNotEmpty()) { - viewHolder.authorView.text = podcast.author - viewHolder.authorView.visibility = View.VISIBLE - } else if (podcast.feedUrl != null && !podcast.feedUrl!!.contains("itunes.apple.com")) { - viewHolder.authorView.text = podcast.feedUrl - viewHolder.authorView.visibility = View.VISIBLE - } else { - viewHolder.authorView.visibility = View.GONE - } - - //Update the empty imageView with the image from the feed - Glide.with(context) - .load(podcast.imageUrl) - .apply(RequestOptions() - .placeholder(R.color.light_gray) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .transform(FitCenter(), - RoundedCorners((4 * context.resources.displayMetrics.density).toInt())) - .dontAnimate()) - .into(viewHolder.coverView) - - //Feed the grid view - return view - } - - /** - * View holder object for the GridView - */ - internal class PodcastViewHolder(view: View) { - /** - * ImageView holding the Podcast image - */ - val coverView: ImageView = view.findViewById(R.id.imgvCover) - - /** - * TextView holding the Podcast title - */ - val titleView: TextView = view.findViewById(R.id.txtvTitle) - - val authorView: TextView = view.findViewById(R.id.txtvAuthor) - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/asynctask/DocumentFileExportWorker.kt b/app/src/main/java/ac/mdiq/podcini/asynctask/DocumentFileExportWorker.kt deleted file mode 100644 index b45793db..00000000 --- a/app/src/main/java/ac/mdiq/podcini/asynctask/DocumentFileExportWorker.kt +++ /dev/null @@ -1,56 +0,0 @@ -package ac.mdiq.podcini.asynctask - -import android.content.Context -import android.net.Uri -import androidx.documentfile.provider.DocumentFile -import ac.mdiq.podcini.core.export.ExportWriter -import ac.mdiq.podcini.core.storage.DBReader -import io.reactivex.Observable -import io.reactivex.ObservableEmitter -import java.io.IOException -import java.io.OutputStream -import java.io.OutputStreamWriter -import java.nio.charset.Charset - -/** - * Writes an OPML file into the user selected export directory in the background. - */ -class DocumentFileExportWorker(private val exportWriter: ExportWriter, - private val context: Context, - private val outputFileUri: Uri -) { - fun exportObservable(): Observable { - val output = DocumentFile.fromSingleUri(context, outputFileUri) - return Observable.create { subscriber: ObservableEmitter -> - var outputStream: OutputStream? = null - var writer: OutputStreamWriter? = null - try { - if (output == null) throw IOException() - val uri = output.uri - outputStream = context.contentResolver.openOutputStream(uri, "wt") - if (outputStream == null) throw IOException() - writer = OutputStreamWriter(outputStream, Charset.forName("UTF-8")) - exportWriter.writeDocument(DBReader.getFeedList(), writer, context) - subscriber.onNext(output) - } catch (e: IOException) { - subscriber.onError(e) - } finally { - if (writer != null) { - try { - writer.close() - } catch (e: IOException) { - subscriber.onError(e) - } - } - if (outputStream != null) { - try { - outputStream.close() - } catch (e: IOException) { - subscriber.onError(e) - } - } - subscriber.onComplete() - } - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/asynctask/ExportWorker.kt b/app/src/main/java/ac/mdiq/podcini/asynctask/ExportWorker.kt deleted file mode 100644 index 02c9b5a6..00000000 --- a/app/src/main/java/ac/mdiq/podcini/asynctask/ExportWorker.kt +++ /dev/null @@ -1,57 +0,0 @@ -package ac.mdiq.podcini.asynctask - -import android.content.Context -import android.util.Log -import ac.mdiq.podcini.core.export.ExportWriter -import ac.mdiq.podcini.core.storage.DBReader -import ac.mdiq.podcini.storage.preferences.UserPreferences.getDataFolder -import io.reactivex.Observable -import io.reactivex.ObservableEmitter -import java.io.File -import java.io.FileOutputStream -import java.io.IOException -import java.io.OutputStreamWriter -import java.nio.charset.Charset - -/** - * Writes an OPML file into the export directory in the background. - */ -class ExportWorker private constructor(private val exportWriter: ExportWriter, - private val output: File, - private val context: Context -) { - constructor(exportWriter: ExportWriter, context: Context) : this(exportWriter, File(getDataFolder(EXPORT_DIR), - DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension()), context) - - fun exportObservable(): Observable { - if (output.exists()) { - val success = output.delete() - Log.w(TAG, "Overwriting previously exported file: $success") - } - return Observable.create { subscriber: ObservableEmitter -> - var writer: OutputStreamWriter? = null - try { - writer = OutputStreamWriter(FileOutputStream(output), Charset.forName("UTF-8")) - exportWriter.writeDocument(DBReader.getFeedList(), writer, context) - subscriber.onNext(output) - } catch (e: IOException) { - subscriber.onError(e) - } finally { - if (writer != null) { - try { - writer.close() - } catch (e: IOException) { - subscriber.onError(e) - } - } - subscriber.onComplete() - } - } - } - - companion object { - private const val EXPORT_DIR = "export/" - private const val TAG = "ExportWorker" - private const val DEFAULT_OUTPUT_NAME = "podcini-feeds" - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/config/ApplicationCallbacksImpl.kt b/app/src/main/java/ac/mdiq/podcini/config/ApplicationCallbacksImpl.kt deleted file mode 100644 index 2d56f9e3..00000000 --- a/app/src/main/java/ac/mdiq/podcini/config/ApplicationCallbacksImpl.kt +++ /dev/null @@ -1,12 +0,0 @@ -package ac.mdiq.podcini.config - -import android.app.Application -import ac.mdiq.podcini.PodciniApp -import ac.mdiq.podcini.core.ApplicationCallbacks - - -class ApplicationCallbacksImpl : ApplicationCallbacks { - override fun getApplicationInstance(): Application { - return PodciniApp.getInstance() - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/AllEpisodesFilterDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/AllEpisodesFilterDialog.kt deleted file mode 100644 index b2e5753b..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/AllEpisodesFilterDialog.kt +++ /dev/null @@ -1,22 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.os.Bundle -import ac.mdiq.podcini.model.feed.FeedItemFilter -import org.greenrobot.eventbus.EventBus - -class AllEpisodesFilterDialog : ItemFilterDialog() { - override fun onFilterChanged(newFilterValues: Set) { - EventBus.getDefault().post(AllEpisodesFilterChangedEvent(newFilterValues)) - } - - class AllEpisodesFilterChangedEvent(val filterValues: Set?) - companion object { - fun newInstance(filter: FeedItemFilter?): AllEpisodesFilterDialog { - val dialog = AllEpisodesFilterDialog() - val arguments = Bundle() - arguments.putSerializable(ARGUMENT_FILTER, filter) - dialog.arguments = arguments - return dialog - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/AuthenticationDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/AuthenticationDialog.kt deleted file mode 100644 index da53f91d..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/AuthenticationDialog.kt +++ /dev/null @@ -1,57 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.content.Context -import android.content.DialogInterface -import android.text.method.HideReturnsTransformationMethod -import android.text.method.PasswordTransformationMethod -import android.view.LayoutInflater -import android.view.View -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.databinding.AuthenticationDialogBinding - -/** - * Displays a dialog with a username and password text field and an optional checkbox to save username and preferences. - */ -abstract class AuthenticationDialog(context: Context?, titleRes: Int, enableUsernameField: Boolean, - usernameInitialValue: String?, passwordInitialValue: String? -) : MaterialAlertDialogBuilder( - context!!) { - var passwordHidden: Boolean = true - - init { - setTitle(titleRes) - val viewBinding = AuthenticationDialogBinding.inflate(LayoutInflater.from(context)) - setView(viewBinding.root) - - viewBinding.usernameEditText.isEnabled = enableUsernameField - if (usernameInitialValue != null) { - viewBinding.usernameEditText.setText(usernameInitialValue) - } - if (passwordInitialValue != null) { - viewBinding.passwordEditText.setText(passwordInitialValue) - } - viewBinding.showPasswordButton.setOnClickListener { v: View? -> - if (passwordHidden) { - viewBinding.passwordEditText.transformationMethod = HideReturnsTransformationMethod.getInstance() - viewBinding.showPasswordButton.alpha = 1.0f - } else { - viewBinding.passwordEditText.transformationMethod = PasswordTransformationMethod.getInstance() - viewBinding.showPasswordButton.alpha = 0.6f - } - passwordHidden = !passwordHidden - } - - setOnCancelListener { dialog: DialogInterface? -> onCancelled() } - setNegativeButton(R.string.cancel_label) { dialog: DialogInterface?, which: Int -> onCancelled() } - setPositiveButton(R.string.confirm_label) { dialog: DialogInterface?, which: Int -> - onConfirmed(viewBinding.usernameEditText.text.toString(), - viewBinding.passwordEditText.text.toString()) - } - } - - protected open fun onCancelled() { - } - - protected abstract fun onConfirmed(username: String, password: String) -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/ChooseDataFolderDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/ChooseDataFolderDialog.kt deleted file mode 100644 index 1dcdedce..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/ChooseDataFolderDialog.kt +++ /dev/null @@ -1,33 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.content.Context -import android.view.View -import androidx.core.util.Consumer -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.adapter.DataFolderAdapter - -object ChooseDataFolderDialog { - fun showDialog(context: Context?, handlerFunc: Consumer) { - val content = View.inflate(context, R.layout.choose_data_folder_dialog, null) - val dialog = MaterialAlertDialogBuilder(context!!) - .setView(content) - .setTitle(R.string.choose_data_directory) - .setMessage(R.string.choose_data_directory_message) - .setNegativeButton(R.string.cancel_label, null) - .create() - val recyclerView = content.findViewById(R.id.recyclerView) - recyclerView.layoutManager = LinearLayoutManager(context) - val adapter = DataFolderAdapter(context) { path: String? -> - dialog.dismiss() - handlerFunc.accept(path) - } - recyclerView.adapter = adapter - - if (adapter.itemCount != 0) { - dialog.show() - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/DownloadLogDetailsDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/DownloadLogDetailsDialog.kt deleted file mode 100644 index 93ea471a..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/DownloadLogDetailsDialog.kt +++ /dev/null @@ -1,60 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.content.ClipData -import android.content.ClipboardManager -import android.content.Context -import android.os.Build -import android.view.View -import android.widget.TextView -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.storage.DBReader -import ac.mdiq.podcini.core.util.DownloadErrorLabel.from -import ac.mdiq.podcini.event.MessageEvent -import ac.mdiq.podcini.model.download.DownloadResult -import ac.mdiq.podcini.model.feed.Feed -import ac.mdiq.podcini.model.feed.FeedMedia -import org.greenrobot.eventbus.EventBus - -class DownloadLogDetailsDialog(context: Context, status: DownloadResult) : MaterialAlertDialogBuilder(context) { - init { - var url = "unknown" - if (status.feedfileType == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { - val media = DBReader.getFeedMedia(status.feedfileId) - if (media != null) { - url = media.download_url?:"" - } - } else if (status.feedfileType == Feed.FEEDFILETYPE_FEED) { - val feed = DBReader.getFeed(status.feedfileId) - if (feed != null) { - url = feed.download_url?:"" - } - } - - var message = context.getString(R.string.download_successful) - if (!status.isSuccessful) { - message = status.reasonDetailed - } - - val messageFull = context.getString(R.string.download_log_details_message, - context.getString(from(status.reason)), message, url) - setTitle(R.string.download_error_details) - setMessage(messageFull) - setPositiveButton("OK", null) - setNeutralButton(R.string.copy_to_clipboard) { dialog, which -> - val clipboard = getContext().getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clip = ClipData.newPlainText(context.getString(R.string.download_error_details), messageFull) - clipboard.setPrimaryClip(clip) - if (Build.VERSION.SDK_INT < 32) { - EventBus.getDefault().post(MessageEvent(context.getString(R.string.copied_to_clipboard))) - } - } - } - - override fun show(): AlertDialog { - val dialog = super.show() - (dialog.findViewById(R.id.message) as? TextView)?.setTextIsSelectable(true) - return dialog - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/DrawerPreferencesDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/DrawerPreferencesDialog.kt deleted file mode 100644 index 06d97029..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/DrawerPreferencesDialog.kt +++ /dev/null @@ -1,47 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.content.Context -import android.content.DialogInterface -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.fragment.NavDrawerFragment -import ac.mdiq.podcini.storage.preferences.UserPreferences -import ac.mdiq.podcini.storage.preferences.UserPreferences.defaultPage -import ac.mdiq.podcini.storage.preferences.UserPreferences.hiddenDrawerItems - -object DrawerPreferencesDialog { - fun show(context: Context, callback: Runnable?) { - val hiddenDrawerItems = hiddenDrawerItems?.toMutableList()?: mutableListOf() - val navTitles = context.resources.getStringArray(R.array.nav_drawer_titles) - val checked = BooleanArray(NavDrawerFragment.NAV_DRAWER_TAGS.size) - for (i in NavDrawerFragment.NAV_DRAWER_TAGS.indices) { - val tag = NavDrawerFragment.NAV_DRAWER_TAGS[i] - if (!hiddenDrawerItems.contains(tag)) { - checked[i] = true - } - } - val builder = MaterialAlertDialogBuilder(context) - builder.setTitle(R.string.drawer_preferences) - builder.setMultiChoiceItems(navTitles, checked) { dialog: DialogInterface?, which: Int, isChecked: Boolean -> - if (isChecked) { - hiddenDrawerItems.remove(NavDrawerFragment.NAV_DRAWER_TAGS[which]) - } else { - hiddenDrawerItems.add(NavDrawerFragment.NAV_DRAWER_TAGS[which]) - } - } - builder.setPositiveButton(R.string.confirm_label) { dialog: DialogInterface?, which: Int -> - UserPreferences.hiddenDrawerItems = hiddenDrawerItems - if (hiddenDrawerItems.contains(defaultPage)) { - for (tag in NavDrawerFragment.NAV_DRAWER_TAGS) { - if (!hiddenDrawerItems.contains(tag)) { - defaultPage = tag - break - } - } - } - callback?.run() - } - builder.setNegativeButton(R.string.cancel_label, null) - builder.create().show() - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/EditUrlSettingsDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/EditUrlSettingsDialog.kt deleted file mode 100644 index c23526f9..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/EditUrlSettingsDialog.kt +++ /dev/null @@ -1,82 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.app.Activity -import android.content.DialogInterface -import android.os.CountDownTimer -import android.view.LayoutInflater -import androidx.appcompat.app.AlertDialog -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.storage.DBWriter -import ac.mdiq.podcini.core.util.download.FeedUpdateManager.runOnce -import ac.mdiq.podcini.databinding.EditTextDialogBinding -import ac.mdiq.podcini.model.feed.Feed -import androidx.media3.common.util.UnstableApi -import java.lang.ref.WeakReference -import java.util.* -import java.util.concurrent.ExecutionException - - @UnstableApi - abstract class EditUrlSettingsDialog(activity: Activity, private val feed: Feed) { - private val activityRef = WeakReference(activity) - - fun show() { - val activity = activityRef.get() ?: return - - val binding = EditTextDialogBinding.inflate(LayoutInflater.from(activity)) - - binding.urlEditText.setText(feed.download_url) - - MaterialAlertDialogBuilder(activity) - .setView(binding.root) - .setTitle(R.string.edit_url_menu) - .setPositiveButton(android.R.string.ok) { d: DialogInterface?, input: Int -> showConfirmAlertDialog(binding.urlEditText.text.toString()) } - .setNegativeButton(R.string.cancel_label, null) - .show() - } - - @UnstableApi private fun onConfirmed(original: String, updated: String) { - try { - DBWriter.updateFeedDownloadURL(original, updated).get() - feed.download_url = updated - runOnce(activityRef.get()!!, feed) - } catch (e: ExecutionException) { - throw RuntimeException(e) - } catch (e: InterruptedException) { - throw RuntimeException(e) - } - } - - @UnstableApi private fun showConfirmAlertDialog(url: String) { - val activity = activityRef.get() - - val alertDialog = MaterialAlertDialogBuilder(activity!!) - .setTitle(R.string.edit_url_menu) - .setMessage(R.string.edit_url_confirmation_msg) - .setPositiveButton(android.R.string.ok) { d: DialogInterface?, input: Int -> - onConfirmed(feed.download_url?:"", url) - setUrl(url) - } - .setNegativeButton(R.string.cancel_label, null) - .show() - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false - - object : CountDownTimer(15000, 1000) { - override fun onTick(millisUntilFinished: Long) { - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).text = String.format(Locale.getDefault(), "%s (%d)", - activity.getString(android.R.string.ok), millisUntilFinished / 1000 + 1) - } - - override fun onFinish() { - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = true - alertDialog.getButton(AlertDialog.BUTTON_POSITIVE).setText(android.R.string.ok) - } - }.start() - } - - protected abstract fun setUrl(url: String?) - - companion object { - const val TAG: String = "EditUrlSettingsDialog" - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/EpisodeFilterDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/EpisodeFilterDialog.kt deleted file mode 100644 index 8e652944..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/EpisodeFilterDialog.kt +++ /dev/null @@ -1,111 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.content.Context -import android.content.DialogInterface -import android.text.TextUtils -import android.view.LayoutInflater -import android.view.View -import android.widget.CompoundButton -import androidx.recyclerview.widget.GridLayoutManager -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.adapter.SimpleChipAdapter -import ac.mdiq.podcini.databinding.EpisodeFilterDialogBinding -import ac.mdiq.podcini.model.feed.FeedFilter -import ac.mdiq.podcini.view.ItemOffsetDecoration - -/** - * Displays a dialog with a text box for filtering episodes and two radio buttons for exclusion/inclusion - */ -abstract class EpisodeFilterDialog(context: Context?, filter: FeedFilter) : MaterialAlertDialogBuilder( - context!!) { - private val viewBinding = EpisodeFilterDialogBinding.inflate(LayoutInflater.from(context)) - private val termList: MutableList - - init { - setTitle(R.string.episode_filters_label) - setView(viewBinding.root) - - viewBinding.durationCheckBox.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean -> - viewBinding.episodeFilterDurationText.isEnabled = isChecked - } - if (filter.hasMinimalDurationFilter()) { - viewBinding.durationCheckBox.isChecked = true - // Store minimal duration in seconds, show in minutes - viewBinding.episodeFilterDurationText - .setText((filter.minimalDurationFilter / 60).toString()) - } else { - viewBinding.episodeFilterDurationText.isEnabled = false - } - - if (filter.excludeOnly()) { - termList = filter.getExcludeFilter().toMutableList() - viewBinding.excludeRadio.isChecked = true - } else { - termList = filter.getIncludeFilter().toMutableList() - viewBinding.includeRadio.isChecked = true - } - setupWordsList() - - setNegativeButton(R.string.cancel_label, null) - setPositiveButton(R.string.confirm_label) { dialog: DialogInterface, which: Int -> - this.onConfirmClick(dialog, - which) - } - } - - private fun setupWordsList() { - viewBinding.termsRecycler.layoutManager = GridLayoutManager(context, 2) - viewBinding.termsRecycler.addItemDecoration(ItemOffsetDecoration(context, 4)) - val adapter: SimpleChipAdapter = object : SimpleChipAdapter(context) { - override fun getChips(): List { - return termList - } - - override fun onRemoveClicked(position: Int) { - termList.removeAt(position) - notifyDataSetChanged() - } - } - viewBinding.termsRecycler.adapter = adapter - viewBinding.termsTextInput.setEndIconOnClickListener { v: View? -> - val newWord = viewBinding.termsTextInput.editText!!.text.toString().replace("\"", "").trim { it <= ' ' } - if (TextUtils.isEmpty(newWord) || termList.contains(newWord)) { - return@setEndIconOnClickListener - } - termList.add(newWord) - viewBinding.termsTextInput.editText!!.setText("") - adapter.notifyDataSetChanged() - } - } - - protected abstract fun onConfirmed(filter: FeedFilter) - - private fun onConfirmClick(dialog: DialogInterface, which: Int) { - var minimalDuration = -1 - if (viewBinding.durationCheckBox.isChecked) { - try { - // Store minimal duration in seconds - minimalDuration = viewBinding.episodeFilterDurationText.text.toString().toInt() * 60 - } catch (e: NumberFormatException) { - // Do not change anything on error - } - } - var excludeFilter = "" - var includeFilter = "" - if (viewBinding.includeRadio.isChecked) { - includeFilter = toFilterString(termList) - } else { - excludeFilter = toFilterString(termList) - } - onConfirmed(FeedFilter(includeFilter, excludeFilter, minimalDuration)) - } - - private fun toFilterString(words: List?): String { - val result = StringBuilder() - for (word in words!!) { - result.append("\"").append(word).append("\" ") - } - return result.toString() - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/FeedItemFilterDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/FeedItemFilterDialog.kt deleted file mode 100644 index 93b90a5f..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/FeedItemFilterDialog.kt +++ /dev/null @@ -1,25 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.os.Bundle -import ac.mdiq.podcini.core.storage.DBWriter -import ac.mdiq.podcini.model.feed.Feed - -class FeedItemFilterDialog : ItemFilterDialog() { - override fun onFilterChanged(newFilterValues: Set) { - val feedId = requireArguments().getLong(ARGUMENT_FEED_ID) - DBWriter.setFeedItemsFilter(feedId, newFilterValues) - } - - companion object { - private const val ARGUMENT_FEED_ID = "feedId" - - fun newInstance(feed: Feed): FeedItemFilterDialog { - val dialog = FeedItemFilterDialog() - val arguments = Bundle() - arguments.putSerializable(ARGUMENT_FILTER, feed.itemFilter) - arguments.putLong(ARGUMENT_FEED_ID, feed.id) - dialog.arguments = arguments - return dialog - } - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/FeedPreferenceSkipDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/FeedPreferenceSkipDialog.kt deleted file mode 100644 index 9fa9f1ee..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/FeedPreferenceSkipDialog.kt +++ /dev/null @@ -1,44 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.content.Context -import android.content.DialogInterface -import android.view.View -import android.widget.EditText -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R - -/** - * Displays a dialog with a username and password text field and an optional checkbox to save username and preferences. - */ -abstract class FeedPreferenceSkipDialog(context: Context?, skipIntroInitialValue: Int, - skipEndInitialValue: Int -) : MaterialAlertDialogBuilder(context!!) { - init { - setTitle(R.string.pref_feed_skip) - val rootView = View.inflate(context, R.layout.feed_pref_skip_dialog, null) - setView(rootView) - - val etxtSkipIntro = rootView.findViewById(R.id.etxtSkipIntro) - val etxtSkipEnd = rootView.findViewById(R.id.etxtSkipEnd) - - etxtSkipIntro.setText(skipIntroInitialValue.toString()) - etxtSkipEnd.setText(skipEndInitialValue.toString()) - - setNegativeButton(R.string.cancel_label, null) - setPositiveButton(R.string.confirm_label) { dialog: DialogInterface?, which: Int -> - var skipIntro = try { - etxtSkipIntro.text.toString().toInt() - } catch (e: NumberFormatException) { - 0 - } - var skipEnding = try { - etxtSkipEnd.text.toString().toInt() - } catch (e: NumberFormatException) { - 0 - } - onConfirmed(skipIntro, skipEnding) - } - } - - protected abstract fun onConfirmed(skipIntro: Int, skipEndig: Int) -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/FeedSortDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/FeedSortDialog.kt deleted file mode 100644 index b4fe5c02..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/FeedSortDialog.kt +++ /dev/null @@ -1,34 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.content.Context -import android.content.DialogInterface -import com.google.android.material.dialog.MaterialAlertDialogBuilder -import ac.mdiq.podcini.R -import ac.mdiq.podcini.event.UnreadItemsUpdateEvent -import ac.mdiq.podcini.storage.preferences.UserPreferences.feedOrder -import ac.mdiq.podcini.storage.preferences.UserPreferences.setFeedOrder -import org.greenrobot.eventbus.EventBus - -object FeedSortDialog { - fun showDialog(context: Context) { - val dialog = MaterialAlertDialogBuilder(context) - dialog.setTitle(context.getString(R.string.pref_nav_drawer_feed_order_title)) - dialog.setNegativeButton(android.R.string.cancel) { d: DialogInterface, listener: Int -> d.dismiss() } - - val selected = feedOrder - val entryValues = - listOf(*context.resources.getStringArray(R.array.nav_drawer_feed_order_values)) - val selectedIndex = entryValues.indexOf("" + selected) - - val items = context.resources.getStringArray(R.array.nav_drawer_feed_order_options) - dialog.setSingleChoiceItems(items, selectedIndex) { d: DialogInterface, which: Int -> - if (selectedIndex != which) { - setFeedOrder(entryValues[which]) - //Update subscriptions - EventBus.getDefault().post(UnreadItemsUpdateEvent()) - } - d.dismiss() - } - dialog.show() - } -} diff --git a/app/src/main/java/ac/mdiq/podcini/dialog/ItemFilterDialog.kt b/app/src/main/java/ac/mdiq/podcini/dialog/ItemFilterDialog.kt deleted file mode 100644 index 83693d12..00000000 --- a/app/src/main/java/ac/mdiq/podcini/dialog/ItemFilterDialog.kt +++ /dev/null @@ -1,116 +0,0 @@ -package ac.mdiq.podcini.dialog - -import android.app.Dialog -import android.content.DialogInterface -import android.os.Bundle -import android.text.TextUtils -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.Button -import android.widget.FrameLayout -import android.widget.LinearLayout -import com.google.android.material.bottomsheet.BottomSheetBehavior -import com.google.android.material.bottomsheet.BottomSheetDialog -import com.google.android.material.bottomsheet.BottomSheetDialogFragment -import com.google.android.material.button.MaterialButtonToggleGroup -import ac.mdiq.podcini.R -import ac.mdiq.podcini.core.feed.FeedItemFilterGroup -import ac.mdiq.podcini.databinding.FilterDialogBinding -import ac.mdiq.podcini.databinding.FilterDialogRowBinding -import ac.mdiq.podcini.model.feed.FeedItemFilter - -abstract class ItemFilterDialog : BottomSheetDialogFragment() { - private var rows: LinearLayout? = null - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - val layout = inflater.inflate(R.layout.filter_dialog, null, false) - val binding = FilterDialogBinding.bind(layout) - rows = binding.filterRows - val filter = requireArguments().getSerializable(ARGUMENT_FILTER) as FeedItemFilter? - - //add filter rows - for (item in FeedItemFilterGroup.entries) { - val rowBinding = FilterDialogRowBinding.inflate(inflater) - rowBinding.root.addOnButtonCheckedListener { group: MaterialButtonToggleGroup?, checkedId: Int, isChecked: Boolean -> - onFilterChanged(newFilterValues) - } - rowBinding.filterButton1.setText(item.values[0].displayName) - rowBinding.filterButton1.tag = item.values[0].filterId - rowBinding.filterButton2.setText(item.values[1].displayName) - rowBinding.filterButton2.tag = item.values[1].filterId - rowBinding.filterButton1.maxLines = 3 - rowBinding.filterButton1.isSingleLine = false - rowBinding.filterButton2.maxLines = 3 - rowBinding.filterButton2.isSingleLine = false - rows!!.addView(rowBinding.root, rows!!.childCount - 1) - } - - binding.confirmFiltermenu.setOnClickListener { view1: View? -> dismiss() } - binding.resetFiltermenu.setOnClickListener { view1: View? -> - onFilterChanged(emptySet()) - for (i in 0 until rows!!.childCount) { - if (rows!!.getChildAt(i) is MaterialButtonToggleGroup) { - (rows!!.getChildAt(i) as MaterialButtonToggleGroup).clearChecked() - } - } - } - - for (filterId in filter!!.values) { - if (!TextUtils.isEmpty(filterId)) { - val button = layout.findViewWithTag