From d929aa66521983f2d347e35b3e12e6c1a1ff018c Mon Sep 17 00:00:00 2001 From: CypherpunkSamurai Date: Thu, 9 Mar 2023 23:41:39 +0530 Subject: [PATCH] reformat --- app/build.gradle | 3 + app/src/main/java/org/schabi/newpipe/App.java | 20 +- .../java/org/schabi/newpipe/BaseFragment.java | 4 +- .../org/schabi/newpipe/DownloaderImpl.java | 15 +- .../java/org/schabi/newpipe/ExitActivity.java | 1 - .../java/org/schabi/newpipe/MainActivity.java | 60 +- .../org/schabi/newpipe/NewPipeDatabase.java | 12 +- .../org/schabi/newpipe/QueueItemMenuUtil.java | 8 +- .../org/schabi/newpipe/RouterActivity.java | 341 +++-- .../schabi/newpipe/database/AppDatabase.java | 5 +- .../org/schabi/newpipe/database/BasicDAO.java | 3 +- .../schabi/newpipe/database/Migrations.java | 164 ++- .../history/dao/SearchHistoryDAO.java | 10 +- .../history/dao/StreamHistoryDAO.java | 12 +- .../history/model/StreamHistoryEntity.java | 9 +- .../database/playlist/PlaylistLocalItem.java | 4 +- .../playlist/PlaylistMetadataEntry.java | 4 +- .../database/playlist/dao/PlaylistDAO.java | 4 +- .../playlist/dao/PlaylistRemoteDAO.java | 9 +- .../playlist/dao/PlaylistStreamDAO.java | 21 +- .../playlist/model/PlaylistRemoteEntity.java | 13 +- .../playlist/model/PlaylistStreamEntity.java | 6 +- .../database/stream/dao/StreamStateDAO.java | 10 +- .../stream/model/StreamStateEntity.java | 18 +- .../subscription/NotificationMode.java | 1 + .../subscription/SubscriptionEntity.java | 25 +- .../newpipe/download/DownloadActivity.java | 15 +- .../newpipe/download/DownloadDialog.java | 79 +- .../newpipe/error/AcraReportSender.java | 2 - .../error/AcraReportSenderFactory.java | 3 - .../schabi/newpipe/error/ErrorActivity.java | 47 +- .../newpipe/error/ReCaptchaActivity.java | 13 +- .../newpipe/fragments/BaseStateFragment.java | 13 +- .../newpipe/fragments/BlankFragment.java | 2 - .../newpipe/fragments/EmptyFragment.java | 2 - .../newpipe/fragments/MainFragment.java | 16 +- .../fragments/detail/DescriptionFragment.java | 20 +- .../newpipe/fragments/detail/StackItem.java | 17 +- .../newpipe/fragments/detail/TabAdapter.java | 1 - .../fragments/detail/VideoDetailFragment.java | 358 +++-- .../detail/VideoDetailPlayerCrasher.java | 48 +- .../fragments/list/BaseListFragment.java | 51 +- .../fragments/list/BaseListInfoFragment.java | 16 +- .../list/channel/ChannelFragment.java | 51 +- .../list/comments/CommentsFragment.java | 27 +- .../list/kiosk/DefaultKioskFragment.java | 1 - .../fragments/list/kiosk/KioskFragment.java | 21 +- .../list/playlist/PlaylistFragment.java | 38 +- .../fragments/list/search/SearchFragment.java | 121 +- .../fragments/list/search/SuggestionItem.java | 2 +- .../list/search/SuggestionListAdapter.java | 2 - .../list/videos/RelatedItemsFragment.java | 19 +- .../newpipe/info_list/InfoItemBuilder.java | 12 +- .../newpipe/info_list/InfoListAdapter.java | 17 +- .../info_list/dialog/InfoItemDialog.java | 74 +- .../dialog/StreamDialogDefaultEntry.java | 71 +- .../info_list/dialog/StreamDialogEntry.java | 2 - .../holder/ChannelGridInfoItemHolder.java | 1 - .../holder/ChannelInfoItemHolder.java | 1 - .../holder/ChannelMiniInfoItemHolder.java | 7 +- .../holder/CommentsInfoItemHolder.java | 4 +- .../holder/CommentsMiniInfoItemHolder.java | 8 +- .../info_list/holder/InfoItemHolder.java | 5 +- .../holder/PlaylistCardInfoItemHolder.java | 1 - .../holder/PlaylistGridInfoItemHolder.java | 1 - .../holder/PlaylistInfoItemHolder.java | 1 - .../holder/PlaylistMiniInfoItemHolder.java | 8 +- .../holder/StreamCardInfoItemHolder.java | 1 - .../holder/StreamGridInfoItemHolder.java | 1 - .../holder/StreamInfoItemHolder.java | 7 +- .../holder/StreamMiniInfoItemHolder.java | 5 +- .../newpipe/local/BaseLocalListFragment.java | 6 +- .../newpipe/local/HeaderFooterHolder.java | 1 - .../newpipe/local/LocalItemBuilder.java | 1 - .../newpipe/local/LocalItemListAdapter.java | 16 +- .../local/bookmark/BookmarkFragment.java | 28 +- .../local/dialog/PlaylistAppendDialog.java | 17 +- .../local/dialog/PlaylistCreationDialog.java | 7 +- .../newpipe/local/dialog/PlaylistDialog.java | 185 ++- .../local/history/HistoryEntryAdapter.java | 2 - .../local/history/HistoryRecordManager.java | 23 +- .../history/StatisticsPlaylistFragment.java | 32 +- .../newpipe/local/holder/LocalItemHolder.java | 5 +- .../holder/LocalPlaylistCardItemHolder.java | 1 - .../holder/LocalPlaylistGridItemHolder.java | 1 - .../local/holder/LocalPlaylistItemHolder.java | 6 +- .../LocalPlaylistStreamCardItemHolder.java | 1 - .../LocalPlaylistStreamGridItemHolder.java | 1 - .../holder/LocalPlaylistStreamItemHolder.java | 10 +- .../LocalStatisticStreamCardItemHolder.java | 1 - .../LocalStatisticStreamGridItemHolder.java | 1 - .../LocalStatisticStreamItemHolder.java | 8 +- .../local/holder/PlaylistItemHolder.java | 3 +- .../holder/RemotePlaylistCardItemHolder.java | 1 - .../holder/RemotePlaylistGridItemHolder.java | 1 - .../holder/RemotePlaylistItemHolder.java | 4 +- .../local/playlist/LocalPlaylistFragment.java | 30 +- .../local/playlist/LocalPlaylistManager.java | 12 +- .../local/playlist/RemotePlaylistManager.java | 7 +- .../ImportConfirmationDialog.java | 7 +- .../SubscriptionsImportFragment.java | 24 +- .../services/BaseImportExportService.java | 26 +- .../services/ImportExportJsonHelper.java | 16 +- .../services/SubscriptionsExportService.java | 13 +- .../services/SubscriptionsImportService.java | 23 +- .../newpipe/player/PlayQueueActivity.java | 28 +- .../org/schabi/newpipe/player/Player.java | 241 ++-- .../schabi/newpipe/player/PlayerService.java | 11 +- .../org/schabi/newpipe/player/PlayerType.java | 26 +- .../NonUriHlsDataSourceFactory.java | 114 +- .../datasource/YoutubeHttpDataSource.java | 574 ++++---- .../player/event/PlayerEventListener.java | 5 +- .../PlayerServiceExtendedEventListener.java | 3 +- .../gesture/CustomBottomSheetBehavior.java | 16 +- .../newpipe/player/helper/AudioReactor.java | 3 +- .../newpipe/player/helper/CacheFactory.java | 2 - .../newpipe/player/helper/LockManager.java | 1 - .../helper/PlaybackParameterDialog.java | 99 +- .../player/helper/PlayerDataSource.java | 155 +-- .../newpipe/player/helper/PlayerHelper.java | 100 +- .../newpipe/player/helper/PlayerHolder.java | 96 +- .../player/helper/PlayerSemitoneHelper.java | 6 +- .../player/mediaitem/ExceptionTag.java | 7 +- .../player/mediaitem/MediaItemTag.java | 27 +- .../player/mediaitem/PlaceholderTag.java | 9 +- .../player/mediaitem/StreamInfoTag.java | 6 +- .../mediasession/MediaSessionPlayerUi.java | 7 +- .../mediasession/PlayQueueNavigator.java | 9 +- .../player/mediasource/FailedMediaSource.java | 58 +- .../player/mediasource/LoadedMediaSource.java | 22 +- .../mediasource/ManagedMediaSource.java | 2 - .../ManagedMediaSourcePlaylist.java | 17 +- .../mediasource/PlaceholderMediaSource.java | 16 +- .../notification/NotificationConstants.java | 152 +- .../notification/NotificationPlayerUi.java | 11 +- .../player/notification/NotificationUtil.java | 18 +- .../player/playback/MediaSourceManager.java | 160 ++- .../player/playback/PlaybackListener.java | 7 +- .../playback/SurfaceHolderCallback.java | 1 - .../playqueue/AbstractInfoPlayQueue.java | 21 +- .../player/playqueue/ChannelPlayQueue.java | 5 +- .../newpipe/player/playqueue/PlayQueue.java | 24 +- .../player/playqueue/PlayQueueAdapter.java | 20 +- .../player/playqueue/PlayQueueItem.java | 6 +- .../playqueue/PlayQueueItemBuilder.java | 1 - .../player/playqueue/PlayQueueItemHolder.java | 8 +- .../player/playqueue/PlaylistPlayQueue.java | 5 +- .../resolver/AudioPlaybackResolver.java | 7 +- .../player/resolver/PlaybackResolver.java | 18 +- .../resolver/VideoPlaybackResolver.java | 17 +- .../SeekbarPreviewThumbnailHelper.java | 32 +- .../SeekbarPreviewThumbnailHolder.java | 9 +- .../newpipe/player/ui/MainPlayerUi.java | 47 +- .../schabi/newpipe/player/ui/PlayerUi.java | 19 +- .../newpipe/player/ui/PlayerUiList.java | 5 +- .../newpipe/player/ui/PopupPlayerUi.java | 205 ++- .../newpipe/player/ui/VideoPlayerUi.java | 76 +- .../settings/AppearanceSettingsFragment.java | 2 - .../settings/BasePreferenceFragment.java | 5 +- .../settings/ContentSettingsFragment.java | 22 +- .../settings/DebugSettingsFragment.java | 3 +- .../settings/DownloadSettingsFragment.java | 9 +- .../settings/HistorySettingsFragment.java | 104 +- .../settings/MainSettingsFragment.java | 2 - .../newpipe/settings/NewPipeSettings.java | 7 +- .../PeertubeInstanceListFragment.java | 66 +- .../settings/SelectChannelFragment.java | 18 +- .../newpipe/settings/SelectKioskFragment.java | 2 - .../settings/SelectPlaylistFragment.java | 24 +- .../newpipe/settings/SettingMigrations.java | 23 +- .../newpipe/settings/SettingsActivity.java | 60 +- .../settings/SettingsResourceRegistry.java | 24 +- .../settings/UpdateSettingsFragment.java | 2 - .../settings/VideoAudioSettingsFragment.java | 5 +- .../custom/NotificationActionsPreference.java | 79 +- .../PreferenceFuzzySearchFunction.java | 1 - .../preferencesearch/PreferenceParser.java | 32 +- .../PreferenceSearchAdapter.java | 2 - .../PreferenceSearchConfiguration.java | 12 +- .../PreferenceSearchFragment.java | 4 +- .../PreferenceSearchItem.java | 2 +- .../PreferenceSearchResultHighlighter.java | 4 +- .../newpipe/settings/tabs/AddTabDialog.java | 2 - .../settings/tabs/ChooseTabsFragment.java | 19 +- .../org/schabi/newpipe/settings/tabs/Tab.java | 16 +- .../newpipe/settings/tabs/TabsJsonHelper.java | 11 +- .../newpipe/settings/tabs/TabsManager.java | 2 - .../schabi/newpipe/streams/DataReader.java | 21 +- .../schabi/newpipe/streams/Mp4DashReader.java | 27 +- .../newpipe/streams/Mp4FromDashWriter.java | 24 +- .../newpipe/streams/OggFromWebMWriter.java | 18 +- .../schabi/newpipe/streams/WebMReader.java | 65 +- .../schabi/newpipe/streams/WebMWriter.java | 8 +- .../streams/io/NoFileManagerSafeGuard.java | 13 +- .../streams/io/StoredDirectoryHelper.java | 201 ++- .../newpipe/streams/io/StoredFileHelper.java | 234 ++-- .../org/schabi/newpipe/util/DeviceUtils.java | 13 +- .../schabi/newpipe/util/ExtractorHelper.java | 42 +- .../newpipe/util/FallbackViewHolder.java | 1 - .../util/FilePickerActivityHelper.java | 3 - .../schabi/newpipe/util/FilenameUtils.java | 5 +- .../org/schabi/newpipe/util/InfoCache.java | 6 +- .../org/schabi/newpipe/util/KeyboardUtil.java | 1 - .../schabi/newpipe/util/KioskTranslator.java | 8 +- .../org/schabi/newpipe/util/ListHelper.java | 30 +- .../org/schabi/newpipe/util/Localization.java | 5 +- .../schabi/newpipe/util/NavigationHelper.java | 40 +- .../newpipe/util/NewPipeTextViewHelper.java | 2 - .../schabi/newpipe/util/PeertubeHelper.java | 16 +- .../newpipe/util/PendingIntentCompat.java | 1 - .../schabi/newpipe/util/PermissionHelper.java | 7 +- .../schabi/newpipe/util/PicassoHelper.java | 33 +- .../newpipe/util/SecondaryStreamHelper.java | 1 - .../schabi/newpipe/util/SerializedCache.java | 8 +- .../schabi/newpipe/util/ServiceHelper.java | 18 +- .../schabi/newpipe/util/SparseItemUtil.java | 13 +- .../org/schabi/newpipe/util/StateSaver.java | 8 +- .../newpipe/util/StreamItemAdapter.java | 19 +- .../org/schabi/newpipe/util/ThemeHelper.java | 9 +- .../org/schabi/newpipe/util/ZipHelper.java | 18 +- .../external_communication/KoreUtils.java | 6 +- .../external_communication/ShareUtils.java | 22 +- .../util/text/CommentTextOnTouchListener.java | 10 +- .../text/HashtagLongPressClickableSpan.java | 2 - .../util/text/InternalUrlsHandler.java | 14 +- .../util/text/LongPressClickableSpan.java | 1 - .../text/LongPressLinkMovementMethod.java | 23 +- .../newpipe/util/text/TextLinkifier.java | 24 +- .../text/TimestampLongPressClickableSpan.java | 31 +- .../schabi/newpipe/util/text/TouchUtils.java | 1 - .../util/text/UrlLongPressClickableSpan.java | 5 +- .../util/urlfinder/PatternsCompat.java | 35 +- .../newpipe/views/AnimatedProgressBar.java | 1 - .../schabi/newpipe/views/CollapsibleView.java | 22 +- .../views/CustomCollapsingToolbarLayout.java | 4 +- .../newpipe/views/ExpandableSurfaceView.java | 11 +- .../newpipe/views/FocusAwareCoordinator.java | 2 - .../newpipe/views/FocusAwareDrawerLayout.java | 5 +- .../newpipe/views/FocusAwareSeekBar.java | 24 +- .../newpipe/views/FocusOverlayView.java | 218 ++- .../schabi/newpipe/views/NewPipeEditText.java | 2 - .../newpipe/views/NewPipeRecyclerView.java | 1 - .../schabi/newpipe/views/NewPipeTextView.java | 2 - .../newpipe/views/ScrollableTabLayout.java | 2 - .../views/SuperScrollLayoutManager.java | 1 - app/src/main/res/animator/custom_fade_in.xml | 10 +- app/src/main/res/animator/custom_fade_out.xml | 10 +- .../background_oval_black_transparent.xml | 4 +- .../main/res/drawable/dashed_border_black.xml | 8 +- .../main/res/drawable/dashed_border_dark.xml | 8 +- .../main/res/drawable/dashed_border_light.xml | 8 +- .../drawer_header_bottom_background.xml | 6 +- app/src/main/res/drawable/ic_add.xml | 14 +- .../res/drawable/ic_add_circle_outline.xml | 14 +- app/src/main/res/drawable/ic_apps.xml | 14 +- app/src/main/res/drawable/ic_arrow_back.xml | 14 +- .../main/res/drawable/ic_arrow_drop_down.xml | 14 +- .../main/res/drawable/ic_arrow_drop_up.xml | 14 +- app/src/main/res/drawable/ic_art_track.xml | 14 +- app/src/main/res/drawable/ic_asterisk.xml | 14 +- app/src/main/res/drawable/ic_attach_money.xml | 14 +- app/src/main/res/drawable/ic_backup.xml | 14 +- app/src/main/res/drawable/ic_bookmark.xml | 14 +- .../main/res/drawable/ic_brightness_high.xml | 14 +- .../main/res/drawable/ic_brightness_low.xml | 14 +- .../res/drawable/ic_brightness_medium.xml | 14 +- app/src/main/res/drawable/ic_bug_report.xml | 14 +- app/src/main/res/drawable/ic_campaign.xml | 14 +- app/src/main/res/drawable/ic_cast.xml | 14 +- app/src/main/res/drawable/ic_checklist.xml | 14 +- app/src/main/res/drawable/ic_child_care.xml | 22 +- app/src/main/res/drawable/ic_circle.xml | 14 +- app/src/main/res/drawable/ic_close.xml | 14 +- app/src/main/res/drawable/ic_cloud.xml | 14 +- .../main/res/drawable/ic_cloud_download.xml | 14 +- app/src/main/res/drawable/ic_comment.xml | 14 +- app/src/main/res/drawable/ic_computer.xml | 14 +- .../main/res/drawable/ic_crop_portrait.xml | 14 +- app/src/main/res/drawable/ic_delete.xml | 14 +- app/src/main/res/drawable/ic_description.xml | 14 +- .../main/res/drawable/ic_directions_bike.xml | 14 +- .../main/res/drawable/ic_directions_car.xml | 14 +- app/src/main/res/drawable/ic_done.xml | 14 +- app/src/main/res/drawable/ic_drag_handle.xml | 14 +- app/src/main/res/drawable/ic_expand_more.xml | 14 +- app/src/main/res/drawable/ic_explore.xml | 14 +- app/src/main/res/drawable/ic_fast_forward.xml | 14 +- app/src/main/res/drawable/ic_fast_rewind.xml | 14 +- app/src/main/res/drawable/ic_fastfood.xml | 14 +- app/src/main/res/drawable/ic_favorite.xml | 14 +- .../main/res/drawable/ic_file_download.xml | 14 +- app/src/main/res/drawable/ic_filter_list.xml | 14 +- .../main/res/drawable/ic_fitness_center.xml | 14 +- app/src/main/res/drawable/ic_fullscreen.xml | 14 +- .../main/res/drawable/ic_fullscreen_exit.xml | 14 +- app/src/main/res/drawable/ic_headset.xml | 14 +- .../main/res/drawable/ic_headset_shadow.xml | 26 +- app/src/main/res/drawable/ic_heart.xml | 14 +- app/src/main/res/drawable/ic_help.xml | 14 +- app/src/main/res/drawable/ic_history.xml | 14 +- .../main/res/drawable/ic_history_future.xml | 20 +- app/src/main/res/drawable/ic_home.xml | 14 +- .../main/res/drawable/ic_hourglass_top.xml | 14 +- app/src/main/res/drawable/ic_info_outline.xml | 14 +- .../main/res/drawable/ic_insert_emoticon.xml | 14 +- app/src/main/res/drawable/ic_language.xml | 14 +- app/src/main/res/drawable/ic_list.xml | 14 +- app/src/main/res/drawable/ic_live_tv.xml | 16 +- app/src/main/res/drawable/ic_menu_book.xml | 24 +- app/src/main/res/drawable/ic_mic.xml | 14 +- app/src/main/res/drawable/ic_more_vert.xml | 14 +- app/src/main/res/drawable/ic_motorcycle.xml | 14 +- app/src/main/res/drawable/ic_movie.xml | 14 +- app/src/main/res/drawable/ic_music_note.xml | 14 +- app/src/main/res/drawable/ic_next.xml | 14 +- .../main/res/drawable/ic_notifications.xml | 14 +- app/src/main/res/drawable/ic_palette.xml | 14 +- app/src/main/res/drawable/ic_pause.xml | 14 +- app/src/main/res/drawable/ic_people.xml | 14 +- app/src/main/res/drawable/ic_person.xml | 14 +- app/src/main/res/drawable/ic_pets.xml | 30 +- .../res/drawable/ic_picture_in_picture.xml | 14 +- app/src/main/res/drawable/ic_pin.xml | 34 +- .../res/drawable/ic_placeholder_bandcamp.xml | 14 +- .../res/drawable/ic_placeholder_media_ccc.xml | 16 +- .../res/drawable/ic_placeholder_peertube.xml | 17 +- app/src/main/res/drawable/ic_play_arrow.xml | 14 +- .../res/drawable/ic_play_arrow_shadow.xml | 42 +- .../res/drawable/ic_play_seek_triangle.xml | 12 +- app/src/main/res/drawable/ic_playlist_add.xml | 14 +- .../res/drawable/ic_playlist_add_check.xml | 26 +- .../main/res/drawable/ic_playlist_play.xml | 26 +- app/src/main/res/drawable/ic_previous.xml | 14 +- app/src/main/res/drawable/ic_public.xml | 14 +- app/src/main/res/drawable/ic_radio.xml | 14 +- app/src/main/res/drawable/ic_refresh.xml | 14 +- app/src/main/res/drawable/ic_repeat.xml | 14 +- app/src/main/res/drawable/ic_replay.xml | 14 +- app/src/main/res/drawable/ic_restaurant.xml | 14 +- app/src/main/res/drawable/ic_rss_feed.xml | 18 +- app/src/main/res/drawable/ic_save.xml | 14 +- app/src/main/res/drawable/ic_school.xml | 14 +- app/src/main/res/drawable/ic_search.xml | 14 +- app/src/main/res/drawable/ic_search_add.xml | 14 +- app/src/main/res/drawable/ic_select_all.xml | 14 +- app/src/main/res/drawable/ic_settings.xml | 14 +- .../drawable/ic_settings_backup_restore.xml | 14 +- app/src/main/res/drawable/ic_share.xml | 14 +- .../main/res/drawable/ic_shopping_cart.xml | 14 +- app/src/main/res/drawable/ic_shuffle.xml | 14 +- .../main/res/drawable/ic_smart_display.xml | 14 +- app/src/main/res/drawable/ic_sort.xml | 14 +- app/src/main/res/drawable/ic_stars.xml | 14 +- .../main/res/drawable/ic_subscriptions.xml | 14 +- app/src/main/res/drawable/ic_subtitles.xml | 14 +- app/src/main/res/drawable/ic_telescope.xml | 14 +- app/src/main/res/drawable/ic_thumb_down.xml | 14 +- app/src/main/res/drawable/ic_thumb_up.xml | 14 +- app/src/main/res/drawable/ic_trending_up.xml | 14 +- app/src/main/res/drawable/ic_tv.xml | 14 +- .../main/res/drawable/ic_videogame_asset.xml | 14 +- .../main/res/drawable/ic_visibility_off.xml | 14 +- .../main/res/drawable/ic_visibility_on.xml | 14 +- app/src/main/res/drawable/ic_volume_down.xml | 14 +- app/src/main/res/drawable/ic_volume_mute.xml | 14 +- app/src/main/res/drawable/ic_volume_off.xml | 14 +- app/src/main/res/drawable/ic_volume_up.xml | 14 +- app/src/main/res/drawable/ic_watch_later.xml | 14 +- app/src/main/res/drawable/ic_wb_sunny.xml | 14 +- app/src/main/res/drawable/ic_whatshot.xml | 14 +- app/src/main/res/drawable/ic_work.xml | 14 +- .../res/drawable/not_available_monkey.xml | 40 +- .../main/res/drawable/placeholder_person.xml | 30 +- .../placeholder_thumbnail_playlist.xml | 26 +- .../drawable/placeholder_thumbnail_video.xml | 20 +- .../drawable/player_controls_background.xml | 6 +- .../player_controls_top_background.xml | 6 +- .../res/drawable/progress_circular_white.xml | 14 +- .../progress_soundcloud_horizontal_dark.xml | 4 +- .../progress_soundcloud_horizontal_light.xml | 4 +- .../progress_youtube_horizontal_dark.xml | 4 +- .../progress_youtube_horizontal_light.xml | 4 +- .../res/drawable/selector_checked_dark.xml | 11 +- .../res/drawable/selector_checked_light.xml | 11 +- app/src/main/res/drawable/selector_dark.xml | 4 +- .../res/drawable/selector_focused_dark.xml | 4 +- .../res/drawable/selector_focused_light.xml | 4 +- app/src/main/res/drawable/selector_light.xml | 4 +- .../main/res/drawable/splash_background.xml | 6 +- .../main/res/drawable/splash_foreground.xml | 14 +- .../main/res/drawable/toolbar_shadow_dark.xml | 6 +- .../res/drawable/toolbar_shadow_light.xml | 6 +- app/src/main/res/layout/activity_about.xml | 56 +- .../main/res/layout/activity_downloader.xml | 16 +- app/src/main/res/layout/activity_error.xml | 194 +-- app/src/main/res/layout/activity_main.xml | 40 +- .../layout/activity_player_queue_control.xml | 478 +++---- .../main/res/layout/activity_recaptcha.xml | 27 +- app/src/main/res/layout/channel_header.xml | 202 +-- app/src/main/res/layout/chip.xml | 14 +- app/src/main/res/layout/dialog_edit_text.xml | 26 +- .../res/layout/dialog_feed_group_create.xml | 324 ++--- .../res/layout/dialog_feed_group_reorder.xml | 44 +- .../res/layout/dialog_playback_parameter.xml | 780 +++++------ app/src/main/res/layout/dialog_playlists.xml | 84 +- app/src/main/res/layout/dialog_title.xml | 72 +- app/src/main/res/layout/download_dialog.xml | 194 +-- app/src/main/res/layout/drawer_header.xml | 182 +-- app/src/main/res/layout/drawer_layout.xml | 16 +- app/src/main/res/layout/error_panel.xml | 144 +- .../layout/feed_group_add_new_grid_item.xml | 70 +- .../res/layout/feed_group_add_new_item.xml | 68 +- .../res/layout/feed_group_card_grid_item.xml | 72 +- .../main/res/layout/feed_group_card_item.xml | 70 +- .../res/layout/feed_group_reorder_item.xml | 98 +- .../main/res/layout/feed_item_carousel.xml | 10 +- app/src/main/res/layout/fragment_about.xml | 206 +-- app/src/main/res/layout/fragment_blank.xml | 32 +- .../main/res/layout/fragment_bookmarks.xml | 60 +- app/src/main/res/layout/fragment_channel.xml | 114 +- .../fragment_channels_notifications.xml | 18 +- .../main/res/layout/fragment_choose_tabs.xml | 78 +- app/src/main/res/layout/fragment_comments.xml | 100 +- .../main/res/layout/fragment_description.xml | 164 +-- app/src/main/res/layout/fragment_empty.xml | 20 +- app/src/main/res/layout/fragment_feed.xml | 236 ++-- app/src/main/res/layout/fragment_import.xml | 80 +- .../res/layout/fragment_instance_list.xml | 76 +- app/src/main/res/layout/fragment_kiosk.xml | 98 +- app/src/main/res/layout/fragment_licenses.xml | 78 +- app/src/main/res/layout/fragment_main.xml | 34 +- app/src/main/res/layout/fragment_playlist.xml | 96 +- .../res/layout/fragment_related_items.xml | 98 +- app/src/main/res/layout/fragment_search.xml | 176 +-- .../main/res/layout/fragment_subscription.xml | 62 +- .../main/res/layout/fragment_video_detail.xml | 1100 +++++++-------- .../main/res/layout/instance_spinner_item.xml | 8 +- .../res/layout/instance_spinner_layout.xml | 14 +- app/src/main/res/layout/item_instance.xml | 130 +- app/src/main/res/layout/item_metadata.xml | 50 +- .../main/res/layout/item_metadata_tags.xml | 46 +- .../res/layout/item_notification_config.xml | 27 +- .../res/layout/item_search_suggestion.xml | 112 +- .../res/layout/item_software_component.xml | 42 +- .../main/res/layout/item_stream_segment.xml | 106 +- .../res/layout/list_channel_grid_item.xml | 100 +- app/src/main/res/layout/list_channel_item.xml | 128 +- .../res/layout/list_channel_mini_item.xml | 82 +- app/src/main/res/layout/list_choose_tabs.xml | 98 +- .../res/layout/list_choose_tabs_dialog.xml | 50 +- .../main/res/layout/list_comments_item.xml | 180 +-- .../res/layout/list_comments_mini_item.xml | 108 +- app/src/main/res/layout/list_empty_view.xml | 36 +- .../layout/list_empty_view_subscriptions.xml | 36 +- .../res/layout/list_playlist_card_item.xml | 148 +- .../res/layout/list_playlist_grid_item.xml | 116 +- .../main/res/layout/list_playlist_item.xml | 112 +- .../res/layout/list_playlist_mini_item.xml | 112 +- .../main/res/layout/list_radio_icon_item.xml | 30 +- .../main/res/layout/list_stream_card_item.xml | 166 +-- .../main/res/layout/list_stream_grid_item.xml | 156 +-- app/src/main/res/layout/list_stream_item.xml | 156 +-- .../main/res/layout/list_stream_mini_item.xml | 132 +- .../layout/list_stream_playlist_card_item.xml | 160 +-- .../layout/list_stream_playlist_grid_item.xml | 156 +-- .../res/layout/list_stream_playlist_item.xml | 160 +-- .../main/res/layout/local_playlist_header.xml | 70 +- app/src/main/res/layout/main_bg.xml | 44 +- app/src/main/res/layout/mission_item.xml | 132 +- .../main/res/layout/mission_item_linear.xml | 138 +- app/src/main/res/layout/missions.xml | 18 +- app/src/main/res/layout/missions_header.xml | 32 +- app/src/main/res/layout/picker_icon_item.xml | 24 +- .../res/layout/picker_subscription_item.xml | 90 +- app/src/main/res/layout/pignate_footer.xml | 18 +- app/src/main/res/layout/play_queue_item.xml | 166 +-- app/src/main/res/layout/player.xml | 1226 ++++++++--------- .../res/layout/player_fast_seek_overlay.xml | 38 +- .../layout/player_fast_seek_seconds_view.xml | 68 +- .../res/layout/player_popup_close_overlay.xml | 24 +- app/src/main/res/layout/playlist_control.xml | 130 +- app/src/main/res/layout/playlist_header.xml | 140 +- .../main/res/layout/related_items_header.xml | 50 +- .../res/layout/select_channel_fragment.xml | 60 +- .../main/res/layout/select_channel_item.xml | 62 +- .../main/res/layout/select_kiosk_fragment.xml | 36 +- app/src/main/res/layout/select_kiosk_item.xml | 60 +- .../res/layout/select_playlist_fragment.xml | 60 +- .../settings_category_header_layout.xml | 26 +- .../layout/settings_category_header_title.xml | 24 +- app/src/main/res/layout/settings_layout.xml | 22 +- .../main/res/layout/settings_notification.xml | 24 +- .../layout/settings_notification_action.xml | 144 +- .../settings_preferencesearch_fragment.xml | 68 +- ...ings_preferencesearch_list_item_result.xml | 54 +- .../res/layout/single_choice_dialog_view.xml | 14 +- .../res/layout/statistic_playlist_control.xml | 62 +- .../main/res/layout/stream_quality_item.xml | 108 +- .../res/layout/subscription_groups_header.xml | 64 +- .../main/res/layout/subscription_header.xml | 26 +- app/src/main/res/layout/toolbar_layout.xml | 31 +- .../main/res/layout/toolbar_search_layout.xml | 86 +- app/src/main/res/menu/dialog_url.xml | 8 +- app/src/main/res/menu/download_menu.xml | 44 +- app/src/main/res/menu/drawer_items.xml | 6 +- app/src/main/res/menu/error_menu.xml | 10 +- app/src/main/res/menu/menu_channel.xml | 56 +- .../main/res/menu/menu_chooser_fragment.xml | 10 +- app/src/main/res/menu/menu_feed_fragment.xml | 40 +- .../main/res/menu/menu_feed_group_dialog.xml | 26 +- app/src/main/res/menu/menu_history.xml | 14 +- app/src/main/res/menu/menu_local_playlist.xml | 22 +- app/src/main/res/menu/menu_main_fragment.xml | 12 +- .../res/menu/menu_notifications_channels.xml | 10 +- app/src/main/res/menu/menu_play_queue.xml | 60 +- app/src/main/res/menu/menu_play_queue_bg.xml | 22 +- .../main/res/menu/menu_play_queue_item.xml | 24 +- app/src/main/res/menu/menu_playlist.xml | 48 +- app/src/main/res/menu/menu_recaptcha.xml | 10 +- .../res/menu/menu_settings_main_fragment.xml | 12 +- app/src/main/res/menu/mission.xml | 50 +- app/src/main/res/values-ar/strings.xml | 267 ++-- app/src/main/res/values-as/strings.xml | 42 +- app/src/main/res/values-az/strings.xml | 305 ++-- app/src/main/res/values-be/strings.xml | 131 +- app/src/main/res/values-bg/strings.xml | 202 ++- app/src/main/res/values-bn/strings.xml | 112 +- app/src/main/res/values-bs/strings.xml | 56 +- app/src/main/res/values-ca/strings.xml | 366 +++-- app/src/main/res/values-cs/strings.xml | 293 ++-- app/src/main/res/values-da/strings.xml | 320 +++-- app/src/main/res/values-de/strings.xml | 362 +++-- app/src/main/res/values-el/strings.xml | 367 +++-- app/src/main/res/values-eo/strings.xml | 154 ++- app/src/main/res/values-es/strings.xml | 360 +++-- app/src/main/res/values-et/strings.xml | 281 ++-- app/src/main/res/values-eu/strings.xml | 355 +++-- app/src/main/res/values-fa/strings.xml | 278 ++-- app/src/main/res/values-fi/strings.xml | 286 ++-- app/src/main/res/values-fr/strings.xml | 410 ++++-- app/src/main/res/values-gl/strings.xml | 351 +++-- app/src/main/res/values/attrs.xml | 30 +- app/src/main/res/values/strings.xml | 259 +++- app/src/main/res/values/styles.xml | 12 +- app/src/main/res/values/styles_misc.xml | 2 +- app/src/main/res/xml/appearance_settings.xml | 102 +- app/src/main/res/xml/content_settings.xml | 230 ++-- app/src/main/res/xml/debug_settings.xml | 114 +- app/src/main/res/xml/download_settings.xml | 120 +- app/src/main/res/xml/history_settings.xml | 114 +- app/src/main/res/xml/main_settings.xml | 74 +- .../main/res/xml/notifications_settings.xml | 74 +- .../res/xml/player_notification_settings.xml | 46 +- app/src/main/res/xml/provider_paths.xml | 8 +- app/src/main/res/xml/update_settings.xml | 26 +- app/src/main/res/xml/video_audio_settings.xml | 382 ++--- 556 files changed, 15082 insertions(+), 13976 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 8312d599f..f13460bd7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -3,10 +3,13 @@ import com.android.tools.profgen.ArtProfileSerializer import com.android.tools.profgen.DexFile plugins { + id 'java' + id 'maven-publish' id "com.android.application" id "kotlin-android" id "kotlin-kapt" id "kotlin-parcelize" + id("org.openrewrite.rewrite") version("5.37.0") id "checkstyle" id "org.sonarqube" version "3.5.0.2730" } diff --git a/app/src/main/java/org/schabi/newpipe/App.java b/app/src/main/java/org/schabi/newpipe/App.java index f4410a31b..25d02e9b8 100644 --- a/app/src/main/java/org/schabi/newpipe/App.java +++ b/app/src/main/java/org/schabi/newpipe/App.java @@ -4,14 +4,17 @@ import android.app.Application; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; - import androidx.annotation.NonNull; import androidx.core.app.NotificationChannelCompat; import androidx.core.app.NotificationManagerCompat; import androidx.preference.PreferenceManager; - import com.jakewharton.processphoenix.ProcessPhoenix; - +import io.reactivex.rxjava3.exceptions.CompositeException; +import io.reactivex.rxjava3.exceptions.MissingBackpressureException; +import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException; +import io.reactivex.rxjava3.exceptions.UndeliverableException; +import io.reactivex.rxjava3.functions.Consumer; +import io.reactivex.rxjava3.plugins.RxJavaPlugins; import org.acra.ACRA; import org.acra.config.CoreConfigurationBuilder; import org.schabi.newpipe.error.ReCaptchaActivity; @@ -30,13 +33,6 @@ import java.net.SocketException; import java.util.List; import java.util.Objects; -import io.reactivex.rxjava3.exceptions.CompositeException; -import io.reactivex.rxjava3.exceptions.MissingBackpressureException; -import io.reactivex.rxjava3.exceptions.OnErrorNotImplementedException; -import io.reactivex.rxjava3.exceptions.UndeliverableException; -import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.plugins.RxJavaPlugins; - /* * Copyright (C) Hans-Christoph Steiner 2016 * App.java is part of NewPipe. @@ -87,8 +83,8 @@ public class App extends Application { NewPipeSettings.initSettings(this); NewPipe.init(getDownloader(), - Localization.getPreferredLocalization(this), - Localization.getPreferredContentCountry(this)); + Localization.getPreferredLocalization(this), + Localization.getPreferredContentCountry(this)); Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext())); StateSaver.init(this); diff --git a/app/src/main/java/org/schabi/newpipe/BaseFragment.java b/app/src/main/java/org/schabi/newpipe/BaseFragment.java index 16ddb8376..4378a2dd2 100644 --- a/app/src/main/java/org/schabi/newpipe/BaseFragment.java +++ b/app/src/main/java/org/schabi/newpipe/BaseFragment.java @@ -4,19 +4,17 @@ import android.content.Context; import android.os.Bundle; import android.util.Log; import android.view.View; - import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; - import icepick.Icepick; import icepick.State; import leakcanary.AppWatcher; public abstract class BaseFragment extends Fragment { - protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected static final boolean DEBUG = MainActivity.DEBUG; + protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected AppCompatActivity activity; //These values are used for controlling fragments when they are part of the frontpage @State diff --git a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java index 9ddbe96df..3e6e7f2df 100644 --- a/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java +++ b/app/src/main/java/org/schabi/newpipe/DownloaderImpl.java @@ -1,11 +1,12 @@ package org.schabi.newpipe; import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; - +import okhttp3.OkHttpClient; +import okhttp3.RequestBody; +import okhttp3.ResponseBody; import org.schabi.newpipe.error.ReCaptchaActivity; import org.schabi.newpipe.extractor.downloader.Downloader; import org.schabi.newpipe.extractor.downloader.Request; @@ -14,19 +15,11 @@ import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; import org.schabi.newpipe.util.InfoCache; import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; import java.util.stream.Stream; -import okhttp3.OkHttpClient; -import okhttp3.RequestBody; -import okhttp3.ResponseBody; - public final class DownloaderImpl extends Downloader { public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; rv:91.0) Gecko/20100101 Firefox/91.0"; diff --git a/app/src/main/java/org/schabi/newpipe/ExitActivity.java b/app/src/main/java/org/schabi/newpipe/ExitActivity.java index bd1351f0c..ab66b6fd4 100644 --- a/app/src/main/java/org/schabi/newpipe/ExitActivity.java +++ b/app/src/main/java/org/schabi/newpipe/ExitActivity.java @@ -4,7 +4,6 @@ import android.annotation.SuppressLint; import android.app.Activity; import android.content.Intent; import android.os.Bundle; - import org.schabi.newpipe.util.NavigationHelper; /* diff --git a/app/src/main/java/org/schabi/newpipe/MainActivity.java b/app/src/main/java/org/schabi/newpipe/MainActivity.java index ee2bb3f05..097b796d9 100644 --- a/app/src/main/java/org/schabi/newpipe/MainActivity.java +++ b/app/src/main/java/org/schabi/newpipe/MainActivity.java @@ -20,29 +20,17 @@ package org.schabi.newpipe; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; +import android.content.*; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.Log; -import android.view.KeyEvent; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.Spinner; - import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; @@ -53,14 +41,8 @@ import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.preference.PreferenceManager; - import com.google.android.material.bottomsheet.BottomSheetBehavior; - -import org.schabi.newpipe.databinding.ActivityMainBinding; -import org.schabi.newpipe.databinding.DrawerHeaderBinding; -import org.schabi.newpipe.databinding.DrawerLayoutBinding; -import org.schabi.newpipe.databinding.InstanceSpinnerLayoutBinding; -import org.schabi.newpipe.databinding.ToolbarLayoutBinding; +import org.schabi.newpipe.databinding.*; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; @@ -75,39 +57,19 @@ import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; -import org.schabi.newpipe.util.Constants; -import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.KioskTranslator; -import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PeertubeHelper; -import org.schabi.newpipe.util.PermissionHelper; -import org.schabi.newpipe.util.SerializedCache; -import org.schabi.newpipe.util.ServiceHelper; -import org.schabi.newpipe.util.StateSaver; -import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.util.*; import org.schabi.newpipe.views.FocusOverlayView; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; + public class MainActivity extends AppCompatActivity { - private static final String TAG = "MainActivity"; @SuppressWarnings("ConstantConditions") public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); - - private ActivityMainBinding mainBinding; - private DrawerHeaderBinding drawerHeaderBinding; - private DrawerLayoutBinding drawerLayoutBinding; - private ToolbarLayoutBinding toolbarLayoutBinding; - - private ActionBarDrawerToggle toggle; - - private boolean servicesShown = false; - - private BroadcastReceiver broadcastReceiver; - + private static final String TAG = "MainActivity"; private static final int ITEM_ID_SUBSCRIPTIONS = -1; private static final int ITEM_ID_FEED = -2; private static final int ITEM_ID_BOOKMARKS = -3; @@ -115,8 +77,14 @@ public class MainActivity extends AppCompatActivity { private static final int ITEM_ID_HISTORY = -5; private static final int ITEM_ID_SETTINGS = 0; private static final int ITEM_ID_ABOUT = 1; - private static final int ORDER = 0; + private ActivityMainBinding mainBinding; + private DrawerHeaderBinding drawerHeaderBinding; + private DrawerLayoutBinding drawerLayoutBinding; + private ToolbarLayoutBinding toolbarLayoutBinding; + private ActionBarDrawerToggle toggle; + private boolean servicesShown = false; + private BroadcastReceiver broadcastReceiver; /*////////////////////////////////////////////////////////////////////////// // Activity's LifeCycle diff --git a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java index fc3423994..257f9469d 100644 --- a/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java +++ b/app/src/main/java/org/schabi/newpipe/NewPipeDatabase.java @@ -1,20 +1,14 @@ package org.schabi.newpipe; -import static org.schabi.newpipe.database.AppDatabase.DATABASE_NAME; -import static org.schabi.newpipe.database.Migrations.MIGRATION_1_2; -import static org.schabi.newpipe.database.Migrations.MIGRATION_2_3; -import static org.schabi.newpipe.database.Migrations.MIGRATION_3_4; -import static org.schabi.newpipe.database.Migrations.MIGRATION_4_5; -import static org.schabi.newpipe.database.Migrations.MIGRATION_5_6; - import android.content.Context; import android.database.Cursor; - import androidx.annotation.NonNull; import androidx.room.Room; - import org.schabi.newpipe.database.AppDatabase; +import static org.schabi.newpipe.database.AppDatabase.DATABASE_NAME; +import static org.schabi.newpipe.database.Migrations.*; + public final class NewPipeDatabase { private static volatile AppDatabase databaseInstance; diff --git a/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java index 3255489b0..b66bf92c6 100644 --- a/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java +++ b/app/src/main/java/org/schabi/newpipe/QueueItemMenuUtil.java @@ -1,15 +1,10 @@ package org.schabi.newpipe; -import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase; -import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText; - import android.content.Context; import android.view.ContextThemeWrapper; import android.view.View; import android.widget.PopupMenu; - import androidx.fragment.app.FragmentManager; - import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.download.DownloadDialog; import org.schabi.newpipe.local.dialog.PlaylistDialog; @@ -20,6 +15,9 @@ import org.schabi.newpipe.util.SparseItemUtil; import java.util.List; +import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase; +import static org.schabi.newpipe.util.external_communication.ShareUtils.shareText; + public final class QueueItemMenuUtil { private QueueItemMenuUtil() { } diff --git a/app/src/main/java/org/schabi/newpipe/RouterActivity.java b/app/src/main/java/org/schabi/newpipe/RouterActivity.java index e9c19a22d..051d5ab31 100644 --- a/app/src/main/java/org/schabi/newpipe/RouterActivity.java +++ b/app/src/main/java/org/schabi/newpipe/RouterActivity.java @@ -1,8 +1,5 @@ package org.schabi.newpipe; -import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO; -import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO; - import android.annotation.SuppressLint; import android.app.IntentService; import android.content.Context; @@ -13,16 +10,11 @@ import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.text.TextUtils; -import android.view.ContextThemeWrapper; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; +import android.view.*; import android.widget.Button; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast; - import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -40,7 +32,14 @@ import androidx.lifecycle.DefaultLifecycleObserver; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.preference.PreferenceManager; - +import icepick.Icepick; +import icepick.State; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.databinding.ListRadioIconItemBinding; import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding; @@ -54,16 +53,7 @@ import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.StreamingService.LinkType; import org.schabi.newpipe.extractor.channel.ChannelInfo; -import org.schabi.newpipe.extractor.exceptions.AgeRestrictedContentException; -import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException; -import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException; -import org.schabi.newpipe.extractor.exceptions.PaidContentException; -import org.schabi.newpipe.extractor.exceptions.PrivateContentException; -import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; -import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException; -import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException; +import org.schabi.newpipe.extractor.exceptions.*; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.ktx.ExceptionUtils; @@ -75,13 +65,7 @@ import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; -import org.schabi.newpipe.util.Constants; -import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PermissionHelper; -import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.util.*; import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.urlfinder.UrlFinder; import org.schabi.newpipe.views.FocusOverlayView; @@ -95,14 +79,8 @@ import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import icepick.Icepick; -import icepick.State; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; +import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO; +import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO; /** * Get the url from the intent and open it in the chosen preferred player. @@ -123,6 +101,53 @@ public class RouterActivity extends AppCompatActivity { private AlertDialog alertDialogChoice = null; private FragmentManager.FragmentLifecycleCallbacks dismissListener = null; + /** + * @param context the context. It will be {@code finish()}ed at the end of the handling if it is + * an instance of {@link RouterActivity}. + * @param errorInfo the error information + */ + private static void handleError(final Context context, final ErrorInfo errorInfo) { + if (errorInfo.getThrowable() != null) { + errorInfo.getThrowable().printStackTrace(); + } + + if (errorInfo.getThrowable() instanceof ReCaptchaException) { + Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show(); + // Starting ReCaptcha Challenge Activity + final Intent intent = new Intent(context, ReCaptchaActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + context.startActivity(intent); + } else if (errorInfo.getThrowable() != null + && ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) { + Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) { + Toast.makeText(context, R.string.restricted_video_no_stream, + Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) { + Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof PaidContentException) { + Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof PrivateContentException) { + Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) { + Toast.makeText(context, R.string.soundcloud_go_plus_content, + Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) { + Toast.makeText(context, R.string.youtube_music_premium_content, + Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) { + Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show(); + } else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) { + Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show(); + } else { + ErrorUtil.createNotification(context, errorInfo); + } + + if (context instanceof RouterActivity) { + ((RouterActivity) context).finish(); + } + } + @Override protected void onCreate(final Bundle savedInstanceState) { ThemeHelper.setDayNightMode(this); @@ -261,53 +286,6 @@ public class RouterActivity extends AppCompatActivity { UserAction.SHARE_TO_NEWPIPE, "Getting service from url: " + url)))); } - /** - * @param context the context. It will be {@code finish()}ed at the end of the handling if it is - * an instance of {@link RouterActivity}. - * @param errorInfo the error information - */ - private static void handleError(final Context context, final ErrorInfo errorInfo) { - if (errorInfo.getThrowable() != null) { - errorInfo.getThrowable().printStackTrace(); - } - - if (errorInfo.getThrowable() instanceof ReCaptchaException) { - Toast.makeText(context, R.string.recaptcha_request_toast, Toast.LENGTH_LONG).show(); - // Starting ReCaptcha Challenge Activity - final Intent intent = new Intent(context, ReCaptchaActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); - } else if (errorInfo.getThrowable() != null - && ExceptionUtils.isNetworkRelated(errorInfo.getThrowable())) { - Toast.makeText(context, R.string.network_error, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof AgeRestrictedContentException) { - Toast.makeText(context, R.string.restricted_video_no_stream, - Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof GeographicRestrictionException) { - Toast.makeText(context, R.string.georestricted_content, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof PaidContentException) { - Toast.makeText(context, R.string.paid_content, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof PrivateContentException) { - Toast.makeText(context, R.string.private_content, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof SoundCloudGoPlusContentException) { - Toast.makeText(context, R.string.soundcloud_go_plus_content, - Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof YoutubeMusicPremiumContentException) { - Toast.makeText(context, R.string.youtube_music_premium_content, - Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof ContentNotAvailableException) { - Toast.makeText(context, R.string.content_not_available, Toast.LENGTH_LONG).show(); - } else if (errorInfo.getThrowable() instanceof ContentNotSupportedException) { - Toast.makeText(context, R.string.content_not_supported, Toast.LENGTH_LONG).show(); - } else { - ErrorUtil.createNotification(context, errorInfo); - } - - if (context instanceof RouterActivity) { - ((RouterActivity) context).finish(); - } - } - protected void showUnsupportedUrlDialog(final String url) { final Context context = getThemeWrapperContext(); new AlertDialog.Builder(context) @@ -396,43 +374,6 @@ public class RouterActivity extends AppCompatActivity { } } - /** - * This is a helper class for checking if the choices are available and/or selected. - */ - class ChoiceAvailabilityChecker { - private final List availableChoices; - private final String selectedChoiceKey; - - ChoiceAvailabilityChecker( - @NonNull final List availableChoices, - @NonNull final String selectedChoiceKey) { - this.availableChoices = availableChoices; - this.selectedChoiceKey = selectedChoiceKey; - } - - public List getAvailableChoices() { - return availableChoices; - } - - public String getSelectedChoiceKey() { - return selectedChoiceKey; - } - - public boolean isAvailableAndSelected(@StringRes final int... wantedKeys) { - return Arrays.stream(wantedKeys).anyMatch(this::isAvailableAndSelected); - } - - public boolean isAvailableAndSelected(@StringRes final int wantedKey) { - final String wanted = getString(wantedKey); - // Check if the wanted option is selected - if (!selectedChoiceKey.equals(wanted)) { - return false; - } - // Check if it's available - return availableChoices.stream().anyMatch(item -> wanted.equals(item.key)); - } - } - private void showDialog(final List choices) { final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); @@ -701,9 +642,61 @@ public class RouterActivity extends AppCompatActivity { return playerType == null || playerType == PlayerType.MAIN; } + private void openAddToPlaylistDialog() { + getPersistFragment().openAddToPlaylistDialog(currentServiceId, currentUrl); + } + + private void openDownloadDialog() { + getPersistFragment().openDownloadDialog(currentServiceId, currentUrl); + } + + private PersistentFragment getPersistFragment() { + final FragmentManager fm = getSupportFragmentManager(); + PersistentFragment persistFragment = + (PersistentFragment) fm.findFragmentByTag("PERSIST_FRAGMENT"); + if (persistFragment == null) { + persistFragment = new PersistentFragment(); + fm.beginTransaction() + .add(persistFragment, "PERSIST_FRAGMENT") + .commitNow(); + } + return persistFragment; + } + + @Override + public void onRequestPermissionsResult(final int requestCode, + @NonNull final String[] permissions, + @NonNull final int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + for (final int i : grantResults) { + if (i == PackageManager.PERMISSION_DENIED) { + finish(); + return; + } + } + if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) { + openDownloadDialog(); + } + } + + @Nullable + private String getUrl(final Intent intent) { + String foundUrl = null; + if (intent.getData() != null) { + // Called from another app + foundUrl = intent.getData().toString(); + } else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) { + // Called from the share menu + final String extraText = intent.getStringExtra(Intent.EXTRA_TEXT); + foundUrl = UrlFinder.firstUrlFromInput(extraText); + } + + return foundUrl; + } + public static class PersistentFragment extends Fragment { - private WeakReference weakContext; private final CompositeDisposable disposables = new CompositeDisposable(); + private WeakReference weakContext; private int running = 0; private synchronized void inFlight(final boolean started) { @@ -770,10 +763,10 @@ public class RouterActivity extends AppCompatActivity { public void onResume(@NonNull final LifecycleOwner owner) { getLifecycle().removeObserver(this); getActivityContext().ifPresentOrElse(context -> - context.runOnUiThread(() -> { - runnable.accept(context); - inFlight(false); - }), + context.runOnUiThread(() -> { + runnable.accept(context); + inFlight(false); + }), () -> inFlight(false) ); } @@ -806,7 +799,7 @@ public class RouterActivity extends AppCompatActivity { Toast.LENGTH_LONG); toast.show(); emitter.setCancellable(toast::cancel); - })))); + })))); } @SuppressLint("CheckResult") @@ -817,13 +810,13 @@ public class RouterActivity extends AppCompatActivity { .observeOn(AndroidSchedulers.mainThread()) .compose(this::pleaseWait) .subscribe(result -> - runOnVisible(ctx -> { - final FragmentManager fm = ctx.getSupportFragmentManager(); - final DownloadDialog downloadDialog = new DownloadDialog(ctx, result); - // dismiss listener to be handled by FragmentManager - downloadDialog.show(fm, "downloadDialog"); - } - ), throwable -> runOnVisible(ctx -> + runOnVisible(ctx -> { + final FragmentManager fm = ctx.getSupportFragmentManager(); + final DownloadDialog downloadDialog = new DownloadDialog(ctx, result); + // dismiss listener to be handled by FragmentManager + downloadDialog.show(fm, "downloadDialog"); + } + ), throwable -> runOnVisible(ctx -> ((RouterActivity) ctx).showUnsupportedUrlDialog(currentUrl)))); } @@ -855,43 +848,6 @@ public class RouterActivity extends AppCompatActivity { } } - private void openAddToPlaylistDialog() { - getPersistFragment().openAddToPlaylistDialog(currentServiceId, currentUrl); - } - - private void openDownloadDialog() { - getPersistFragment().openDownloadDialog(currentServiceId, currentUrl); - } - - private PersistentFragment getPersistFragment() { - final FragmentManager fm = getSupportFragmentManager(); - PersistentFragment persistFragment = - (PersistentFragment) fm.findFragmentByTag("PERSIST_FRAGMENT"); - if (persistFragment == null) { - persistFragment = new PersistentFragment(); - fm.beginTransaction() - .add(persistFragment, "PERSIST_FRAGMENT") - .commitNow(); - } - return persistFragment; - } - - @Override - public void onRequestPermissionsResult(final int requestCode, - @NonNull final String[] permissions, - @NonNull final int[] grantResults) { - super.onRequestPermissionsResult(requestCode, permissions, grantResults); - for (final int i : grantResults) { - if (i == PackageManager.PERMISSION_DENIED) { - finish(); - return; - } - } - if (requestCode == PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE) { - openDownloadDialog(); - } - } - private static class AdapterChoiceItem { final String description; final String key; @@ -949,10 +905,9 @@ public class RouterActivity extends AppCompatActivity { } final Serializable serializable = intent.getSerializableExtra(KEY_CHOICE); - if (!(serializable instanceof Choice)) { + if (!(serializable instanceof final Choice playerChoice)) { return; } - final Choice playerChoice = (Choice) serializable; handleChoice(playerChoice); } @@ -1058,18 +1013,40 @@ public class RouterActivity extends AppCompatActivity { // Utils //////////////////////////////////////////////////////////////////////////*/ - @Nullable - private String getUrl(final Intent intent) { - String foundUrl = null; - if (intent.getData() != null) { - // Called from another app - foundUrl = intent.getData().toString(); - } else if (intent.getStringExtra(Intent.EXTRA_TEXT) != null) { - // Called from the share menu - final String extraText = intent.getStringExtra(Intent.EXTRA_TEXT); - foundUrl = UrlFinder.firstUrlFromInput(extraText); + /** + * This is a helper class for checking if the choices are available and/or selected. + */ + class ChoiceAvailabilityChecker { + private final List availableChoices; + private final String selectedChoiceKey; + + ChoiceAvailabilityChecker( + @NonNull final List availableChoices, + @NonNull final String selectedChoiceKey) { + this.availableChoices = availableChoices; + this.selectedChoiceKey = selectedChoiceKey; } - return foundUrl; + public List getAvailableChoices() { + return availableChoices; + } + + public String getSelectedChoiceKey() { + return selectedChoiceKey; + } + + public boolean isAvailableAndSelected(@StringRes final int... wantedKeys) { + return Arrays.stream(wantedKeys).anyMatch(this::isAvailableAndSelected); + } + + public boolean isAvailableAndSelected(@StringRes final int wantedKey) { + final String wanted = getString(wantedKey); + // Check if the wanted option is selected + if (!selectedChoiceKey.equals(wanted)) { + return false; + } + // Check if it's available + return availableChoices.stream().anyMatch(item -> wanted.equals(item.key)); + } } } diff --git a/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java b/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java index 563e80b17..61f950803 100644 --- a/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java +++ b/app/src/main/java/org/schabi/newpipe/database/AppDatabase.java @@ -1,11 +1,8 @@ package org.schabi.newpipe.database; -import static org.schabi.newpipe.database.Migrations.DB_VER_6; - import androidx.room.Database; import androidx.room.RoomDatabase; import androidx.room.TypeConverters; - import org.schabi.newpipe.database.feed.dao.FeedDAO; import org.schabi.newpipe.database.feed.dao.FeedGroupDAO; import org.schabi.newpipe.database.feed.model.FeedEntity; @@ -29,6 +26,8 @@ import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.database.subscription.SubscriptionDAO; import org.schabi.newpipe.database.subscription.SubscriptionEntity; +import static org.schabi.newpipe.database.Migrations.DB_VER_6; + @TypeConverters({Converters.class}) @Database( entities = { diff --git a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.java b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.java index 255f5ba8d..5813f6777 100644 --- a/app/src/main/java/org/schabi/newpipe/database/BasicDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/BasicDAO.java @@ -4,12 +4,11 @@ import androidx.room.Dao; import androidx.room.Delete; import androidx.room.Insert; import androidx.room.Update; +import io.reactivex.rxjava3.core.Flowable; import java.util.Collection; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; - @Dao public interface BasicDAO { /* Inserts */ diff --git a/app/src/main/java/org/schabi/newpipe/database/Migrations.java b/app/src/main/java/org/schabi/newpipe/database/Migrations.java index e301e3f1f..f72a6bca7 100644 --- a/app/src/main/java/org/schabi/newpipe/database/Migrations.java +++ b/app/src/main/java/org/schabi/newpipe/database/Migrations.java @@ -1,11 +1,9 @@ package org.schabi.newpipe.database; import android.util.Log; - import androidx.annotation.NonNull; import androidx.room.migration.Migration; import androidx.sqlite.db.SupportSQLiteDatabase; - import org.schabi.newpipe.MainActivity; public final class Migrations { @@ -24,10 +22,86 @@ public final class Migrations { public static final int DB_VER_4 = 4; public static final int DB_VER_5 = 5; public static final int DB_VER_6 = 6; - - private static final String TAG = Migrations.class.getName(); public static final boolean DEBUG = MainActivity.DEBUG; + public static final Migration MIGRATION_2_3 = new Migration(DB_VER_2, DB_VER_3) { + @Override + public void migrate(@NonNull final SupportSQLiteDatabase database) { + // Add NOT NULLs and new fields + database.execSQL("CREATE TABLE IF NOT EXISTS streams_new " + + "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + + "service_id INTEGER NOT NULL, url TEXT NOT NULL, title TEXT NOT NULL, " + + "stream_type TEXT NOT NULL, duration INTEGER NOT NULL, " + + "uploader TEXT NOT NULL, thumbnail_url TEXT, view_count INTEGER, " + + "textual_upload_date TEXT, upload_date INTEGER, " + + "is_upload_date_approximation INTEGER)"); + database.execSQL("INSERT INTO streams_new (uid, service_id, url, title, stream_type, " + + "duration, uploader, thumbnail_url, view_count, textual_upload_date, " + + "upload_date, is_upload_date_approximation) " + + + "SELECT uid, service_id, url, ifnull(title, ''), " + + "ifnull(stream_type, 'VIDEO_STREAM'), ifnull(duration, 0), " + + "ifnull(uploader, ''), ifnull(thumbnail_url, ''), NULL, NULL, NULL, NULL " + + + "FROM streams WHERE url IS NOT NULL"); + + database.execSQL("DROP TABLE streams"); + database.execSQL("ALTER TABLE streams_new RENAME TO streams"); + database.execSQL("CREATE UNIQUE INDEX index_streams_service_id_url " + + "ON streams (service_id, url)"); + + // Tables for feed feature + database.execSQL("CREATE TABLE IF NOT EXISTS feed " + + "(stream_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, " + + "PRIMARY KEY(stream_id, subscription_id), " + + "FOREIGN KEY(stream_id) REFERENCES streams(uid) " + + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, " + + "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) " + + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); + database.execSQL("CREATE INDEX index_feed_subscription_id ON feed (subscription_id)"); + database.execSQL("CREATE TABLE IF NOT EXISTS feed_group " + + "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, " + + "icon_id INTEGER NOT NULL, sort_order INTEGER NOT NULL)"); + database.execSQL("CREATE INDEX index_feed_group_sort_order ON feed_group (sort_order)"); + database.execSQL("CREATE TABLE IF NOT EXISTS feed_group_subscription_join " + + "(group_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, " + + "PRIMARY KEY(group_id, subscription_id), " + + "FOREIGN KEY(group_id) REFERENCES feed_group(uid) " + + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, " + + "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) " + + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); + database.execSQL("CREATE INDEX index_feed_group_subscription_join_subscription_id " + + "ON feed_group_subscription_join (subscription_id)"); + database.execSQL("CREATE TABLE IF NOT EXISTS feed_last_updated " + + "(subscription_id INTEGER NOT NULL, last_updated INTEGER, " + + "PRIMARY KEY(subscription_id), " + + "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) " + + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); + } + }; + public static final Migration MIGRATION_3_4 = new Migration(DB_VER_3, DB_VER_4) { + @Override + public void migrate(@NonNull final SupportSQLiteDatabase database) { + database.execSQL( + "ALTER TABLE streams ADD COLUMN uploader_url TEXT" + ); + } + }; + public static final Migration MIGRATION_4_5 = new Migration(DB_VER_4, DB_VER_5) { + @Override + public void migrate(@NonNull final SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE `subscriptions` ADD COLUMN `notification_mode` " + + "INTEGER NOT NULL DEFAULT 0"); + } + }; + public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) { + @Override + public void migrate(@NonNull final SupportSQLiteDatabase database) { + database.execSQL("ALTER TABLE `playlists` ADD COLUMN `is_thumbnail_permanent` " + + "INTEGER NOT NULL DEFAULT 0"); + } + }; + private static final String TAG = Migrations.class.getName(); public static final Migration MIGRATION_1_2 = new Migration(DB_VER_1, DB_VER_2) { @Override public void migrate(@NonNull final SupportSQLiteDatabase database) { @@ -115,88 +189,6 @@ public final class Migrations { } }; - public static final Migration MIGRATION_2_3 = new Migration(DB_VER_2, DB_VER_3) { - @Override - public void migrate(@NonNull final SupportSQLiteDatabase database) { - // Add NOT NULLs and new fields - database.execSQL("CREATE TABLE IF NOT EXISTS streams_new " - + "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " - + "service_id INTEGER NOT NULL, url TEXT NOT NULL, title TEXT NOT NULL, " - + "stream_type TEXT NOT NULL, duration INTEGER NOT NULL, " - + "uploader TEXT NOT NULL, thumbnail_url TEXT, view_count INTEGER, " - + "textual_upload_date TEXT, upload_date INTEGER, " - + "is_upload_date_approximation INTEGER)"); - - database.execSQL("INSERT INTO streams_new (uid, service_id, url, title, stream_type, " - + "duration, uploader, thumbnail_url, view_count, textual_upload_date, " - + "upload_date, is_upload_date_approximation) " - - + "SELECT uid, service_id, url, ifnull(title, ''), " - + "ifnull(stream_type, 'VIDEO_STREAM'), ifnull(duration, 0), " - + "ifnull(uploader, ''), ifnull(thumbnail_url, ''), NULL, NULL, NULL, NULL " - - + "FROM streams WHERE url IS NOT NULL"); - - database.execSQL("DROP TABLE streams"); - database.execSQL("ALTER TABLE streams_new RENAME TO streams"); - database.execSQL("CREATE UNIQUE INDEX index_streams_service_id_url " - + "ON streams (service_id, url)"); - - // Tables for feed feature - database.execSQL("CREATE TABLE IF NOT EXISTS feed " - + "(stream_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, " - + "PRIMARY KEY(stream_id, subscription_id), " - + "FOREIGN KEY(stream_id) REFERENCES streams(uid) " - + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, " - + "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) " - + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); - database.execSQL("CREATE INDEX index_feed_subscription_id ON feed (subscription_id)"); - database.execSQL("CREATE TABLE IF NOT EXISTS feed_group " - + "(uid INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT NOT NULL, " - + "icon_id INTEGER NOT NULL, sort_order INTEGER NOT NULL)"); - database.execSQL("CREATE INDEX index_feed_group_sort_order ON feed_group (sort_order)"); - database.execSQL("CREATE TABLE IF NOT EXISTS feed_group_subscription_join " - + "(group_id INTEGER NOT NULL, subscription_id INTEGER NOT NULL, " - + "PRIMARY KEY(group_id, subscription_id), " - + "FOREIGN KEY(group_id) REFERENCES feed_group(uid) " - + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, " - + "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) " - + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); - database.execSQL("CREATE INDEX index_feed_group_subscription_join_subscription_id " - + "ON feed_group_subscription_join (subscription_id)"); - database.execSQL("CREATE TABLE IF NOT EXISTS feed_last_updated " - + "(subscription_id INTEGER NOT NULL, last_updated INTEGER, " - + "PRIMARY KEY(subscription_id), " - + "FOREIGN KEY(subscription_id) REFERENCES subscriptions(uid) " - + "ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)"); - } - }; - - public static final Migration MIGRATION_3_4 = new Migration(DB_VER_3, DB_VER_4) { - @Override - public void migrate(@NonNull final SupportSQLiteDatabase database) { - database.execSQL( - "ALTER TABLE streams ADD COLUMN uploader_url TEXT" - ); - } - }; - - public static final Migration MIGRATION_4_5 = new Migration(DB_VER_4, DB_VER_5) { - @Override - public void migrate(@NonNull final SupportSQLiteDatabase database) { - database.execSQL("ALTER TABLE `subscriptions` ADD COLUMN `notification_mode` " - + "INTEGER NOT NULL DEFAULT 0"); - } - }; - - public static final Migration MIGRATION_5_6 = new Migration(DB_VER_5, DB_VER_6) { - @Override - public void migrate(@NonNull final SupportSQLiteDatabase database) { - database.execSQL("ALTER TABLE `playlists` ADD COLUMN `is_thumbnail_permanent` " - + "INTEGER NOT NULL DEFAULT 0"); - } - }; - private Migrations() { } } diff --git a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java index 8a281bdb4..098d6f6d4 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/dao/SearchHistoryDAO.java @@ -3,18 +3,12 @@ package org.schabi.newpipe.database.history.dao; import androidx.annotation.Nullable; import androidx.room.Dao; import androidx.room.Query; - +import io.reactivex.rxjava3.core.Flowable; import org.schabi.newpipe.database.history.model.SearchHistoryEntry; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; - -import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.CREATION_DATE; -import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.ID; -import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SEARCH; -import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.SERVICE_ID; -import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.TABLE_NAME; +import static org.schabi.newpipe.database.history.model.SearchHistoryEntry.*; @Dao public interface SearchHistoryDAO extends HistoryDAO { diff --git a/app/src/main/java/org/schabi/newpipe/database/history/dao/StreamHistoryDAO.java b/app/src/main/java/org/schabi/newpipe/database/history/dao/StreamHistoryDAO.java index 150d4a8e5..b257e0d5b 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/dao/StreamHistoryDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/dao/StreamHistoryDAO.java @@ -4,26 +4,20 @@ import androidx.annotation.Nullable; import androidx.room.Dao; import androidx.room.Query; import androidx.room.RewriteQueriesToDropUnusedColumns; - +import io.reactivex.rxjava3.core.Flowable; import org.schabi.newpipe.database.history.model.StreamHistoryEntity; import org.schabi.newpipe.database.history.model.StreamHistoryEntry; import org.schabi.newpipe.database.stream.StreamStatisticsEntry; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; - import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID; -import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE; -import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE; -import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_REPEAT_COUNT; +import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.*; import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_LATEST_DATE; import static org.schabi.newpipe.database.stream.StreamStatisticsEntry.STREAM_WATCH_COUNT; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID; import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; +import static org.schabi.newpipe.database.stream.model.StreamStateEntity.*; @Dao public abstract class StreamHistoryDAO implements HistoryDAO { diff --git a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java index a9d69afe8..e0b333055 100644 --- a/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/history/model/StreamHistoryEntity.java @@ -5,15 +5,12 @@ import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.ForeignKey; import androidx.room.Index; - import org.schabi.newpipe.database.stream.model.StreamEntity; import java.time.OffsetDateTime; import static androidx.room.ForeignKey.CASCADE; -import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.JOIN_STREAM_ID; -import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_ACCESS_DATE; -import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.STREAM_HISTORY_TABLE; +import static org.schabi.newpipe.database.history.model.StreamHistoryEntity.*; @Entity(tableName = STREAM_HISTORY_TABLE, primaryKeys = {JOIN_STREAM_ID, STREAM_ACCESS_DATE}, @@ -42,8 +39,8 @@ public class StreamHistoryEntity { private long repeatCount; /** - * @param streamUid the stream id this history item will refer to - * @param accessDate the last time the stream was accessed + * @param streamUid the stream id this history item will refer to + * @param accessDate the last time the stream was accessed * @param repeatCount the total number of views this stream received */ public StreamHistoryEntity(final long streamUid, diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java index 695f9ec5a..db976178f 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistLocalItem.java @@ -9,8 +9,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; public interface PlaylistLocalItem extends LocalItem { - String getOrderingName(); - static List merge( final List localPlaylists, final List remotePlaylists) { @@ -19,4 +17,6 @@ public interface PlaylistLocalItem extends LocalItem { Comparator.nullsLast(String.CASE_INSENSITIVE_ORDER))) .collect(Collectors.toList()); } + + String getOrderingName(); } diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java index a13894030..96bf36a15 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/PlaylistMetadataEntry.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.database.playlist; import androidx.room.ColumnInfo; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.*; public class PlaylistMetadataEntry implements PlaylistLocalItem { public static final String PLAYLIST_STREAM_COUNT = "streamCount"; diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java index 70aaa3b2d..737765aaf 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistDAO.java @@ -2,14 +2,12 @@ package org.schabi.newpipe.database.playlist.dao; import androidx.room.Dao; import androidx.room.Query; - +import io.reactivex.rxjava3.core.Flowable; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistEntity; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; - import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java index 6bb849428..a58b0e5e5 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistRemoteDAO.java @@ -3,18 +3,13 @@ package org.schabi.newpipe.database.playlist.dao; import androidx.room.Dao; import androidx.room.Query; import androidx.room.Transaction; - +import io.reactivex.rxjava3.core.Flowable; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; - -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.*; @Dao public interface PlaylistRemoteDAO extends BasicDAO { diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java index 2036dc205..a527aa9fd 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/dao/PlaylistStreamDAO.java @@ -4,7 +4,7 @@ import androidx.room.Dao; import androidx.room.Query; import androidx.room.RewriteQueriesToDropUnusedColumns; import androidx.room.Transaction; - +import io.reactivex.rxjava3.core.Flowable; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; @@ -12,23 +12,12 @@ import org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; - import static org.schabi.newpipe.database.playlist.PlaylistMetadataEntry.PLAYLIST_STREAM_COUNT; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_NAME; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_TABLE; -import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.PLAYLIST_THUMBNAIL_URL; -import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX; -import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID; +import static org.schabi.newpipe.database.playlist.model.PlaylistEntity.*; import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_STREAM_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE; -import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_ID; -import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_TABLE; -import static org.schabi.newpipe.database.stream.model.StreamEntity.STREAM_THUMBNAIL_URL; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID_ALIAS; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_PROGRESS_MILLIS; -import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; +import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.*; +import static org.schabi.newpipe.database.stream.model.StreamEntity.*; +import static org.schabi.newpipe.database.stream.model.StreamStateEntity.*; @Dao public interface PlaylistStreamDAO extends BasicDAO { diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java index 2e9a15d7d..2ecac5ee6 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistRemoteEntity.java @@ -1,22 +1,13 @@ package org.schabi.newpipe.database.playlist.model; import android.text.TextUtils; - -import androidx.room.ColumnInfo; -import androidx.room.Entity; -import androidx.room.Ignore; -import androidx.room.Index; -import androidx.room.PrimaryKey; - +import androidx.room.*; import org.schabi.newpipe.database.playlist.PlaylistLocalItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import org.schabi.newpipe.util.Constants; import static org.schabi.newpipe.database.LocalItem.LocalItemType.PLAYLIST_REMOTE_ITEM; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_NAME; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_SERVICE_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_TABLE; -import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.REMOTE_PLAYLIST_URL; +import static org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity.*; @Entity(tableName = REMOTE_PLAYLIST_TABLE, indices = { diff --git a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistStreamEntity.java b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistStreamEntity.java index f3208b6d5..1a05989c0 100644 --- a/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistStreamEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/playlist/model/PlaylistStreamEntity.java @@ -4,14 +4,10 @@ import androidx.room.ColumnInfo; import androidx.room.Entity; import androidx.room.ForeignKey; import androidx.room.Index; - import org.schabi.newpipe.database.stream.model.StreamEntity; import static androidx.room.ForeignKey.CASCADE; -import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_INDEX; -import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_PLAYLIST_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.JOIN_STREAM_ID; -import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.PLAYLIST_STREAM_JOIN_TABLE; +import static org.schabi.newpipe.database.playlist.model.PlaylistStreamEntity.*; @Entity(tableName = PLAYLIST_STREAM_JOIN_TABLE, primaryKeys = {JOIN_PLAYLIST_ID, JOIN_INDEX}, diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java index 06371248d..7335eb329 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/dao/StreamStateDAO.java @@ -1,18 +1,12 @@ package org.schabi.newpipe.database.stream.dao; -import androidx.room.Dao; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; -import androidx.room.Query; -import androidx.room.Transaction; - +import androidx.room.*; +import io.reactivex.rxjava3.core.Flowable; import org.schabi.newpipe.database.BasicDAO; import org.schabi.newpipe.database.stream.model.StreamStateEntity; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; - import static org.schabi.newpipe.database.stream.model.StreamStateEntity.JOIN_STREAM_ID; import static org.schabi.newpipe.database.stream.model.StreamStateEntity.STREAM_STATE_TABLE; diff --git a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java index 75766850f..20d16d5ff 100644 --- a/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/stream/model/StreamStateEntity.java @@ -26,21 +26,19 @@ public class StreamStateEntity { // for some other joins already public static final String JOIN_STREAM_ID_ALIAS = "stream_id_alias"; public static final String STREAM_PROGRESS_MILLIS = "progress_time"; - - /** - * Playback state will not be saved, if playback time is less than this threshold (5000ms = 5s). - */ - private static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000; - /** * Stream will be considered finished if the playback time left exceeds this threshold * (60000ms = 60s). + * * @see #isFinished(long) * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams() * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long) */ public static final long PLAYBACK_FINISHED_END_MILLISECONDS = 60000; - + /** + * Playback state will not be saved, if playback time is less than this threshold (5000ms = 5s). + */ + private static final long PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS = 5000; @ColumnInfo(name = JOIN_STREAM_ID) private long streamUid; @@ -71,6 +69,7 @@ public class StreamStateEntity { /** * The state will be considered valid, and thus be saved, if the progress is more than {@link * #PLAYBACK_SAVE_THRESHOLD_START_MILLISECONDS} or at least 1/4 of the video length. + * * @param durationInSeconds the duration of the stream connected with this state, in seconds * @return whether this stream state entity should be saved or not */ @@ -85,10 +84,11 @@ public class StreamStateEntity { * The state will be saved anyway, so that it can be shown under stream info items, but the * player will not resume if a state is considered as finished. Finished streams are also the * ones that can be filtered out in the feed fragment. - * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams() - * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long) + * * @param durationInSeconds the duration of the stream connected with this state, in seconds * @return whether the stream is finished or not + * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreams() + * @see org.schabi.newpipe.database.feed.dao.FeedDAO#getLiveOrNotPlayedStreamsForGroup(long) */ public boolean isFinished(final long durationInSeconds) { return progressMillis >= durationInSeconds * 1000 - PLAYBACK_FINISHED_END_MILLISECONDS diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java b/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java index 07e0eb7d3..b23a44a13 100644 --- a/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java +++ b/app/src/main/java/org/schabi/newpipe/database/subscription/NotificationMode.java @@ -1,6 +1,7 @@ package org.schabi.newpipe.database.subscription; import androidx.annotation.IntDef; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java index 0e4bda490..e65c563cd 100644 --- a/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java +++ b/app/src/main/java/org/schabi/newpipe/database/subscription/SubscriptionEntity.java @@ -1,19 +1,12 @@ package org.schabi.newpipe.database.subscription; import androidx.annotation.NonNull; -import androidx.room.ColumnInfo; -import androidx.room.Entity; -import androidx.room.Ignore; -import androidx.room.Index; -import androidx.room.PrimaryKey; - +import androidx.room.*; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.util.Constants; -import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_SERVICE_ID; -import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_TABLE; -import static org.schabi.newpipe.database.subscription.SubscriptionEntity.SUBSCRIPTION_URL; +import static org.schabi.newpipe.database.subscription.SubscriptionEntity.*; @Entity(tableName = SUBSCRIPTION_TABLE, indices = {@Index(value = {SUBSCRIPTION_SERVICE_ID, SUBSCRIPTION_URL}, unique = true)}) @@ -26,7 +19,7 @@ public class SubscriptionEntity { public static final String SUBSCRIPTION_AVATAR_URL = "avatar_url"; public static final String SUBSCRIPTION_SUBSCRIBER_COUNT = "subscriber_count"; public static final String SUBSCRIPTION_DESCRIPTION = "description"; - public static final String SUBSCRIPTION_NOTIFICATION_MODE = "notification_mode"; + public static final String SUBSCRIPTION_NOTIFICATION_MODE = "notification_mode"; @PrimaryKey(autoGenerate = true) private long uid = 0; @@ -167,20 +160,16 @@ public class SubscriptionEntity { if (!url.equals(that.url)) { return false; } - if (name != null ? !name.equals(that.name) : that.name != null) { + if (!java.util.Objects.equals(name, that.name)) { return false; } - if (avatarUrl != null ? !avatarUrl.equals(that.avatarUrl) : that.avatarUrl != null) { + if (!java.util.Objects.equals(avatarUrl, that.avatarUrl)) { return false; } - if (subscriberCount != null - ? !subscriberCount.equals(that.subscriberCount) - : that.subscriberCount != null) { + if (!java.util.Objects.equals(subscriberCount, that.subscriberCount)) { return false; } - return description != null - ? description.equals(that.description) - : that.description == null; + return java.util.Objects.equals(description, that.description); } @Override diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java index 37eefed96..1c06fccdf 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadActivity.java @@ -6,17 +6,14 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.ViewTreeObserver; - import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.FragmentTransaction; - import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ActivityDownloaderBinding; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.views.FocusOverlayView; - import us.shandian.giga.service.DownloadManagerService; import us.shandian.giga.ui.fragment.MissionsFragment; @@ -53,12 +50,12 @@ public class DownloadActivity extends AppCompatActivity { getWindow().getDecorView().getViewTreeObserver() .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - @Override - public void onGlobalLayout() { - updateFragments(); - getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this); - } - }); + @Override + public void onGlobalLayout() { + updateFragments(); + getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + }); if (DeviceUtils.isTv(this)) { FocusOverlayView.setupFocusObserver(this); diff --git a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java index d1ee0ee88..d7e9bef36 100644 --- a/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java +++ b/app/src/main/java/org/schabi/newpipe/download/DownloadDialog.java @@ -1,17 +1,8 @@ package org.schabi.newpipe.download; -import static org.schabi.newpipe.extractor.stream.DeliveryMethod.PROGRESSIVE_HTTP; -import static org.schabi.newpipe.util.ListHelper.getStreamsOfSpecifiedDelivery; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - import android.app.Activity; -import android.content.ComponentName; -import android.content.Context; -import android.content.DialogInterface; +import android.content.*; import android.content.DialogInterface.OnDismissListener; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.SharedPreferences; import android.net.Uri; import android.os.Bundle; import android.os.Environment; @@ -24,7 +15,6 @@ import android.widget.AdapterView; import android.widget.RadioGroup; import android.widget.SeekBar; import android.widget.Toast; - import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; @@ -39,9 +29,10 @@ import androidx.collection.SparseArrayCompat; import androidx.documentfile.provider.DocumentFile; import androidx.fragment.app.DialogFragment; import androidx.preference.PreferenceManager; - import com.nononsenseapps.filepicker.Utils; - +import icepick.Icepick; +import icepick.State; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.DownloadDialogBinding; @@ -51,24 +42,19 @@ import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.localization.Localization; -import org.schabi.newpipe.extractor.stream.AudioStream; -import org.schabi.newpipe.extractor.stream.Stream; -import org.schabi.newpipe.extractor.stream.StreamInfo; -import org.schabi.newpipe.extractor.stream.SubtitlesStream; -import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.StoredDirectoryHelper; import org.schabi.newpipe.streams.io.StoredFileHelper; -import org.schabi.newpipe.util.FilePickerActivityHelper; -import org.schabi.newpipe.util.FilenameUtils; -import org.schabi.newpipe.util.ListHelper; -import org.schabi.newpipe.util.PermissionHelper; -import org.schabi.newpipe.util.SecondaryStreamHelper; -import org.schabi.newpipe.util.SimpleOnSeekBarChangeListener; -import org.schabi.newpipe.util.StreamItemAdapter; +import org.schabi.newpipe.util.*; import org.schabi.newpipe.util.StreamItemAdapter.StreamSizeWrapper; -import org.schabi.newpipe.util.ThemeHelper; +import us.shandian.giga.get.MissionRecoveryInfo; +import us.shandian.giga.postprocessing.Postprocessing; +import us.shandian.giga.service.DownloadManager; +import us.shandian.giga.service.DownloadManagerService; +import us.shandian.giga.service.DownloadManagerService.DownloadManagerBinder; +import us.shandian.giga.service.MissionState; import java.io.File; import java.io.IOException; @@ -77,21 +63,15 @@ import java.util.Locale; import java.util.Objects; import java.util.Optional; -import icepick.Icepick; -import icepick.State; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import us.shandian.giga.get.MissionRecoveryInfo; -import us.shandian.giga.postprocessing.Postprocessing; -import us.shandian.giga.service.DownloadManager; -import us.shandian.giga.service.DownloadManagerService; -import us.shandian.giga.service.DownloadManagerService.DownloadManagerBinder; -import us.shandian.giga.service.MissionState; +import static org.schabi.newpipe.extractor.stream.DeliveryMethod.PROGRESSIVE_HTTP; +import static org.schabi.newpipe.util.ListHelper.getStreamsOfSpecifiedDelivery; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; public class DownloadDialog extends DialogFragment implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener { private static final String TAG = "DialogFragment"; private static final boolean DEBUG = MainActivity.DEBUG; - + private final CompositeDisposable disposables = new CompositeDisposable(); @State StreamInfo currentInfo; @State @@ -106,34 +86,25 @@ public class DownloadDialog extends DialogFragment int selectedAudioIndex = 0; // default to the first item @State int selectedSubtitleIndex = 0; // default to the first item - @Nullable private OnDismissListener onDismissListener = null; - private StoredDirectoryHelper mainStorageAudio = null; private StoredDirectoryHelper mainStorageVideo = null; private DownloadManager downloadManager = null; private ActionMenuItemView okButton = null; private Context context; private boolean askForSavePath; - private StreamItemAdapter audioStreamsAdapter; private StreamItemAdapter videoStreamsAdapter; private StreamItemAdapter subtitleStreamsAdapter; - - private final CompositeDisposable disposables = new CompositeDisposable(); - private DownloadDialogBinding dialogBinding; - - private SharedPreferences prefs; - - // Variables for file name and MIME type when picking new folder because it's not set yet - private String filenameTmp; - private String mimeTmp; - private final ActivityResultLauncher requestDownloadSaveAsLauncher = registerForActivityResult( new StartActivityForResult(), this::requestDownloadSaveAsResult); + private SharedPreferences prefs; + // Variables for file name and MIME type when picking new folder because it's not set yet + private String filenameTmp; + private String mimeTmp; private final ActivityResultLauncher requestDownloadPickAudioFolderLauncher = registerForActivityResult( new StartActivityForResult(), this::requestDownloadPickAudioFolderResult); @@ -1031,7 +1002,7 @@ public class DownloadDialog extends DialogFragment if (selectedStream.getFormat() == MediaFormat.TTML) { psName = Postprocessing.ALGORITHM_TTML_CONVERTER; - psArgs = new String[] { + psArgs = new String[]{ selectedStream.getFormat().getSuffix(), "false" // ignore empty frames }; @@ -1042,10 +1013,10 @@ public class DownloadDialog extends DialogFragment } if (secondaryStream == null) { - urls = new String[] { + urls = new String[]{ selectedStream.getContent() }; - recoveryInfo = new MissionRecoveryInfo[] { + recoveryInfo = new MissionRecoveryInfo[]{ new MissionRecoveryInfo(selectedStream) }; } else { @@ -1054,10 +1025,10 @@ public class DownloadDialog extends DialogFragment + secondaryStream.getDeliveryMethod()); } - urls = new String[] { + urls = new String[]{ selectedStream.getContent(), secondaryStream.getContent() }; - recoveryInfo = new MissionRecoveryInfo[] {new MissionRecoveryInfo(selectedStream), + recoveryInfo = new MissionRecoveryInfo[]{new MissionRecoveryInfo(selectedStream), new MissionRecoveryInfo(secondaryStream)}; } diff --git a/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java b/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java index 4d9966364..2b45632e4 100644 --- a/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java +++ b/app/src/main/java/org/schabi/newpipe/error/AcraReportSender.java @@ -1,9 +1,7 @@ package org.schabi.newpipe.error; import android.content.Context; - import androidx.annotation.NonNull; - import org.acra.ReportField; import org.acra.data.CrashReportData; import org.acra.sender.ReportSender; diff --git a/app/src/main/java/org/schabi/newpipe/error/AcraReportSenderFactory.java b/app/src/main/java/org/schabi/newpipe/error/AcraReportSenderFactory.java index e63d55063..357863e6d 100644 --- a/app/src/main/java/org/schabi/newpipe/error/AcraReportSenderFactory.java +++ b/app/src/main/java/org/schabi/newpipe/error/AcraReportSenderFactory.java @@ -1,11 +1,8 @@ package org.schabi.newpipe.error; import android.content.Context; - import androidx.annotation.NonNull; - import com.google.auto.service.AutoService; - import org.acra.config.CoreConfiguration; import org.acra.sender.ReportSender; import org.acra.sender.ReportSenderFactory; diff --git a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java index e1dd929d4..0b92f00eb 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java +++ b/app/src/main/java/org/schabi/newpipe/error/ErrorActivity.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.error; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -12,14 +10,11 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; - import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; - import com.grack.nanojson.JsonWriter; - import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; @@ -33,6 +28,8 @@ import java.time.format.DateTimeFormatter; import java.util.Arrays; import java.util.stream.Collectors; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; + /* * Created by Christian Schabesberger on 24.10.15. * @@ -83,6 +80,25 @@ public class ErrorActivity extends AppCompatActivity { // Activity lifecycle //////////////////////////////////////////////////////////////////////// + /** + * Get the checked activity. + * + * @param returnActivity the activity to return to + * @return the casted return activity or null + */ + @Nullable + static Class getReturnActivity(final Class returnActivity) { + Class checkedReturnActivity = null; + if (returnActivity != null) { + if (Activity.class.isAssignableFrom(returnActivity)) { + checkedReturnActivity = returnActivity.asSubclass(Activity.class); + } else { + checkedReturnActivity = MainActivity.class; + } + } + return checkedReturnActivity; + } + @Override protected void onCreate(final Bundle savedInstanceState) { assureCorrectAppLanguage(this); @@ -188,25 +204,6 @@ public class ErrorActivity extends AppCompatActivity { .collect(Collectors.joining(separator + "\n", separator + "\n", separator)); } - /** - * Get the checked activity. - * - * @param returnActivity the activity to return to - * @return the casted return activity or null - */ - @Nullable - static Class getReturnActivity(final Class returnActivity) { - Class checkedReturnActivity = null; - if (returnActivity != null) { - if (Activity.class.isAssignableFrom(returnActivity)) { - checkedReturnActivity = returnActivity.asSubclass(Activity.class); - } else { - checkedReturnActivity = MainActivity.class; - } - } - return checkedReturnActivity; - } - private void buildInfo(final ErrorInfo info) { String text = ""; @@ -267,7 +264,7 @@ public class ErrorActivity extends AppCompatActivity { htmlErrorReport .append("## Exception") .append("\n* __User Action:__ ") - .append(getUserActionString(errorInfo.getUserAction())) + .append(getUserActionString(errorInfo.getUserAction())) .append("\n* __Request:__ ").append(errorInfo.getRequest()) .append("\n* __Content Country:__ ").append(getContentCountryString()) .append("\n* __Content Language:__ ").append(getContentLanguageString()) diff --git a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java index 3c14cfe4c..1e77a355e 100644 --- a/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java +++ b/app/src/main/java/org/schabi/newpipe/error/ReCaptchaActivity.java @@ -7,19 +7,13 @@ import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuItem; -import android.webkit.CookieManager; -import android.webkit.WebResourceRequest; -import android.webkit.WebSettings; -import android.webkit.WebView; -import android.webkit.WebViewClient; - +import android.webkit.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.NavUtils; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; @@ -54,6 +48,8 @@ public class ReCaptchaActivity extends AppCompatActivity { public static final String TAG = ReCaptchaActivity.class.toString(); public static final String YT_URL = "https://www.youtube.com"; public static final String RECAPTCHA_COOKIES_KEY = "recaptcha_cookies"; + private ActivityRecaptchaBinding recaptchaBinding; + private String foundCookies = ""; public static String sanitizeRecaptchaUrl(@Nullable final String url) { if (url == null || url.trim().isEmpty()) { @@ -64,9 +60,6 @@ public class ReCaptchaActivity extends AppCompatActivity { } } - private ActivityRecaptchaBinding recaptchaBinding; - private String foundCookies = ""; - @SuppressLint("SetJavaScriptEnabled") @Override protected void onCreate(final Bundle savedInstanceState) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java index 9b4bf8377..60c3da324 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BaseStateFragment.java @@ -4,11 +4,10 @@ import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.ProgressBar; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - +import icepick.State; import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; @@ -18,24 +17,20 @@ import org.schabi.newpipe.util.InfoCache; import java.util.concurrent.atomic.AtomicBoolean; -import icepick.State; - import static org.schabi.newpipe.ktx.ViewUtils.animate; public abstract class BaseStateFragment extends BaseFragment implements ViewContract { @State protected AtomicBoolean wasLoading = new AtomicBoolean(); protected AtomicBoolean isLoading = new AtomicBoolean(); - + @Nullable + @State + protected ErrorInfo lastPanelError = null; @Nullable private View emptyStateView; @Nullable private ProgressBar loadingProgressBar; - private ErrorPanelHelper errorPanelHelper; - @Nullable - @State - protected ErrorInfo lastPanelError = null; @Override public void onViewCreated(@NonNull final View rootView, final Bundle savedInstanceState) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java index fe4eef37a..cc4c50911 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/BlankFragment.java @@ -4,9 +4,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.Nullable; - import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java index d4e73bcac..8fb352e3e 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/EmptyFragment.java @@ -4,9 +4,7 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.Nullable; - import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java index de68269e9..d5adb1e45 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/MainFragment.java @@ -3,13 +3,7 @@ package org.schabi.newpipe.fragments; import android.content.Context; import android.os.Bundle; import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; - +import android.view.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; @@ -17,9 +11,7 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentStatePagerAdapterMenuWorkaround; import androidx.preference.PreferenceManager; - import com.google.android.material.tabs.TabLayout; - import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.FragmentMainBinding; @@ -34,10 +26,9 @@ import java.util.ArrayList; import java.util.List; public class MainFragment extends BaseFragment implements TabLayout.OnTabSelectedListener { + private final List tabsList = new ArrayList<>(); private FragmentMainBinding binding; private SelectedTabsPagerAdapter pagerAdapter; - - private final List tabsList = new ArrayList<>(); private TabsManager tabsManager; private boolean hasTabsChanged = false; @@ -199,7 +190,8 @@ public class MainFragment extends BaseFragment implements TabLayout.OnTabSelecte } @Override - public void onTabUnselected(final TabLayout.Tab tab) { } + public void onTabUnselected(final TabLayout.Tab tab) { + } @Override public void onTabReselected(final TabLayout.Tab tab) { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java index d364c0c0f..c3f7d0e7c 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/DescriptionFragment.java @@ -1,25 +1,18 @@ package org.schabi.newpipe.fragments.detail; -import static android.text.TextUtils.isEmpty; -import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; -import static org.schabi.newpipe.extractor.utils.Utils.isBlank; -import static org.schabi.newpipe.util.Localization.getAppLocale; -import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; - import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.widget.TooltipCompat; import androidx.core.text.HtmlCompat; - import com.google.android.material.chip.Chip; - +import icepick.State; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.FragmentDescriptionBinding; @@ -32,14 +25,17 @@ import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.util.text.TextLinkifier; -import icepick.State; -import io.reactivex.rxjava3.disposables.CompositeDisposable; +import static android.text.TextUtils.isEmpty; +import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; +import static org.schabi.newpipe.extractor.utils.Utils.isBlank; +import static org.schabi.newpipe.util.Localization.getAppLocale; +import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; public class DescriptionFragment extends BaseFragment { + final CompositeDisposable descriptionDisposables = new CompositeDisposable(); @State StreamInfo streamInfo = null; - final CompositeDisposable descriptionDisposables = new CompositeDisposable(); FragmentDescriptionBinding binding; public DescriptionFragment() { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java index 5016a49f6..2db692ab4 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/StackItem.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.fragments.detail; import androidx.annotation.NonNull; - import org.schabi.newpipe.player.playqueue.PlayQueue; import java.io.Serializable; @@ -20,14 +19,6 @@ class StackItem implements Serializable { this.playQueue = playQueue; } - public void setUrl(final String url) { - this.url = url; - } - - public void setPlayQueue(final PlayQueue queue) { - this.playQueue = queue; - } - public int getServiceId() { return serviceId; } @@ -44,10 +35,18 @@ class StackItem implements Serializable { return url; } + public void setUrl(final String url) { + this.url = url; + } + public PlayQueue getPlayQueue() { return playQueue; } + public void setPlayQueue(final PlayQueue queue) { + this.playQueue = queue; + } + @NonNull @Override public String toString() { diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdapter.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdapter.java index 1a11836d4..cbccdc325 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/TabAdapter.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.fragments.detail; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java index 0fb137528..d67b45282 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailFragment.java @@ -1,26 +1,9 @@ package org.schabi.newpipe.fragments.detail; -import static android.text.TextUtils.isEmpty; -import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS; -import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; -import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; -import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; -import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; -import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; -import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams; -import static org.schabi.newpipe.util.NavigationHelper.openPlayQueue; -import static org.schabi.newpipe.util.NavigationHelper.playWithKore; - import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; +import android.content.*; import android.content.pm.ActivityInfo; import android.database.ContentObserver; import android.graphics.Color; @@ -34,17 +17,11 @@ import android.provider.Settings; import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.WindowManager; +import android.view.*; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; import android.widget.RelativeLayout; import android.widget.Toast; - import androidx.annotation.AttrRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -58,14 +35,17 @@ import androidx.core.view.WindowCompat; import androidx.core.view.WindowInsetsCompat; import androidx.core.view.WindowInsetsControllerCompat; import androidx.preference.PreferenceManager; - import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.bottomsheet.BottomSheetBehavior; import com.google.android.material.tabs.TabLayout; import com.squareup.picasso.Callback; - +import icepick.State; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.App; import org.schabi.newpipe.R; import org.schabi.newpipe.database.stream.model.StreamEntity; @@ -79,11 +59,7 @@ import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.stream.AudioStream; -import org.schabi.newpipe.extractor.stream.Stream; -import org.schabi.newpipe.extractor.stream.StreamInfo; -import org.schabi.newpipe.extractor.stream.StreamType; -import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.fragments.BackPressable; import org.schabi.newpipe.fragments.BaseStateFragment; import org.schabi.newpipe.fragments.EmptyFragment; @@ -104,33 +80,26 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.player.ui.MainPlayerUi; import org.schabi.newpipe.player.ui.VideoPlayerUi; -import org.schabi.newpipe.util.Constants; -import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.ListHelper; -import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PermissionHelper; -import org.schabi.newpipe.util.PicassoHelper; -import org.schabi.newpipe.util.StreamTypeUtil; -import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.util.*; import org.schabi.newpipe.util.external_communication.KoreUtils; import org.schabi.newpipe.util.external_communication.ShareUtils; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; -import icepick.State; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; +import static android.text.TextUtils.isEmpty; +import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.COMMENTS; +import static org.schabi.newpipe.extractor.stream.StreamExtractor.NO_AGE_LIMIT; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; +import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; +import static org.schabi.newpipe.player.helper.PlayerHelper.isClearingQueueConfirmationRequired; +import static org.schabi.newpipe.player.playqueue.PlayQueueItem.RECOVERY_UNSET; +import static org.schabi.newpipe.util.ExtractorHelper.showMetaInfoInTextView; +import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams; +import static org.schabi.newpipe.util.NavigationHelper.openPlayQueue; +import static org.schabi.newpipe.util.NavigationHelper.playWithKore; public final class VideoDetailFragment extends BaseStateFragment @@ -138,10 +107,6 @@ public final class VideoDetailFragment PlayerServiceExtendedEventListener, OnKeyDownListener { public static final String KEY_SWITCHING_PLAYERS = "switching_players"; - - private static final float MAX_OVERLAY_ALPHA = 0.9f; - private static final float MAX_PLAYER_HEIGHT = 0.7f; - public static final String ACTION_SHOW_MAIN_PLAYER = App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER"; public static final String ACTION_HIDE_MAIN_PLAYER = @@ -152,24 +117,50 @@ public final class VideoDetailFragment App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_VIDEO_FRAGMENT_RESUMED"; public static final String ACTION_VIDEO_FRAGMENT_STOPPED = App.PACKAGE_NAME + ".VideoDetailFragment.ACTION_VIDEO_FRAGMENT_STOPPED"; - + private static final float MAX_OVERLAY_ALPHA = 0.9f; + private static final float MAX_PLAYER_HEIGHT = 0.7f; private static final String COMMENTS_TAB_TAG = "COMMENTS"; private static final String RELATED_TAB_TAG = "NEXT VIDEO"; private static final String DESCRIPTION_TAB_TAG = "DESCRIPTION TAB"; private static final String EMPTY_TAB_TAG = "EMPTY TAB"; private static final String PICASSO_VIDEO_DETAILS_TAG = "PICASSO_VIDEO_DETAILS_TAG"; - + /** + * Stack that contains the "navigation history".
+ * The peek is the current video. + */ + private static LinkedList stack = new LinkedList<>(); + @AttrRes + @NonNull + final List tabIcons = new ArrayList<>(); + @StringRes + @NonNull + final List tabContentDescriptions = new ArrayList<>(); + @NonNull + private final CompositeDisposable disposables = new CompositeDisposable(); + private final PlayerHolder playerHolder = PlayerHolder.getInstance(); + @State + private int serviceId = Constants.NO_SERVICE_ID; + @State + @NonNull + private String title = ""; + @State + @Nullable + private String url = null; + @Nullable + private PlayQueue playQueue = null; + @State + private boolean autoPlayEnabled = true; + @State + int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED; + @State + int lastStableBottomSheetState = BottomSheetBehavior.STATE_EXPANDED; // tabs private boolean showComments; private boolean showRelatedItems; private boolean showDescription; private String selectedTabTag; - @AttrRes @NonNull final List tabIcons = new ArrayList<>(); - @StringRes @NonNull final List tabContentDescriptions = new ArrayList<>(); private boolean tabSettingsChanged = false; - private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates - private final SharedPreferences.OnSharedPreferenceChangeListener preferenceChangeListener = (sharedPreferences, key) -> { if (getString(R.string.show_comments_key).equals(key)) { @@ -183,49 +174,43 @@ public final class VideoDetailFragment tabSettingsChanged = true; } }; - - @State - protected int serviceId = Constants.NO_SERVICE_ID; - @State - @NonNull - protected String title = ""; - @State - @Nullable - protected String url = null; - @Nullable - protected PlayQueue playQueue = null; - @State - int bottomSheetState = BottomSheetBehavior.STATE_EXPANDED; - @State - int lastStableBottomSheetState = BottomSheetBehavior.STATE_EXPANDED; - @State - protected boolean autoPlayEnabled = true; - + private int lastAppBarVerticalOffset = Integer.MAX_VALUE; // prevents useless updates @Nullable private StreamInfo currentInfo = null; private Disposable currentWorker; - @NonNull - private final CompositeDisposable disposables = new CompositeDisposable(); @Nullable private Disposable positionSubscriber = null; - private BottomSheetBehavior bottomSheetBehavior; - private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback; - private BroadcastReceiver broadcastReceiver; /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ - + private BottomSheetBehavior.BottomSheetCallback bottomSheetCallback; + private BroadcastReceiver broadcastReceiver; private FragmentVideoDetailBinding binding; - private TabAdapter pageAdapter; - private ContentObserver settingsContentObserver; @Nullable private PlayerService playerService; private Player player; - private final PlayerHolder playerHolder = PlayerHolder.getInstance(); + + public static VideoDetailFragment getInstance(final int serviceId, + @Nullable final String videoUrl, + @NonNull final String name, + @Nullable final PlayQueue queue) { + final VideoDetailFragment instance = new VideoDetailFragment(); + instance.setInitialData(serviceId, videoUrl, name, queue); + return instance; + } + + + /*////////////////////////////////////////////////////////////////////////*/ + + public static VideoDetailFragment getInstanceInCollapsedState() { + final VideoDetailFragment instance = new VideoDetailFragment(); + instance.updateBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED); + return instance; + } /*////////////////////////////////////////////////////////////////////////// // Service management @@ -267,6 +252,11 @@ public final class VideoDetailFragment updateOverlayPlayQueueButtonVisibility(); } + + /*////////////////////////////////////////////////////////////////////////// + // Fragment's Lifecycle + //////////////////////////////////////////////////////////////////////////*/ + @Override public void onServiceDisconnected() { playerService = null; @@ -274,29 +264,6 @@ public final class VideoDetailFragment restoreDefaultBrightness(); } - - /*////////////////////////////////////////////////////////////////////////*/ - - public static VideoDetailFragment getInstance(final int serviceId, - @Nullable final String videoUrl, - @NonNull final String name, - @Nullable final PlayQueue queue) { - final VideoDetailFragment instance = new VideoDetailFragment(); - instance.setInitialData(serviceId, videoUrl, name, queue); - return instance; - } - - public static VideoDetailFragment getInstanceInCollapsedState() { - final VideoDetailFragment instance = new VideoDetailFragment(); - instance.updateBottomSheetState(BottomSheetBehavior.STATE_COLLAPSED); - return instance; - } - - - /*////////////////////////////////////////////////////////////////////////// - // Fragment's Lifecycle - //////////////////////////////////////////////////////////////////////////*/ - @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -422,28 +389,25 @@ public final class VideoDetailFragment binding = null; } - @Override - public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { - super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case ReCaptchaActivity.RECAPTCHA_REQUEST: - if (resultCode == Activity.RESULT_OK) { - NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), - serviceId, url, title, null, false); - } else { - Log.e(TAG, "ReCaptcha failed"); - } - break; - default: - Log.e(TAG, "Request code from activity not supported [" + requestCode + "]"); - break; - } - } - /*////////////////////////////////////////////////////////////////////////// // OnClick //////////////////////////////////////////////////////////////////////////*/ + @Override + public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == ReCaptchaActivity.RECAPTCHA_REQUEST) { + if (resultCode == Activity.RESULT_OK) { + NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), + serviceId, url, title, null, false); + } else { + Log.e(TAG, "ReCaptcha failed"); + } + } else { + Log.e(TAG, "Request code from activity not supported [" + requestCode + "]"); + } + } + private void setOnClickListeners() { binding.detailTitleRootLayout.setOnClickListener(v -> toggleTitleAndSecondaryControls()); binding.detailUploaderRootLayout.setOnClickListener(makeOnClickListener(info -> { @@ -574,6 +538,10 @@ public final class VideoDetailFragment } } + /*////////////////////////////////////////////////////////////////////////// + // Init + //////////////////////////////////////////////////////////////////////////*/ + private void toggleTitleAndSecondaryControls() { if (binding.detailSecondaryControlPanel.getVisibility() == View.GONE) { binding.detailVideoTitleView.setMaxLines(10); @@ -590,10 +558,6 @@ public final class VideoDetailFragment updateTabLayoutVisibility(); } - /*////////////////////////////////////////////////////////////////////////// - // Init - //////////////////////////////////////////////////////////////////////////*/ - @Override // called from onViewCreated in {@link BaseFragment#onViewCreated} protected void initViews(final View rootView, final Bundle savedInstanceState) { super.initViews(rootView, savedInstanceState); @@ -628,7 +592,7 @@ public final class VideoDetailFragment final View.OnTouchListener controlsTouchListener = (view, motionEvent) -> { if (motionEvent.getAction() == MotionEvent.ACTION_DOWN - && PreferenceManager.getDefaultSharedPreferences(activity) + && PreferenceManager.getDefaultSharedPreferences(activity) .getBoolean(getString(R.string.show_hold_to_append_key), true)) { animate(binding.touchAppendDetail, true, 250, AnimationType.ALPHA, 0, () -> @@ -656,6 +620,10 @@ public final class VideoDetailFragment } } + /*////////////////////////////////////////////////////////////////////////// + // OwnStack + //////////////////////////////////////////////////////////////////////////*/ + private void initThumbnailViews(@NonNull final StreamInfo info) { PicassoHelper.loadDetailsThumbnail(info.getThumbnailUrl()).tag(PICASSO_VIDEO_DETAILS_TAG) .into(binding.detailThumbnailImageView, new Callback() { @@ -677,16 +645,6 @@ public final class VideoDetailFragment .into(binding.detailUploaderThumbnailView); } - /*////////////////////////////////////////////////////////////////////////// - // OwnStack - //////////////////////////////////////////////////////////////////////////*/ - - /** - * Stack that contains the "navigation history".
- * The peek is the current video. - */ - private static LinkedList stack = new LinkedList<>(); - @Override public boolean onKeyDown(final int keyCode) { return isPlayerAvailable() @@ -817,7 +775,7 @@ public final class VideoDetailFragment } - protected void prepareAndLoadInfo() { + private void prepareAndLoadInfo() { scrollToTop(); startLoading(false); } @@ -1286,23 +1244,6 @@ public final class VideoDetailFragment binding.playerPlaceholder.requestLayout(); } - private final ViewTreeObserver.OnPreDrawListener preDrawListener = - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - final DisplayMetrics metrics = getResources().getDisplayMetrics(); - - if (getView() != null) { - final int height = (DeviceUtils.isInMultiWindow(activity) - ? requireView() - : activity.getWindow().getDecorView()).getHeight(); - setHeightThumbnail(height, metrics); - getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); - } - return false; - } - }; - /** * Method which controls the size of thumbnail and the size of main player inside * a layout with thumbnail. It decides what height the player should have in both @@ -1331,7 +1272,22 @@ public final class VideoDetailFragment : metrics.heightPixels / 2.0f); setHeightThumbnail(height, metrics); } - } + } private final ViewTreeObserver.OnPreDrawListener preDrawListener = + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + final DisplayMetrics metrics = getResources().getDisplayMetrics(); + + if (getView() != null) { + final int height = (DeviceUtils.isInMultiWindow(activity) + ? requireView() + : activity.getWindow().getDecorView()).getHeight(); + setHeightThumbnail(height, metrics); + getView().getViewTreeObserver().removeOnPreDrawListener(preDrawListener); + } + return false; + } + }; private void setHeightThumbnail(final int newHeight, final DisplayMetrics metrics) { binding.detailThumbnailImageView.setLayoutParams( @@ -1350,10 +1306,10 @@ public final class VideoDetailFragment binding.detailContentRootHiding.setVisibility(View.VISIBLE); } - protected void setInitialData(final int newServiceId, - @Nullable final String newUrl, - @NonNull final String newTitle, - @Nullable final PlayQueue newPlayQueue) { + private void setInitialData(final int newServiceId, + @Nullable final String newUrl, + @NonNull final String newTitle, + @Nullable final PlayQueue newPlayQueue) { this.serviceId = newServiceId; this.url = newUrl; this.title = newTitle; @@ -1422,11 +1378,6 @@ public final class VideoDetailFragment activity.registerReceiver(broadcastReceiver, intentFilter); } - - /*////////////////////////////////////////////////////////////////////////// - // Orientation listener - //////////////////////////////////////////////////////////////////////////*/ - private void restoreDefaultOrientation() { if (isPlayerAvailable() && player.videoPlayerSelected()) { toggleFullscreenIfInFullscreenMode(); @@ -1441,8 +1392,9 @@ public final class VideoDetailFragment } } + /*////////////////////////////////////////////////////////////////////////// - // Contract + // Orientation listener //////////////////////////////////////////////////////////////////////////*/ @Override @@ -1482,6 +1434,10 @@ public final class VideoDetailFragment binding.detailSubChannelThumbnailView.setImageBitmap(null); } + /*////////////////////////////////////////////////////////////////////////// + // Contract + //////////////////////////////////////////////////////////////////////////*/ + @Override public void handleResult(@NonNull final StreamInfo info) { super.handleResult(info); @@ -1670,10 +1626,6 @@ public final class VideoDetailFragment } } - /*////////////////////////////////////////////////////////////////////////// - // Stream Results - //////////////////////////////////////////////////////////////////////////*/ - private void updateProgressInfo(@NonNull final StreamInfo info) { if (positionSubscriber != null) { positionSubscriber.dispose(); @@ -1726,6 +1678,10 @@ public final class VideoDetailFragment }); } + /*////////////////////////////////////////////////////////////////////////// + // Stream Results + //////////////////////////////////////////////////////////////////////////*/ + private void showPlaybackProgress(final long progress, final long duration) { final int progressSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(progress); final int durationSeconds = (int) TimeUnit.MILLISECONDS.toSeconds(duration); @@ -1749,15 +1705,15 @@ public final class VideoDetailFragment } } - /*////////////////////////////////////////////////////////////////////////// - // Player event listener - //////////////////////////////////////////////////////////////////////////*/ - @Override public void onViewCreated() { tryAddVideoPlayerView(); } + /*////////////////////////////////////////////////////////////////////////// + // Player event listener + //////////////////////////////////////////////////////////////////////////*/ + @Override public void onQueueUpdate(final PlayQueue queue) { playQueue = queue; @@ -1806,16 +1762,14 @@ public final class VideoDetailFragment final PlaybackParameters parameters) { setOverlayPlayPauseImage(player != null && player.isPlaying()); - switch (state) { - case Player.STATE_PLAYING: - if (binding.positionView.getAlpha() != 1.0f - && player.getPlayQueue() != null - && player.getPlayQueue().getItem() != null - && player.getPlayQueue().getItem().getUrl().equals(url)) { - animate(binding.positionView, true, 100); - animate(binding.detailPositionView, true, 100); - } - break; + if (state == Player.STATE_PLAYING) { + if (binding.positionView.getAlpha() != 1.0f + && player.getPlayQueue() != null + && player.getPlayQueue().getItem() != null + && player.getPlayQueue().getItem().getUrl().equals(url)) { + animate(binding.positionView, true, 100); + animate(binding.detailPositionView, true, 100); + } } } @@ -1948,10 +1902,6 @@ public final class VideoDetailFragment valueAnimator.start(); } - /*////////////////////////////////////////////////////////////////////////// - // Player related utils - //////////////////////////////////////////////////////////////////////////*/ - private void showSystemUi() { if (DEBUG) { Log.d(TAG, "showSystemUi() called"); @@ -1974,6 +1924,10 @@ public final class VideoDetailFragment android.R.attr.colorPrimary)); } + /*////////////////////////////////////////////////////////////////////////// + // Player related utils + //////////////////////////////////////////////////////////////////////////*/ + private void hideSystemUi() { if (DEBUG) { Log.d(TAG, "hideSystemUi() called"); @@ -2197,10 +2151,6 @@ public final class VideoDetailFragment updateOverlayData(null, null, null); } - /*////////////////////////////////////////////////////////////////////////// - // Bottom mini player - //////////////////////////////////////////////////////////////////////////*/ - /** * That's for Android TV support. Move focus from main fragment to the player or back * based on what is currently selected @@ -2235,6 +2185,10 @@ public final class VideoDetailFragment } } + /*////////////////////////////////////////////////////////////////////////// + // Bottom mini player + //////////////////////////////////////////////////////////////////////////*/ + /** * When the mini player exists the view underneath it is not touchable. * Bottom padding should be equal to the mini player's height in this case @@ -2443,4 +2397,6 @@ public final class VideoDetailFragment lastStableBottomSheetState = newState; } } + + } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java index c816723ff..187fe1aa7 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/detail/VideoDetailPlayerCrasher.java @@ -1,9 +1,5 @@ package org.schabi.newpipe.fragments.detail; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_DECODING_FAILED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_UNSPECIFIED; - import android.content.Context; import android.util.Log; import android.util.Pair; @@ -13,15 +9,12 @@ import android.view.ViewGroup; import android.widget.RadioButton; import android.widget.RadioGroup; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; - import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ExoPlaybackException; import com.google.android.exoplayer2.PlaybackException; - import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ListRadioIconItemBinding; import org.schabi.newpipe.databinding.SingleChoiceDialogViewBinding; @@ -32,6 +25,8 @@ import java.io.IOException; import java.util.List; import java.util.function.Supplier; +import static com.google.android.exoplayer2.PlaybackException.*; + /** * Outsourced logic for crashing the player in the {@link VideoDetailFragment}. */ @@ -46,25 +41,25 @@ public final class VideoDetailPlayerCrasher { private static final List>> AVAILABLE_EXCEPTION_TYPES = List.of( - new Pair<>("Source", () -> ExoPlaybackException.createForSource( - new IOException(DEFAULT_MSG), - ERROR_CODE_BEHIND_LIVE_WINDOW - )), - new Pair<>("Renderer", () -> ExoPlaybackException.createForRenderer( - new Exception(DEFAULT_MSG), - "Dummy renderer", - 0, - null, - C.FORMAT_HANDLED, - /*isRecoverable=*/false, - ERROR_CODE_DECODING_FAILED - )), - new Pair<>("Unexpected", () -> ExoPlaybackException.createForUnexpected( - new RuntimeException(DEFAULT_MSG), - ERROR_CODE_UNSPECIFIED - )), - new Pair<>("Remote", () -> ExoPlaybackException.createForRemote(DEFAULT_MSG)) - ); + new Pair<>("Source", () -> ExoPlaybackException.createForSource( + new IOException(DEFAULT_MSG), + ERROR_CODE_BEHIND_LIVE_WINDOW + )), + new Pair<>("Renderer", () -> ExoPlaybackException.createForRenderer( + new Exception(DEFAULT_MSG), + "Dummy renderer", + 0, + null, + C.FORMAT_HANDLED, + /*isRecoverable=*/false, + ERROR_CODE_DECODING_FAILED + )), + new Pair<>("Unexpected", () -> ExoPlaybackException.createForUnexpected( + new RuntimeException(DEFAULT_MSG), + ERROR_CODE_UNSPECIFIED + )), + new Pair<>("Remote", () -> ExoPlaybackException.createForRemote(DEFAULT_MSG)) + ); private VideoDetailPlayerCrasher() { // No impls @@ -127,6 +122,7 @@ public final class VideoDetailPlayerCrasher { /** * Note that this method does not crash the underlying exoplayer directly (it's not possible). * It simply supplies a Exception to {@link Player#onPlayerError(PlaybackException)}. + * * @param player * @param exception */ diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java index 8a117a47a..d7eebc3fa 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListFragment.java @@ -1,8 +1,5 @@ package org.schabi.newpipe.fragments.list; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; - import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; @@ -11,14 +8,12 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.View; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.InfoItem; @@ -38,21 +33,22 @@ import java.util.List; import java.util.Queue; import java.util.function.Supplier; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateHideRecyclerViewAllowingScrolling; + public abstract class BaseListFragment extends BaseStateFragment implements ListViewContract, StateSaver.WriteRead, SharedPreferences.OnSharedPreferenceChangeListener { private static final int LIST_MODE_UPDATE_FLAG = 0x32; protected org.schabi.newpipe.util.SavedState savedState; - - private boolean useDefaultStateSaving = true; - private int updateFlags = 0; + protected InfoListAdapter infoListAdapter; + protected RecyclerView itemsList; /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ - - protected InfoListAdapter infoListAdapter; - protected RecyclerView itemsList; + private boolean useDefaultStateSaving = true; + private int updateFlags = 0; private int focusedPosition = -1; /*////////////////////////////////////////////////////////////////////////// @@ -366,13 +362,6 @@ public abstract class BaseListFragment extends BaseStateFragment }); } - class DefaultItemListOnScrolledDownListener extends OnScrollBelowItemsListener { - @Override - public void onScrolledDown(final RecyclerView recyclerView) { - onScrollToBottom(); - } - } - private void onStreamSelected(final StreamInfoItem selectedItem) { onItemSelected(selectedItem); NavigationHelper.openVideoDetailFragment(requireContext(), getFM(), @@ -394,10 +383,6 @@ public abstract class BaseListFragment extends BaseStateFragment } } - /*////////////////////////////////////////////////////////////////////////// - // Menu - //////////////////////////////////////////////////////////////////////////*/ - @Override public void onCreateOptionsMenu(@NonNull final Menu menu, @NonNull final MenuInflater inflater) { @@ -414,7 +399,7 @@ public abstract class BaseListFragment extends BaseStateFragment } /*////////////////////////////////////////////////////////////////////////// - // Load and handle + // Menu //////////////////////////////////////////////////////////////////////////*/ @Override @@ -423,20 +408,24 @@ public abstract class BaseListFragment extends BaseStateFragment super.startLoading(forceLoad); } + /*////////////////////////////////////////////////////////////////////////// + // Load and handle + //////////////////////////////////////////////////////////////////////////*/ + protected abstract void loadMoreItems(); protected abstract boolean hasMoreItems(); - /*////////////////////////////////////////////////////////////////////////// - // Contract - //////////////////////////////////////////////////////////////////////////*/ - @Override public void showLoading() { super.showLoading(); animateHideRecyclerViewAllowingScrolling(itemsList); } + /*////////////////////////////////////////////////////////////////////////// + // Contract + //////////////////////////////////////////////////////////////////////////*/ + @Override public void hideLoading() { super.hideLoading(); @@ -481,9 +470,17 @@ public abstract class BaseListFragment extends BaseStateFragment /** * Returns preferred item view mode. + * * @return ItemViewMode */ protected ItemViewMode getItemViewMode() { return ThemeHelper.getItemViewMode(requireContext()); } + + class DefaultItemListOnScrolledDownListener extends OnScrollBelowItemsListener { + @Override + public void onScrolledDown(final RecyclerView recyclerView) { + onScrollToBottom(); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java index 35424437d..f8b43a1be 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/BaseListInfoFragment.java @@ -4,9 +4,12 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.view.View; - import androidx.annotation.NonNull; - +import icepick.State; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.InfoItem; @@ -22,22 +25,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Queue; -import icepick.State; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; - public abstract class BaseListInfoFragment> extends BaseListFragment> { + private final UserAction errorUserAction; @State protected int serviceId = Constants.NO_SERVICE_ID; @State protected String name; @State protected String url; - - private final UserAction errorUserAction; protected L currentInfo; protected Page currentNextPage; protected Disposable currentWorker; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java index 8ed9389c3..631aabd8a 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/channel/ChannelFragment.java @@ -1,31 +1,28 @@ package org.schabi.newpipe.fragments.list.channel; -import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; - import android.content.Context; import android.graphics.Color; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; import android.util.TypedValue; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.Button; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; import androidx.core.content.ContextCompat; - import com.google.android.material.snackbar.Snackbar; import com.jakewharton.rxbinding4.view.RxView; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.functions.Action; +import io.reactivex.rxjava3.functions.Consumer; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.NotificationMode; import org.schabi.newpipe.database.subscription.SubscriptionEntity; @@ -41,16 +38,12 @@ import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.fragments.list.BaseListInfoFragment; import org.schabi.newpipe.ktx.AnimationType; -import org.schabi.newpipe.local.subscription.SubscriptionManager; import org.schabi.newpipe.local.feed.notifications.NotificationHelper; +import org.schabi.newpipe.local.subscription.SubscriptionManager; import org.schabi.newpipe.player.PlayerType; import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueue; -import org.schabi.newpipe.util.ExtractorHelper; -import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PicassoHelper; -import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.util.*; import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.List; @@ -58,15 +51,9 @@ import java.util.concurrent.TimeUnit; import java.util.function.Supplier; import java.util.stream.Collectors; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.functions.Action; -import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.schedulers.Schedulers; +import static org.schabi.newpipe.ktx.TextViewUtils.animateTextColor; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateBackgroundColor; public class ChannelFragment extends BaseListInfoFragment implements View.OnClickListener { @@ -92,6 +79,10 @@ public class ChannelFragment extends BaseListInfoFragment { private final CompositeDisposable disposables = new CompositeDisposable(); private TextView emptyStateDesc; + public CommentsFragment() { + super(UserAction.REQUESTED_COMMENTS); + } + public static CommentsFragment getInstance(final int serviceId, final String url, final String name) { final CommentsFragment instance = new CommentsFragment(); @@ -36,10 +33,6 @@ public class CommentsFragment extends BaseListInfoFragment @@ -65,6 +58,10 @@ public class KioskFragment extends BaseListInfoFragment { @@ -84,6 +75,10 @@ public class PlaylistFragment extends BaseListInfoFragment> implements BackPressable { @@ -103,53 +80,40 @@ public class SearchFragment extends BaseListFragment suggestionPublisher = PublishSubject.create(); - - @State - int filterItemCheckedId = -1; - + private final SparseArrayCompat menuItemToFilterName = new SparseArrayCompat<>(); + private final CompositeDisposable disposables = new CompositeDisposable(); @State protected int serviceId = Constants.NO_SERVICE_ID; - + @State + int filterItemCheckedId = -1; // these three represents the current search query @State String searchString; - /** * No content filter should add like contentFilter = all * be aware of this when implementing an extractor. */ @State String[] contentFilter = new String[0]; - @State String sortFilter; - // these represents the last search @State String lastSearchedString; - @State String searchSuggestion; - @State boolean isCorrectedSearch; - @State MetaInfo[] metaInfo; - @State boolean wasSearchFocused = false; - - private final SparseArrayCompat menuItemToFilterName = new SparseArrayCompat<>(); private StreamingService service; private Page nextPage; private boolean showLocalSuggestions = true; private boolean showRemoteSuggestions = true; - private Disposable searchDisposable; private Disposable suggestionDisposable; - private final CompositeDisposable disposables = new CompositeDisposable(); - private SuggestionListAdapter suggestionListAdapter; private HistoryRecordManager historyRecordManager; @@ -717,9 +681,9 @@ public class SearchFragment extends BaseListFragment - searchHistoryEntries.stream() - .map(entry -> new SuggestionItem(true, entry)) - .collect(Collectors.toList())); + searchHistoryEntries.stream() + .map(entry -> new SuggestionItem(true, entry)) + .collect(Collectors.toList())); } private Observable> getRemoteSuggestionsObservable(final String query) { @@ -754,14 +718,14 @@ public class SearchFragment extends BaseListFragment { - remote.removeIf(remoteItem -> local.stream().anyMatch( - localItem -> localItem.equals(remoteItem))); - local.addAll(remote); - return local; - }) + getLocalSuggestionsObservable(query, 3), + getRemoteSuggestionsObservable(query), + (local, remote) -> { + remote.removeIf(remoteItem -> local.stream().anyMatch( + localItem -> localItem.equals(remoteItem))); + local.addAll(remote); + return local; + }) .materialize(); } else if (showLocalSuggestions) { return getLocalSuggestionsObservable(query, 25) @@ -786,12 +750,12 @@ public class SearchFragment extends BaseListFragment showSnackBarError(new ErrorInfo( - throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId))); + throwable, UserAction.GET_SUGGESTIONS, searchString, serviceId))); } @Override @@ -839,7 +803,8 @@ public class SearchFragment extends BaseListFragment { }, + ignored -> { + }, throwable -> showSnackBarError(new ErrorInfo(throwable, UserAction.SEARCHED, theSearchString, serviceId)) )); @@ -855,9 +820,9 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) @@ -876,11 +841,11 @@ public class SearchFragment extends BaseListFragment isLoading.set(false)) diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionItem.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionItem.java index 83f68dbb5..a2e21e6b0 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionItem.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionItem.java @@ -3,8 +3,8 @@ package org.schabi.newpipe.fragments.list.search; import androidx.annotation.NonNull; public class SuggestionItem { - final boolean fromHistory; public final String query; + final boolean fromHistory; public SuggestionItem(final boolean fromHistory, final String query) { this.fromHistory = fromHistory; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java index 856ba22f1..54658d358 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/search/SuggestionListAdapter.java @@ -2,12 +2,10 @@ package org.schabi.newpipe.fragments.list.search; import android.view.LayoutInflater; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ItemSearchSuggestionBinding; diff --git a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java index 65664e0e3..6b475f5d3 100644 --- a/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/fragments/list/videos/RelatedItemsFragment.java @@ -2,16 +2,11 @@ package org.schabi.newpipe.fragments.list.videos; import android.content.SharedPreferences; import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.View; -import android.view.ViewGroup; - +import android.view.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.preference.PreferenceManager; - +import io.reactivex.rxjava3.core.Single; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.RelatedItemsHeaderBinding; import org.schabi.newpipe.error.UserAction; @@ -26,8 +21,6 @@ import org.schabi.newpipe.util.RelatedItemInfo; import java.io.Serializable; import java.util.function.Supplier; -import io.reactivex.rxjava3.core.Single; - public class RelatedItemsFragment extends BaseListInfoFragment implements SharedPreferences.OnSharedPreferenceChangeListener { private static final String INFO_KEY = "related_info_key"; @@ -40,16 +33,16 @@ public class RelatedItemsFragment extends BaseListInfoFragment - entries.get(index).action.onClick(fragment, info); + entries.get(index).action.onClick(fragment, info); dialog = new AlertDialog.Builder(activity) .setCustomTitle(bannerView) @@ -95,11 +93,16 @@ public final class InfoItemDialog { * {@link #setAction(StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)}. */ public static class Builder { - @NonNull private final Activity activity; - @NonNull private final Context context; - @NonNull private final StreamInfoItem infoItem; - @NonNull private final Fragment fragment; - @NonNull private final List entries = new ArrayList<>(); + @NonNull + private final Activity activity; + @NonNull + private final Context context; + @NonNull + private final StreamInfoItem infoItem; + @NonNull + private final Fragment fragment; + @NonNull + private final List entries = new ArrayList<>(); private final boolean addDefaultEntriesAutomatically; /** @@ -132,9 +135,9 @@ public final class InfoItemDialog { * @param context * @param fragment * @param infoItem the item for this dialog; all entries and their actions work with - * this {@link StreamInfoItem} + * this {@link StreamInfoItem} * @throws IllegalArgumentException if activity, context - * or resources is null + * or resources is null */ public Builder(final Activity activity, final Context context, @@ -173,15 +176,14 @@ public final class InfoItemDialog { * @param context * @param fragment * @param infoItem - * @param addDefaultEntriesAutomatically - * whether default entries added with {@link #addDefaultBeginningEntries()} - * and {@link #addDefaultEndEntries()} are added automatically when generating - * the {@link InfoItemDialog}. - *
- * Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and - * {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between. + * @param addDefaultEntriesAutomatically whether default entries added with {@link #addDefaultBeginningEntries()} + * and {@link #addDefaultEndEntries()} are added automatically when generating + * the {@link InfoItemDialog}. + *
+ * Entries added with {@link #addEntry(StreamDialogDefaultEntry)} and + * {@link #addAllEntries(StreamDialogDefaultEntry...)} are added in between. * @throws IllegalArgumentException if activity, context - * or resources is null + * or resources is null */ public Builder(final Activity activity, final Context context, @@ -205,8 +207,18 @@ public final class InfoItemDialog { } } + public static void reportErrorDuringInitialization(final Throwable throwable, + final InfoItem item) { + ErrorUtil.showSnackbar(App.getApp().getBaseContext(), new ErrorInfo( + throwable, + UserAction.OPEN_INFO_ITEM_DIALOG, + "none", + item.getServiceId())); + } + /** * Adds a new entry and appends it to the current entry list. + * * @param entry the entry to add * @return the current {@link Builder} instance */ @@ -217,6 +229,7 @@ public final class InfoItemDialog { /** * Adds new entries. These are appended to the current entry list. + * * @param newEntries the entries to add * @return the current {@link Builder} instance */ @@ -230,12 +243,13 @@ public final class InfoItemDialog { *

Warning: Only use this method when the entry has been already added. * Changing the action of an entry which has not been added to the Builder yet * does not have an effect.

- * @param entry the entry to change + * + * @param entry the entry to change * @param action the action to perform when the entry is selected * @return the current {@link Builder} instance */ public Builder setAction(@NonNull final StreamDialogDefaultEntry entry, - @NonNull final StreamDialogEntry.StreamDialogEntryAction action) { + @NonNull final StreamDialogEntry.StreamDialogEntryAction action) { for (int i = 0; i < entries.size(); i++) { if (entries.get(i).resource == entry.resource) { entries.set(i, new StreamDialogEntry(entry.resource, action)); @@ -249,6 +263,7 @@ public final class InfoItemDialog { * Adds {@link StreamDialogDefaultEntry#ENQUEUE} if the player is open and * {@link StreamDialogDefaultEntry#ENQUEUE_NEXT} if there are multiple streams * in the play queue. + * * @return the current {@link Builder} instance */ public Builder addEnqueueEntriesIfNeeded() { @@ -267,6 +282,7 @@ public final class InfoItemDialog { * Adds the {@link StreamDialogDefaultEntry#START_HERE_ON_BACKGROUND}. * If the {@link #infoItem} is not a pure audio (live) stream, * {@link StreamDialogDefaultEntry#START_HERE_ON_POPUP} is added, too. + * * @return the current {@link Builder} instance */ public Builder addStartHereEntries() { @@ -280,6 +296,7 @@ public final class InfoItemDialog { /** * Adds {@link StreamDialogDefaultEntry#MARK_AS_WATCHED} if the watch history is enabled * and the stream is not a livestream. + * * @return the current {@link Builder} instance */ public Builder addMarkAsWatchedEntryIfNeeded() { @@ -294,6 +311,7 @@ public final class InfoItemDialog { /** * Adds the {@link StreamDialogDefaultEntry#PLAY_WITH_KODI} entry if it is needed. + * * @return the current {@link Builder} instance */ public Builder addPlayWithKodiEntryIfNeeded() { @@ -308,6 +326,7 @@ public final class InfoItemDialog { *
* This method adds the "enqueue" (see {@link #addEnqueueEntriesIfNeeded()}) * and "start here" (see {@link #addStartHereEntries()} entries. + * * @return the current {@link Builder} instance */ public Builder addDefaultBeginningEntries() { @@ -318,6 +337,7 @@ public final class InfoItemDialog { /** * Add the entries which are usually at the bottom of the action list. + * * @return the current {@link Builder} instance */ public Builder addDefaultEndEntries() { @@ -335,6 +355,7 @@ public final class InfoItemDialog { /** * Creates the {@link InfoItemDialog}. + * * @return a new instance of {@link InfoItemDialog} */ public InfoItemDialog create() { @@ -343,14 +364,5 @@ public final class InfoItemDialog { } return new InfoItemDialog(this.activity, this.fragment, this.infoItem, this.entries); } - - public static void reportErrorDuringInitialization(final Throwable throwable, - final InfoItem item) { - ErrorUtil.showSnackbar(App.getApp().getBaseContext(), new ErrorInfo( - throwable, - UserAction.OPEN_INFO_ITEM_DIALOG, - "none", - item.getServiceId())); - } } } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java index c67880d0e..1b79588b9 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogDefaultEntry.java @@ -1,15 +1,9 @@ package org.schabi.newpipe.info_list.dialog; -import static org.schabi.newpipe.util.NavigationHelper.openChannelFragment; -import static org.schabi.newpipe.util.SparseItemUtil.fetchItemInfoIfSparse; -import static org.schabi.newpipe.util.SparseItemUtil.fetchStreamInfoAndSaveToDatabase; -import static org.schabi.newpipe.util.SparseItemUtil.fetchUploaderUrlIfSparse; - import android.net.Uri; - import androidx.annotation.NonNull; import androidx.annotation.StringRes; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import org.schabi.newpipe.R; import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.download.DownloadDialog; @@ -22,22 +16,23 @@ import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.List; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import static org.schabi.newpipe.util.NavigationHelper.openChannelFragment; +import static org.schabi.newpipe.util.SparseItemUtil.*; /** *

- * This enum provides entries that are accepted - * by the {@link InfoItemDialog.Builder}. + * This enum provides entries that are accepted + * by the {@link InfoItemDialog.Builder}. *

*

- * These entries contain a String {@link #resource} which is displayed in the dialog and - * a default {@link #action} that is executed - * when the entry is selected (via onClick()). - *
- * They action can be overridden by using the Builder's - * {@link InfoItemDialog.Builder#setAction( - * StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)} - * method. + * These entries contain a String {@link #resource} which is displayed in the dialog and + * a default {@link #action} that is executed + * when the entry is selected (via onClick()). + *
+ * They action can be overridden by using the Builder's + * {@link InfoItemDialog.Builder#setAction( + *StreamDialogDefaultEntry, StreamDialogEntry.StreamDialogEntryAction)} + * method. *

*/ public enum StreamDialogDefaultEntry { @@ -51,7 +46,7 @@ public enum StreamDialogDefaultEntry { */ ENQUEUE(R.string.enqueue_stream, (fragment, item) -> fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> - NavigationHelper.enqueueOnPlayer(fragment.getContext(), singlePlayQueue)) + NavigationHelper.enqueueOnPlayer(fragment.getContext(), singlePlayQueue)) ), /** @@ -60,17 +55,17 @@ public enum StreamDialogDefaultEntry { */ ENQUEUE_NEXT(R.string.enqueue_next_stream, (fragment, item) -> fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> - NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), singlePlayQueue)) + NavigationHelper.enqueueNextOnPlayer(fragment.getContext(), singlePlayQueue)) ), START_HERE_ON_BACKGROUND(R.string.start_here_on_background, (fragment, item) -> fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> - NavigationHelper.playOnBackgroundPlayer( - fragment.getContext(), singlePlayQueue, true))), + NavigationHelper.playOnBackgroundPlayer( + fragment.getContext(), singlePlayQueue, true))), START_HERE_ON_POPUP(R.string.start_here_on_popup, (fragment, item) -> fetchItemInfoIfSparse(fragment.requireContext(), item, singlePlayQueue -> - NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))), + NavigationHelper.playOnPopupPlayer(fragment.getContext(), singlePlayQueue, true))), SET_AS_PLAYLIST_THUMBNAIL(R.string.set_as_playlist_thumbnail, (fragment, item) -> { throw new UnsupportedOperationException("This needs to be implemented manually " @@ -87,16 +82,16 @@ public enum StreamDialogDefaultEntry { * or create a new playlist if there are no local playlists. */ APPEND_PLAYLIST(R.string.add_to_playlist, (fragment, item) -> - PlaylistDialog.createCorrespondingDialog( - fragment.getContext(), - List.of(new StreamEntity(item)), - dialog -> dialog.show( - fragment.getParentFragmentManager(), - "StreamDialogEntry@" - + (dialog instanceof PlaylistAppendDialog ? "append" : "create") - + "_playlist" - ) - ) + PlaylistDialog.createCorrespondingDialog( + fragment.getContext(), + List.of(new StreamEntity(item)), + dialog -> dialog.show( + fragment.getParentFragmentManager(), + "StreamDialogEntry@" + + (dialog instanceof PlaylistAppendDialog ? "append" : "create") + + "_playlist" + ) + ) ), PLAY_WITH_KODI(R.string.play_with_kodi_title, (fragment, item) -> { @@ -133,11 +128,11 @@ public enum StreamDialogDefaultEntry { MARK_AS_WATCHED(R.string.mark_as_watched, (fragment, item) -> - new HistoryRecordManager(fragment.getContext()) - .markAsWatched(item) - .onErrorComplete() - .observeOn(AndroidSchedulers.mainThread()) - .subscribe() + new HistoryRecordManager(fragment.getContext()) + .markAsWatched(item) + .onErrorComplete() + .observeOn(AndroidSchedulers.mainThread()) + .subscribe() ); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java index 9d82e3b58..580a1d1e6 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/dialog/StreamDialogEntry.java @@ -1,11 +1,9 @@ package org.schabi.newpipe.info_list.dialog; import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.fragment.app.Fragment; - import org.schabi.newpipe.extractor.stream.StreamInfoItem; public class StreamDialogEntry { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelGridInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelGridInfoItemHolder.java index a4755052e..352e9ad90 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelGridInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelGridInfoItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.InfoItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelInfoItemHolder.java index f8133d3de..69efac512 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelInfoItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.InfoItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java index c625c3c5c..af3a88a26 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/ChannelMiniInfoItemHolder.java @@ -4,17 +4,15 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import androidx.annotation.Nullable; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.channel.ChannelInfoItem; import org.schabi.newpipe.extractor.utils.Utils; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.PicassoHelper; public class ChannelMiniInfoItemHolder extends InfoItemHolder { private final ImageView itemThumbnailView; @@ -40,10 +38,9 @@ public class ChannelMiniInfoItemHolder extends InfoItemHolder { @Override public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) { - if (!(infoItem instanceof ChannelInfoItem)) { + if (!(infoItem instanceof final ChannelInfoItem item)) { return; } - final ChannelInfoItem item = (ChannelInfoItem) infoItem; itemTitleView.setText(item.getName()); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsInfoItemHolder.java index 4fc2d9f84..6d440d89f 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsInfoItemHolder.java @@ -4,7 +4,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.comments.CommentsInfoItem; @@ -49,10 +48,9 @@ public class CommentsInfoItemHolder extends CommentsMiniInfoItemHolder { final HistoryRecordManager historyRecordManager) { super.updateFromItem(infoItem, historyRecordManager); - if (!(infoItem instanceof CommentsInfoItem)) { + if (!(infoItem instanceof final CommentsInfoItem item)) { return; } - final CommentsInfoItem item = (CommentsInfoItem) infoItem; itemTitleView.setText(item.getUploaderName()); diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java index 799aee8ba..ca8f73a2a 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/CommentsMiniInfoItemHolder.java @@ -11,11 +11,10 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.RelativeLayout; import android.widget.TextView; - import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.core.text.HtmlCompat; - +import io.reactivex.rxjava3.disposables.CompositeDisposable; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.InfoItem; @@ -37,8 +36,6 @@ import org.schabi.newpipe.util.text.TextLinkifier; import java.util.function.Consumer; -import io.reactivex.rxjava3.disposables.CompositeDisposable; - public class CommentsMiniInfoItemHolder extends InfoItemHolder { private static final String TAG = "CommentsMiniIIHolder"; private static final String ELLIPSIS = "…"; @@ -91,10 +88,9 @@ public class CommentsMiniInfoItemHolder extends InfoItemHolder { @Override public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) { - if (!(infoItem instanceof CommentsInfoItem)) { + if (!(infoItem instanceof final CommentsInfoItem item)) { return; } - final CommentsInfoItem item = (CommentsInfoItem) infoItem; PicassoHelper.loadAvatar(item.getUploaderAvatarUrl()).into(itemThumbnailView); if (PicassoHelper.getShouldLoadImages()) { diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/InfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/InfoItemHolder.java index 9e1561786..6aaeb23c4 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/InfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/InfoItemHolder.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.info_list.holder; import android.view.LayoutInflater; import android.view.ViewGroup; - import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; @@ -42,5 +40,6 @@ public abstract class InfoItemHolder extends RecyclerView.ViewHolder { HistoryRecordManager historyRecordManager); public void updateState(final InfoItem infoItem, - final HistoryRecordManager historyRecordManager) { } + final HistoryRecordManager historyRecordManager) { + } } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistCardInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistCardInfoItemHolder.java index f1682b4e4..283781a0a 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistCardInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistCardInfoItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.InfoItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistGridInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistGridInfoItemHolder.java index 1cb69208b..beb4a5817 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistGridInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistGridInfoItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.InfoItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java index 7691a377d..e9c20024d 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistInfoItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.InfoItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java index bf5f57db3..fc31b7fc8 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/PlaylistMiniInfoItemHolder.java @@ -3,20 +3,19 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem; import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.PicassoHelper; public class PlaylistMiniInfoItemHolder extends InfoItemHolder { public final ImageView itemThumbnailView; - private final TextView itemStreamCountView; public final TextView itemTitleView; public final TextView itemUploaderView; + private final TextView itemStreamCountView; public PlaylistMiniInfoItemHolder(final InfoItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { @@ -36,10 +35,9 @@ public class PlaylistMiniInfoItemHolder extends InfoItemHolder { @Override public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) { - if (!(infoItem instanceof PlaylistInfoItem)) { + if (!(infoItem instanceof final PlaylistInfoItem item)) { return; } - final PlaylistInfoItem item = (PlaylistInfoItem) infoItem; itemTitleView.setText(item.getName()); itemStreamCountView.setText(Localization diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamCardInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamCardInfoItemHolder.java index 807bad6e0..38d540694 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamCardInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamCardInfoItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.InfoItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamGridInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamGridInfoItemHolder.java index 8e4a1914e..d87d8dd25 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamGridInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamGridInfoItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.info_list.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.info_list.InfoItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java index a84c98404..7c8eda953 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamInfoItemHolder.java @@ -3,7 +3,7 @@ package org.schabi.newpipe.info_list.holder; import android.text.TextUtils; import android.view.ViewGroup; import android.widget.TextView; - +import androidx.preference.PreferenceManager; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItem; @@ -12,8 +12,6 @@ import org.schabi.newpipe.info_list.InfoItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.Localization; -import androidx.preference.PreferenceManager; - import static org.schabi.newpipe.MainActivity.DEBUG; /* @@ -58,10 +56,9 @@ public class StreamInfoItemHolder extends StreamMiniInfoItemHolder { final HistoryRecordManager historyRecordManager) { super.updateFromItem(infoItem, historyRecordManager); - if (!(infoItem instanceof StreamInfoItem)) { + if (!(infoItem instanceof final StreamInfoItem item)) { return; } - final StreamInfoItem item = (StreamInfoItem) infoItem; itemAdditionalDetails.setText(getStreamInfoDetailLine(item)); } diff --git a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java index 8d17017d2..7ff106fad 100644 --- a/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/info_list/holder/StreamMiniInfoItemHolder.java @@ -4,9 +4,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import androidx.core.content.ContextCompat; - import org.schabi.newpipe.R; import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.extractor.InfoItem; @@ -46,10 +44,9 @@ public class StreamMiniInfoItemHolder extends InfoItemHolder { @Override public void updateFromItem(final InfoItem infoItem, final HistoryRecordManager historyRecordManager) { - if (!(infoItem instanceof StreamInfoItem)) { + if (!(infoItem instanceof final StreamInfoItem item)) { return; } - final StreamInfoItem item = (StreamInfoItem) infoItem; itemVideoTitleView.setText(item.getName()); itemUploaderView.setText(item.getUploaderName()); diff --git a/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java b/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java index 53fe1677b..5e618df4b 100644 --- a/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/BaseLocalListFragment.java @@ -7,7 +7,6 @@ import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.View; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.ActionBar; @@ -17,7 +16,6 @@ import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; - import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.PignateFooterBinding; import org.schabi.newpipe.fragments.BaseStateFragment; @@ -48,10 +46,10 @@ public abstract class BaseLocalListFragment extends BaseStateFragment //////////////////////////////////////////////////////////////////////////*/ private static final int LIST_MODE_UPDATE_FLAG = 0x32; - private ViewBinding headerRootBinding; - private ViewBinding footerRootBinding; protected LocalItemListAdapter itemListAdapter; protected RecyclerView itemsList; + private ViewBinding headerRootBinding; + private ViewBinding footerRootBinding; private int updateFlags = 0; /*////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/local/HeaderFooterHolder.java b/app/src/main/java/org/schabi/newpipe/local/HeaderFooterHolder.java index 5aac75119..9e1062af7 100644 --- a/app/src/main/java/org/schabi/newpipe/local/HeaderFooterHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/HeaderFooterHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local; import android.view.View; - import androidx.recyclerview.widget.RecyclerView; public class HeaderFooterHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java index 041d16d43..d7c7171f9 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemBuilder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local; import android.content.Context; - import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.util.OnClickGesture; diff --git a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java index b9409cb9d..a705293bd 100644 --- a/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/LocalItemListAdapter.java @@ -4,29 +4,15 @@ import android.content.Context; import android.util.Log; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.model.StreamStateEntity; import org.schabi.newpipe.info_list.ItemViewMode; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.local.holder.LocalItemHolder; -import org.schabi.newpipe.local.holder.LocalPlaylistCardItemHolder; -import org.schabi.newpipe.local.holder.LocalPlaylistGridItemHolder; -import org.schabi.newpipe.local.holder.LocalPlaylistItemHolder; -import org.schabi.newpipe.local.holder.LocalPlaylistStreamCardItemHolder; -import org.schabi.newpipe.local.holder.LocalPlaylistStreamGridItemHolder; -import org.schabi.newpipe.local.holder.LocalPlaylistStreamItemHolder; -import org.schabi.newpipe.local.holder.LocalStatisticStreamCardItemHolder; -import org.schabi.newpipe.local.holder.LocalStatisticStreamGridItemHolder; -import org.schabi.newpipe.local.holder.LocalStatisticStreamItemHolder; -import org.schabi.newpipe.local.holder.RemotePlaylistCardItemHolder; -import org.schabi.newpipe.local.holder.RemotePlaylistGridItemHolder; -import org.schabi.newpipe.local.holder.RemotePlaylistItemHolder; +import org.schabi.newpipe.local.holder.*; import org.schabi.newpipe.util.FallbackViewHolder; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.OnClickGesture; diff --git a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java index e2f493a8a..93e5f30d0 100644 --- a/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/bookmark/BookmarkFragment.java @@ -8,12 +8,16 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.FragmentManager; - +import icepick.State; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.NewPipeDatabase; @@ -35,16 +39,9 @@ import org.schabi.newpipe.util.OnClickGesture; import java.util.ArrayList; import java.util.List; -import icepick.State; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; - public final class BookmarkFragment extends BaseLocalListFragment, Void> { @State - protected Parcelable itemsListState; + private Parcelable itemsListState; private Subscription databaseSubscription; private CompositeDisposable disposables = new CompositeDisposable(); @@ -105,13 +102,11 @@ public final class BookmarkFragment extends BaseLocalListFragment streamEntities) { @@ -101,7 +96,9 @@ public final class PlaylistAppendDialog extends PlaylistDialog { // Helper //////////////////////////////////////////////////////////////////////////*/ - /** Display create playlist dialog. */ + /** + * Display create playlist dialog. + */ public void openCreatePlaylistDialog() { if (getStreamEntities() == null || !isAdded()) { return; diff --git a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java index 0d5cfac23..676bf7a96 100644 --- a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java +++ b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistCreationDialog.java @@ -4,11 +4,10 @@ import android.app.Dialog; import android.os.Bundle; import android.text.InputType; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog.Builder; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.stream.model.StreamEntity; @@ -18,14 +17,12 @@ import org.schabi.newpipe.util.ThemeHelper; import java.util.List; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; - public final class PlaylistCreationDialog extends PlaylistDialog { /** * Create a new instance of {@link PlaylistCreationDialog}. * - * @param streamEntities a list of {@link StreamEntity} to be added to playlists + * @param streamEntities a list of {@link StreamEntity} to be added to playlists * @return a new instance of {@link PlaylistCreationDialog} */ public static PlaylistCreationDialog newInstance(final List streamEntities) { diff --git a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistDialog.java b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistDialog.java index 612c38181..20bf8498a 100644 --- a/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistDialog.java +++ b/app/src/main/java/org/schabi/newpipe/local/dialog/PlaylistDialog.java @@ -5,12 +5,12 @@ import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; import android.view.Window; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.FragmentManager; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.Disposable; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.database.stream.model.StreamEntity; import org.schabi.newpipe.local.playlist.LocalPlaylistManager; @@ -24,9 +24,6 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.disposables.Disposable; - public abstract class PlaylistDialog extends DialogFragment implements StateSaver.WriteRead { @Nullable @@ -40,95 +37,6 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave // LifeCycle //////////////////////////////////////////////////////////////////////////*/ - @Override - public void onCreate(@Nullable final Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - savedState = StateSaver.tryToRestore(savedInstanceState, this); - } - - @Override - public void onDestroy() { - super.onDestroy(); - StateSaver.onDestroy(savedState); - } - - public List getStreamEntities() { - return streamEntities; - } - - @NonNull - @Override - public Dialog onCreateDialog(final Bundle savedInstanceState) { - final Dialog dialog = super.onCreateDialog(savedInstanceState); - //remove title - final Window window = dialog.getWindow(); - if (window != null) { - window.requestFeature(Window.FEATURE_NO_TITLE); - } - return dialog; - } - - @Override - public void onDismiss(@NonNull final DialogInterface dialog) { - super.onDismiss(dialog); - if (onDismissListener != null) { - onDismissListener.onDismiss(dialog); - } - } - - /*////////////////////////////////////////////////////////////////////////// - // State Saving - //////////////////////////////////////////////////////////////////////////*/ - - @Override - public String generateSuffix() { - final int size = streamEntities == null ? 0 : streamEntities.size(); - return "." + size + ".list"; - } - - @Override - public void writeTo(final Queue objectsToSave) { - objectsToSave.add(streamEntities); - } - - @Override - @SuppressWarnings("unchecked") - public void readFrom(@NonNull final Queue savedObjects) { - streamEntities = (List) savedObjects.poll(); - } - - @Override - public void onSaveInstanceState(@NonNull final Bundle outState) { - super.onSaveInstanceState(outState); - if (getActivity() != null) { - savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(), - savedState, outState, this); - } - } - - /*////////////////////////////////////////////////////////////////////////// - // Getter + Setter - //////////////////////////////////////////////////////////////////////////*/ - - @Nullable - public DialogInterface.OnDismissListener getOnDismissListener() { - return onDismissListener; - } - - public void setOnDismissListener( - @Nullable final DialogInterface.OnDismissListener onDismissListener - ) { - this.onDismissListener = onDismissListener; - } - - protected void setStreamEntities(final List streamEntities) { - this.streamEntities = streamEntities; - } - - /*////////////////////////////////////////////////////////////////////////// - // Dialog creation - //////////////////////////////////////////////////////////////////////////*/ - /** * Creates a {@link PlaylistAppendDialog} when playlists exists, * otherwise a {@link PlaylistCreationDialog}. @@ -178,4 +86,93 @@ public abstract class PlaylistDialog extends DialogFragment implements StateSave return PlaylistDialog.createCorrespondingDialog(player.getContext(), streamEntities, dialog -> dialog.show(fragmentManager, "PlaylistDialog")); } + + @Override + public void onCreate(@Nullable final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + savedState = StateSaver.tryToRestore(savedInstanceState, this); + } + + @Override + public void onDestroy() { + super.onDestroy(); + StateSaver.onDestroy(savedState); + } + + public List getStreamEntities() { + return streamEntities; + } + + /*////////////////////////////////////////////////////////////////////////// + // State Saving + //////////////////////////////////////////////////////////////////////////*/ + + protected void setStreamEntities(final List streamEntities) { + this.streamEntities = streamEntities; + } + + @NonNull + @Override + public Dialog onCreateDialog(final Bundle savedInstanceState) { + final Dialog dialog = super.onCreateDialog(savedInstanceState); + //remove title + final Window window = dialog.getWindow(); + if (window != null) { + window.requestFeature(Window.FEATURE_NO_TITLE); + } + return dialog; + } + + @Override + public void onDismiss(@NonNull final DialogInterface dialog) { + super.onDismiss(dialog); + if (onDismissListener != null) { + onDismissListener.onDismiss(dialog); + } + } + + @Override + public String generateSuffix() { + final int size = streamEntities == null ? 0 : streamEntities.size(); + return "." + size + ".list"; + } + + /*////////////////////////////////////////////////////////////////////////// + // Getter + Setter + //////////////////////////////////////////////////////////////////////////*/ + + @Override + public void writeTo(final Queue objectsToSave) { + objectsToSave.add(streamEntities); + } + + @Override + @SuppressWarnings("unchecked") + public void readFrom(@NonNull final Queue savedObjects) { + streamEntities = (List) savedObjects.poll(); + } + + @Override + public void onSaveInstanceState(@NonNull final Bundle outState) { + super.onSaveInstanceState(outState); + if (getActivity() != null) { + savedState = StateSaver.tryToSave(getActivity().isChangingConfigurations(), + savedState, outState, this); + } + } + + /*////////////////////////////////////////////////////////////////////////// + // Dialog creation + //////////////////////////////////////////////////////////////////////////*/ + + @Nullable + public DialogInterface.OnDismissListener getOnDismissListener() { + return onDismissListener; + } + + public void setOnDismissListener( + @Nullable final DialogInterface.OnDismissListener onDismissListener + ) { + this.onDismissListener = onDismissListener; + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryEntryAdapter.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryEntryAdapter.java index 709a16b68..db9a6a9e6 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryEntryAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryEntryAdapter.java @@ -1,11 +1,9 @@ package org.schabi.newpipe.local.history; import android.content.Context; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.util.Localization; import java.text.DateFormat; diff --git a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java index b8d2eae2d..0b18c7cac 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/HistoryRecordManager.java @@ -20,10 +20,13 @@ package org.schabi.newpipe.local.history; import android.content.Context; import android.content.SharedPreferences; - import androidx.annotation.NonNull; import androidx.preference.PreferenceManager; - +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.AppDatabase; @@ -52,12 +55,6 @@ import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; -import io.reactivex.rxjava3.core.Completable; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.core.Maybe; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.schedulers.Schedulers; - public class HistoryRecordManager { private final AppDatabase database; private final StreamDAO streamTable; @@ -87,9 +84,9 @@ public class HistoryRecordManager { * Marks a stream item as watched such that it is hidden from the feed if watched videos are * hidden. Adds a history entry and updates the stream progress to 100%. * - * @see FeedViewModel#togglePlayedItems * @param info the item to mark as watched * @return a Maybe containing the ID of the item if successful + * @see FeedViewModel#togglePlayedItems */ public Maybe markAsWatched(final StreamInfoItem info) { if (!isStreamHistoryEnabled()) { @@ -103,10 +100,10 @@ public class HistoryRecordManager { // Duration will not exist if the item was loaded with fast mode, so fetch it if empty if (info.getDuration() < 0) { final StreamInfo completeInfo = ExtractorHelper.getStreamInfo( - info.getServiceId(), - info.getUrl(), - false - ) + info.getServiceId(), + info.getUrl(), + false + ) .subscribeOn(Schedulers.io()) .blockingGet(); duration = completeInfo.getDuration(); diff --git a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java index a20a80ae9..b43b5b453 100644 --- a/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/history/StatisticsPlaylistFragment.java @@ -3,20 +3,16 @@ package org.schabi.newpipe.local.history; import android.content.Context; import android.os.Bundle; import android.os.Parcelable; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.viewbinding.ViewBinding; - import com.google.android.material.snackbar.Snackbar; - +import icepick.State; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.R; @@ -29,24 +25,15 @@ import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.info_list.dialog.InfoItemDialog; +import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry; import org.schabi.newpipe.local.BaseLocalListFragment; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import org.schabi.newpipe.settings.HistorySettingsFragment; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.OnClickGesture; -import org.schabi.newpipe.info_list.dialog.StreamDialogDefaultEntry; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; -import java.util.Objects; - -import icepick.State; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; +import java.util.*; public class StatisticsPlaylistFragment extends BaseLocalListFragment, Void> { @@ -313,7 +300,7 @@ public class StatisticsPlaylistFragment sortMode = StatisticSortMode.LAST_PLAYED; setTitle(getString(R.string.title_last_played)); headerBinding.sortButtonIcon.setImageResource( - R.drawable.ic_filter_list); + R.drawable.ic_filter_list); headerBinding.sortButtonText.setText(R.string.title_most_played); } startLoading(true); @@ -351,8 +338,7 @@ public class StatisticsPlaylistFragment private void deleteEntry(final int index) { final LocalItem infoItem = itemListAdapter.getItemsList().get(index); - if (infoItem instanceof StreamStatisticsEntry) { - final StreamStatisticsEntry entry = (StreamStatisticsEntry) infoItem; + if (infoItem instanceof final StreamStatisticsEntry entry) { final Disposable onDelete = recordManager .deleteStreamHistoryAndState(entry.getStreamId()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java index a093d93e1..1566c9151 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalItemHolder.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.local.holder; import android.view.LayoutInflater; import android.view.ViewGroup; - import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; @@ -44,5 +42,6 @@ public abstract class LocalItemHolder extends RecyclerView.ViewHolder { DateTimeFormatter dateTimeFormatter); public void updateState(final LocalItem localItem, - final HistoryRecordManager historyRecordManager) { } + final HistoryRecordManager historyRecordManager) { + } } diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistCardItemHolder.java index 33418ec98..00b44ac8c 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistCardItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistCardItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistGridItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistGridItemHolder.java index 2b493f4ee..f59f788aa 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistGridItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistGridItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java index f8c5176ec..77668b7a3 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistItemHolder.java @@ -2,13 +2,12 @@ package org.schabi.newpipe.local.holder; import android.view.View; import android.view.ViewGroup; - import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; import org.schabi.newpipe.local.LocalItemBuilder; import org.schabi.newpipe.local.history.HistoryRecordManager; -import org.schabi.newpipe.util.PicassoHelper; import org.schabi.newpipe.util.Localization; +import org.schabi.newpipe.util.PicassoHelper; import java.time.format.DateTimeFormatter; @@ -26,10 +25,9 @@ public class LocalPlaylistItemHolder extends PlaylistItemHolder { public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistMetadataEntry)) { + if (!(localItem instanceof final PlaylistMetadataEntry item)) { return; } - final PlaylistMetadataEntry item = (PlaylistMetadataEntry) localItem; itemTitleView.setText(item.name); itemStreamCountView.setText(Localization.localizeStreamCountMini( diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamCardItemHolder.java index 7f81a527f..78815d355 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamCardItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamCardItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamGridItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamGridItemHolder.java index e2f936792..0b3b822af 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamGridItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamGridItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java index d39758326..567945ad6 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalPlaylistStreamItemHolder.java @@ -5,9 +5,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import androidx.core.content.ContextCompat; - import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.PlaylistStreamEntry; @@ -25,8 +23,8 @@ import java.util.concurrent.TimeUnit; public class LocalPlaylistStreamItemHolder extends LocalItemHolder { public final ImageView itemThumbnailView; public final TextView itemVideoTitleView; - private final TextView itemAdditionalDetailsView; public final TextView itemDurationView; + private final TextView itemAdditionalDetailsView; private final View itemHandleView; private final AnimatedProgressBar itemProgressView; @@ -51,10 +49,9 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistStreamEntry)) { + if (!(localItem instanceof final PlaylistStreamEntry item)) { return; } - final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem; itemVideoTitleView.setText(item.getStreamEntity().getTitle()); itemAdditionalDetailsView.setText(Localization @@ -104,10 +101,9 @@ public class LocalPlaylistStreamItemHolder extends LocalItemHolder { @Override public void updateState(final LocalItem localItem, final HistoryRecordManager historyRecordManager) { - if (!(localItem instanceof PlaylistStreamEntry)) { + if (!(localItem instanceof final PlaylistStreamEntry item)) { return; } - final PlaylistStreamEntry item = (PlaylistStreamEntry) localItem; if (item.getProgressMillis() > 0 && item.getStreamEntity().getDuration() > 0) { itemProgressView.setMax((int) item.getStreamEntity().getDuration()); diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamCardItemHolder.java index 4e03d5fb1..10ae36012 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamCardItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamCardItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamGridItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamGridItemHolder.java index 39a43b034..1f6b47554 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamGridItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamGridItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java index 0d88eecba..6447e2f94 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/LocalStatisticStreamItemHolder.java @@ -4,10 +4,8 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; - import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.stream.StreamStatisticsEntry; @@ -82,10 +80,9 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof StreamStatisticsEntry)) { + if (!(localItem instanceof final StreamStatisticsEntry item)) { return; } - final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem; itemVideoTitleView.setText(item.getStreamEntity().getTitle()); itemUploaderView.setText(item.getStreamEntity().getUploader()); @@ -136,10 +133,9 @@ public class LocalStatisticStreamItemHolder extends LocalItemHolder { @Override public void updateState(final LocalItem localItem, final HistoryRecordManager historyRecordManager) { - if (!(localItem instanceof StreamStatisticsEntry)) { + if (!(localItem instanceof final StreamStatisticsEntry item)) { return; } - final StreamStatisticsEntry item = (StreamStatisticsEntry) localItem; if (item.getProgressMillis() > 0 && item.getStreamEntity().getDuration() > 0) { itemProgressView.setMax((int) item.getStreamEntity().getDuration()); diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java index e8c53161e..69bb7a090 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/PlaylistItemHolder.java @@ -3,7 +3,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.local.LocalItemBuilder; @@ -13,9 +12,9 @@ import java.time.format.DateTimeFormatter; public abstract class PlaylistItemHolder extends LocalItemHolder { public final ImageView itemThumbnailView; - final TextView itemStreamCountView; public final TextView itemTitleView; public final TextView itemUploaderView; + final TextView itemStreamCountView; public PlaylistItemHolder(final LocalItemBuilder infoItemBuilder, final int layoutId, final ViewGroup parent) { diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistCardItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistCardItemHolder.java index 74a67c3db..1295abaf2 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistCardItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistCardItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistGridItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistGridItemHolder.java index 00dcefbda..b59da60d5 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistGridItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistGridItemHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.local.holder; import android.view.ViewGroup; - import org.schabi.newpipe.R; import org.schabi.newpipe.local.LocalItemBuilder; diff --git a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java index 70987a6fc..a75607b1d 100644 --- a/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java +++ b/app/src/main/java/org/schabi/newpipe/local/holder/RemotePlaylistItemHolder.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.local.holder; import android.text.TextUtils; import android.view.ViewGroup; - import org.schabi.newpipe.database.LocalItem; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; import org.schabi.newpipe.local.LocalItemBuilder; @@ -28,10 +27,9 @@ public class RemotePlaylistItemHolder extends PlaylistItemHolder { public void updateFromItem(final LocalItem localItem, final HistoryRecordManager historyRecordManager, final DateTimeFormatter dateTimeFormatter) { - if (!(localItem instanceof PlaylistRemoteEntity)) { + if (!(localItem instanceof final PlaylistRemoteEntity item)) { return; } - final PlaylistRemoteEntity item = (PlaylistRemoteEntity) localItem; itemTitleView.setText(item.getName()); itemStreamCountView.setText(Localization.localizeStreamCountMini( diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java index 68a35e72b..6214a92eb 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistFragment.java @@ -1,9 +1,5 @@ package org.schabi.newpipe.local.playlist; -import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; - import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; @@ -12,14 +8,8 @@ import android.text.InputType; import android.text.TextUtils; import android.util.Log; import android.util.Pair; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -27,7 +17,13 @@ import androidx.preference.PreferenceManager; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; import androidx.viewbinding.ViewBinding; - +import icepick.State; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.PublishSubject; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.NewPipeDatabase; @@ -61,13 +57,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; -import icepick.State; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; +import static org.schabi.newpipe.error.ErrorUtil.showUiErrorSnackbar; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.util.ThemeHelper.shouldUseGridLayout; public class LocalPlaylistFragment extends BaseLocalListFragment, Void> { // Save the list 10 seconds after the last change occurred diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java index 4007d0e09..a747551be 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/LocalPlaylistManager.java @@ -1,7 +1,11 @@ package org.schabi.newpipe.local.playlist; import androidx.annotation.Nullable; - +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.R; import org.schabi.newpipe.database.AppDatabase; import org.schabi.newpipe.database.playlist.PlaylistMetadataEntry; @@ -16,12 +20,6 @@ import org.schabi.newpipe.database.stream.model.StreamEntity; import java.util.ArrayList; import java.util.List; -import io.reactivex.rxjava3.core.Completable; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.core.Maybe; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.schedulers.Schedulers; - public class LocalPlaylistManager { private final AppDatabase database; private final StreamDAO streamTable; diff --git a/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java b/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java index 5221139e3..3746971bc 100644 --- a/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java +++ b/app/src/main/java/org/schabi/newpipe/local/playlist/RemotePlaylistManager.java @@ -1,5 +1,8 @@ package org.schabi.newpipe.local.playlist; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.database.AppDatabase; import org.schabi.newpipe.database.playlist.dao.PlaylistRemoteDAO; import org.schabi.newpipe.database.playlist.model.PlaylistRemoteEntity; @@ -7,10 +10,6 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo; import java.util.List; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.schedulers.Schedulers; - public class RemotePlaylistManager { private final PlaylistRemoteDAO playlistRemoteTable; diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java b/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java index da8e1070a..895638043 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/ImportConfirmationDialog.java @@ -3,17 +3,14 @@ package org.schabi.newpipe.local.subscription; import android.app.Dialog; import android.content.Intent; import android.os.Bundle; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.DialogFragment; import androidx.fragment.app.Fragment; - -import org.schabi.newpipe.R; - import icepick.Icepick; import icepick.State; +import org.schabi.newpipe.R; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; @@ -25,7 +22,7 @@ public class ImportConfirmationDialog extends DialogFragment { @NonNull final Intent resultServiceIntent) { final ImportConfirmationDialog confirmationDialog = new ImportConfirmationDialog(); confirmationDialog.setResultServiceIntent(resultServiceIntent); - confirmationDialog.show(fragment.getParentFragmentManager(), null); + show(fragment.getParentFragmentManager(), null); } public void setResultServiceIntent(final Intent resultServiceIntent) { diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java index 56972b60d..ccfdf13b6 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/SubscriptionsImportFragment.java @@ -1,11 +1,5 @@ package org.schabi.newpipe.local.subscription; -import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL; -import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.CHANNEL_URL_MODE; -import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.INPUT_STREAM_MODE; -import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_MODE; -import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.KEY_VALUE; - import android.app.Activity; import android.content.Intent; import android.os.Bundle; @@ -17,7 +11,6 @@ import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; - import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; @@ -26,7 +19,7 @@ import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.appcompat.app.ActionBar; import androidx.core.text.util.LinkifyCompat; - +import icepick.State; import org.schabi.newpipe.BaseFragment; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; @@ -44,29 +37,26 @@ import org.schabi.newpipe.util.ServiceHelper; import java.util.Collections; import java.util.List; -import icepick.State; +import static org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.ContentSource.CHANNEL_URL; +import static org.schabi.newpipe.local.subscription.services.SubscriptionsImportService.*; public class SubscriptionsImportFragment extends BaseFragment { @State int currentServiceId = Constants.NO_SERVICE_ID; - + private final ActivityResultLauncher requestImportFileLauncher = + registerForActivityResult(new StartActivityForResult(), this::requestImportFileResult); private List supportedSources; private String relatedUrl; - @StringRes - private int instructionsString; - /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ - + @StringRes + private int instructionsString; private TextView infoTextView; private EditText inputText; private Button inputButton; - private final ActivityResultLauncher requestImportFileLauncher = - registerForActivityResult(new StartActivityForResult(), this::requestImportFileResult); - public static SubscriptionsImportFragment getInstance(final int serviceId) { final SubscriptionsImportFragment instance = new SubscriptionsImportFragment(); instance.setInitialData(serviceId); diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java b/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java index b7c11b160..580489962 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/services/BaseImportExportService.java @@ -25,14 +25,17 @@ import android.os.Build; import android.os.IBinder; import android.text.TextUtils; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.app.ServiceCompat; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.processors.PublishProcessor; import org.reactivestreams.Publisher; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; @@ -46,24 +49,11 @@ import java.io.FileNotFoundException; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.processors.PublishProcessor; - public abstract class BaseImportExportService extends Service { + private static final int NOTIFICATION_SAMPLING_PERIOD = 2500; protected final String TAG = this.getClass().getSimpleName(); - protected final CompositeDisposable disposables = new CompositeDisposable(); protected final PublishProcessor notificationUpdater = PublishProcessor.create(); - - protected NotificationManagerCompat notificationManager; - protected NotificationCompat.Builder notificationBuilder; - protected SubscriptionManager subscriptionManager; - - private static final int NOTIFICATION_SAMPLING_PERIOD = 2500; - protected final AtomicInteger currentProgress = new AtomicInteger(-1); protected final AtomicInteger maxProgress = new AtomicInteger(-1); protected final ImportExportEventListener eventListener = new ImportExportEventListener() { @@ -79,7 +69,9 @@ public abstract class BaseImportExportService extends Service { notificationUpdater.onNext(itemName); } }; - + protected NotificationManagerCompat notificationManager; + protected NotificationCompat.Builder notificationBuilder; + protected SubscriptionManager subscriptionManager; protected Toast toast; @Nullable diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelper.java b/app/src/main/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelper.java index 611a1cd30..d5dfa5abf 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelper.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/services/ImportExportJsonHelper.java @@ -20,13 +20,7 @@ package org.schabi.newpipe.local.subscription.services; import androidx.annotation.Nullable; - -import com.grack.nanojson.JsonAppendableWriter; -import com.grack.nanojson.JsonArray; -import com.grack.nanojson.JsonObject; -import com.grack.nanojson.JsonParser; -import com.grack.nanojson.JsonWriter; - +import com.grack.nanojson.*; import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor.InvalidSourceException; import org.schabi.newpipe.extractor.subscription.SubscriptionItem; @@ -54,7 +48,8 @@ public final class ImportExportJsonHelper { private static final String JSON_URL_KEY = "url"; private static final String JSON_NAME_KEY = "name"; - private ImportExportJsonHelper() { } + private ImportExportJsonHelper() { + } /** * Read a JSON source through the input stream. @@ -86,8 +81,7 @@ public final class ImportExportJsonHelper { } for (final Object o : channelsArray) { - if (o instanceof JsonObject) { - final JsonObject itemObject = (JsonObject) o; + if (o instanceof final JsonObject itemObject) { final int serviceId = itemObject.getInt(JSON_SERVICE_ID_KEY, 0); final String url = itemObject.getString(JSON_URL_KEY); final String name = itemObject.getString(JSON_NAME_KEY); @@ -122,10 +116,10 @@ public final class ImportExportJsonHelper { } /** - * @see #writeTo(List, OutputStream, ImportExportEventListener) * @param items the list of subscriptions items * @param writer the output {@link JsonAppendableWriter} * @param eventListener listener for the events generated + * @see #writeTo(List, OutputStream, ImportExportEventListener) */ public static void writeTo(final List items, final JsonAppendableWriter writer, diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java index d56d16f3c..fc51ca0c8 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsExportService.java @@ -19,14 +19,13 @@ package org.schabi.newpipe.local.subscription.services; -import static org.schabi.newpipe.MainActivity.DEBUG; - import android.content.Intent; import android.net.Uri; import android.util.Log; - import androidx.localbroadcastmanager.content.LocalBroadcastManager; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.App; @@ -41,9 +40,7 @@ import java.io.OutputStream; import java.util.ArrayList; import java.util.List; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.schedulers.Schedulers; +import static org.schabi.newpipe.MainActivity.DEBUG; public class SubscriptionsExportService extends BaseImportExportService { public static final String KEY_FILE_PATH = "key_file_path"; @@ -68,7 +65,7 @@ public class SubscriptionsExportService extends BaseImportExportService { final Uri path = intent.getParcelableExtra(KEY_FILE_PATH); if (path == null) { stopAndReportError(new IllegalStateException( - "Exporting to a file, but the path is null"), + "Exporting to a file, but the path is null"), "Exporting subscriptions"); return START_NOT_STICKY; } diff --git a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java index af598b106..0dd408bf1 100644 --- a/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java +++ b/app/src/main/java/org/schabi/newpipe/local/subscription/services/SubscriptionsImportService.java @@ -19,18 +19,19 @@ package org.schabi.newpipe.local.subscription.services; -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.streams.io.StoredFileHelper.DEFAULT_MIME; - import android.content.Intent; import android.net.Uri; import android.text.TextUtils; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.localbroadcastmanager.content.LocalBroadcastManager; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.core.Notification; +import io.reactivex.rxjava3.functions.Consumer; +import io.reactivex.rxjava3.functions.Function; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.App; @@ -51,12 +52,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.core.Notification; -import io.reactivex.rxjava3.functions.Consumer; -import io.reactivex.rxjava3.functions.Function; -import io.reactivex.rxjava3.schedulers.Schedulers; +import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.streams.io.StoredFileHelper.DEFAULT_MIME; public class SubscriptionsImportService extends BaseImportExportService { public static final int CHANNEL_URL_MODE = 0; @@ -108,7 +105,7 @@ public class SubscriptionsImportService extends BaseImportExportService { final Uri uri = intent.getParcelableExtra(KEY_VALUE); if (uri == null) { stopAndReportError(new IllegalStateException( - "Importing from input stream, but file path is null"), + "Importing from input stream, but file path is null"), "Importing subscriptions"); return START_NOT_STICKY; } @@ -194,7 +191,7 @@ public class SubscriptionsImportService extends BaseImportExportService { } flowable.doOnNext(subscriptionItems -> - eventListener.onSizeReceived(subscriptionItems.size())) + eventListener.onSizeReceived(subscriptionItems.size())) .flatMap(Flowable::fromIterable) .parallel(PARALLEL_EXTRACTIONS) diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java index 9ce99c15b..f2687999d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayQueueActivity.java @@ -1,9 +1,5 @@ package org.schabi.newpipe.player; -import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu; -import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; @@ -16,15 +12,12 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.SeekBar; - import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - import com.google.android.exoplayer2.PlaybackParameters; - import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ActivityPlayerQueueControlBinding; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -32,17 +25,12 @@ import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; import org.schabi.newpipe.local.dialog.PlaylistDialog; import org.schabi.newpipe.player.event.PlayerEventListener; import org.schabi.newpipe.player.helper.PlaybackParameterDialog; -import org.schabi.newpipe.player.playqueue.PlayQueue; -import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; -import org.schabi.newpipe.player.playqueue.PlayQueueItem; -import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; -import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; -import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; -import org.schabi.newpipe.util.Localization; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PermissionHelper; -import org.schabi.newpipe.util.ServiceHelper; -import org.schabi.newpipe.util.ThemeHelper; +import org.schabi.newpipe.player.playqueue.*; +import org.schabi.newpipe.util.*; + +import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu; +import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; public final class PlayQueueActivity extends AppCompatActivity implements PlayerEventListener, SeekBar.OnSeekBarChangeListener, @@ -74,7 +62,7 @@ public final class PlayQueueActivity extends AppCompatActivity //////////////////////////////////////////////////////////////////////////// @Override - protected void onCreate(final Bundle savedInstanceState) { + private void onCreate(final Bundle savedInstanceState) { assureCorrectAppLanguage(this); super.onCreate(savedInstanceState); ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this)); @@ -157,7 +145,7 @@ public final class PlayQueueActivity extends AppCompatActivity } @Override - protected void onDestroy() { + private void onDestroy() { super.onDestroy(); unbind(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/Player.java b/app/src/main/java/org/schabi/newpipe/player/Player.java index c90f251b8..2bcc11b2a 100644 --- a/app/src/main/java/org/schabi/newpipe/player/Player.java +++ b/app/src/main/java/org/schabi/newpipe/player/Player.java @@ -1,78 +1,17 @@ package org.schabi.newpipe.player; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_BEHIND_LIVE_WINDOW; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_CLEARTEXT_NOT_PERMITTED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_FILE_NOT_FOUND; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_INVALID_HTTP_CONTENT_TYPE; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_TIMEOUT; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_NO_PERMISSION; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_READ_POSITION_OUT_OF_RANGE; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_IO_UNSPECIFIED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_PARSING_CONTAINER_MALFORMED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_PARSING_CONTAINER_UNSUPPORTED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_PARSING_MANIFEST_MALFORMED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_PARSING_MANIFEST_UNSUPPORTED; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_TIMEOUT; -import static com.google.android.exoplayer2.PlaybackException.ERROR_CODE_UNSPECIFIED; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_AUTO_TRANSITION; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_INTERNAL; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_REMOVE; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SEEK_ADJUSTMENT; -import static com.google.android.exoplayer2.Player.DISCONTINUITY_REASON_SKIP; -import static com.google.android.exoplayer2.Player.DiscontinuityReason; -import static com.google.android.exoplayer2.Player.Listener; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static com.google.android.exoplayer2.Player.RepeatMode; -import static org.schabi.newpipe.extractor.ServiceList.YouTube; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; -import static org.schabi.newpipe.player.helper.PlayerHelper.isPlaybackResumeEnabled; -import static org.schabi.newpipe.player.helper.PlayerHelper.nextRepeatMode; -import static org.schabi.newpipe.player.helper.PlayerHelper.retrievePlaybackParametersFromPrefs; -import static org.schabi.newpipe.player.helper.PlayerHelper.retrieveSeekDurationFromPreferences; -import static org.schabi.newpipe.player.helper.PlayerHelper.savePlaybackParametersToPrefs; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_FORWARD; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_REWIND; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_NEXT; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PAUSE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PREVIOUS; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_REPEAT; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_SHUFFLE; -import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; -import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; +import android.content.*; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.util.Log; import android.view.LayoutInflater; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.math.MathUtils; import androidx.preference.PreferenceManager; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.DefaultRenderersFactory; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player.PositionInfo; -import com.google.android.exoplayer2.RenderersFactory; -import com.google.android.exoplayer2.Timeline; -import com.google.android.exoplayer2.Tracks; +import com.google.android.exoplayer2.*; +import com.google.android.exoplayer2.Player.*; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.text.CueGroup; import com.google.android.exoplayer2.trackselection.DefaultTrackSelector; @@ -82,7 +21,11 @@ import com.google.android.exoplayer2.util.Util; import com.google.android.exoplayer2.video.VideoSize; import com.squareup.picasso.Picasso; import com.squareup.picasso.Target; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.disposables.SerialDisposable; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.PlayerBinding; @@ -110,27 +53,23 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem; import org.schabi.newpipe.player.resolver.AudioPlaybackResolver; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver; import org.schabi.newpipe.player.resolver.VideoPlaybackResolver.SourceType; -import org.schabi.newpipe.player.ui.MainPlayerUi; -import org.schabi.newpipe.player.ui.PlayerUi; -import org.schabi.newpipe.player.ui.PlayerUiList; -import org.schabi.newpipe.player.ui.PopupPlayerUi; -import org.schabi.newpipe.player.ui.VideoPlayerUi; -import org.schabi.newpipe.util.DeviceUtils; -import org.schabi.newpipe.util.ListHelper; -import org.schabi.newpipe.util.NavigationHelper; -import org.schabi.newpipe.util.PicassoHelper; -import org.schabi.newpipe.util.SerializedCache; -import org.schabi.newpipe.util.StreamTypeUtil; +import org.schabi.newpipe.player.ui.*; +import org.schabi.newpipe.util.*; import java.util.List; import java.util.Optional; import java.util.stream.IntStream; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.disposables.SerialDisposable; +import static com.google.android.exoplayer2.PlaybackException.*; +import static com.google.android.exoplayer2.Player.*; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static org.schabi.newpipe.extractor.ServiceList.YouTube; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; +import static org.schabi.newpipe.player.helper.PlayerHelper.*; +import static org.schabi.newpipe.player.notification.NotificationConstants.*; +import static org.schabi.newpipe.util.ListHelper.getPopupResolutionIndex; +import static org.schabi.newpipe.util.ListHelper.getResolutionIndex; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; public final class Player implements PlaybackListener, Listener { public static final boolean DEBUG = MainActivity.DEBUG; @@ -179,71 +118,75 @@ public final class Player implements PlaybackListener, Listener { /*////////////////////////////////////////////////////////////////////////// // Playback //////////////////////////////////////////////////////////////////////////*/ - - // play queue might be null e.g. while player is starting - @Nullable private PlayQueue playQueue; - - @Nullable private MediaSourceManager playQueueManager; - - @Nullable private PlayQueueItem currentItem; - @Nullable private MediaItemTag currentMetadata; - @Nullable private Bitmap currentThumbnail; + @NonNull + private final DefaultTrackSelector trackSelector; + @NonNull + private final LoadController loadController; + @NonNull + private final RenderersFactory renderFactory; + @NonNull + private final VideoPlaybackResolver videoResolver; + @NonNull + private final AudioPlaybackResolver audioResolver; /*////////////////////////////////////////////////////////////////////////// // Player //////////////////////////////////////////////////////////////////////////*/ - - private ExoPlayer simpleExoPlayer; - private AudioReactor audioReactor; - - @NonNull private final DefaultTrackSelector trackSelector; - @NonNull private final LoadController loadController; - @NonNull private final RenderersFactory renderFactory; - - @NonNull private final VideoPlaybackResolver videoResolver; - @NonNull private final AudioPlaybackResolver audioResolver; - private final PlayerService service; //TODO try to remove and replace everything with context + @SuppressWarnings({"MemberName", "java:S116"}) // keep the unusual member name + private final PlayerUiList UIs; + @NonNull + private final SerialDisposable progressUpdateDisposable = new SerialDisposable(); + @NonNull + private final CompositeDisposable databaseUpdateDisposable = new CompositeDisposable(); + // This is the only listener we need for thumbnail loading, since there is always at most only + // one thumbnail being loaded at a time. This field is also here to maintain a strong reference, + // which would otherwise be garbage collected since Picasso holds weak references to targets. + @NonNull + private final Target currentThumbnailTarget; + @NonNull + private final Context context; + @NonNull + private final SharedPreferences prefs; + @NonNull + private final HistoryRecordManager recordManager; /*////////////////////////////////////////////////////////////////////////// // Player states //////////////////////////////////////////////////////////////////////////*/ - - private PlayerType playerType = PlayerType.MAIN; - private int currentState = STATE_PREFLIGHT; - - // audio only mode does not mean that player type is background, but that the player was - // minimized to background but will resume automatically to the original player type - private boolean isAudioOnly = false; - private boolean isPrepared = false; + // play queue might be null e.g. while player is starting + @Nullable + private PlayQueue playQueue; + @Nullable + private MediaSourceManager playQueueManager; + @Nullable + private PlayQueueItem currentItem; + @Nullable + private MediaItemTag currentMetadata; /*////////////////////////////////////////////////////////////////////////// // UIs, listeners and disposables //////////////////////////////////////////////////////////////////////////*/ - - @SuppressWarnings({"MemberName", "java:S116"}) // keep the unusual member name - private final PlayerUiList UIs; - + @Nullable + private Bitmap currentThumbnail; + private ExoPlayer simpleExoPlayer; + private AudioReactor audioReactor; + private PlayerType playerType = PlayerType.MAIN; + private int currentState = STATE_PREFLIGHT; + // audio only mode does not mean that player type is background, but that the player was + // minimized to background but will resume automatically to the original player type + private boolean isAudioOnly = false; + private boolean isPrepared = false; private BroadcastReceiver broadcastReceiver; - private IntentFilter intentFilter; - @Nullable private PlayerServiceEventListener fragmentListener = null; - @Nullable private PlayerEventListener activityListener = null; - - @NonNull private final SerialDisposable progressUpdateDisposable = new SerialDisposable(); - @NonNull private final CompositeDisposable databaseUpdateDisposable = new CompositeDisposable(); - - // This is the only listener we need for thumbnail loading, since there is always at most only - // one thumbnail being loaded at a time. This field is also here to maintain a strong reference, - // which would otherwise be garbage collected since Picasso holds weak references to targets. - @NonNull private final Target currentThumbnailTarget; /*////////////////////////////////////////////////////////////////////////// // Utils //////////////////////////////////////////////////////////////////////////*/ - - @NonNull private final Context context; - @NonNull private final SharedPreferences prefs; - @NonNull private final HistoryRecordManager recordManager; + private IntentFilter intentFilter; + @Nullable + private PlayerServiceEventListener fragmentListener = null; + @Nullable + private PlayerEventListener activityListener = null; /*////////////////////////////////////////////////////////////////////////// @@ -334,7 +277,7 @@ public final class Player implements PlaybackListener, Listener { playQueue.append(newQueue.getStreams()); return; - // Resolve enqueue next intents + // Resolve enqueue next intents } else if (intent.getBooleanExtra(ENQUEUE_NEXT, false) && playQueue != null) { final int currentIndex = playQueue.getIndex(); playQueue.append(newQueue.getStreams()); @@ -911,7 +854,7 @@ public final class Player implements PlaybackListener, Listener { private Disposable getProgressUpdateDisposable() { return Observable.interval(PROGRESS_LOOP_INTERVAL_MILLIS, MILLISECONDS, - AndroidSchedulers.mainThread()) + AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(ignored -> triggerProgressUpdate(), error -> Log.e(TAG, "Progress update failure: ", error)); @@ -920,7 +863,6 @@ public final class Player implements PlaybackListener, Listener { //endregion - /*////////////////////////////////////////////////////////////////////////// // Playback states //////////////////////////////////////////////////////////////////////////*/ @@ -1349,6 +1291,7 @@ public final class Player implements PlaybackListener, Listener { // Errors //////////////////////////////////////////////////////////////////////////*/ //region Errors + /** * Process exceptions produced by {@link com.google.android.exoplayer2.ExoPlayer ExoPlayer}. *

There are multiple types of errors:

@@ -1375,8 +1318,9 @@ public final class Player implements PlaybackListener, Listener { * For any error above that is not explicitly catchable, the player will * create a notification so users are aware. * + * * @see com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException) - * */ + */ // Any error code not explicitly covered here are either unrelated to NewPipe use case // (e.g. DRM) or not recoverable (e.g. Decoder error). In both cases, the player should // shutdown. @@ -1935,15 +1879,6 @@ public final class Player implements PlaybackListener, Listener { //////////////////////////////////////////////////////////////////////////*/ //region Activity / fragment binding - public void setFragmentListener(final PlayerServiceEventListener listener) { - fragmentListener = listener; - UIs.call(PlayerUi::onFragmentListenerSet); - notifyQueueUpdateToListeners(); - notifyMetadataUpdateToListeners(); - notifyPlaybackUpdateToListeners(); - triggerProgressUpdate(); - } - public void removeFragmentListener(final PlayerServiceEventListener listener) { if (fragmentListener == listener) { fragmentListener = null; @@ -2113,7 +2048,7 @@ public final class Player implements PlaybackListener, Listener { // because the stream source will be probably the same as the current played if (sourceType == SourceType.VIDEO_WITH_SEPARATED_AUDIO || (sourceType == SourceType.VIDEO_WITH_AUDIO_OR_AUDIO_ONLY - && isNullOrEmpty(streamInfo.getAudioStreams()))) { + && isNullOrEmpty(streamInfo.getAudioStreams()))) { // It's not needed to reload the play queue manager only if the content's stream type // is a video stream, a live stream or an ended live stream return !StreamTypeUtil.isVideo(streamType); @@ -2122,6 +2057,10 @@ public final class Player implements PlaybackListener, Listener { // Other cases: the play queue manager reload is needed return true; } + + public Optional getCurrentStreamInfo() { + return Optional.ofNullable(currentMetadata).flatMap(MediaItemTag::getMaybeStreamInfo); + } //endregion @@ -2130,10 +2069,6 @@ public final class Player implements PlaybackListener, Listener { //////////////////////////////////////////////////////////////////////////*/ //region Getters - public Optional getCurrentStreamInfo() { - return Optional.ofNullable(currentMetadata).flatMap(MediaItemTag::getMaybeStreamInfo); - } - public int getCurrentState() { return currentState; } @@ -2178,7 +2113,6 @@ public final class Player implements PlaybackListener, Listener { videoResolver.setPlaybackQuality(quality); } - @NonNull public Context getContext() { return context; @@ -2189,7 +2123,6 @@ public final class Player implements PlaybackListener, Listener { return prefs; } - public PlayerType getPlayerType() { return playerType; } @@ -2206,7 +2139,6 @@ public final class Player implements PlaybackListener, Listener { return playerType == PlayerType.POPUP; } - @Nullable public PlayQueue getPlayQueue() { return playQueue; @@ -2243,6 +2175,15 @@ public final class Player implements PlaybackListener, Listener { return Optional.ofNullable(fragmentListener); } + public void setFragmentListener(final PlayerServiceEventListener listener) { + fragmentListener = listener; + UIs.call(PlayerUi::onFragmentListenerSet); + notifyQueueUpdateToListeners(); + notifyMetadataUpdateToListeners(); + notifyPlaybackUpdateToListeners(); + triggerProgressUpdate(); + } + /** * @return the user interfaces connected with the player */ @@ -2253,7 +2194,7 @@ public final class Player implements PlaybackListener, Listener { /** * Get the video renderer index of the current playing stream. - * + *

* This method returns the video renderer index of the current * {@link MappingTrackSelector.MappedTrackInfo} or {@link #RENDERER_UNAVAILABLE} if the current * {@link MappingTrackSelector.MappedTrackInfo} is null or if there is no video renderer index. diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayerService.java b/app/src/main/java/org/schabi/newpipe/player/PlayerService.java index d81301205..2380f9520 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayerService.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayerService.java @@ -19,18 +19,17 @@ package org.schabi.newpipe.player; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; import android.util.Log; - import org.schabi.newpipe.player.mediasession.MediaSessionPlayerUi; import org.schabi.newpipe.util.ThemeHelper; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; + /** * One service for all players. @@ -38,10 +37,8 @@ import org.schabi.newpipe.util.ThemeHelper; public final class PlayerService extends Service { private static final String TAG = PlayerService.class.getSimpleName(); private static final boolean DEBUG = Player.DEBUG; - - private Player player; - private final IBinder mBinder = new PlayerService.LocalBinder(); + private Player player; /*////////////////////////////////////////////////////////////////////////// @@ -125,7 +122,7 @@ public final class PlayerService extends Service { } @Override - protected void attachBaseContext(final Context base) { + private void attachBaseContext(final Context base) { super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(base)); } diff --git a/app/src/main/java/org/schabi/newpipe/player/PlayerType.java b/app/src/main/java/org/schabi/newpipe/player/PlayerType.java index 171a70395..2c6fe6f17 100644 --- a/app/src/main/java/org/schabi/newpipe/player/PlayerType.java +++ b/app/src/main/java/org/schabi/newpipe/player/PlayerType.java @@ -1,32 +1,32 @@ package org.schabi.newpipe.player; -import static org.schabi.newpipe.player.Player.PLAYER_TYPE; - import android.content.Intent; +import static org.schabi.newpipe.player.Player.PLAYER_TYPE; + public enum PlayerType { MAIN, AUDIO, POPUP; - /** - * @return an integer representing this {@link PlayerType}, to be used to save it in intents - * @see #retrieveFromIntent(Intent) Use retrieveFromIntent() to retrieve and convert player type - * integers from an intent - */ - public int valueForIntent() { - return ordinal(); - } - /** * @param intent the intent to retrieve a player type from * @return the player type integer retrieved from the intent, converted back into a {@link - * PlayerType}, or {@link PlayerType#MAIN} if there is no player type extra in the - * intent + * PlayerType}, or {@link PlayerType#MAIN} if there is no player type extra in the + * intent * @throws ArrayIndexOutOfBoundsException if the intent contains an invalid player type integer * @see #valueForIntent() Use valueForIntent() to obtain valid player type integers */ public static PlayerType retrieveFromIntent(final Intent intent) { return values()[intent.getIntExtra(PLAYER_TYPE, MAIN.valueForIntent())]; } + + /** + * @return an integer representing this {@link PlayerType}, to be used to save it in intents + * @see #retrieveFromIntent(Intent) Use retrieveFromIntent() to retrieve and convert player type + * integers from an intent + */ + public int valueForIntent() { + return ordinal(); + } } diff --git a/app/src/main/java/org/schabi/newpipe/player/datasource/NonUriHlsDataSourceFactory.java b/app/src/main/java/org/schabi/newpipe/player/datasource/NonUriHlsDataSourceFactory.java index 676443a9c..009bb4bb4 100644 --- a/app/src/main/java/org/schabi/newpipe/player/datasource/NonUriHlsDataSourceFactory.java +++ b/app/src/main/java/org/schabi/newpipe/player/datasource/NonUriHlsDataSourceFactory.java @@ -1,9 +1,6 @@ package org.schabi.newpipe.player.datasource; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; - import androidx.annotation.NonNull; - import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.source.hls.HlsDataSourceFactory; import com.google.android.exoplayer2.upstream.ByteArrayDataSource; @@ -11,6 +8,8 @@ import com.google.android.exoplayer2.upstream.DataSource; import java.nio.charset.StandardCharsets; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + /** * A {@link HlsDataSourceFactory} which allows playback of non-URI media HLS playlists for * {@link com.google.android.exoplayer2.source.hls.HlsMediaSource HlsMediaSource}s. @@ -29,63 +28,8 @@ import java.nio.charset.StandardCharsets; */ public final class NonUriHlsDataSourceFactory implements HlsDataSourceFactory { - /** - * Builder class of {@link NonUriHlsDataSourceFactory} instances. - */ - public static final class Builder { - private DataSource.Factory dataSourceFactory; - private String playlistString; - - /** - * Set the {@link DataSource.Factory} which will be used to create non manifest contents - * {@link DataSource}s. - * - * @param dataSourceFactoryForNonManifestContents the {@link DataSource.Factory} which will - * be used to create non manifest contents - * {@link DataSource}s, which cannot be null - */ - public void setDataSourceFactory( - @NonNull final DataSource.Factory dataSourceFactoryForNonManifestContents) { - this.dataSourceFactory = dataSourceFactoryForNonManifestContents; - } - - /** - * Set the HLS playlist which will be used for manifests requests. - * - * @param hlsPlaylistString the string which correspond to the response of the HLS - * manifest, which cannot be null or empty - */ - public void setPlaylistString(@NonNull final String hlsPlaylistString) { - this.playlistString = hlsPlaylistString; - } - - /** - * Create a new {@link NonUriHlsDataSourceFactory} with the given data source factory and - * the given HLS playlist. - * - * @return a {@link NonUriHlsDataSourceFactory} - * @throws IllegalArgumentException if the data source factory is null or if the HLS - * playlist string set is null or empty - */ - @NonNull - public NonUriHlsDataSourceFactory build() { - if (dataSourceFactory == null) { - throw new IllegalArgumentException( - "No DataSource.Factory valid instance has been specified."); - } - - if (isNullOrEmpty(playlistString)) { - throw new IllegalArgumentException("No HLS valid playlist has been specified."); - } - - return new NonUriHlsDataSourceFactory(dataSourceFactory, - playlistString.getBytes(StandardCharsets.UTF_8)); - } - } - private final DataSource.Factory dataSourceFactory; private final byte[] playlistStringByteArray; - /** * Create a {@link NonUriHlsDataSourceFactory} instance. * @@ -133,4 +77,58 @@ public final class NonUriHlsDataSourceFactory implements HlsDataSourceFactory { return dataSourceFactory.createDataSource(); } + + /** + * Builder class of {@link NonUriHlsDataSourceFactory} instances. + */ + public static final class Builder { + private DataSource.Factory dataSourceFactory; + private String playlistString; + + /** + * Set the {@link DataSource.Factory} which will be used to create non manifest contents + * {@link DataSource}s. + * + * @param dataSourceFactoryForNonManifestContents the {@link DataSource.Factory} which will + * be used to create non manifest contents + * {@link DataSource}s, which cannot be null + */ + public void setDataSourceFactory( + @NonNull final DataSource.Factory dataSourceFactoryForNonManifestContents) { + this.dataSourceFactory = dataSourceFactoryForNonManifestContents; + } + + /** + * Set the HLS playlist which will be used for manifests requests. + * + * @param hlsPlaylistString the string which correspond to the response of the HLS + * manifest, which cannot be null or empty + */ + public void setPlaylistString(@NonNull final String hlsPlaylistString) { + this.playlistString = hlsPlaylistString; + } + + /** + * Create a new {@link NonUriHlsDataSourceFactory} with the given data source factory and + * the given HLS playlist. + * + * @return a {@link NonUriHlsDataSourceFactory} + * @throws IllegalArgumentException if the data source factory is null or if the HLS + * playlist string set is null or empty + */ + @NonNull + public NonUriHlsDataSourceFactory build() { + if (dataSourceFactory == null) { + throw new IllegalArgumentException( + "No DataSource.Factory valid instance has been specified."); + } + + if (isNullOrEmpty(playlistString)) { + throw new IllegalArgumentException("No HLS valid playlist has been specified."); + } + + return new NonUriHlsDataSourceFactory(dataSourceFactory, + playlistString.getBytes(StandardCharsets.UTF_8)); + } + } } diff --git a/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java b/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java index cf1f03b45..7ca908edb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/datasource/YoutubeHttpDataSource.java @@ -7,35 +7,13 @@ package org.schabi.newpipe.player.datasource; -import static com.google.android.exoplayer2.upstream.DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS; -import static com.google.android.exoplayer2.upstream.DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS; -import static com.google.android.exoplayer2.upstream.HttpUtil.buildRangeRequestHeader; -import static com.google.android.exoplayer2.util.Assertions.checkNotNull; -import static com.google.android.exoplayer2.util.Util.castNonNull; -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getAndroidUserAgent; -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.getIosUserAgent; -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isAndroidStreamingUrl; -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isIosStreamingUrl; -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isWebStreamingUrl; -import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isTvHtml5SimplyEmbeddedPlayerStreamingUrl; -import static java.lang.Math.min; - import android.net.Uri; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.PlaybackException; -import com.google.android.exoplayer2.upstream.BaseDataSource; -import com.google.android.exoplayer2.upstream.DataSource; -import com.google.android.exoplayer2.upstream.DataSourceException; -import com.google.android.exoplayer2.upstream.DataSpec; +import com.google.android.exoplayer2.upstream.*; import com.google.android.exoplayer2.upstream.DataSpec.HttpMethod; -import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; -import com.google.android.exoplayer2.upstream.HttpDataSource; -import com.google.android.exoplayer2.upstream.HttpUtil; -import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.Util; import com.google.common.base.Predicate; @@ -43,7 +21,6 @@ import com.google.common.collect.ForwardingMap; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Sets; import com.google.common.net.HttpHeaders; - import org.schabi.newpipe.DownloaderImpl; import java.io.IOException; @@ -55,13 +32,17 @@ import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.NoRouteToHostException; import java.net.URL; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.zip.GZIPInputStream; +import static com.google.android.exoplayer2.upstream.DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS; +import static com.google.android.exoplayer2.upstream.DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS; +import static com.google.android.exoplayer2.upstream.HttpUtil.buildRangeRequestHeader; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; +import static com.google.android.exoplayer2.util.Util.castNonNull; +import static java.lang.Math.min; +import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.*; + /** * An {@link HttpDataSource} that uses Android's {@link HttpURLConnection}, based on * {@link com.google.android.exoplayer2.upstream.DefaultHttpDataSource}, for YouTube streams. @@ -71,7 +52,7 @@ import java.util.zip.GZIPInputStream; * (only where it's relevant) and also more parameters, such as {@code rn} and replaces the use of * the {@code Range} header by the corresponding parameter ({@code range}), if enabled. *

- * + *

* There are many unused methods in this class because everything was copied from {@link * com.google.android.exoplayer2.upstream.DefaultHttpDataSource} with as little changes as possible. * SonarQube warnings were also suppressed for the same reason. @@ -79,209 +60,22 @@ import java.util.zip.GZIPInputStream; @SuppressWarnings({"squid:S3011", "squid:S4738"}) public final class YoutubeHttpDataSource extends BaseDataSource implements HttpDataSource { - /** - * {@link DataSource.Factory} for {@link YoutubeHttpDataSource} instances. - */ - public static final class Factory implements HttpDataSource.Factory { - - private final RequestProperties defaultRequestProperties; - - @Nullable - private TransferListener transferListener; - @Nullable - private Predicate contentTypePredicate; - private int connectTimeoutMs; - private int readTimeoutMs; - private boolean allowCrossProtocolRedirects; - private boolean keepPostFor302Redirects; - - private boolean rangeParameterEnabled; - private boolean rnParameterEnabled; - - /** - * Creates an instance. - */ - public Factory() { - defaultRequestProperties = new RequestProperties(); - connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS; - readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS; - } - - @NonNull - @Override - public Factory setDefaultRequestProperties( - @NonNull final Map defaultRequestPropertiesMap) { - defaultRequestProperties.clearAndSet(defaultRequestPropertiesMap); - return this; - } - - /** - * Sets the connect timeout, in milliseconds. - * - *

- * The default is {@link DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS}. - *

- * - * @param connectTimeoutMsValue The connect timeout, in milliseconds, that will be used. - * @return This factory. - */ - public Factory setConnectTimeoutMs(final int connectTimeoutMsValue) { - connectTimeoutMs = connectTimeoutMsValue; - return this; - } - - /** - * Sets the read timeout, in milliseconds. - * - *

The default is {@link DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS}. - * - * @param readTimeoutMsValue The connect timeout, in milliseconds, that will be used. - * @return This factory. - */ - public Factory setReadTimeoutMs(final int readTimeoutMsValue) { - readTimeoutMs = readTimeoutMsValue; - return this; - } - - /** - * Sets whether to allow cross protocol redirects. - * - *

The default is {@code false}. - * - * @param allowCrossProtocolRedirectsValue Whether to allow cross protocol redirects. - * @return This factory. - */ - public Factory setAllowCrossProtocolRedirects( - final boolean allowCrossProtocolRedirectsValue) { - allowCrossProtocolRedirects = allowCrossProtocolRedirectsValue; - return this; - } - - /** - * Sets whether the use of the {@code range} parameter instead of the {@code Range} header - * to request ranges of streams is enabled. - * - *

- * Note that it must be not enabled on streams which are using a {@link - * com.google.android.exoplayer2.source.ProgressiveMediaSource}, as it will break playback - * for them (some exceptions may be thrown). - *

- * - * @param rangeParameterEnabledValue whether the use of the {@code range} parameter instead - * of the {@code Range} header (must be only enabled when - * non-{@code ProgressiveMediaSource}s) - * @return This factory. - */ - public Factory setRangeParameterEnabled(final boolean rangeParameterEnabledValue) { - rangeParameterEnabled = rangeParameterEnabledValue; - return this; - } - - /** - * Sets whether the use of the {@code rn}, which stands for request number, parameter is - * enabled. - * - *

- * Note that it should be not enabled on streams which are using {@code /} to delimit URLs - * parameters, such as the streams of HLS manifests. - *

- * - * @param rnParameterEnabledValue whether the appending the {@code rn} parameter to - * {@code videoplayback} URLs - * @return This factory. - */ - public Factory setRnParameterEnabled(final boolean rnParameterEnabledValue) { - rnParameterEnabled = rnParameterEnabledValue; - return this; - } - - /** - * Sets a content type {@link Predicate}. If a content type is rejected by the predicate - * then a {@link HttpDataSource.InvalidContentTypeException} is thrown from - * {@link YoutubeHttpDataSource#open(DataSpec)}. - * - *

- * The default is {@code null}. - *

- * - * @param contentTypePredicateToSet The content type {@link Predicate}, or {@code null} to - * clear a predicate that was previously set. - * @return This factory. - */ - public Factory setContentTypePredicate( - @Nullable final Predicate contentTypePredicateToSet) { - this.contentTypePredicate = contentTypePredicateToSet; - return this; - } - - /** - * Sets the {@link TransferListener} that will be used. - * - *

The default is {@code null}. - * - *

See {@link DataSource#addTransferListener(TransferListener)}. - * - * @param transferListenerToUse The listener that will be used. - * @return This factory. - */ - public Factory setTransferListener( - @Nullable final TransferListener transferListenerToUse) { - this.transferListener = transferListenerToUse; - return this; - } - - /** - * Sets whether we should keep the POST method and body when we have HTTP 302 redirects for - * a POST request. - * - * @param keepPostFor302RedirectsValue Whether we should keep the POST method and body when - * we have HTTP 302 redirects for a POST request. - * @return This factory. - */ - public Factory setKeepPostFor302Redirects(final boolean keepPostFor302RedirectsValue) { - this.keepPostFor302Redirects = keepPostFor302RedirectsValue; - return this; - } - - @NonNull - @Override - public YoutubeHttpDataSource createDataSource() { - final YoutubeHttpDataSource dataSource = new YoutubeHttpDataSource( - connectTimeoutMs, - readTimeoutMs, - allowCrossProtocolRedirects, - rangeParameterEnabled, - rnParameterEnabled, - defaultRequestProperties, - contentTypePredicate, - keepPostFor302Redirects); - if (transferListener != null) { - dataSource.addTransferListener(transferListener); - } - return dataSource; - } - } - private static final String TAG = YoutubeHttpDataSource.class.getSimpleName(); private static final int MAX_REDIRECTS = 20; // Same limit as okhttp. private static final int HTTP_STATUS_TEMPORARY_REDIRECT = 307; private static final int HTTP_STATUS_PERMANENT_REDIRECT = 308; private static final long MAX_BYTES_TO_DRAIN = 2048; - private static final String RN_PARAMETER = "&rn="; private static final String YOUTUBE_BASE_URL = "https://www.youtube.com"; - private final boolean allowCrossProtocolRedirects; private final boolean rangeParameterEnabled; private final boolean rnParameterEnabled; - private final int connectTimeoutMillis; private final int readTimeoutMillis; @Nullable private final RequestProperties defaultRequestProperties; private final RequestProperties requestProperties; private final boolean keepPostFor302Redirects; - @Nullable private final Predicate contentTypePredicate; @Nullable @@ -294,7 +88,6 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD private int responseCode; private long bytesToRead; private long bytesRead; - private long requestNumber; @SuppressWarnings("checkstyle:ParameterNumber") @@ -319,6 +112,92 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD this.requestNumber = 0; } + /** + * On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can + * block for a long time if the stream has a lot of data remaining. Call this method before + * closing the input stream to make a best effort to cause the input stream to encounter an + * unexpected end of input, working around this issue. On other platform API levels, the method + * does nothing. + * + * @param connection The connection whose {@link InputStream} should be terminated. + * @param bytesRemaining The number of bytes remaining to be read from the input stream if its + * length is known. {@link C#LENGTH_UNSET} otherwise. + */ + private static void maybeTerminateInputStream(@Nullable final HttpURLConnection connection, + final long bytesRemaining) { + if (connection == null || Util.SDK_INT < 19 || Util.SDK_INT > 20) { + return; + } + + try { + final InputStream inputStream = connection.getInputStream(); + if (bytesRemaining == C.LENGTH_UNSET) { + // If the input stream has already ended, do nothing. The socket may be re-used. + if (inputStream.read() == -1) { + return; + } + } else if (bytesRemaining <= MAX_BYTES_TO_DRAIN) { + // There isn't much data left. Prefer to allow it to drain, which may allow the + // socket to be re-used. + return; + } + final String className = inputStream.getClass().getName(); + if ("com.android.okhttp.internal.http.HttpTransport$ChunkedInputStream" + .equals(className) + || "com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream" + .equals(className)) { + final Class superclass = inputStream.getClass().getSuperclass(); + final Method unexpectedEndOfInput = checkNotNull(superclass).getDeclaredMethod( + "unexpectedEndOfInput"); + unexpectedEndOfInput.setAccessible(true); + unexpectedEndOfInput.invoke(inputStream); + } + } catch (final Exception e) { + // If an IOException then the connection didn't ever have an input stream, or it was + // closed already. If another type of exception then something went wrong, most likely + // the device isn't using okhttp. + } + } + + private static boolean isCompressed(@NonNull final HttpURLConnection connection) { + final String contentEncoding = connection.getHeaderField("Content-Encoding"); + return "gzip".equalsIgnoreCase(contentEncoding); + } + + /** + * Builds a {@code range} parameter for the given position and length. + * + *

+ * To fetch its contents, YouTube use range requests which append a {@code range} parameter + * to videoplayback URLs instead of the {@code Range} header (even if the server respond + * correctly when requesting a range of a ressouce with it). + *

+ * + *

+ * The parameter works in the same way as the header. + *

+ * + * @param position The request position. + * @param length The request length, or {@link C#LENGTH_UNSET} if the request is unbounded. + * @return The corresponding {@code range} parameter, or {@code null} if this parameter is + * unnecessary because the whole resource is being requested. + */ + @Nullable + private static String buildRangeParameter(final long position, final long length) { + if (position == 0 && length == C.LENGTH_UNSET) { + return null; + } + + final StringBuilder rangeParameter = new StringBuilder(); + rangeParameter.append("&range="); + rangeParameter.append(position); + rangeParameter.append("-"); + if (length != C.LENGTH_UNSET) { + rangeParameter.append(position + length - 1); + } + return rangeParameter.toString(); + } + @Override @Nullable public Uri getUri() { @@ -776,7 +655,7 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD * @param bytesToSkip The number of bytes to skip. * @param dataSpecToUse The {@link DataSpec}. * @throws IOException If the thread is interrupted during the operation, or if the data ended - * before skipping the specified number of bytes. + * before skipping the specified number of bytes. */ @SuppressWarnings("checkstyle:FinalParameters") private void skipFully(long bytesToSkip, final DataSpec dataSpecToUse) throws IOException { @@ -848,53 +727,6 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD return read; } - /** - * On platform API levels 19 and 20, okhttp's implementation of {@link InputStream#close} can - * block for a long time if the stream has a lot of data remaining. Call this method before - * closing the input stream to make a best effort to cause the input stream to encounter an - * unexpected end of input, working around this issue. On other platform API levels, the method - * does nothing. - * - * @param connection The connection whose {@link InputStream} should be terminated. - * @param bytesRemaining The number of bytes remaining to be read from the input stream if its - * length is known. {@link C#LENGTH_UNSET} otherwise. - */ - private static void maybeTerminateInputStream(@Nullable final HttpURLConnection connection, - final long bytesRemaining) { - if (connection == null || Util.SDK_INT < 19 || Util.SDK_INT > 20) { - return; - } - - try { - final InputStream inputStream = connection.getInputStream(); - if (bytesRemaining == C.LENGTH_UNSET) { - // If the input stream has already ended, do nothing. The socket may be re-used. - if (inputStream.read() == -1) { - return; - } - } else if (bytesRemaining <= MAX_BYTES_TO_DRAIN) { - // There isn't much data left. Prefer to allow it to drain, which may allow the - // socket to be re-used. - return; - } - final String className = inputStream.getClass().getName(); - if ("com.android.okhttp.internal.http.HttpTransport$ChunkedInputStream" - .equals(className) - || "com.android.okhttp.internal.http.HttpTransport$FixedLengthInputStream" - .equals(className)) { - final Class superclass = inputStream.getClass().getSuperclass(); - final Method unexpectedEndOfInput = checkNotNull(superclass).getDeclaredMethod( - "unexpectedEndOfInput"); - unexpectedEndOfInput.setAccessible(true); - unexpectedEndOfInput.invoke(inputStream); - } - } catch (final Exception e) { - // If an IOException then the connection didn't ever have an input stream, or it was - // closed already. If another type of exception then something went wrong, most likely - // the device isn't using okhttp. - } - } - /** * Closes the current connection quietly, if there is one. */ @@ -909,43 +741,187 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD } } - private static boolean isCompressed(@NonNull final HttpURLConnection connection) { - final String contentEncoding = connection.getHeaderField("Content-Encoding"); - return "gzip".equalsIgnoreCase(contentEncoding); - } - /** - * Builds a {@code range} parameter for the given position and length. - * - *

- * To fetch its contents, YouTube use range requests which append a {@code range} parameter - * to videoplayback URLs instead of the {@code Range} header (even if the server respond - * correctly when requesting a range of a ressouce with it). - *

- * - *

- * The parameter works in the same way as the header. - *

- * - * @param position The request position. - * @param length The request length, or {@link C#LENGTH_UNSET} if the request is unbounded. - * @return The corresponding {@code range} parameter, or {@code null} if this parameter is - * unnecessary because the whole resource is being requested. + * {@link DataSource.Factory} for {@link YoutubeHttpDataSource} instances. */ - @Nullable - private static String buildRangeParameter(final long position, final long length) { - if (position == 0 && length == C.LENGTH_UNSET) { - return null; + public static final class Factory implements HttpDataSource.Factory { + + private final RequestProperties defaultRequestProperties; + + @Nullable + private TransferListener transferListener; + @Nullable + private Predicate contentTypePredicate; + private int connectTimeoutMs; + private int readTimeoutMs; + private boolean allowCrossProtocolRedirects; + private boolean keepPostFor302Redirects; + + private boolean rangeParameterEnabled; + private boolean rnParameterEnabled; + + /** + * Creates an instance. + */ + public Factory() { + defaultRequestProperties = new RequestProperties(); + connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MILLIS; + readTimeoutMs = DEFAULT_READ_TIMEOUT_MILLIS; } - final StringBuilder rangeParameter = new StringBuilder(); - rangeParameter.append("&range="); - rangeParameter.append(position); - rangeParameter.append("-"); - if (length != C.LENGTH_UNSET) { - rangeParameter.append(position + length - 1); + @NonNull + @Override + public Factory setDefaultRequestProperties( + @NonNull final Map defaultRequestPropertiesMap) { + defaultRequestProperties.clearAndSet(defaultRequestPropertiesMap); + return this; + } + + /** + * Sets the connect timeout, in milliseconds. + * + *

+ * The default is {@link DefaultHttpDataSource#DEFAULT_CONNECT_TIMEOUT_MILLIS}. + *

+ * + * @param connectTimeoutMsValue The connect timeout, in milliseconds, that will be used. + * @return This factory. + */ + public Factory setConnectTimeoutMs(final int connectTimeoutMsValue) { + connectTimeoutMs = connectTimeoutMsValue; + return this; + } + + /** + * Sets the read timeout, in milliseconds. + * + *

The default is {@link DefaultHttpDataSource#DEFAULT_READ_TIMEOUT_MILLIS}. + * + * @param readTimeoutMsValue The connect timeout, in milliseconds, that will be used. + * @return This factory. + */ + public Factory setReadTimeoutMs(final int readTimeoutMsValue) { + readTimeoutMs = readTimeoutMsValue; + return this; + } + + /** + * Sets whether to allow cross protocol redirects. + * + *

The default is {@code false}. + * + * @param allowCrossProtocolRedirectsValue Whether to allow cross protocol redirects. + * @return This factory. + */ + public Factory setAllowCrossProtocolRedirects( + final boolean allowCrossProtocolRedirectsValue) { + allowCrossProtocolRedirects = allowCrossProtocolRedirectsValue; + return this; + } + + /** + * Sets whether the use of the {@code range} parameter instead of the {@code Range} header + * to request ranges of streams is enabled. + * + *

+ * Note that it must be not enabled on streams which are using a {@link + * com.google.android.exoplayer2.source.ProgressiveMediaSource}, as it will break playback + * for them (some exceptions may be thrown). + *

+ * + * @param rangeParameterEnabledValue whether the use of the {@code range} parameter instead + * of the {@code Range} header (must be only enabled when + * non-{@code ProgressiveMediaSource}s) + * @return This factory. + */ + public Factory setRangeParameterEnabled(final boolean rangeParameterEnabledValue) { + rangeParameterEnabled = rangeParameterEnabledValue; + return this; + } + + /** + * Sets whether the use of the {@code rn}, which stands for request number, parameter is + * enabled. + * + *

+ * Note that it should be not enabled on streams which are using {@code /} to delimit URLs + * parameters, such as the streams of HLS manifests. + *

+ * + * @param rnParameterEnabledValue whether the appending the {@code rn} parameter to + * {@code videoplayback} URLs + * @return This factory. + */ + public Factory setRnParameterEnabled(final boolean rnParameterEnabledValue) { + rnParameterEnabled = rnParameterEnabledValue; + return this; + } + + /** + * Sets a content type {@link Predicate}. If a content type is rejected by the predicate + * then a {@link HttpDataSource.InvalidContentTypeException} is thrown from + * {@link YoutubeHttpDataSource#open(DataSpec)}. + * + *

+ * The default is {@code null}. + *

+ * + * @param contentTypePredicateToSet The content type {@link Predicate}, or {@code null} to + * clear a predicate that was previously set. + * @return This factory. + */ + public Factory setContentTypePredicate( + @Nullable final Predicate contentTypePredicateToSet) { + this.contentTypePredicate = contentTypePredicateToSet; + return this; + } + + /** + * Sets the {@link TransferListener} that will be used. + * + *

The default is {@code null}. + * + *

See {@link DataSource#addTransferListener(TransferListener)}. + * + * @param transferListenerToUse The listener that will be used. + * @return This factory. + */ + public Factory setTransferListener( + @Nullable final TransferListener transferListenerToUse) { + this.transferListener = transferListenerToUse; + return this; + } + + /** + * Sets whether we should keep the POST method and body when we have HTTP 302 redirects for + * a POST request. + * + * @param keepPostFor302RedirectsValue Whether we should keep the POST method and body when + * we have HTTP 302 redirects for a POST request. + * @return This factory. + */ + public Factory setKeepPostFor302Redirects(final boolean keepPostFor302RedirectsValue) { + this.keepPostFor302Redirects = keepPostFor302RedirectsValue; + return this; + } + + @NonNull + @Override + public YoutubeHttpDataSource createDataSource() { + final YoutubeHttpDataSource dataSource = new YoutubeHttpDataSource( + connectTimeoutMs, + readTimeoutMs, + allowCrossProtocolRedirects, + rangeParameterEnabled, + rnParameterEnabled, + defaultRequestProperties, + contentTypePredicate, + keepPostFor302Redirects); + if (transferListener != null) { + dataSource.addTransferListener(transferListener); + } + return dataSource; } - return rangeParameter.toString(); } private static final class NullFilteringHeadersMap @@ -958,7 +934,7 @@ public final class YoutubeHttpDataSource extends BaseDataSource implements HttpD @NonNull @Override - protected Map> delegate() { + private Map> delegate() { return headers; } diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java index 84bd9d277..ee9c82529 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerEventListener.java @@ -1,15 +1,18 @@ package org.schabi.newpipe.player.event; import com.google.android.exoplayer2.PlaybackParameters; - import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.playqueue.PlayQueue; public interface PlayerEventListener { void onQueueUpdate(PlayQueue queue); + void onPlaybackUpdate(int state, int repeatMode, boolean shuffled, PlaybackParameters parameters); + void onProgressUpdate(int currentProgress, int duration, int bufferPercent); + void onMetadataUpdate(StreamInfo info, PlayQueue queue); + void onServiceStopped(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceExtendedEventListener.java b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceExtendedEventListener.java index 8effe2f0e..4a54e29e7 100644 --- a/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceExtendedEventListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/event/PlayerServiceExtendedEventListener.java @@ -1,11 +1,12 @@ package org.schabi.newpipe.player.event; -import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.Player; +import org.schabi.newpipe.player.PlayerService; public interface PlayerServiceExtendedEventListener extends PlayerServiceEventListener { void onServiceConnected(Player player, PlayerService playerService, boolean playAfterConnect); + void onServiceDisconnected(); } diff --git a/app/src/main/java/org/schabi/newpipe/player/gesture/CustomBottomSheetBehavior.java b/app/src/main/java/org/schabi/newpipe/player/gesture/CustomBottomSheetBehavior.java index 0970dbeb6..e123b29e1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/gesture/CustomBottomSheetBehavior.java +++ b/app/src/main/java/org/schabi/newpipe/player/gesture/CustomBottomSheetBehavior.java @@ -6,30 +6,26 @@ import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.coordinatorlayout.widget.CoordinatorLayout; - import com.google.android.material.bottomsheet.BottomSheetBehavior; - import org.schabi.newpipe.R; import java.util.List; public class CustomBottomSheetBehavior extends BottomSheetBehavior { - public CustomBottomSheetBehavior(@NonNull final Context context, - @Nullable final AttributeSet attrs) { - super(context, attrs); - } - - Rect globalRect = new Rect(); - private boolean skippingInterception = false; private final List skipInterceptionOfElements = List.of( R.id.detail_content_root_layout, R.id.relatedItemsLayout, R.id.itemsListPanel, R.id.view_pager, R.id.tab_layout, R.id.bottomControls, R.id.playPauseButton, R.id.playPreviousButton, R.id.playNextButton); + Rect globalRect = new Rect(); + private boolean skippingInterception = false; + public CustomBottomSheetBehavior(@NonNull final Context context, + @Nullable final AttributeSet attrs) { + super(context, attrs); + } @Override public boolean onInterceptTouchEvent(@NonNull final CoordinatorLayout parent, diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java index a05990816..9384dabe8 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/AudioReactor.java @@ -8,12 +8,10 @@ import android.content.Intent; import android.media.AudioManager; import android.media.audiofx.AudioEffect; import android.util.Log; - import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.media.AudioFocusRequestCompat; import androidx.media.AudioManagerCompat; - import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.analytics.AnalyticsListener; @@ -153,6 +151,7 @@ public class AudioReactor implements AudioManager.OnAudioFocusChangeListener, An final int audioSessionId) { notifyAudioSessionUpdate(true, audioSessionId); } + private void notifyAudioSessionUpdate(final boolean active, final int audioSessionId) { if (!PlayerHelper.isUsingDSP()) { return; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java b/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java index 41fcc823a..cff008f87 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/CacheFactory.java @@ -1,9 +1,7 @@ package org.schabi.newpipe.player.helper; import android.content.Context; - import androidx.annotation.NonNull; - import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultDataSource; import com.google.android.exoplayer2.upstream.FileDataSource; diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java b/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java index 270156fe9..66adc56b6 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/LockManager.java @@ -4,7 +4,6 @@ import android.content.Context; import android.net.wifi.WifiManager; import android.os.PowerManager; import android.util.Log; - import androidx.core.content.ContextCompat; public class LockManager { diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java index 796208a04..ea7bc919b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlaybackParameterDialog.java @@ -1,10 +1,5 @@ package org.schabi.newpipe.player.helper; -import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; -import static org.schabi.newpipe.player.Player.DEBUG; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; -import static org.schabi.newpipe.util.ThemeHelper.resolveDrawable; - import android.app.Dialog; import android.content.Context; import android.graphics.drawable.Drawable; @@ -15,7 +10,6 @@ import android.view.View; import android.widget.CheckBox; import android.widget.SeekBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; @@ -23,7 +17,8 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.math.MathUtils; import androidx.fragment.app.DialogFragment; import androidx.preference.PreferenceManager; - +import icepick.Icepick; +import icepick.State; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.DialogPlaybackParameterBinding; import org.schabi.newpipe.player.ui.VideoPlayerUi; @@ -37,8 +32,10 @@ import java.util.function.DoubleConsumer; import java.util.function.DoubleFunction; import java.util.function.DoubleSupplier; -import icepick.Icepick; -import icepick.State; +import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; +import static org.schabi.newpipe.player.Player.DEBUG; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; +import static org.schabi.newpipe.util.ThemeHelper.resolveDrawable; public class PlaybackParameterDialog extends DialogFragment { private static final String TAG = "PlaybackParameterDialog"; @@ -78,24 +75,20 @@ public class PlaybackParameterDialog extends DialogFragment { return PlayerSemitoneHelper.semitonesToPercent(progress - 12); } }; - - @Nullable - private Callback callback; - @State double initialTempo = DEFAULT_TEMPO; @State double initialPitchPercent = DEFAULT_PITCH_PERCENT; @State boolean initialSkipSilence = DEFAULT_SKIP_SILENCE; - @State double tempo = DEFAULT_TEMPO; @State double pitchPercent = DEFAULT_PITCH_PERCENT; @State boolean skipSilence = DEFAULT_SKIP_SILENCE; - + @Nullable + private Callback callback; private DialogPlaybackParameterBinding binding; public static PlaybackParameterDialog newInstance( @@ -122,6 +115,29 @@ public class PlaybackParameterDialog extends DialogFragment { // Lifecycle //////////////////////////////////////////////////////////////////////////*/ + @NonNull + private static String getStepUpPercentString(final double percent) { + return '+' + getPercentString(percent); + } + + @NonNull + private static String getStepDownPercentString(final double percent) { + return '-' + getPercentString(percent); + } + + /*////////////////////////////////////////////////////////////////////////// + // Dialog + //////////////////////////////////////////////////////////////////////////*/ + + @NonNull + private static String getPercentString(final double percent) { + return PlayerHelper.formatPitch(percent); + } + + /*////////////////////////////////////////////////////////////////////////// + // UI Initialization and Control + //////////////////////////////////////////////////////////////////////////*/ + @Override public void onAttach(@NonNull final Context context) { super.onAttach(context); @@ -132,15 +148,15 @@ public class PlaybackParameterDialog extends DialogFragment { } } + // -- General formatting -- + @Override public void onSaveInstanceState(@NonNull final Bundle outState) { super.onSaveInstanceState(outState); Icepick.saveInstanceState(this, outState); } - /*////////////////////////////////////////////////////////////////////////// - // Dialog - //////////////////////////////////////////////////////////////////////////*/ + // -- Steps -- @NonNull @Override @@ -171,10 +187,6 @@ public class PlaybackParameterDialog extends DialogFragment { return dialogBuilder.create(); } - /*////////////////////////////////////////////////////////////////////////// - // UI Initialization and Control - //////////////////////////////////////////////////////////////////////////*/ - private void initUI() { // Tempo setText(binding.tempoMinimumText, PlayerHelper::formatSpeed, MIN_PITCH_OR_SPEED); @@ -279,7 +291,7 @@ public class PlaybackParameterDialog extends DialogFragment { changePitchControlMode(isCurrentPitchControlModeSemitone()); } - // -- General formatting -- + // -- Pitch -- private void setText( final TextView textView, @@ -289,8 +301,6 @@ public class PlaybackParameterDialog extends DialogFragment { Objects.requireNonNull(textView).setText(formatter.apply(value)); } - // -- Steps -- - private void registerOnStepClickListener( final TextView stepTextView, final DoubleSupplier currentValueSupplier, @@ -316,8 +326,6 @@ public class PlaybackParameterDialog extends DialogFragment { }); } - // -- Pitch -- - private void setupPitchControlModeTextView( final boolean semitones, final TextView textView @@ -332,6 +340,8 @@ public class PlaybackParameterDialog extends DialogFragment { }); } + // -- Steps (Set) -- + private Map getPitchControlModeComponentMappings() { return Map.of(PITCH_CTRL_MODE_PERCENT, binding.pitchControlModePercent, PITCH_CTRL_MODE_SEMITONE, binding.pitchControlModeSemitone); @@ -386,8 +396,6 @@ public class PlaybackParameterDialog extends DialogFragment { PITCH_CTRL_MODE_PERCENT); } - // -- Steps (Set) -- - private void setupStepTextView( final double stepSizeValue, final TextView textView @@ -403,6 +411,8 @@ public class PlaybackParameterDialog extends DialogFragment { }); } + // -- Additional options -- + private Map getStepSizeComponentMappings() { return Map.of(STEP_1_PERCENT_VALUE, binding.stepSizeOnePercent, STEP_5_PERCENT_VALUE, binding.stepSizeFivePercent, @@ -439,7 +449,9 @@ public class PlaybackParameterDialog extends DialogFragment { .getFloat(getString(R.string.adjustment_step_key), (float) DEFAULT_STEP); } - // -- Additional options -- + /*////////////////////////////////////////////////////////////////////////// + // Sliders + //////////////////////////////////////////////////////////////////////////*/ private void setAndUpdateSkipSilence(final boolean newSkipSilence) { this.skipSilence = newSkipSilence; @@ -484,10 +496,6 @@ public class PlaybackParameterDialog extends DialogFragment { } } - /*////////////////////////////////////////////////////////////////////////// - // Sliders - //////////////////////////////////////////////////////////////////////////*/ - private SeekBar.OnSeekBarChangeListener getTempoOrPitchSeekbarChangeListener( final SliderStrategy sliderStrategy, final DoubleConsumer newValueConsumer @@ -526,6 +534,10 @@ public class PlaybackParameterDialog extends DialogFragment { setAndUpdatePitch(newValue); } + /*////////////////////////////////////////////////////////////////////////// + // Helper + //////////////////////////////////////////////////////////////////////////*/ + private void setAndUpdateTempo(final double newTempo) { this.tempo = MathUtils.clamp(newTempo, MIN_PITCH_OR_SPEED, MAX_PITCH_OR_SPEED); @@ -557,10 +569,6 @@ public class PlaybackParameterDialog extends DialogFragment { PlayerSemitoneHelper.percentToSemitones(calcPitch)); } - /*////////////////////////////////////////////////////////////////////////// - // Helper - //////////////////////////////////////////////////////////////////////////*/ - private void updateCallback() { if (callback == null) { return; @@ -575,21 +583,6 @@ public class PlaybackParameterDialog extends DialogFragment { callback.onPlaybackParameterChanged((float) tempo, (float) pitchPercent, skipSilence); } - @NonNull - private static String getStepUpPercentString(final double percent) { - return '+' + getPercentString(percent); - } - - @NonNull - private static String getStepDownPercentString(final double percent) { - return '-' + getPercentString(percent); - } - - @NonNull - private static String getPercentString(final double percent) { - return PlayerHelper.formatPitch(percent); - } - public interface Callback { void onPlaybackParameterChanged(float playbackTempo, float playbackPitch, boolean playbackSkipSilence); diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java index 0530d56e9..14af4e804 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerDataSource.java @@ -1,12 +1,8 @@ package org.schabi.newpipe.player.helper; -import static org.schabi.newpipe.MainActivity.DEBUG; - import android.content.Context; import android.util.Log; - import androidx.annotation.Nullable; - import com.google.android.exoplayer2.database.StandaloneDatabaseProvider; import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.source.SingleSampleMediaSource; @@ -22,7 +18,6 @@ import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; import com.google.android.exoplayer2.upstream.TransferListener; import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor; import com.google.android.exoplayer2.upstream.cache.SimpleCache; - import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeOtfDashManifestCreator; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubePostLiveStreamDvrDashManifestCreator; @@ -32,6 +27,8 @@ import org.schabi.newpipe.player.datasource.YoutubeHttpDataSource; import java.io.File; +import static org.schabi.newpipe.MainActivity.DEBUG; + public class PlayerDataSource { public static final String TAG = PlayerDataSource.class.getSimpleName(); @@ -108,82 +105,6 @@ public class PlayerDataSource { MAX_MANIFEST_CACHE_SIZE); } - - //region Live media source factories - public SsMediaSource.Factory getLiveSsMediaSourceFactory() { - return getSSMediaSourceFactory().setLivePresentationDelayMs(LIVE_STREAM_EDGE_GAP_MILLIS); - } - - public HlsMediaSource.Factory getLiveHlsMediaSourceFactory() { - return new HlsMediaSource.Factory(cachelessDataSourceFactory) - .setAllowChunklessPreparation(true) - .setPlaylistTrackerFactory((dataSourceFactory, loadErrorHandlingPolicy, - playlistParserFactory) -> - new DefaultHlsPlaylistTracker(dataSourceFactory, loadErrorHandlingPolicy, - playlistParserFactory, - PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT)); - } - - public DashMediaSource.Factory getLiveDashMediaSourceFactory() { - return new DashMediaSource.Factory( - getDefaultDashChunkSourceFactory(cachelessDataSourceFactory), - cachelessDataSourceFactory); - } - //endregion - - - //region Generic media source factories - public HlsMediaSource.Factory getHlsMediaSourceFactory( - @Nullable final NonUriHlsDataSourceFactory.Builder hlsDataSourceFactoryBuilder) { - if (hlsDataSourceFactoryBuilder != null) { - hlsDataSourceFactoryBuilder.setDataSourceFactory(cacheDataSourceFactory); - return new HlsMediaSource.Factory(hlsDataSourceFactoryBuilder.build()); - } - - return new HlsMediaSource.Factory(cacheDataSourceFactory); - } - - public DashMediaSource.Factory getDashMediaSourceFactory() { - return new DashMediaSource.Factory( - getDefaultDashChunkSourceFactory(cacheDataSourceFactory), - cacheDataSourceFactory); - } - - public ProgressiveMediaSource.Factory getProgressiveMediaSourceFactory() { - return new ProgressiveMediaSource.Factory(cacheDataSourceFactory) - .setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes); - } - - public SsMediaSource.Factory getSSMediaSourceFactory() { - return new SsMediaSource.Factory( - new DefaultSsChunkSource.Factory(cachelessDataSourceFactory), - cachelessDataSourceFactory); - } - - public SingleSampleMediaSource.Factory getSingleSampleMediaSourceFactory() { - return new SingleSampleMediaSource.Factory(cacheDataSourceFactory); - } - //endregion - - - //region YouTube media source factories - public HlsMediaSource.Factory getYoutubeHlsMediaSourceFactory() { - return new HlsMediaSource.Factory(ytHlsCacheDataSourceFactory); - } - - public DashMediaSource.Factory getYoutubeDashMediaSourceFactory() { - return new DashMediaSource.Factory( - getDefaultDashChunkSourceFactory(ytDashCacheDataSourceFactory), - ytDashCacheDataSourceFactory); - } - - public ProgressiveMediaSource.Factory getYoutubeProgressiveMediaSourceFactory() { - return new ProgressiveMediaSource.Factory(ytProgressiveDashCacheDataSourceFactory) - .setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes); - } - //endregion - - //region Static methods private static DefaultDashChunkSource.Factory getDefaultDashChunkSourceFactory( final DataSource.Factory dataSourceFactory) { @@ -214,4 +135,76 @@ public class PlayerDataSource { } } //endregion + + //region Live media source factories + public SsMediaSource.Factory getLiveSsMediaSourceFactory() { + return getSSMediaSourceFactory().setLivePresentationDelayMs(LIVE_STREAM_EDGE_GAP_MILLIS); + } + + public HlsMediaSource.Factory getLiveHlsMediaSourceFactory() { + return new HlsMediaSource.Factory(cachelessDataSourceFactory) + .setAllowChunklessPreparation(true) + .setPlaylistTrackerFactory((dataSourceFactory, loadErrorHandlingPolicy, + playlistParserFactory) -> + new DefaultHlsPlaylistTracker(dataSourceFactory, loadErrorHandlingPolicy, + playlistParserFactory, + PLAYLIST_STUCK_TARGET_DURATION_COEFFICIENT)); + } + + public DashMediaSource.Factory getLiveDashMediaSourceFactory() { + return new DashMediaSource.Factory( + getDefaultDashChunkSourceFactory(cachelessDataSourceFactory), + cachelessDataSourceFactory); + } + + //region Generic media source factories + public HlsMediaSource.Factory getHlsMediaSourceFactory( + @Nullable final NonUriHlsDataSourceFactory.Builder hlsDataSourceFactoryBuilder) { + if (hlsDataSourceFactoryBuilder != null) { + hlsDataSourceFactoryBuilder.setDataSourceFactory(cacheDataSourceFactory); + return new HlsMediaSource.Factory(hlsDataSourceFactoryBuilder.build()); + } + + return new HlsMediaSource.Factory(cacheDataSourceFactory); + } + + public DashMediaSource.Factory getDashMediaSourceFactory() { + return new DashMediaSource.Factory( + getDefaultDashChunkSourceFactory(cacheDataSourceFactory), + cacheDataSourceFactory); + } + //endregion + + public ProgressiveMediaSource.Factory getProgressiveMediaSourceFactory() { + return new ProgressiveMediaSource.Factory(cacheDataSourceFactory) + .setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes); + } + + public SsMediaSource.Factory getSSMediaSourceFactory() { + return new SsMediaSource.Factory( + new DefaultSsChunkSource.Factory(cachelessDataSourceFactory), + cachelessDataSourceFactory); + } + + public SingleSampleMediaSource.Factory getSingleSampleMediaSourceFactory() { + return new SingleSampleMediaSource.Factory(cacheDataSourceFactory); + } + //endregion + + //region YouTube media source factories + public HlsMediaSource.Factory getYoutubeHlsMediaSourceFactory() { + return new HlsMediaSource.Factory(ytHlsCacheDataSourceFactory); + } + + public DashMediaSource.Factory getYoutubeDashMediaSourceFactory() { + return new DashMediaSource.Factory( + getDefaultDashChunkSourceFactory(ytDashCacheDataSourceFactory), + ytDashCacheDataSourceFactory); + } + + public ProgressiveMediaSource.Factory getYoutubeProgressiveMediaSourceFactory() { + return new ProgressiveMediaSource.Factory(ytProgressiveDashCacheDataSourceFactory) + .setContinueLoadingCheckIntervalBytes(progressiveLoadIntervalBytes); + } + //endregion } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java index abde7c3d1..69f54fcca 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHelper.java @@ -1,30 +1,17 @@ package org.schabi.newpipe.player.helper; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_OFF; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_ALWAYS; -import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_NEVER; -import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.AUTOPLAY_TYPE_WIFI; -import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND; -import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE; -import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP; -import static java.lang.annotation.RetentionPolicy.SOURCE; - import android.annotation.SuppressLint; import android.content.Context; import android.content.SharedPreferences; import android.provider.Settings; import android.view.accessibility.CaptioningManager; - import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; - import com.google.android.exoplayer2.PlaybackParameters; -import com.google.android.exoplayer2.Player.RepeatMode; +import com.google.android.exoplayer2.Player.*; import com.google.android.exoplayer2.SeekParameters; import com.google.android.exoplayer2.source.ProgressiveMediaSource; import com.google.android.exoplayer2.trackselection.AdaptiveTrackSelection; @@ -33,7 +20,6 @@ import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout.ResizeMode; import com.google.android.exoplayer2.ui.CaptionStyleCompat; import com.google.android.exoplayer2.util.MimeTypes; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.InfoItem; import org.schabi.newpipe.extractor.MediaFormat; @@ -50,16 +36,14 @@ import org.schabi.newpipe.util.ListHelper; import java.lang.annotation.Retention; import java.text.DecimalFormat; import java.text.NumberFormat; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Formatter; -import java.util.HashSet; -import java.util.List; -import java.util.Locale; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; +import static com.google.android.exoplayer2.Player.*; +import static java.lang.annotation.RetentionPolicy.SOURCE; +import static org.schabi.newpipe.player.helper.PlayerHelper.AutoplayType.*; +import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.*; + public final class PlayerHelper { private static final StringBuilder STRING_BUILDER = new StringBuilder(); private static final Formatter STRING_FORMATTER = @@ -67,31 +51,9 @@ public final class PlayerHelper { private static final NumberFormat SPEED_FORMATTER = new DecimalFormat("0.##x"); private static final NumberFormat PITCH_FORMATTER = new DecimalFormat("##%"); - @Retention(SOURCE) - @IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI, - AUTOPLAY_TYPE_NEVER}) - public @interface AutoplayType { - int AUTOPLAY_TYPE_ALWAYS = 0; - int AUTOPLAY_TYPE_WIFI = 1; - int AUTOPLAY_TYPE_NEVER = 2; - } - - @Retention(SOURCE) - @IntDef({MINIMIZE_ON_EXIT_MODE_NONE, MINIMIZE_ON_EXIT_MODE_BACKGROUND, - MINIMIZE_ON_EXIT_MODE_POPUP}) - public @interface MinimizeMode { - int MINIMIZE_ON_EXIT_MODE_NONE = 0; - int MINIMIZE_ON_EXIT_MODE_BACKGROUND = 1; - int MINIMIZE_ON_EXIT_MODE_POPUP = 2; - } - private PlayerHelper() { } - //////////////////////////////////////////////////////////////////////////// - // Exposed helpers - //////////////////////////////////////////////////////////////////////////// - @NonNull public static String getTimeString(final int milliSeconds) { final int seconds = (milliSeconds % 60000) / 1000; @@ -113,6 +75,10 @@ public final class PlayerHelper { return SPEED_FORMATTER.format(speed); } + //////////////////////////////////////////////////////////////////////////// + // Exposed helpers + //////////////////////////////////////////////////////////////////////////// + @NonNull public static String formatPitch(final double pitch) { return PITCH_FORMATTER.format(pitch); @@ -218,10 +184,6 @@ public final class PlayerHelper { ? null : getAutoQueuedSinglePlayQueue(autoQueueItems.get(0)); } - //////////////////////////////////////////////////////////////////////////// - // Settings Resolution - //////////////////////////////////////////////////////////////////////////// - public static boolean isResumeAfterAudioFocusGain(@NonNull final Context context) { return getPreferences(context) .getBoolean(context.getString(R.string.resume_on_audio_focus_gain_key), false); @@ -232,6 +194,10 @@ public final class PlayerHelper { .getBoolean(context.getString(R.string.volume_gesture_control_key), true); } + //////////////////////////////////////////////////////////////////////////// + // Settings Resolution + //////////////////////////////////////////////////////////////////////////// + public static boolean isBrightnessGestureEnabled(@NonNull final Context context) { return getPreferences(context) .getBoolean(context.getString(R.string.brightness_gesture_control_key), true); @@ -399,10 +365,6 @@ public final class PlayerHelper { return Integer.parseInt(preferredIntervalBytes) * 1024; } - //////////////////////////////////////////////////////////////////////////// - // Private helpers - //////////////////////////////////////////////////////////////////////////// - @NonNull private static SharedPreferences getPreferences(@NonNull final Context context) { return PreferenceManager.getDefaultSharedPreferences(context); @@ -413,6 +375,10 @@ public final class PlayerHelper { .getBoolean(context.getString(R.string.use_inexact_seek_key), false); } + //////////////////////////////////////////////////////////////////////////// + // Private helpers + //////////////////////////////////////////////////////////////////////////// + private static SinglePlayQueue getAutoQueuedSinglePlayQueue( final StreamInfoItem streamInfoItem) { final SinglePlayQueue singlePlayQueue = new SinglePlayQueue(streamInfoItem); @@ -420,11 +386,6 @@ public final class PlayerHelper { return singlePlayQueue; } - - //////////////////////////////////////////////////////////////////////////// - // Utils used by player - //////////////////////////////////////////////////////////////////////////// - public static boolean isPlaybackResumeEnabled(final Player player) { return player.getPrefs().getBoolean( player.getContext().getString(R.string.enable_watch_history_key), true) @@ -445,6 +406,11 @@ public final class PlayerHelper { } } + + //////////////////////////////////////////////////////////////////////////// + // Utils used by player + //////////////////////////////////////////////////////////////////////////// + @ResizeMode public static int retrieveResizeModeFromPrefs(final Player player) { return player.getPrefs().getInt(player.getContext().getString(R.string.last_resize_mode), @@ -504,4 +470,22 @@ public final class PlayerHelper { player.getContext().getString(R.string.seek_duration_key), player.getContext().getString(R.string.seek_duration_default_value)))); } + + @Retention(SOURCE) + @IntDef({AUTOPLAY_TYPE_ALWAYS, AUTOPLAY_TYPE_WIFI, + AUTOPLAY_TYPE_NEVER}) + public @interface AutoplayType { + int AUTOPLAY_TYPE_ALWAYS = 0; + int AUTOPLAY_TYPE_WIFI = 1; + int AUTOPLAY_TYPE_NEVER = 2; + } + + @Retention(SOURCE) + @IntDef({MINIMIZE_ON_EXIT_MODE_NONE, MINIMIZE_ON_EXIT_MODE_BACKGROUND, + MINIMIZE_ON_EXIT_MODE_POPUP}) + public @interface MinimizeMode { + int MINIMIZE_ON_EXIT_MODE_NONE = 0; + int MINIMIZE_ON_EXIT_MODE_BACKGROUND = 1; + int MINIMIZE_ON_EXIT_MODE_POPUP = 2; + } } diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java index b55a6547a..406ee3ca2 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerHolder.java @@ -6,18 +6,15 @@ import android.content.Intent; import android.content.ServiceConnection; import android.os.IBinder; import android.util.Log; - import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; - import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.PlaybackParameters; - import org.schabi.newpipe.App; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.extractor.stream.StreamInfo; -import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.Player; +import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.PlayerType; import org.schabi.newpipe.player.event.PlayerServiceEventListener; import org.schabi.newpipe.player.event.PlayerServiceExtendedEventListener; @@ -25,10 +22,20 @@ import org.schabi.newpipe.player.playqueue.PlayQueue; public final class PlayerHolder { + private static final boolean DEBUG = MainActivity.DEBUG; + private static final String TAG = PlayerHolder.class.getSimpleName(); + private static PlayerHolder instance; + private final PlayerServiceConnection serviceConnection = new PlayerServiceConnection(); + @Nullable + private PlayerServiceExtendedEventListener listener; + private boolean bound; + @Nullable + private PlayerService playerService; + @Nullable + private Player player; private PlayerHolder() { } - private static PlayerHolder instance; public static synchronized PlayerHolder getInstance() { if (PlayerHolder.instance == null) { PlayerHolder.instance = new PlayerHolder(); @@ -36,16 +43,6 @@ public final class PlayerHolder { return PlayerHolder.instance; } - private static final boolean DEBUG = MainActivity.DEBUG; - private static final String TAG = PlayerHolder.class.getSimpleName(); - - @Nullable private PlayerServiceExtendedEventListener listener; - - private final PlayerServiceConnection serviceConnection = new PlayerServiceConnection(); - private boolean bound; - @Nullable private PlayerService playerService; - @Nullable private Player player; - /** * Returns the current {@link PlayerType} of the {@link PlayerService} service, * otherwise `null` if no service is running. @@ -74,6 +71,7 @@ public final class PlayerHolder { /** * Use this method to only allow the user to manipulate the play queue (e.g. by enqueueing via * the stream long press menu) when there actually is a play queue to manipulate. + * * @return true only if the player is open and its play queue is ready (i.e. it is not null) */ public boolean isPlayQueueReady() { @@ -141,40 +139,6 @@ public final class PlayerHolder { context.stopService(new Intent(context, PlayerService.class)); } - class PlayerServiceConnection implements ServiceConnection { - - private boolean playAfterConnect = false; - - public void doPlayAfterConnect(final boolean playAfterConnection) { - this.playAfterConnect = playAfterConnection; - } - - @Override - public void onServiceDisconnected(final ComponentName compName) { - if (DEBUG) { - Log.d(TAG, "Player service is disconnected"); - } - - final Context context = getCommonContext(); - unbind(context); - } - - @Override - public void onServiceConnected(final ComponentName compName, final IBinder service) { - if (DEBUG) { - Log.d(TAG, "Player service is connected"); - } - final PlayerService.LocalBinder localBinder = (PlayerService.LocalBinder) service; - - playerService = localBinder.getService(); - player = localBinder.getPlayer(); - if (listener != null) { - listener.onServiceConnected(player, playerService, playAfterConnect); - } - startPlayerListener(); - } - } - private void bind(final Context context) { if (DEBUG) { Log.d(TAG, "bind() called"); @@ -217,6 +181,40 @@ public final class PlayerHolder { } } + class PlayerServiceConnection implements ServiceConnection { + + private boolean playAfterConnect = false; + + public void doPlayAfterConnect(final boolean playAfterConnection) { + this.playAfterConnect = playAfterConnection; + } + + @Override + public void onServiceDisconnected(final ComponentName compName) { + if (DEBUG) { + Log.d(TAG, "Player service is disconnected"); + } + + final Context context = getCommonContext(); + unbind(context); + } + + @Override + public void onServiceConnected(final ComponentName compName, final IBinder service) { + if (DEBUG) { + Log.d(TAG, "Player service is connected"); + } + final PlayerService.LocalBinder localBinder = (PlayerService.LocalBinder) service; + + playerService = localBinder.getService(); + player = localBinder.getPlayer(); + if (listener != null) { + listener.onServiceConnected(player, playerService, playAfterConnect); + } + startPlayerListener(); + } + } + private final PlayerServiceEventListener internalListener = new PlayerServiceEventListener() { @Override diff --git a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerSemitoneHelper.java b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerSemitoneHelper.java index f1ba90f8e..476d5d693 100644 --- a/app/src/main/java/org/schabi/newpipe/player/helper/PlayerSemitoneHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/helper/PlayerSemitoneHelper.java @@ -5,9 +5,9 @@ import androidx.core.math.MathUtils; /** * Converts between percent and 12-tone equal temperament semitones. *
- * @see - * - * Wikipedia: Equal temperament#Twelve-tone equal temperament + * + * @see + * Wikipedia: Equal temperament#Twelve-tone equal temperament * */ public final class PlayerSemitoneHelper { diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java index ebedf8c71..04347b745 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/ExceptionTag.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.player.mediaitem; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.player.playqueue.PlayQueueItem; @@ -7,15 +9,12 @@ import org.schabi.newpipe.player.playqueue.PlayQueueItem; import java.util.List; import java.util.Optional; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - /** * This {@link MediaItemTag} object is designed to contain metadata for a stream * that has failed to load. It supplies metadata from an underlying * {@link PlayQueueItem}, which is used by the internal players to resolve actual * playback info. - * + *

* This {@link MediaItemTag} does not contain any {@link StreamInfo} that can be * used to start playback and can be detected by checking {@link ExceptionTag#getErrors()} * when in generic form. diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java index f08086287..9f9de498c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/MediaItemTag.java @@ -1,12 +1,12 @@ package org.schabi.newpipe.player.mediaitem; import android.net.Uri; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.MediaItem.RequestMetadata; import com.google.android.exoplayer2.MediaMetadata; import com.google.android.exoplayer2.Player; - import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.VideoStream; @@ -15,18 +15,24 @@ import java.util.List; import java.util.Optional; import java.util.UUID; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - /** * Metadata container and accessor used by player internals. - * + *

* This interface ensures consistency of fetching metadata on each stream, * which is encapsulated in a {@link MediaItem} and delivered via ExoPlayer's * {@link Player.Listener} on event triggers to the downstream users. **/ public interface MediaItemTag { + @NonNull + static Optional from(@Nullable final MediaItem mediaItem) { + return Optional.ofNullable(mediaItem) + .map(item -> item.localConfiguration) + .map(localConfiguration -> localConfiguration.tag) + .filter(MediaItemTag.class::isInstance) + .map(MediaItemTag.class::cast); + } + List getErrors(); int getServiceId(); @@ -59,15 +65,6 @@ public interface MediaItemTag { MediaItemTag withExtras(@NonNull T extra); - @NonNull - static Optional from(@Nullable final MediaItem mediaItem) { - return Optional.ofNullable(mediaItem) - .map(item -> item.localConfiguration) - .map(localConfiguration -> localConfiguration.tag) - .filter(MediaItemTag.class::isInstance) - .map(MediaItemTag.class::cast); - } - @NonNull default String makeMediaId() { return UUID.randomUUID().toString() + "[" + getTitle() + "]"; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/PlaceholderTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/PlaceholderTag.java index cce4e9f17..6d4649bbf 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/PlaceholderTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/PlaceholderTag.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.player.mediaitem; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.util.Constants; @@ -7,15 +9,12 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - /** * This is a Placeholding {@link MediaItemTag}, designed as a dummy metadata object for * any stream that has not been resolved. - * + *

* This object cannot be instantiated and does not hold real metadata of any form. - * */ + */ public final class PlaceholderTag implements MediaItemTag { public static final PlaceholderTag EMPTY = new PlaceholderTag(null); private static final String UNKNOWN_VALUE_INTERNAL = "Placeholder"; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java index 4095f2bc8..c766a58c4 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediaitem/StreamInfoTag.java @@ -1,7 +1,8 @@ package org.schabi.newpipe.player.mediaitem; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.MediaItem; - import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.extractor.stream.StreamType; import org.schabi.newpipe.extractor.stream.VideoStream; @@ -10,9 +11,6 @@ import java.util.Collections; import java.util.List; import java.util.Optional; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - /** * This {@link MediaItemTag} object contains metadata for a resolved stream * that is ready for playback. This object guarantees the {@link StreamInfo} diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java index e9541ab06..45494b585 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/MediaSessionPlayerUi.java @@ -1,20 +1,15 @@ package org.schabi.newpipe.player.mediasession; -import static org.schabi.newpipe.MainActivity.DEBUG; - import android.content.Intent; import android.graphics.Bitmap; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.media.session.MediaButtonReceiver; - import com.google.android.exoplayer2.ForwardingPlayer; import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; - import org.schabi.newpipe.R; import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.ui.PlayerUi; @@ -23,6 +18,8 @@ import org.schabi.newpipe.util.StreamTypeUtil; import java.util.Optional; +import static org.schabi.newpipe.MainActivity.DEBUG; + public class MediaSessionPlayerUi extends PlayerUi { private static final String TAG = "MediaSessUi"; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java b/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java index 2e54b1129..9be6dd723 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasession/PlayQueueNavigator.java @@ -1,22 +1,15 @@ package org.schabi.newpipe.player.mediasession; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS; -import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_QUEUE_ITEM; - import android.net.Uri; import android.os.Bundle; import android.os.ResultReceiver; import android.support.v4.media.MediaDescriptionCompat; import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaSessionCompat; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector; import com.google.android.exoplayer2.util.Util; - import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.player.playqueue.PlayQueueItem; @@ -26,6 +19,8 @@ import java.util.Collections; import java.util.List; import java.util.Optional; +import static android.support.v4.media.session.PlaybackStateCompat.*; + public class PlayQueueNavigator implements MediaSessionConnector.QueueNavigator { private static final int MAX_QUEUE_SIZE = 10; diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java index b9ca90d89..c12a12f50 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/FailedMediaSource.java @@ -1,7 +1,8 @@ package org.schabi.newpipe.player.mediasource; import android.util.Log; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.PlaybackException; import com.google.android.exoplayer2.Timeline; @@ -11,7 +12,6 @@ import com.google.android.exoplayer2.source.SilenceMediaSource; import com.google.android.exoplayer2.source.SinglePeriodTimeline; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.TransferListener; - import org.schabi.newpipe.player.mediaitem.ExceptionTag; import org.schabi.newpipe.player.playqueue.PlayQueueItem; @@ -19,18 +19,15 @@ import java.io.IOException; import java.util.List; import java.util.concurrent.TimeUnit; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSource { /** * Play 2 seconds of silenced audio when a stream fails to resolve due to a known issue, * such as {@link org.schabi.newpipe.extractor.exceptions.ExtractionException}. - * + *

* This silence duration allows user to react and have time to jump to a previous stream, * while still provide a smooth playback experience. A duration lower than 1 second is * not recommended, it may cause ExoPlayer to buffer for a while. - * */ + */ public static final long SILENCE_DURATION_US = TimeUnit.SECONDS.toMicros(2); public static final MediaPeriod SILENT_MEDIA = makeSilentMediaPeriod(SILENCE_DURATION_US); @@ -39,9 +36,10 @@ public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSo private final Exception error; private final long retryTimestamp; private final MediaItem mediaItem; + /** * Fail the play queue item associated with this source, with potential future retries. - * + *

* The error will be propagated if the cause for load exception is unspecified. * This means the error might be caused by reasons outside of extraction (e.g. no network). * Otherwise, a silenced stream will play instead. @@ -72,6 +70,24 @@ public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSo System.currentTimeMillis() + retryWaitMillis); } + private static Timeline makeSilentMediaTimeline(final long durationUs, + @NonNull final MediaItem mediaItem) { + return new SinglePeriodTimeline( + durationUs, + /* isSeekable= */ true, + /* isDynamic= */ false, + /* useLiveConfiguration= */ false, + /* manifest= */ null, + mediaItem); + } + + private static MediaPeriod makeSilentMediaPeriod(final long durationUs) { + return new SilenceMediaSource.Factory() + .setDurationUs(durationUs) + .createMediaSource() + .createPeriod(null, null, 0); + } + public PlayQueueItem getStream() { return playQueueItem; } @@ -121,7 +137,7 @@ public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSo * {@link com.google.android.exoplayer2.Player.Listener#onPlayerError(PlaybackException)}. * * @throws IOException An error which will always result in - * {@link com.google.android.exoplayer2.PlaybackException#ERROR_CODE_IO_UNSPECIFIED}. + * {@link com.google.android.exoplayer2.PlaybackException#ERROR_CODE_IO_UNSPECIFIED}. */ @Override public void maybeThrowSourceInfoRefreshError() throws IOException { @@ -135,9 +151,9 @@ public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSo * refreshes the source info with no exception. All parameters are ignored as this * returns a static and reused piece of silent audio. * - * @param id The identifier of the period. - * @param allocator An {@link Allocator} from which to obtain media buffer allocations. - * @param startPositionUs The expected start position, in microseconds. + * @param id The identifier of the period. + * @param allocator An {@link Allocator} from which to obtain media buffer allocations. + * @param startPositionUs The expected start position, in microseconds. * @return The common {@link MediaPeriod} holding the silence. */ @Override @@ -189,22 +205,4 @@ public class FailedMediaSource extends BaseMediaSource implements ManagedMediaSo super(cause); } } - - private static Timeline makeSilentMediaTimeline(final long durationUs, - @NonNull final MediaItem mediaItem) { - return new SinglePeriodTimeline( - durationUs, - /* isSeekable= */ true, - /* isDynamic= */ false, - /* useLiveConfiguration= */ false, - /* manifest= */ null, - mediaItem); - } - - private static MediaPeriod makeSilentMediaPeriod(final long durationUs) { - return new SilenceMediaSource.Factory() - .setDurationUs(durationUs) - .createMediaSource() - .createPeriod(null, null, 0); - } } diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java index 95524cf69..5727ee31c 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/LoadedMediaSource.java @@ -1,5 +1,7 @@ package org.schabi.newpipe.player.mediasource; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.CompositeMediaSource; @@ -7,13 +9,9 @@ import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.upstream.Allocator; import com.google.android.exoplayer2.upstream.TransferListener; - import org.schabi.newpipe.player.mediaitem.MediaItemTag; import org.schabi.newpipe.player.playqueue.PlayQueueItem; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - public class LoadedMediaSource extends CompositeMediaSource implements ManagedMediaSource { private final MediaSource source; private final PlayQueueItem stream; @@ -26,11 +24,11 @@ public class LoadedMediaSource extends CompositeMediaSource implements * timestamp as a {@link ManagedMediaSource} to allow explicit playlist management under * {@link ManagedMediaSourcePlaylist}. * - * @param source The child media source with actual media. - * @param tag Metadata for the child media source. - * @param stream The queue item associated with the media source. - * @param expireTimestamp The timestamp when the media source expires and might not be - * available for playback. + * @param source The child media source with actual media. + * @param tag Metadata for the child media source. + * @param stream The queue item associated with the media source. + * @param expireTimestamp The timestamp when the media source expires and might not be + * available for playback. */ public LoadedMediaSource(@NonNull final MediaSource source, @NonNull final MediaItemTag tag, @@ -74,9 +72,9 @@ public class LoadedMediaSource extends CompositeMediaSource implements * new {@link Timeline}, otherwise {@link #createPeriod(MediaPeriodId, Allocator, long)} * will not be called and playback may be stalled. * - * @param id The unique id used to prepare the child source. - * @param mediaSource The child source whose source info has been refreshed. - * @param timeline The new timeline of the child source. + * @param id The unique id used to prepare the child source. + * @param mediaSource The child source whose source info has been refreshed. + * @param timeline The new timeline of the child source. */ @Override protected void onChildSourceInfoRefreshed(final Integer id, diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java index 9d6b94893..d7e5d2350 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSource.java @@ -1,9 +1,7 @@ package org.schabi.newpipe.player.mediasource; import androidx.annotation.NonNull; - import com.google.android.exoplayer2.source.MediaSource; - import org.schabi.newpipe.player.playqueue.PlayQueueItem; public interface ManagedMediaSource extends MediaSource { diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java index 4c0380767..e879006de 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/ManagedMediaSourcePlaylist.java @@ -1,13 +1,10 @@ package org.schabi.newpipe.player.mediasource; import android.os.Handler; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.source.ConcatenatingMediaSource; import com.google.android.exoplayer2.source.ShuffleOrder; - import org.schabi.newpipe.player.mediaitem.MediaItemTag; public class ManagedMediaSourcePlaylist { @@ -68,8 +65,8 @@ public class ManagedMediaSourcePlaylist { /** * Appends a {@link ManagedMediaSource} to the end of {@link ConcatenatingMediaSource}. * - * @see ConcatenatingMediaSource#addMediaSource * @param source {@link ManagedMediaSource} to append + * @see ConcatenatingMediaSource#addMediaSource */ public synchronized void append(@NonNull final ManagedMediaSource source) { internalSource.addMediaSource(source); @@ -79,8 +76,8 @@ public class ManagedMediaSourcePlaylist { * Removes a {@link ManagedMediaSource} from {@link ConcatenatingMediaSource} * at the given index. If this index is out of bound, then the removal is ignored. * - * @see ConcatenatingMediaSource#removeMediaSource(int) * @param index of {@link ManagedMediaSource} to be removed + * @see ConcatenatingMediaSource#removeMediaSource(int) */ public synchronized void remove(final int index) { if (index < 0 || index > internalSource.getSize()) { @@ -95,9 +92,9 @@ public class ManagedMediaSourcePlaylist { * from the given source index to the target index. If either index is out of bound, * then the call is ignored. * - * @see ConcatenatingMediaSource#moveMediaSource(int, int) * @param source original index of {@link ManagedMediaSource} * @param target new index of {@link ManagedMediaSource} + * @see ConcatenatingMediaSource#moveMediaSource(int, int) */ public synchronized void move(final int source, final int target) { if (source < 0 || target < 0) { @@ -114,11 +111,11 @@ public class ManagedMediaSourcePlaylist { * Invalidates the {@link ManagedMediaSource} at the given index by replacing it * with a {@link PlaceholderMediaSource}. * - * @see #update(int, ManagedMediaSource, Handler, Runnable) * @param index index of {@link ManagedMediaSource} to invalidate * @param handler the {@link Handler} to run {@code finalizingAction} * @param finalizingAction a {@link Runnable} which is executed immediately * after the media source has been removed from the playlist + * @see #update(int, ManagedMediaSource, Handler, Runnable) */ public synchronized void invalidate(final int index, @Nullable final Handler handler, @@ -133,9 +130,9 @@ public class ManagedMediaSourcePlaylist { * Updates the {@link ManagedMediaSource} in {@link ConcatenatingMediaSource} * at the given index with a given {@link ManagedMediaSource}. * - * @see #update(int, ManagedMediaSource, Handler, Runnable) * @param index index of {@link ManagedMediaSource} to update * @param source new {@link ManagedMediaSource} to use + * @see #update(int, ManagedMediaSource, Handler, Runnable) */ public synchronized void update(final int index, @NonNull final ManagedMediaSource source) { update(index, source, null, /*doNothing=*/null); @@ -146,13 +143,13 @@ public class ManagedMediaSourcePlaylist { * at the given index with a given {@link ManagedMediaSource}. If the index is out of bound, * then the replacement is ignored. * - * @see ConcatenatingMediaSource#addMediaSource - * @see ConcatenatingMediaSource#removeMediaSource(int, Handler, Runnable) * @param index index of {@link ManagedMediaSource} to update * @param source new {@link ManagedMediaSource} to use * @param handler the {@link Handler} to run {@code finalizingAction} * @param finalizingAction a {@link Runnable} which is executed immediately * after the media source has been removed from the playlist + * @see ConcatenatingMediaSource#addMediaSource + * @see ConcatenatingMediaSource#removeMediaSource(int, Handler, Runnable) */ public synchronized void update(final int index, @NonNull final ManagedMediaSource source, @Nullable final Handler handler, diff --git a/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java b/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java index 92d4403c8..f8b3f3bb9 100644 --- a/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java +++ b/app/src/main/java/org/schabi/newpipe/player/mediasource/PlaceholderMediaSource.java @@ -1,23 +1,22 @@ package org.schabi.newpipe.player.mediasource; +import androidx.annotation.NonNull; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.Timeline; import com.google.android.exoplayer2.source.CompositeMediaSource; import com.google.android.exoplayer2.source.MediaPeriod; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.upstream.Allocator; - import org.schabi.newpipe.player.mediaitem.PlaceholderTag; import org.schabi.newpipe.player.playqueue.PlayQueueItem; -import androidx.annotation.NonNull; - final class PlaceholderMediaSource extends CompositeMediaSource implements ManagedMediaSource { public static final PlaceholderMediaSource COPY = new PlaceholderMediaSource(); private static final MediaItem MEDIA_ITEM = PlaceholderTag.EMPTY.withExtras(COPY).asMediaItem(); - private PlaceholderMediaSource() { } + private PlaceholderMediaSource() { + } @Override public MediaItem getMediaItem() { @@ -25,9 +24,9 @@ final class PlaceholderMediaSource } @Override - protected void onChildSourceInfoRefreshed(final Void id, - final MediaSource mediaSource, - final Timeline timeline) { + private void onChildSourceInfoRefreshed(final Void id, + final MediaSource mediaSource, + final Timeline timeline) { /* Do nothing, no timeline updates or error will stall playback */ } @@ -38,7 +37,8 @@ final class PlaceholderMediaSource } @Override - public void releasePeriod(final MediaPeriod mediaPeriod) { } + public void releasePeriod(final MediaPeriod mediaPeriod) { + } @Override public boolean shouldBeReplacedWith(@NonNull final PlayQueueItem newIdentity, diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java index 89bf0b22a..2f4b42e1d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationConstants.java @@ -2,11 +2,9 @@ package org.schabi.newpipe.player.notification; import android.content.Context; import android.content.SharedPreferences; - import androidx.annotation.DrawableRes; import androidx.annotation.IntDef; import androidx.annotation.NonNull; - import org.schabi.newpipe.App; import org.schabi.newpipe.R; import org.schabi.newpipe.util.Localization; @@ -20,15 +18,69 @@ import java.util.TreeSet; public final class NotificationConstants { - private NotificationConstants() { - } + public static final int NOTHING = 0; /*////////////////////////////////////////////////////////////////////////// // Intent actions //////////////////////////////////////////////////////////////////////////*/ - + public static final int PREVIOUS = 1; + public static final int NEXT = 2; + public static final int REWIND = 3; + public static final int FORWARD = 4; + public static final int SMART_REWIND_PREVIOUS = 5; + public static final int SMART_FORWARD_NEXT = 6; + public static final int PLAY_PAUSE = 7; + public static final int PLAY_PAUSE_BUFFERING = 8; + public static final int REPEAT = 9; + public static final int SHUFFLE = 10; + public static final int CLOSE = 11; + @DrawableRes + public static final int[] ACTION_ICONS = { + 0, + R.drawable.exo_icon_previous, + R.drawable.exo_icon_next, + R.drawable.exo_icon_rewind, + R.drawable.exo_icon_fastforward, + R.drawable.exo_icon_previous, + R.drawable.exo_icon_next, + R.drawable.ic_pause, + R.drawable.ic_hourglass_top, + R.drawable.exo_icon_repeat_all, + R.drawable.exo_icon_shuffle_on, + R.drawable.ic_close, + }; + @Action + public static final int[] SLOT_DEFAULTS = { + SMART_REWIND_PREVIOUS, + PLAY_PAUSE_BUFFERING, + SMART_FORWARD_NEXT, + REPEAT, + CLOSE, + }; + @Action + public static final int[][] SLOT_ALLOWED_ACTIONS = { + new int[]{PREVIOUS, REWIND, SMART_REWIND_PREVIOUS}, + new int[]{REWIND, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, + new int[]{NEXT, FORWARD, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, + new int[]{NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, + SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, + new int[]{NOTHING, NEXT, FORWARD, SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, + }; + public static final int[] SLOT_PREF_KEYS = { + R.string.notification_slot_0_key, + R.string.notification_slot_1_key, + R.string.notification_slot_2_key, + R.string.notification_slot_3_key, + R.string.notification_slot_4_key, + }; + public static final List SLOT_COMPACT_DEFAULTS = List.of(0, 1, 2); + public static final int[] SLOT_COMPACT_PREF_KEYS = { + R.string.notification_slot_compact_0_key, + R.string.notification_slot_compact_1_key, + R.string.notification_slot_compact_2_key, + }; private static final String BASE_ACTION = App.PACKAGE_NAME + ".player.MainPlayer."; public static final String ACTION_CLOSE = @@ -51,77 +103,8 @@ public final class NotificationConstants { BASE_ACTION + ".player.MainPlayer.ACTION_RECREATE_NOTIFICATION"; - public static final int NOTHING = 0; - public static final int PREVIOUS = 1; - public static final int NEXT = 2; - public static final int REWIND = 3; - public static final int FORWARD = 4; - public static final int SMART_REWIND_PREVIOUS = 5; - public static final int SMART_FORWARD_NEXT = 6; - public static final int PLAY_PAUSE = 7; - public static final int PLAY_PAUSE_BUFFERING = 8; - public static final int REPEAT = 9; - public static final int SHUFFLE = 10; - public static final int CLOSE = 11; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT, - PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, SHUFFLE, CLOSE}) - public @interface Action { } - - @DrawableRes - public static final int[] ACTION_ICONS = { - 0, - R.drawable.exo_icon_previous, - R.drawable.exo_icon_next, - R.drawable.exo_icon_rewind, - R.drawable.exo_icon_fastforward, - R.drawable.exo_icon_previous, - R.drawable.exo_icon_next, - R.drawable.ic_pause, - R.drawable.ic_hourglass_top, - R.drawable.exo_icon_repeat_all, - R.drawable.exo_icon_shuffle_on, - R.drawable.ic_close, - }; - - - @Action - public static final int[] SLOT_DEFAULTS = { - SMART_REWIND_PREVIOUS, - PLAY_PAUSE_BUFFERING, - SMART_FORWARD_NEXT, - REPEAT, - CLOSE, - }; - - @Action - public static final int[][] SLOT_ALLOWED_ACTIONS = { - new int[] {PREVIOUS, REWIND, SMART_REWIND_PREVIOUS}, - new int[] {REWIND, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, - new int[] {NEXT, FORWARD, SMART_FORWARD_NEXT, PLAY_PAUSE, PLAY_PAUSE_BUFFERING}, - new int[] {NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, - SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, - new int[] {NOTHING, NEXT, FORWARD, SMART_FORWARD_NEXT, REPEAT, SHUFFLE, CLOSE}, - }; - - public static final int[] SLOT_PREF_KEYS = { - R.string.notification_slot_0_key, - R.string.notification_slot_1_key, - R.string.notification_slot_2_key, - R.string.notification_slot_3_key, - R.string.notification_slot_4_key, - }; - - - public static final List SLOT_COMPACT_DEFAULTS = List.of(0, 1, 2); - - public static final int[] SLOT_COMPACT_PREF_KEYS = { - R.string.notification_slot_compact_0_key, - R.string.notification_slot_compact_1_key, - R.string.notification_slot_compact_2_key, - }; - + private NotificationConstants() { + } public static String getActionName(@NonNull final Context context, @Action final int action) { switch (action) { @@ -156,17 +139,17 @@ public final class NotificationConstants { return context.getString(R.string.notification_action_shuffle); case CLOSE: return context.getString(R.string.close); - case NOTHING: default: + case NOTHING: + default: return context.getString(R.string.notification_action_nothing); } } - /** - * @param context the context to use + * @param context the context to use * @param sharedPreferences the shared preferences to query values from - * @param slotCount remove indices >= than this value (set to {@code 5} to do nothing, or make - * it lower if there are slots with empty actions) + * @param slotCount remove indices >= than this value (set to {@code 5} to do nothing, or make + * it lower if there are slots with empty actions) * @return a sorted list of the indices of the slots to use as compact slots */ public static List getCompactSlotsFromPreferences( @@ -190,4 +173,11 @@ public final class NotificationConstants { } return new ArrayList<>(compactSlots); } + + + @Retention(RetentionPolicy.SOURCE) + @IntDef({NOTHING, PREVIOUS, NEXT, REWIND, FORWARD, SMART_REWIND_PREVIOUS, SMART_FORWARD_NEXT, + PLAY_PAUSE, PLAY_PAUSE_BUFFERING, REPEAT, SHUFFLE, CLOSE}) + public @interface Action { + } } diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationPlayerUi.java index 4b1fc417f..21d10dfe1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationPlayerUi.java @@ -1,24 +1,21 @@ package org.schabi.newpipe.player.notification; -import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION; - import android.content.Intent; import android.graphics.Bitmap; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.Player.RepeatMode; - import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.ui.PlayerUi; +import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION; + public final class NotificationPlayerUi extends PlayerUi { - private boolean foregroundNotificationAlreadyCreated = false; private final NotificationUtil notificationUtil; + private boolean foregroundNotificationAlreadyCreated = false; public NotificationPlayerUi(@NonNull final Player player) { super(player); diff --git a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java index 6e50dda7d..5a40653fb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java +++ b/app/src/main/java/org/schabi/newpipe/player/notification/NotificationUtil.java @@ -6,7 +6,6 @@ import android.content.pm.ServiceInfo; import android.graphics.Bitmap; import android.os.Build; import android.util.Log; - import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -15,7 +14,6 @@ import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationManagerCompat; import androidx.core.app.ServiceCompat; import androidx.core.content.ContextCompat; - import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.player.Player; @@ -31,14 +29,7 @@ import static android.app.PendingIntent.FLAG_UPDATE_CURRENT; import static androidx.media.app.NotificationCompat.MediaStyle; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_CLOSE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_FORWARD; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_FAST_REWIND; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_NEXT; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PAUSE; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PREVIOUS; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_REPEAT; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_SHUFFLE; +import static org.schabi.newpipe.player.notification.NotificationConstants.*; /** * This is a utility class for player notifications. @@ -50,12 +41,10 @@ public final class NotificationUtil { @NotificationConstants.Action private final int[] notificationSlots = NotificationConstants.SLOT_DEFAULTS.clone(); - + private final Player player; private NotificationManagerCompat notificationManager; private NotificationCompat.Builder notificationBuilder; - private final Player player; - public NotificationUtil(final Player player) { this.player = player; } @@ -68,6 +57,7 @@ public final class NotificationUtil { /** * Creates the notification if it does not exist already and recreates it if forceRecreate is * true. Updates the notification with the data in the player. + * * @param forceRecreate whether to force the recreation of the notification even if it already * exists */ @@ -99,7 +89,7 @@ public final class NotificationUtil { notificationManager = NotificationManagerCompat.from(player.getContext()); final NotificationCompat.Builder builder = new NotificationCompat.Builder(player.getContext(), - player.getContext().getString(R.string.notification_channel_id)); + player.getContext().getString(R.string.notification_channel_id)); initializeNotificationSlots(); diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java index 88d7145bc..470b3a5e1 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/MediaSourceManager.java @@ -2,11 +2,17 @@ package org.schabi.newpipe.player.playback; import android.os.Handler; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.ArraySet; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observable; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; +import io.reactivex.rxjava3.schedulers.Schedulers; +import io.reactivex.rxjava3.subjects.PublishSubject; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.schabi.newpipe.extractor.exceptions.ExtractionException; @@ -29,24 +35,12 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Observable; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.internal.subscriptions.EmptySubscription; -import io.reactivex.rxjava3.schedulers.Schedulers; -import io.reactivex.rxjava3.subjects.PublishSubject; - import static org.schabi.newpipe.player.mediasource.FailedMediaSource.MediaSourceResolutionException; import static org.schabi.newpipe.player.mediasource.FailedMediaSource.StreamInfoLoadException; import static org.schabi.newpipe.player.playqueue.PlayQueue.DEBUG; import static org.schabi.newpipe.util.ServiceHelper.getCacheExpirationMillis; public class MediaSourceManager { - @NonNull - private final String TAG = "MediaSourceManager@" + hashCode(); - /** * Determines how many streams before and after the current stream should be loaded. * The default value (1) ensures seamless playback under typical network settings. @@ -58,7 +52,6 @@ public class MediaSourceManager { * @see #onMediaSourceReceived(PlayQueueItem, ManagedMediaSource) */ private static final int WINDOW_SIZE = 1; - /** * Determines the maximum number of disposables allowed in the {@link #loaderReactor}. * Once exceeded, new calls to {@link #loadImmediate()} will evict all disposables in the @@ -68,7 +61,8 @@ public class MediaSourceManager { * @see #maybeLoadItem(PlayQueueItem) */ private static final int MAXIMUM_LOADER_SIZE = WINDOW_SIZE * 2 + 1; - + @NonNull + private final String TAG = "MediaSourceManager@" + hashCode(); @NonNull private final PlaybackListener playbackListener; @NonNull @@ -108,23 +102,18 @@ public class MediaSourceManager { private final Disposable debouncedLoader; @NonNull private final PublishSubject debouncedSignal; - - @NonNull - private Subscription playQueueReactor; - @NonNull private final CompositeDisposable loaderReactor; @NonNull private final Set loadingItems; - @NonNull private final AtomicBoolean isBlocked; - + private final Handler removeMediaSourceHandler = new Handler(); + @NonNull + private Subscription playQueueReactor; @NonNull private ManagedMediaSourcePlaylist playlist; - private final Handler removeMediaSourceHandler = new Handler(); - public MediaSourceManager(@NonNull final PlaybackListener listener, @NonNull final PlayQueue playQueue) { this(listener, playQueue, 400L, @@ -175,6 +164,39 @@ public class MediaSourceManager { // Exposed Methods //////////////////////////////////////////////////////////////////////////*/ + @Nullable + private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue) { + // The current item has higher priority + final int currentIndex = playQueue.getIndex(); + final PlayQueueItem currentItem = playQueue.getItem(currentIndex); + if (currentItem == null) { + return null; + } + + // The rest are just for seamless playback + // Although timeline is not updated prior to the current index, these sources are still + // loaded into the cache for faster retrieval at a potentially later time. + final int leftBound = Math.max(0, currentIndex - MediaSourceManager.WINDOW_SIZE); + final int rightLimit = currentIndex + MediaSourceManager.WINDOW_SIZE + 1; + final int rightBound = Math.min(playQueue.size(), rightLimit); + final Set neighbors = new ArraySet<>( + playQueue.getStreams().subList(leftBound, rightBound)); + + // Do a round robin + final int excess = rightLimit - playQueue.size(); + if (excess >= 0) { + neighbors.addAll(playQueue.getStreams() + .subList(0, Math.min(playQueue.size(), excess))); + } + neighbors.remove(currentItem); + + return new ItemsToLoad(currentItem, neighbors); + } + + /*////////////////////////////////////////////////////////////////////////// + // Event Reactor + //////////////////////////////////////////////////////////////////////////*/ + /** * Dispose the manager and releases all message buses and loaders. */ @@ -190,10 +212,6 @@ public class MediaSourceManager { loaderReactor.dispose(); } - /*////////////////////////////////////////////////////////////////////////// - // Event Reactor - //////////////////////////////////////////////////////////////////////////*/ - private Subscriber getReactor() { return new Subscriber<>() { @Override @@ -218,6 +236,10 @@ public class MediaSourceManager { }; } + /*////////////////////////////////////////////////////////////////////////// + // Playback Locking + //////////////////////////////////////////////////////////////////////////*/ + private void onPlayQueueChanged(final PlayQueueEvent event) { if (playQueue.isEmpty() && playQueue.isComplete()) { playbackListener.onPlaybackShutdown(); @@ -257,10 +279,16 @@ public class MediaSourceManager { // Loading and Syncing switch (event.type()) { - case INIT: case REORDER: case ERROR: case SELECT: + case INIT: + case REORDER: + case ERROR: + case SELECT: loadImmediate(); // low frequency, critical events break; - case APPEND: case REMOVE: case MOVE: case RECOVERY: + case APPEND: + case REMOVE: + case MOVE: + case RECOVERY: default: loadDebounced(); // high frequency or noncritical events break; @@ -268,7 +296,10 @@ public class MediaSourceManager { // update ui and notification switch (event.type()) { - case APPEND: case REMOVE: case MOVE: case REORDER: + case APPEND: + case REMOVE: + case MOVE: + case REORDER: playbackListener.onPlayQueueEdited(); } @@ -279,10 +310,6 @@ public class MediaSourceManager { playQueueReactor.request(1); } - /*////////////////////////////////////////////////////////////////////////// - // Playback Locking - //////////////////////////////////////////////////////////////////////////*/ - private boolean isPlayQueueReady() { final boolean isWindowLoaded = playQueue.size() - playQueue.getIndex() > WINDOW_SIZE; return playQueue.isComplete() || isWindowLoaded; @@ -317,6 +344,10 @@ public class MediaSourceManager { isBlocked.set(true); } + /*////////////////////////////////////////////////////////////////////////// + // Metadata Synchronization + //////////////////////////////////////////////////////////////////////////*/ + private boolean maybeUnblock() { if (DEBUG) { Log.d(TAG, "maybeUnblock() called."); @@ -331,10 +362,6 @@ public class MediaSourceManager { return false; } - /*////////////////////////////////////////////////////////////////////////// - // Metadata Synchronization - //////////////////////////////////////////////////////////////////////////*/ - private void maybeSync(final boolean wasBlocked) { if (DEBUG) { Log.d(TAG, "maybeSync() called."); @@ -348,6 +375,10 @@ public class MediaSourceManager { playbackListener.onPlaybackSynchronize(currentItem, wasBlocked); } + /*////////////////////////////////////////////////////////////////////////// + // MediaSource Loading + //////////////////////////////////////////////////////////////////////////*/ + private synchronized void maybeSynchronizePlayer() { if (isPlayQueueReady() && isPlaybackReady()) { final boolean isBlockReleased = maybeUnblock(); @@ -355,13 +386,9 @@ public class MediaSourceManager { } } - /*////////////////////////////////////////////////////////////////////////// - // MediaSource Loading - //////////////////////////////////////////////////////////////////////////*/ - private Observable getEdgeIntervalSignal() { return Observable.interval(progressUpdateIntervalMillis, - TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) + TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()) .filter(ignored -> playbackListener.isApproachingPlaybackEdge(playbackNearEndGapMillis)); } @@ -529,6 +556,10 @@ public class MediaSourceManager { playlist.invalidate(currentIndex, removeMediaSourceHandler, this::loadImmediate); } + /*////////////////////////////////////////////////////////////////////////// + // MediaSource Playlist Helpers + //////////////////////////////////////////////////////////////////////////*/ + private void maybeClearLoaders() { if (DEBUG) { Log.d(TAG, "MediaSource - maybeClearLoaders() called."); @@ -540,10 +571,6 @@ public class MediaSourceManager { } } - /*////////////////////////////////////////////////////////////////////////// - // MediaSource Playlist Helpers - //////////////////////////////////////////////////////////////////////////*/ - private void resetSources() { if (DEBUG) { Log.d(TAG, "resetSources() called."); @@ -551,6 +578,10 @@ public class MediaSourceManager { playlist = new ManagedMediaSourcePlaylist(); } + /*////////////////////////////////////////////////////////////////////////// + // Manager Helpers + //////////////////////////////////////////////////////////////////////////*/ + private void populateSources() { if (DEBUG) { Log.d(TAG, "populateSources() called."); @@ -560,39 +591,6 @@ public class MediaSourceManager { } } - /*////////////////////////////////////////////////////////////////////////// - // Manager Helpers - //////////////////////////////////////////////////////////////////////////*/ - - @Nullable - private static ItemsToLoad getItemsToLoad(@NonNull final PlayQueue playQueue) { - // The current item has higher priority - final int currentIndex = playQueue.getIndex(); - final PlayQueueItem currentItem = playQueue.getItem(currentIndex); - if (currentItem == null) { - return null; - } - - // The rest are just for seamless playback - // Although timeline is not updated prior to the current index, these sources are still - // loaded into the cache for faster retrieval at a potentially later time. - final int leftBound = Math.max(0, currentIndex - MediaSourceManager.WINDOW_SIZE); - final int rightLimit = currentIndex + MediaSourceManager.WINDOW_SIZE + 1; - final int rightBound = Math.min(playQueue.size(), rightLimit); - final Set neighbors = new ArraySet<>( - playQueue.getStreams().subList(leftBound, rightBound)); - - // Do a round robin - final int excess = rightLimit - playQueue.size(); - if (excess >= 0) { - neighbors.addAll(playQueue.getStreams() - .subList(0, Math.min(playQueue.size(), excess))); - } - neighbors.remove(currentItem); - - return new ItemsToLoad(currentItem, neighbors); - } - private static class ItemsToLoad { @NonNull private final PlayQueueItem center; diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java index 737607001..5c9f05a95 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/PlaybackListener.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.player.playback; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.source.MediaSource; - import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.playqueue.PlayQueueItem; @@ -51,8 +49,8 @@ public interface PlaybackListener { * May be called anytime at any amount once unblock is called. *

* - * @param item item the player should be playing/synchronized to - * @param wasBlocked was the player recently released from blocking state + * @param item item the player should be playing/synchronized to + * @param wasBlocked was the player recently released from blocking state */ void onPlaybackSynchronize(@NonNull PlayQueueItem item, boolean wasBlocked); @@ -62,6 +60,7 @@ public interface PlaybackListener { *

* May be called at any time. *

+ * * @param item * @param info * @return the corresponding {@link MediaSource} diff --git a/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java b/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java index da6cb36d4..28066c723 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java +++ b/app/src/main/java/org/schabi/newpipe/player/playback/SurfaceHolderCallback.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.player.playback; import android.content.Context; import android.view.SurfaceHolder; - import com.google.android.exoplayer2.Player; import com.google.android.exoplayer2.video.PlaceholderSurface; diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/AbstractInfoPlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/AbstractInfoPlayQueue.java index e51ee4720..0641e321f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/AbstractInfoPlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/AbstractInfoPlayQueue.java @@ -1,9 +1,9 @@ package org.schabi.newpipe.player.playqueue; import android.util.Log; - import androidx.annotation.NonNull; - +import io.reactivex.rxjava3.core.SingleObserver; +import io.reactivex.rxjava3.disposables.Disposable; import org.schabi.newpipe.extractor.ListExtractor; import org.schabi.newpipe.extractor.ListInfo; import org.schabi.newpipe.extractor.Page; @@ -12,18 +12,13 @@ import org.schabi.newpipe.extractor.stream.StreamInfoItem; import java.util.List; import java.util.stream.Collectors; -import io.reactivex.rxjava3.core.SingleObserver; -import io.reactivex.rxjava3.disposables.Disposable; - abstract class AbstractInfoPlayQueue> extends PlayQueue { - boolean isInitial; - private boolean isComplete; - final int serviceId; final String baseUrl; + boolean isInitial; Page nextPage; - + private boolean isComplete; private transient Disposable fetchReactor; protected AbstractInfoPlayQueue(final T info) { @@ -45,6 +40,10 @@ abstract class AbstractInfoPlayQueue> this.isComplete = !isInitial && !Page.isValid(nextPage); } + private static List extractListItems(final List infoItems) { + return infoItems.stream().map(PlayQueueItem::new).collect(Collectors.toList()); + } + protected abstract String getTag(); @Override @@ -130,8 +129,4 @@ abstract class AbstractInfoPlayQueue> } fetchReactor = null; } - - private static List extractListItems(final List infoItems) { - return infoItems.stream().map(PlayQueueItem::new).collect(Collectors.toList()); - } } diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/ChannelPlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/ChannelPlayQueue.java index 1e1fef85e..f9083a02b 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/ChannelPlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/ChannelPlayQueue.java @@ -1,6 +1,8 @@ package org.schabi.newpipe.player.playqueue; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.stream.StreamInfoItem; @@ -8,9 +10,6 @@ import org.schabi.newpipe.util.ExtractorHelper; import java.util.List; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.schedulers.Schedulers; - public final class ChannelPlayQueue extends AbstractInfoPlayQueue { public ChannelPlayQueue(final ChannelInfo info) { diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java index 4c6230964..aafc2fc80 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueue.java @@ -2,17 +2,12 @@ package org.schabi.newpipe.player.playqueue; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.BackpressureStrategy; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.subjects.BehaviorSubject; import org.schabi.newpipe.MainActivity; -import org.schabi.newpipe.player.playqueue.events.AppendEvent; -import org.schabi.newpipe.player.playqueue.events.ErrorEvent; -import org.schabi.newpipe.player.playqueue.events.InitEvent; -import org.schabi.newpipe.player.playqueue.events.MoveEvent; -import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent; -import org.schabi.newpipe.player.playqueue.events.RecoveryEvent; -import org.schabi.newpipe.player.playqueue.events.RemoveEvent; -import org.schabi.newpipe.player.playqueue.events.ReorderEvent; -import org.schabi.newpipe.player.playqueue.events.SelectEvent; +import org.schabi.newpipe.player.playqueue.events.*; import java.io.Serializable; import java.util.ArrayList; @@ -20,11 +15,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.atomic.AtomicInteger; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.BackpressureStrategy; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.subjects.BehaviorSubject; - /** * PlayQueue is responsible for keeping track of a list of streams and the index of * the stream that should be currently playing. @@ -493,12 +483,12 @@ public abstract class PlayQueue implements Serializable { /** * Selects previous played item. - * + *

* This method removes currently playing item from history and * starts playing the last item from history if it exists * * @return true if history is not empty and the item can be played - * */ + */ public synchronized boolean previous() { if (history.size() <= 1) { return false; diff --git a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueAdapter.java b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueAdapter.java index dd95fb4d5..7bf05f7eb 100644 --- a/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/player/playqueue/PlayQueueAdapter.java @@ -5,24 +5,16 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; - +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; import org.schabi.newpipe.R; -import org.schabi.newpipe.player.playqueue.events.AppendEvent; -import org.schabi.newpipe.player.playqueue.events.ErrorEvent; -import org.schabi.newpipe.player.playqueue.events.MoveEvent; -import org.schabi.newpipe.player.playqueue.events.PlayQueueEvent; -import org.schabi.newpipe.player.playqueue.events.RemoveEvent; -import org.schabi.newpipe.player.playqueue.events.SelectEvent; +import org.schabi.newpipe.player.playqueue.events.*; import org.schabi.newpipe.util.FallbackViewHolder; import java.util.List; -import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; - /** * Created by Christian Schabesberger on 01.08.16. *

@@ -89,7 +81,8 @@ public class PlayQueueAdapter extends RecyclerView.Adapter { public PlaylistPlayQueue(final PlaylistInfo info) { diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java index e87c93114..30ec8bebd 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/AudioPlaybackResolver.java @@ -1,15 +1,10 @@ package org.schabi.newpipe.player.resolver; -import static org.schabi.newpipe.util.ListHelper.getNonTorrentStreams; - import android.content.Context; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.source.MediaSource; - import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -21,6 +16,8 @@ import org.schabi.newpipe.util.ListHelper; import java.util.List; +import static org.schabi.newpipe.util.ListHelper.getNonTorrentStreams; + public class AudioPlaybackResolver implements PlaybackResolver { private static final String TAG = AudioPlaybackResolver.class.getSimpleName(); diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java index 9c8cbb8f6..4492a7055 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/PlaybackResolver.java @@ -1,14 +1,8 @@ package org.schabi.newpipe.player.resolver; -import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE; -import static org.schabi.newpipe.extractor.stream.VideoStream.RESOLUTION_UNKNOWN; -import static org.schabi.newpipe.player.helper.PlayerDataSource.LIVE_STREAM_EDGE_GAP_MILLIS; - import android.net.Uri; import android.util.Log; - import androidx.annotation.Nullable; - import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.source.MediaSource; @@ -20,7 +14,6 @@ import com.google.android.exoplayer2.source.hls.HlsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.SsMediaSource; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifest; import com.google.android.exoplayer2.source.smoothstreaming.manifest.SsManifestParser; - import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.services.youtube.ItagItem; @@ -28,12 +21,7 @@ import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.Creati import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeOtfDashManifestCreator; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubePostLiveStreamDvrDashManifestCreator; import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeProgressiveDashManifestCreator; -import org.schabi.newpipe.extractor.stream.AudioStream; -import org.schabi.newpipe.extractor.stream.DeliveryMethod; -import org.schabi.newpipe.extractor.stream.Stream; -import org.schabi.newpipe.extractor.stream.StreamInfo; -import org.schabi.newpipe.extractor.stream.StreamType; -import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.player.datasource.NonUriHlsDataSourceFactory; import org.schabi.newpipe.player.helper.PlayerDataSource; import org.schabi.newpipe.player.mediaitem.MediaItemTag; @@ -45,6 +33,10 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Objects; +import static org.schabi.newpipe.extractor.stream.AudioStream.UNKNOWN_BITRATE; +import static org.schabi.newpipe.extractor.stream.VideoStream.RESOLUTION_UNKNOWN; +import static org.schabi.newpipe.player.helper.PlayerDataSource.LIVE_STREAM_EDGE_GAP_MILLIS; + /** * This interface is just a shorthand for {@link Resolver} with {@link StreamInfo} as source and * {@link MediaSource} as product. It contains many static methods that can be used by classes diff --git a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java index cf7d73558..49f29526d 100644 --- a/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java +++ b/app/src/main/java/org/schabi/newpipe/player/resolver/VideoPlaybackResolver.java @@ -3,15 +3,12 @@ package org.schabi.newpipe.player.resolver; import android.content.Context; import android.net.Uri; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.MediaItem; import com.google.android.exoplayer2.source.MediaSource; import com.google.android.exoplayer2.source.MergingMediaSource; - import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -28,8 +25,8 @@ import java.util.List; import java.util.Optional; import static com.google.android.exoplayer2.C.TIME_UNSET; -import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams; import static org.schabi.newpipe.util.ListHelper.getNonTorrentStreams; +import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams; public class VideoPlaybackResolver implements PlaybackResolver { private static final String TAG = VideoPlaybackResolver.class.getSimpleName(); @@ -45,12 +42,6 @@ public class VideoPlaybackResolver implements PlaybackResolver { @Nullable private String playbackQuality; - public enum SourceType { - LIVE_STREAM, - VIDEO_WITH_SEPARATED_AUDIO, - VIDEO_WITH_AUDIO_OR_AUDIO_ONLY - } - public VideoPlaybackResolver(@NonNull final Context context, @NonNull final PlayerDataSource dataSource, @NonNull final QualityResolver qualityResolver) { @@ -179,6 +170,12 @@ public class VideoPlaybackResolver implements PlaybackResolver { this.playbackQuality = playbackQuality; } + public enum SourceType { + LIVE_STREAM, + VIDEO_WITH_SEPARATED_AUDIO, + VIDEO_WITH_AUDIO_OR_AUDIO_ONLY + } + public interface QualityResolver { int getDefaultResolutionIndex(List sortedVideos); diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java index 28856d606..63961662f 100644 --- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java +++ b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHelper.java @@ -5,14 +5,12 @@ import android.graphics.Bitmap; import android.util.Log; import android.view.View; import android.widget.ImageView; - import androidx.annotation.IntDef; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.graphics.BitmapCompat; import androidx.core.math.MathUtils; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import org.schabi.newpipe.util.DeviceUtils; @@ -20,9 +18,7 @@ import java.lang.annotation.Retention; import java.util.function.IntSupplier; import static java.lang.annotation.RetentionPolicy.SOURCE; -import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType.HIGH_QUALITY; -import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType.LOW_QUALITY; -import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType.NONE; +import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType.*; /** * Helper for the seekbar preview. @@ -38,19 +34,6 @@ public final class SeekbarPreviewThumbnailHelper { // No impl pls } - @Retention(SOURCE) - @IntDef({HIGH_QUALITY, LOW_QUALITY, - NONE}) - public @interface SeekbarPreviewThumbnailType { - int HIGH_QUALITY = 0; - int LOW_QUALITY = 1; - int NONE = 2; - } - - //////////////////////////////////////////////////////////////////////////// - // Settings Resolution - /////////////////////////////////////////////////////////////////////////// - @SeekbarPreviewThumbnailType public static int getSeekbarPreviewThumbnailType(@NonNull final Context context) { final String type = PreferenceManager.getDefaultSharedPreferences(context).getString( @@ -64,6 +47,10 @@ public final class SeekbarPreviewThumbnailHelper { } } + //////////////////////////////////////////////////////////////////////////// + // Settings Resolution + /////////////////////////////////////////////////////////////////////////// + public static void tryResizeAndSetSeekbarPreviewThumbnail( @NonNull final Context context, @Nullable final Bitmap previewThumbnail, @@ -99,4 +86,13 @@ public final class SeekbarPreviewThumbnailHelper { previewThumbnail.recycle(); } } + + @Retention(SOURCE) + @IntDef({HIGH_QUALITY, LOW_QUALITY, + NONE}) + public @interface SeekbarPreviewThumbnailType { + int HIGH_QUALITY = 0; + int LOW_QUALITY = 1; + int NONE = 2; + } } diff --git a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java index 50ffa2f2a..e3a5ae785 100644 --- a/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java +++ b/app/src/main/java/org/schabi/newpipe/player/seekbarpreview/SeekbarPreviewThumbnailHolder.java @@ -1,18 +1,12 @@ package org.schabi.newpipe.player.seekbarpreview; -import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType; -import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.getSeekbarPreviewThumbnailType; - import android.content.Context; import android.graphics.Bitmap; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.SparseArrayCompat; - import com.google.common.base.Stopwatch; - import org.schabi.newpipe.extractor.stream.Frameset; import org.schabi.newpipe.util.PicassoHelper; @@ -24,6 +18,9 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Supplier; +import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.SeekbarPreviewThumbnailType; +import static org.schabi.newpipe.player.seekbarpreview.SeekbarPreviewThumbnailHelper.getSeekbarPreviewThumbnailType; + public class SeekbarPreviewThumbnailHolder { // This has to be <= 23 chars on devices running Android 7 or lower (API <= 25) diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java index a5c745ae6..e909c5b06 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/MainPlayerUi.java @@ -1,20 +1,5 @@ package org.schabi.newpipe.player.ui; -import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu; -import static org.schabi.newpipe.extractor.ServiceList.YouTube; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.player.Player.STATE_COMPLETED; -import static org.schabi.newpipe.player.Player.STATE_PAUSED; -import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_BACKGROUND; -import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE; -import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP; -import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimizeOnExitAction; -import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; -import static org.schabi.newpipe.player.helper.PlayerHelper.globalScreenOrientationLocked; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PAUSE; - import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -34,7 +19,6 @@ import android.view.ViewGroup; import android.view.ViewParent; import android.widget.FrameLayout; import android.widget.LinearLayout; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; @@ -44,9 +28,7 @@ import androidx.core.view.WindowInsetsCompat; import androidx.fragment.app.FragmentActivity; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.RecyclerView; - import com.google.android.exoplayer2.video.VideoSize; - import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.PlayerBinding; import org.schabi.newpipe.extractor.stream.StreamInfo; @@ -64,12 +46,7 @@ import org.schabi.newpipe.player.gesture.MainPlayerGestureListener; import org.schabi.newpipe.player.helper.PlaybackParameterDialog; import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.mediaitem.MediaItemTag; -import org.schabi.newpipe.player.playqueue.PlayQueue; -import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; -import org.schabi.newpipe.player.playqueue.PlayQueueItem; -import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; -import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; -import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; +import org.schabi.newpipe.player.playqueue.*; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.KoreUtils; @@ -80,6 +57,17 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; +import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.QueueItemMenuUtil.openPopupMenu; +import static org.schabi.newpipe.extractor.ServiceList.YouTube; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.player.Player.STATE_COMPLETED; +import static org.schabi.newpipe.player.Player.STATE_PAUSED; +import static org.schabi.newpipe.player.helper.PlayerHelper.MinimizeMode.*; +import static org.schabi.newpipe.player.helper.PlayerHelper.*; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_PLAY_PAUSE; + public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutChangeListener { private static final String TAG = MainPlayerUi.class.getSimpleName(); @@ -378,7 +366,8 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh NavigationHelper.playOnPopupPlayer(activity, player.getPlayQueue(), true); }); break; - case MINIMIZE_ON_EXIT_MODE_NONE: default: + case MINIMIZE_ON_EXIT_MODE_NONE: + default: player.pause(); break; } @@ -692,10 +681,10 @@ public final class MainPlayerUi extends VideoPlayerUi implements View.OnLayoutCh animate(binding.itemsListPanel, false, DEFAULT_CONTROLS_DURATION, AnimationType.SLIDE_AND_ALPHA, 0, () -> - // Even when queueLayout is GONE it receives touch events - // and ruins normal behavior of the app. This line fixes it - binding.itemsListPanel.setTranslationY( - -binding.itemsListPanel.getHeight() * 5.0f)); + // Even when queueLayout is GONE it receives touch events + // and ruins normal behavior of the app. This line fixes it + binding.itemsListPanel.setTranslationY( + -binding.itemsListPanel.getHeight() * 5.0f)); // clear focus, otherwise a white rectangle remains on top of the player binding.itemsListClose.clearFocus(); diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUi.java index 57e2ec2a2..8dd329d77 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUi.java @@ -3,16 +3,13 @@ package org.schabi.newpipe.player.ui; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.google.android.exoplayer2.PlaybackParameters; import com.google.android.exoplayer2.Player.RepeatMode; import com.google.android.exoplayer2.Tracks; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.video.VideoSize; - import org.schabi.newpipe.extractor.stream.StreamInfo; import org.schabi.newpipe.player.Player; @@ -25,8 +22,10 @@ import java.util.List; */ public abstract class PlayerUi { - @NonNull protected final Context context; - @NonNull protected final Player player; + @NonNull + protected final Context context; + @NonNull + protected final Player player; /** * @param player the player instance that will be usable throughout the lifetime of this UI; its @@ -105,6 +104,7 @@ public abstract class PlayerUi { * Broadcasts that the player receives will also be notified to UIs here. If you want to * register new broadcast actions to receive here, add them to {@link * Player#setupBroadcastReceiver()}. + * * @param intent the broadcast intent received by the player */ public void onBroadcastReceived(final Intent intent) { @@ -114,6 +114,7 @@ public abstract class PlayerUi { * Called when stream progress (i.e. the current time in the seekbar) or stream duration change. * Will surely be called every {@link Player#PROGRESS_LOOP_INTERVAL_MILLIS} while a stream is * playing. + * * @param currentProgress the current progress in milliseconds * @param duration the duration of the stream being played * @param bufferPercent the percentage of stream already buffered, see {@link @@ -155,15 +156,15 @@ public abstract class PlayerUi { } /** - * @see com.google.android.exoplayer2.Player.Listener#onTracksChanged(Tracks) * @param currentTracks the available tracks information + * @see com.google.android.exoplayer2.Player.Listener#onTracksChanged(Tracks) */ public void onTextTracksChanged(@NonNull final Tracks currentTracks) { } /** - * @see com.google.android.exoplayer2.Player.Listener#onPlaybackParametersChanged * @param playbackParameters the new playback parameters + * @see com.google.android.exoplayer2.Player.Listener#onPlaybackParametersChanged */ public void onPlaybackParametersChanged(@NonNull final PlaybackParameters playbackParameters) { } @@ -175,14 +176,15 @@ public abstract class PlayerUi { } /** - * @see com.google.android.exoplayer2.text.TextOutput#onCues * @param cues the cues to pass to the subtitle view + * @see com.google.android.exoplayer2.text.TextOutput#onCues */ public void onCues(@NonNull final List cues) { } /** * Called when the stream being played changes. + * * @param info the {@link StreamInfo} metadata object, along with data about the selected and * available video streams (to be used to build the resolution menus, for example) */ @@ -191,6 +193,7 @@ public abstract class PlayerUi { /** * Called when the thumbnail for the current metadata was loaded. + * * @param bitmap the thumbnail to process, or null if there is no thumbnail or there was an * error when loading the thumbnail */ diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUiList.java b/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUiList.java index 24fec3b8a..c4c111549 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUiList.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/PlayerUiList.java @@ -27,6 +27,7 @@ public final class PlayerUiList { * are removed and re-added, the player will not call e.g. initPlayer again since the exoplayer * is already initialized, but we need to notify the newly built UI that the player is ready * nonetheless. + * * @param playerUi the player ui to prepare and add to the list; its {@link * PlayerUi#getPlayer()} will be used to query information about the player * state @@ -49,6 +50,7 @@ public final class PlayerUiList { /** * Destroys all matching player UIs and removes them from the list. + * * @param playerUiType the class of the player UI to destroy; the {@link * Class#isInstance(Object)} method will be used, so even subclasses will be * destroyed and removed @@ -70,7 +72,7 @@ public final class PlayerUiList { * be returned * @param the class type parameter * @return the first player UI of the required type found in the list, or an empty {@link - * Optional} otherwise + * Optional} otherwise */ public Optional get(final Class playerUiType) { return playerUis.stream() @@ -81,6 +83,7 @@ public final class PlayerUiList { /** * Calls the provided consumer on all player UIs in the list, in order of addition. + * * @param consumer the consumer to call with player UIs */ public void call(final Consumer consumer) { diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java index 90c24c0c6..f110878db 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/PopupPlayerUi.java @@ -1,9 +1,5 @@ package org.schabi.newpipe.player.ui; -import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimumVideoHeight; - import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.annotation.SuppressLint; @@ -16,23 +12,14 @@ import android.graphics.PixelFormat; import android.os.Build; import android.util.DisplayMetrics; import android.util.Log; -import android.view.Gravity; -import android.view.LayoutInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowInsets; -import android.view.WindowManager; +import android.view.*; import android.view.animation.AnticipateInterpolator; import android.widget.LinearLayout; - import androidx.annotation.NonNull; import androidx.core.content.ContextCompat; import androidx.core.math.MathUtils; - import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.SubtitleView; - import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.PlayerBinding; import org.schabi.newpipe.databinding.PlayerPopupCloseOverlayBinding; @@ -41,9 +28,20 @@ import org.schabi.newpipe.player.gesture.BasePlayerGestureListener; import org.schabi.newpipe.player.gesture.PopupPlayerGestureListener; import org.schabi.newpipe.player.helper.PlayerHelper; -public final class PopupPlayerUi extends VideoPlayerUi { - private static final String TAG = PopupPlayerUi.class.getSimpleName(); +import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; +import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.player.helper.PlayerHelper.getMinimumVideoHeight; +public final class PopupPlayerUi extends VideoPlayerUi { + public static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + public static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS + | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; + + /*////////////////////////////////////////////////////////////////////////// + // Popup player + //////////////////////////////////////////////////////////////////////////*/ + private static final String TAG = PopupPlayerUi.class.getSimpleName(); /** * Maximum opacity allowed for Android 12 and higher to allow touches on other apps when using * NewPipe's popup player. @@ -57,29 +55,16 @@ public final class PopupPlayerUi extends VideoPlayerUi { * @see WindowManager.LayoutParams#FLAG_NOT_TOUCHABLE */ private static final float MAXIMUM_OPACITY_ALLOWED_FOR_S_AND_HIGHER = 0.8f; - - /*////////////////////////////////////////////////////////////////////////// - // Popup player - //////////////////////////////////////////////////////////////////////////*/ - + private final WindowManager windowManager; private PlayerPopupCloseOverlayBinding closeOverlayBinding; - private boolean isPopupClosing = false; - - private int screenWidth; - private int screenHeight; - /*////////////////////////////////////////////////////////////////////////// // Popup player window manager //////////////////////////////////////////////////////////////////////////*/ - - public static final int IDLE_WINDOW_FLAGS = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - public static final int ONGOING_PLAYBACK_WINDOW_FLAGS = IDLE_WINDOW_FLAGS - | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON; - + private boolean isPopupClosing = false; + private int screenWidth; + private int screenHeight; private WindowManager.LayoutParams popupLayoutParams; // null if player is not popup - private final WindowManager windowManager; /*////////////////////////////////////////////////////////////////////////// @@ -93,6 +78,36 @@ public final class PopupPlayerUi extends VideoPlayerUi { windowManager = ContextCompat.getSystemService(context, WindowManager.class); } + @SuppressLint("RtlHardcoded") + public static WindowManager.LayoutParams buildCloseOverlayLayoutParams() { + final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; + + final WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, + popupLayoutParamType(), + flags, + PixelFormat.TRANSLUCENT); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + // Setting maximum opacity allowed for touch events to other apps for Android 12 and + // higher to prevent non interaction when using other apps with the popup player + closeOverlayLayoutParams.alpha = MAXIMUM_OPACITY_ALLOWED_FOR_S_AND_HIGHER; + } + + closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; + closeOverlayLayoutParams.softInputMode = + WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; + return closeOverlayLayoutParams; + } + + public static int popupLayoutParamType() { + return Build.VERSION.SDK_INT < Build.VERSION_CODES.O + ? WindowManager.LayoutParams.TYPE_PHONE + : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; + } + @Override public void setupAfterIntent() { super.setupAfterIntent(); @@ -186,18 +201,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { resources.getDimensionPixelSize(R.dimen.player_popup_buttons_padding) ); } - - @Override - public void removeViewFromParent() { - // view was added by windowManager for popup player - windowManager.removeViewImmediate(binding.getRoot()); - } - - @Override - public void destroy() { - super.destroy(); - removePopupFromView(); - } //endregion @@ -206,6 +209,25 @@ public final class PopupPlayerUi extends VideoPlayerUi { //////////////////////////////////////////////////////////////////////////*/ //region Broadcast receiver + @Override + public void removeViewFromParent() { + // view was added by windowManager for popup player + windowManager.removeViewImmediate(binding.getRoot()); + } + //endregion + + + /*////////////////////////////////////////////////////////////////////////// + // Popup position and size + //////////////////////////////////////////////////////////////////////////*/ + //region Popup position and size + + @Override + public void destroy() { + super.destroy(); + removePopupFromView(); + } + @Override public void onBroadcastReceived(final Intent intent) { super.onBroadcastReceived(intent); @@ -223,13 +245,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { } } } - //endregion - - - /*////////////////////////////////////////////////////////////////////////// - // Popup position and size - //////////////////////////////////////////////////////////////////////////*/ - //region Popup position and size /** * Check if {@link #popupLayoutParams}' position is within a arbitrary boundary @@ -275,9 +290,17 @@ public final class PopupPlayerUi extends VideoPlayerUi { + screenWidth + "], screenHeight = [" + screenHeight + "]"); } } + //endregion + + + /*////////////////////////////////////////////////////////////////////////// + // Popup closing + //////////////////////////////////////////////////////////////////////////*/ + //region Popup closing /** * Changes the size of the popup based on the width. + * * @param width the new width, height is calculated with * {@link PlayerHelper#getMinimumVideoHeight(float)} */ @@ -310,13 +333,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { // for the main player so that it is enlarged correctly inside the fragment return bitmap.getHeight(); } - //endregion - - - /*////////////////////////////////////////////////////////////////////////// - // Popup closing - //////////////////////////////////////////////////////////////////////////*/ - //region Popup closing public void closePopup() { if (DEBUG) { @@ -336,6 +352,12 @@ public final class PopupPlayerUi extends VideoPlayerUi { public boolean isPopupClosing() { return isPopupClosing; } + //endregion + + /*////////////////////////////////////////////////////////////////////////// + // Playback states + //////////////////////////////////////////////////////////////////////////*/ + //region Playback states public void removePopupFromView() { // wrap in try-catch since it could sometimes generate errors randomly @@ -386,12 +408,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { } }).start(); } - //endregion - - /*////////////////////////////////////////////////////////////////////////// - // Playback states - //////////////////////////////////////////////////////////////////////////*/ - //region Playback states private void changePopupWindowFlags(final int flags) { if (DEBUG) { @@ -421,6 +437,13 @@ public final class PopupPlayerUi extends VideoPlayerUi { super.onCompleted(); changePopupWindowFlags(IDLE_WINDOW_FLAGS); } + //endregion + + + /*////////////////////////////////////////////////////////////////////////// + // Gestures + //////////////////////////////////////////////////////////////////////////*/ + //region Gestures @Override protected void setupSubtitleView(final float captionScale) { @@ -434,13 +457,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { playbackSpeedPopupMenu.show(); isSomePopupMenuVisible = true; } - //endregion - - - /*////////////////////////////////////////////////////////////////////////// - // Gestures - //////////////////////////////////////////////////////////////////////////*/ - //region Gestures private int distanceFromCloseButton(@NonNull final MotionEvent popupMotionEvent) { final int closeOverlayButtonX = closeOverlayBinding.closeButton.getLeft() @@ -454,6 +470,13 @@ public final class PopupPlayerUi extends VideoPlayerUi { return (int) Math.sqrt(Math.pow(closeOverlayButtonX - fingerX, 2) + Math.pow(closeOverlayButtonY - fingerY, 2)); } + //endregion + + + /*////////////////////////////////////////////////////////////////////////// + // Popup & closing overlay layout params + saving popup position and size + //////////////////////////////////////////////////////////////////////////*/ + //region Popup & closing overlay layout params + saving popup position and size private float getClosingRadius() { final int buttonRadius = closeOverlayBinding.closeButton.getWidth() / 2; @@ -464,16 +487,10 @@ public final class PopupPlayerUi extends VideoPlayerUi { public boolean isInsideClosingRadius(@NonNull final MotionEvent popupMotionEvent) { return distanceFromCloseButton(popupMotionEvent) <= getClosingRadius(); } - //endregion - - - /*////////////////////////////////////////////////////////////////////////// - // Popup & closing overlay layout params + saving popup position and size - //////////////////////////////////////////////////////////////////////////*/ - //region Popup & closing overlay layout params + saving popup position and size /** * {@code screenWidth} and {@code screenHeight} must have been initialized. + * * @return the popup starting layout params */ @SuppressLint("RtlHardcoded") @@ -520,36 +537,6 @@ public final class PopupPlayerUi extends VideoPlayerUi { .apply(); } } - - @SuppressLint("RtlHardcoded") - public static WindowManager.LayoutParams buildCloseOverlayLayoutParams() { - final int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE - | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE - | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; - - final WindowManager.LayoutParams closeOverlayLayoutParams = new WindowManager.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - popupLayoutParamType(), - flags, - PixelFormat.TRANSLUCENT); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - // Setting maximum opacity allowed for touch events to other apps for Android 12 and - // higher to prevent non interaction when using other apps with the popup player - closeOverlayLayoutParams.alpha = MAXIMUM_OPACITY_ALLOWED_FOR_S_AND_HIGHER; - } - - closeOverlayLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; - closeOverlayLayoutParams.softInputMode = - WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; - return closeOverlayLayoutParams; - } - - public static int popupLayoutParamType() { - return Build.VERSION.SDK_INT < Build.VERSION_CODES.O - ? WindowManager.LayoutParams.TYPE_PHONE - : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; - } //endregion diff --git a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java index e4f5b05e1..9e3f240db 100644 --- a/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java +++ b/app/src/main/java/org/schabi/newpipe/player/ui/VideoPlayerUi.java @@ -1,21 +1,5 @@ package org.schabi.newpipe.player.ui; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; -import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.ktx.ViewUtils.animate; -import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; -import static org.schabi.newpipe.player.Player.RENDERER_UNAVAILABLE; -import static org.schabi.newpipe.player.Player.STATE_BUFFERING; -import static org.schabi.newpipe.player.Player.STATE_COMPLETED; -import static org.schabi.newpipe.player.Player.STATE_PAUSED; -import static org.schabi.newpipe.player.Player.STATE_PAUSED_SEEK; -import static org.schabi.newpipe.player.Player.STATE_PLAYING; -import static org.schabi.newpipe.player.helper.PlayerHelper.formatSpeed; -import static org.schabi.newpipe.player.helper.PlayerHelper.getTimeString; -import static org.schabi.newpipe.player.helper.PlayerHelper.nextResizeModeAndSaveToPrefs; -import static org.schabi.newpipe.player.helper.PlayerHelper.retrieveSeekDurationFromPreferences; - import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; @@ -27,16 +11,10 @@ import android.os.Build; import android.os.Handler; import android.os.Looper; import android.util.Log; -import android.view.GestureDetector; -import android.view.Gravity; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; +import android.view.*; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.SeekBar; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; @@ -47,18 +25,12 @@ import androidx.core.graphics.Insets; import androidx.core.math.MathUtils; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; - -import com.google.android.exoplayer2.C; -import com.google.android.exoplayer2.ExoPlayer; -import com.google.android.exoplayer2.Format; -import com.google.android.exoplayer2.PlaybackParameters; +import com.google.android.exoplayer2.*; import com.google.android.exoplayer2.Player.RepeatMode; -import com.google.android.exoplayer2.Tracks; import com.google.android.exoplayer2.text.Cue; import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import com.google.android.exoplayer2.ui.CaptionStyleCompat; import com.google.android.exoplayer2.video.VideoSize; - import org.schabi.newpipe.App; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.PlayerBinding; @@ -88,16 +60,22 @@ import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; +import static com.google.android.exoplayer2.Player.REPEAT_MODE_ALL; +import static com.google.android.exoplayer2.Player.REPEAT_MODE_ONE; +import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.ktx.ViewUtils.animate; +import static org.schabi.newpipe.ktx.ViewUtils.animateRotation; +import static org.schabi.newpipe.player.Player.*; +import static org.schabi.newpipe.player.helper.PlayerHelper.*; + public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBarChangeListener, PopupMenu.OnMenuItemClickListener, PopupMenu.OnDismissListener { - private static final String TAG = VideoPlayerUi.class.getSimpleName(); - // time constants public static final long DEFAULT_CONTROLS_DURATION = 300; // 300 millis public static final long DEFAULT_CONTROLS_HIDE_TIME = 2000; // 2 Seconds public static final long DPAD_CONTROLS_HIDE_TIME = 7000; // 7 Seconds public static final int SEEK_OVERLAY_DURATION = 450; // 450 millis - + private static final String TAG = VideoPlayerUi.class.getSimpleName(); // other constants (TODO remove playback speeds and use normal menu for popup, too) private static final float[] PLAYBACK_SPEEDS = {0.5f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f}; @@ -105,40 +83,36 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa /*////////////////////////////////////////////////////////////////////////// // Views //////////////////////////////////////////////////////////////////////////*/ - - protected PlayerBinding binding; + private static final int POPUP_MENU_ID_QUALITY = 69; + private static final int POPUP_MENU_ID_PLAYBACK_SPEED = 79; + private static final int POPUP_MENU_ID_CAPTION = 89; private final Handler controlsVisibilityHandler = new Handler(Looper.getMainLooper()); - @Nullable private SurfaceHolderCallback surfaceHolderCallback; - boolean surfaceIsSetup = false; /*////////////////////////////////////////////////////////////////////////// // Popup menus ("popup" means that they pop up, not that they belong to the popup player) //////////////////////////////////////////////////////////////////////////*/ - - private static final int POPUP_MENU_ID_QUALITY = 69; - private static final int POPUP_MENU_ID_PLAYBACK_SPEED = 79; - private static final int POPUP_MENU_ID_CAPTION = 89; - + @NonNull + private final SeekbarPreviewThumbnailHolder seekbarPreviewThumbnailHolder = + new SeekbarPreviewThumbnailHolder(); + protected PlayerBinding binding; protected boolean isSomePopupMenuVisible = false; - private PopupMenu qualityPopupMenu; protected PopupMenu playbackSpeedPopupMenu; - private PopupMenu captionPopupMenu; + boolean surfaceIsSetup = false; + @Nullable + private SurfaceHolderCallback surfaceHolderCallback; + private PopupMenu qualityPopupMenu; /*////////////////////////////////////////////////////////////////////////// // Gestures //////////////////////////////////////////////////////////////////////////*/ - + private PopupMenu captionPopupMenu; private GestureDetector gestureDetector; private BasePlayerGestureListener playerGestureListener; @Nullable private View.OnLayoutChangeListener onLayoutChangeListener = null; - @NonNull - private final SeekbarPreviewThumbnailHolder seekbarPreviewThumbnailHolder = - new SeekbarPreviewThumbnailHolder(); - /*////////////////////////////////////////////////////////////////////////// // Constructor, setup, destroy @@ -146,7 +120,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa //region Constructor, setup, destroy protected VideoPlayerUi(@NonNull final Player player, - @NonNull final PlayerBinding playerBinding) { + @NonNull final PlayerBinding playerBinding) { super(player); binding = playerBinding; setupFromView(); @@ -524,6 +498,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa /** * Sets the current duration into the corresponding elements. + * * @param currentProgress the current progress, in milliseconds */ private void updatePlayBackElementsCurrentDuration(final int currentProgress) { @@ -536,6 +511,7 @@ public abstract class VideoPlayerUi extends PlayerUi implements SeekBar.OnSeekBa /** * Sets the video duration time into all control components (e.g. seekbar). + * * @param duration the video duration, in milliseconds */ private void setVideoDurationToControls(final int duration) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java index ef0e8670c..5a8adaa4e 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/AppearanceSettingsFragment.java @@ -5,10 +5,8 @@ import android.content.Intent; import android.os.Bundle; import android.provider.Settings; import android.widget.Toast; - import androidx.core.app.ActivityCompat; import androidx.preference.Preference; - import org.schabi.newpipe.R; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.ThemeHelper; diff --git a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java index 619579f3a..bd6c65665 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/BasePreferenceFragment.java @@ -3,23 +3,20 @@ package org.schabi.newpipe.settings; import android.content.SharedPreferences; import android.os.Bundle; import android.view.View; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.util.ThemeHelper; import java.util.Objects; public abstract class BasePreferenceFragment extends PreferenceFragmentCompat { - protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); protected static final boolean DEBUG = MainActivity.DEBUG; - + protected final String TAG = getClass().getSimpleName() + "@" + Integer.toHexString(hashCode()); SharedPreferences defaultPreferences; @Override diff --git a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java index 37f83057b..13d2ed9de 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/ContentSettingsFragment.java @@ -1,8 +1,5 @@ package org.schabi.newpipe.settings; -import static org.schabi.newpipe.extractor.utils.Utils.isBlank; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - import android.app.Activity; import android.content.Context; import android.content.Intent; @@ -11,7 +8,6 @@ import android.net.Uri; import android.os.Bundle; import android.util.Log; import android.widget.Toast; - import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; @@ -19,7 +15,6 @@ import androidx.appcompat.app.AlertDialog; import androidx.core.content.ContextCompat; import androidx.preference.Preference; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; @@ -40,6 +35,9 @@ import java.util.Date; import java.util.Locale; import java.util.Objects; +import static org.schabi.newpipe.extractor.utils.Utils.isBlank; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; + public class ContentSettingsFragment extends BasePreferenceFragment { private static final String ZIP_MIME_TYPE = "application/zip"; @@ -47,17 +45,15 @@ public class ContentSettingsFragment extends BasePreferenceFragment { new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US); private ContentSettingsManager manager; - + private final ActivityResultLauncher requestImportPathLauncher = + registerForActivityResult(new StartActivityForResult(), this::requestImportPathResult); private String importExportDataPathKey; + private final ActivityResultLauncher requestExportPathLauncher = + registerForActivityResult(new StartActivityForResult(), this::requestExportPathResult); private String youtubeRestrictedModeEnabledKey; - private Localization initialSelectedLocalization; private ContentCountry initialSelectedContentCountry; private String initialLanguage; - private final ActivityResultLauncher requestImportPathLauncher = - registerForActivityResult(new StartActivityForResult(), this::requestImportPathResult); - private final ActivityResultLauncher requestExportPathLauncher = - registerForActivityResult(new StartActivityForResult(), this::requestExportPathResult); @Override public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { @@ -110,7 +106,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { try { PicassoHelper.clearCache(preference.getContext()); Toast.makeText(preference.getContext(), - R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) + R.string.thumbnail_cache_wipe_complete_notice, Toast.LENGTH_SHORT) .show(); } catch (final IOException e) { Log.e(TAG, "Unable to clear Picasso cache", e); @@ -218,7 +214,7 @@ public class ContentSettingsFragment extends BasePreferenceFragment { if (!manager.extractDb(file)) { Toast.makeText(getContext(), R.string.could_not_import_all_files, Toast.LENGTH_LONG) - .show(); + .show(); } // if settings file exist, ask if it should be imported. diff --git a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java index 0f4c9765e..151a3e9c6 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DebugSettingsFragment.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.settings; import android.content.Intent; import android.os.Bundle; - import androidx.preference.Preference; - import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; @@ -90,6 +88,7 @@ public class DebugSettingsFragment extends BasePreferenceFragment { /** * Tries to find the {@link DebugSettingsBVDLeakCanaryAPI#IMPL_CLASS} and loads it if available. + * * @return An {@link Optional} which is empty if the implementation class couldn't be loaded. */ private Optional getBVDLeakCanary() { diff --git a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java index 782b7b906..3ed677974 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/DownloadSettingsFragment.java @@ -1,8 +1,5 @@ package org.schabi.newpipe.settings; -import static org.schabi.newpipe.extractor.utils.Utils.decodeUrlUtf8; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - import android.app.Activity; import android.content.ContentResolver; import android.content.Context; @@ -11,7 +8,6 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.util.Log; - import androidx.activity.result.ActivityResult; import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; @@ -20,9 +16,7 @@ import androidx.annotation.StringRes; import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; import androidx.preference.SwitchPreferenceCompat; - import com.nononsenseapps.filepicker.Utils; - import org.schabi.newpipe.R; import org.schabi.newpipe.streams.io.NoFileManagerSafeGuard; import org.schabi.newpipe.streams.io.StoredDirectoryHelper; @@ -33,6 +27,9 @@ import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; +import static org.schabi.newpipe.extractor.utils.Utils.decodeUrlUtf8; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; + public class DownloadSettingsFragment extends BasePreferenceFragment { public static final boolean IGNORE_RELEASE_ON_OLD_PATH = true; private String downloadPathVideoPreference; diff --git a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java index 86e651e2b..29394cd66 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/HistorySettingsFragment.java @@ -3,11 +3,12 @@ package org.schabi.newpipe.settings; import android.content.Context; import android.os.Bundle; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.preference.Preference; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; @@ -17,10 +18,6 @@ import org.schabi.newpipe.error.UserAction; import org.schabi.newpipe.local.history.HistoryRecordManager; import org.schabi.newpipe.util.InfoCache; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; - public class HistorySettingsFragment extends BasePreferenceFragment { private String cacheWipeKey; private String viewsHistoryClearKey; @@ -29,58 +26,13 @@ public class HistorySettingsFragment extends BasePreferenceFragment { private HistoryRecordManager recordManager; private CompositeDisposable disposables; - @Override - public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { - addPreferencesFromResourceRegistry(); - - cacheWipeKey = getString(R.string.metadata_cache_wipe_key); - viewsHistoryClearKey = getString(R.string.clear_views_history_key); - playbackStatesClearKey = getString(R.string.clear_playback_states_key); - searchHistoryClearKey = getString(R.string.clear_search_history_key); - recordManager = new HistoryRecordManager(getActivity()); - disposables = new CompositeDisposable(); - - final Preference clearCookiePref = requirePreference(R.string.clear_cookie_key); - clearCookiePref.setOnPreferenceClickListener(preference -> { - defaultPreferences.edit() - .putString(getString(R.string.recaptcha_cookies_key), "").apply(); - DownloaderImpl.getInstance().setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, ""); - Toast.makeText(getActivity(), R.string.recaptcha_cookies_cleared, - Toast.LENGTH_SHORT).show(); - clearCookiePref.setEnabled(false); - return true; - }); - - if (defaultPreferences.getString(getString(R.string.recaptcha_cookies_key), "").isEmpty()) { - clearCookiePref.setEnabled(false); - } - } - - @Override - public boolean onPreferenceTreeClick(final Preference preference) { - if (preference.getKey().equals(cacheWipeKey)) { - InfoCache.getInstance().clearCache(); - Toast.makeText(requireContext(), - R.string.metadata_cache_wipe_complete_notice, Toast.LENGTH_SHORT).show(); - } else if (preference.getKey().equals(viewsHistoryClearKey)) { - openDeleteWatchHistoryDialog(requireContext(), recordManager, disposables); - } else if (preference.getKey().equals(playbackStatesClearKey)) { - openDeletePlaybackStatesDialog(requireContext(), recordManager, disposables); - } else if (preference.getKey().equals(searchHistoryClearKey)) { - openDeleteSearchHistoryDialog(requireContext(), recordManager, disposables); - } else { - return super.onPreferenceTreeClick(preference); - } - return true; - } - private static Disposable getDeletePlaybackStatesDisposable( @NonNull final Context context, final HistoryRecordManager recordManager) { return recordManager.deleteCompleteStreamStateHistory() .observeOn(AndroidSchedulers.mainThread()) .subscribe( howManyDeleted -> Toast.makeText(context, - R.string.watch_history_states_deleted, Toast.LENGTH_SHORT).show(), + R.string.watch_history_states_deleted, Toast.LENGTH_SHORT).show(), throwable -> ErrorUtil.openActivity(context, new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY, "Delete playback states"))); @@ -103,7 +55,8 @@ public class HistorySettingsFragment extends BasePreferenceFragment { return recordManager.removeOrphanedRecords() .observeOn(AndroidSchedulers.mainThread()) .subscribe( - howManyDeleted -> { }, + howManyDeleted -> { + }, throwable -> ErrorUtil.openActivity(context, new ErrorInfo(throwable, UserAction.DELETE_FROM_HISTORY, "Clear orphaned records"))); @@ -159,4 +112,49 @@ public class HistorySettingsFragment extends BasePreferenceFragment { .create() .show(); } + + @Override + public void onCreatePreferences(final Bundle savedInstanceState, final String rootKey) { + addPreferencesFromResourceRegistry(); + + cacheWipeKey = getString(R.string.metadata_cache_wipe_key); + viewsHistoryClearKey = getString(R.string.clear_views_history_key); + playbackStatesClearKey = getString(R.string.clear_playback_states_key); + searchHistoryClearKey = getString(R.string.clear_search_history_key); + recordManager = new HistoryRecordManager(getActivity()); + disposables = new CompositeDisposable(); + + final Preference clearCookiePref = requirePreference(R.string.clear_cookie_key); + clearCookiePref.setOnPreferenceClickListener(preference -> { + defaultPreferences.edit() + .putString(getString(R.string.recaptcha_cookies_key), "").apply(); + DownloaderImpl.getInstance().setCookie(ReCaptchaActivity.RECAPTCHA_COOKIES_KEY, ""); + Toast.makeText(getActivity(), R.string.recaptcha_cookies_cleared, + Toast.LENGTH_SHORT).show(); + clearCookiePref.setEnabled(false); + return true; + }); + + if (defaultPreferences.getString(getString(R.string.recaptcha_cookies_key), "").isEmpty()) { + clearCookiePref.setEnabled(false); + } + } + + @Override + public boolean onPreferenceTreeClick(final Preference preference) { + if (preference.getKey().equals(cacheWipeKey)) { + InfoCache.getInstance().clearCache(); + Toast.makeText(requireContext(), + R.string.metadata_cache_wipe_complete_notice, Toast.LENGTH_SHORT).show(); + } else if (preference.getKey().equals(viewsHistoryClearKey)) { + openDeleteWatchHistoryDialog(requireContext(), recordManager, disposables); + } else if (preference.getKey().equals(playbackStatesClearKey)) { + openDeletePlaybackStatesDialog(requireContext(), recordManager, disposables); + } else if (preference.getKey().equals(searchHistoryClearKey)) { + openDeleteSearchHistoryDialog(requireContext(), recordManager, disposables); + } else { + return super.onPreferenceTreeClick(preference); + } + return true; + } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java index 3776d78f6..9ac7bbee5 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/MainSettingsFragment.java @@ -4,9 +4,7 @@ import android.os.Bundle; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; - import androidx.annotation.NonNull; - import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.util.ReleaseVersionUtil; diff --git a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java index 16df646f9..16923395a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java +++ b/app/src/main/java/org/schabi/newpipe/settings/NewPipeSettings.java @@ -4,11 +4,9 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Build; import android.os.Environment; - import androidx.annotation.NonNull; import androidx.annotation.StringRes; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import org.schabi.newpipe.util.DeviceUtils; @@ -41,14 +39,15 @@ import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; * Helper class for global settings. */ public final class NewPipeSettings { - private NewPipeSettings() { } + private NewPipeSettings() { + } public static void initSettings(final Context context) { // check if there are entries in the prefs to determine whether this is the first app run Boolean isFirstRun = null; final Set prefsKeys = PreferenceManager.getDefaultSharedPreferences(context) .getAll().keySet(); - for (final String key: prefsKeys) { + for (final String key : prefsKeys) { // ACRA stores some info in the prefs during app initialization // which happens before this method is called. Therefore ignore ACRA-related keys. if (!key.toLowerCase().startsWith("acra")) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java b/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java index 1158b3d83..cc92fb473 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/PeertubeInstanceListFragment.java @@ -5,30 +5,22 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.text.InputType; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.RadioButton; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; import androidx.fragment.app.Fragment; import androidx.preference.PreferenceManager; -import androidx.recyclerview.widget.DiffUtil; -import androidx.recyclerview.widget.ItemTouchHelper; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.ListAdapter; -import androidx.recyclerview.widget.RecyclerView; - +import androidx.recyclerview.widget.*; import com.grack.nanojson.JsonStringWriter; import com.grack.nanojson.JsonWriter; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.DialogEditTextBinding; import org.schabi.newpipe.databinding.FragmentInstanceListBinding; @@ -41,12 +33,6 @@ import org.schabi.newpipe.util.ThemeHelper; import java.util.ArrayList; import java.util.Collections; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; - public class PeertubeInstanceListFragment extends Fragment { private PeertubeInstance selectedInstance; private String savedInstanceListKey; @@ -206,10 +192,10 @@ public class PeertubeInstanceListFragment extends Fragment { } binding.loadingProgressBar.setVisibility(View.VISIBLE); final Disposable disposable = Single.fromCallable(() -> { - final PeertubeInstance instance = new PeertubeInstance(cleanUrl); - instance.fetchInstanceMetaData(); - return instance; - }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) + final PeertubeInstance instance = new PeertubeInstance(cleanUrl); + instance.fetchInstanceMetaData(); + return instance; + }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) .subscribe((instance) -> { binding.loadingProgressBar.setVisibility(View.GONE); add(instance); @@ -319,6 +305,21 @@ public class PeertubeInstanceListFragment extends Fragment { // List Handling //////////////////////////////////////////////////////////////////////////*/ + private static class PeertubeInstanceCallback extends DiffUtil.ItemCallback { + @Override + public boolean areItemsTheSame(@NonNull final PeertubeInstance oldItem, + @NonNull final PeertubeInstance newItem) { + return oldItem.getUrl().equals(newItem.getUrl()); + } + + @Override + public boolean areContentsTheSame(@NonNull final PeertubeInstance oldItem, + @NonNull final PeertubeInstance newItem) { + return oldItem.getName().equals(newItem.getName()) + && oldItem.getUrl().equals(newItem.getUrl()); + } + } + private class InstanceListAdapter extends ListAdapter { private final LayoutInflater inflater; @@ -395,19 +396,4 @@ public class PeertubeInstanceListFragment extends Fragment { } } } - - private static class PeertubeInstanceCallback extends DiffUtil.ItemCallback { - @Override - public boolean areItemsTheSame(@NonNull final PeertubeInstance oldItem, - @NonNull final PeertubeInstance newItem) { - return oldItem.getUrl().equals(newItem.getUrl()); - } - - @Override - public boolean areContentsTheSame(@NonNull final PeertubeInstance oldItem, - @NonNull final PeertubeInstance newItem) { - return oldItem.getName().equals(newItem.getName()) - && oldItem.getUrl().equals(newItem.getUrl()); - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java index 0f25be630..2acc4d401 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectChannelFragment.java @@ -8,13 +8,15 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.DialogFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Observer; +import io.reactivex.rxjava3.disposables.Disposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.R; import org.schabi.newpipe.database.subscription.SubscriptionEntity; import org.schabi.newpipe.error.ErrorUtil; @@ -25,11 +27,6 @@ import org.schabi.newpipe.util.ThemeHelper; import java.util.List; import java.util.Vector; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Observer; -import io.reactivex.rxjava3.disposables.Disposable; -import io.reactivex.rxjava3.schedulers.Schedulers; - /** * Created by Christian Schabesberger on 26.09.17. * SelectChannelFragment.java is part of NewPipe. @@ -144,7 +141,8 @@ public class SelectChannelFragment extends DialogFragment { private Observer> getSubscriptionObserver() { return new Observer>() { @Override - public void onSubscribe(@NonNull final Disposable disposable) { } + public void onSubscribe(@NonNull final Disposable disposable) { + } @Override public void onNext(@NonNull final List newSubscriptions) { @@ -158,7 +156,8 @@ public class SelectChannelFragment extends DialogFragment { } @Override - public void onComplete() { } + public void onComplete() { + } }; } @@ -202,6 +201,7 @@ public class SelectChannelFragment extends DialogFragment { public final View view; final ImageView thumbnailView; final TextView titleView; + SelectChannelItemHolder(final View v) { super(v); this.view = v; diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java index 383390506..ffaa67757 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectKioskFragment.java @@ -6,14 +6,12 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.content.res.AppCompatResources; import androidx.fragment.app.DialogFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.NewPipe; diff --git a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java index e8491d52c..9f5e8175b 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SelectPlaylistFragment.java @@ -7,12 +7,13 @@ import android.view.ViewGroup; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.fragment.app.DialogFragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Flowable; +import io.reactivex.rxjava3.disposables.Disposable; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.AppDatabase; @@ -30,10 +31,6 @@ import org.schabi.newpipe.util.PicassoHelper; import java.util.List; import java.util.Vector; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Flowable; -import io.reactivex.rxjava3.disposables.Disposable; - public class SelectPlaylistFragment extends DialogFragment { private OnSelectedListener onSelectedListener = null; @@ -91,7 +88,7 @@ public class SelectPlaylistFragment extends DialogFragment { final RemotePlaylistManager remotePlaylistManager = new RemotePlaylistManager(database); disposable = Flowable.combineLatest(localPlaylistManager.getPlaylists(), - remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge) + remotePlaylistManager.getPlaylists(), PlaylistLocalItem::merge) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::displayPlaylists, this::onError); } @@ -116,12 +113,10 @@ public class SelectPlaylistFragment extends DialogFragment { if (onSelectedListener != null) { final LocalItem selectedItem = playlists.get(position); - if (selectedItem instanceof PlaylistMetadataEntry) { - final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); + if (selectedItem instanceof final PlaylistMetadataEntry entry) { onSelectedListener.onLocalPlaylistSelected(entry.uid, entry.name); - } else if (selectedItem instanceof PlaylistRemoteEntity) { - final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); + } else if (selectedItem instanceof final PlaylistRemoteEntity entry) { onSelectedListener.onRemotePlaylistSelected( entry.getServiceId(), entry.getUrl(), entry.getName()); } @@ -135,6 +130,7 @@ public class SelectPlaylistFragment extends DialogFragment { public interface OnSelectedListener { void onLocalPlaylistSelected(long id, String name); + void onRemotePlaylistSelected(int serviceId, String url, String name); } @@ -154,15 +150,13 @@ public class SelectPlaylistFragment extends DialogFragment { final int position) { final PlaylistLocalItem selectedItem = playlists.get(position); - if (selectedItem instanceof PlaylistMetadataEntry) { - final PlaylistMetadataEntry entry = ((PlaylistMetadataEntry) selectedItem); + if (selectedItem instanceof final PlaylistMetadataEntry entry) { holder.titleView.setText(entry.name); holder.view.setOnClickListener(view -> clickedItem(position)); PicassoHelper.loadPlaylistThumbnail(entry.thumbnailUrl).into(holder.thumbnailView); - } else if (selectedItem instanceof PlaylistRemoteEntity) { - final PlaylistRemoteEntity entry = ((PlaylistRemoteEntity) selectedItem); + } else if (selectedItem instanceof final PlaylistRemoteEntity entry) { holder.titleView.setText(entry.getName()); holder.view.setOnClickListener(view -> clickedItem(position)); diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java index b1e2c04eb..be8233029 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingMigrations.java @@ -3,9 +3,7 @@ package org.schabi.newpipe.settings; import android.content.Context; import android.content.SharedPreferences; import android.util.Log; - import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; @@ -21,15 +19,18 @@ import static org.schabi.newpipe.MainActivity.DEBUG; /** * In order to add a migration, follow these steps, given P is the previous version:
* - in the class body add a new {@code MIGRATION_P_P+1 = new Migration(P, P+1) { ... }} and put in - * the {@code migrate()} method the code that need to be run when migrating from P to P+1
+ * the {@code migrate()} method the code that need to be run when migrating from P to P+1
* - add {@code MIGRATION_P_P+1} at the end of {@link SettingMigrations#SETTING_MIGRATIONS}
* - increment {@link SettingMigrations#VERSION}'s value by 1 (so it should become P+1) */ public final class SettingMigrations { + /** + * Version number for preferences. Must be incremented every time a migration is necessary. + */ + public static final int VERSION = 4; private static final String TAG = SettingMigrations.class.toString(); private static SharedPreferences sp; - public static final Migration MIGRATION_0_1 = new Migration(0, 1) { @Override public void migrate(final Context context) { @@ -43,7 +44,6 @@ public final class SettingMigrations { editor.apply(); } }; - public static final Migration MIGRATION_1_2 = new Migration(1, 2) { @Override protected void migrate(final Context context) { @@ -62,7 +62,6 @@ public final class SettingMigrations { } } }; - public static final Migration MIGRATION_2_3 = new Migration(2, 3) { @Override protected void migrate(final Context context) { @@ -78,7 +77,6 @@ public final class SettingMigrations { ).apply(); } }; - public static final Migration MIGRATION_3_4 = new Migration(3, 4) { @Override protected void migrate(final Context context) { @@ -107,7 +105,6 @@ public final class SettingMigrations { showSearchSuggestionsKey, showSearchSuggestionsValueList).apply(); } }; - /** * List of all implemented migrations. *

@@ -121,11 +118,9 @@ public final class SettingMigrations { MIGRATION_3_4, }; - /** - * Version number for preferences. Must be incremented every time a migration is necessary. - */ - public static final int VERSION = 4; + private SettingMigrations() { + } public static void initMigrations(final Context context, final boolean isFirstRun) { // setup migrations and check if there is something to do @@ -161,7 +156,7 @@ public final class SettingMigrations { UserAction.PREFERENCES_MIGRATION, "Migrating preferences from version " + lastPrefVersion + " to " + VERSION + ". " - + "Error at " + currentVersion + " => " + ++currentVersion + + "Error at " + currentVersion + " => " + ++currentVersion )); return; } @@ -171,8 +166,6 @@ public final class SettingMigrations { sp.edit().putInt(lastPrefVersionKey, currentVersion).apply(); } - private SettingMigrations() { } - abstract static class Migration { public final int oldVersion; public final int newVersion; diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java index 3ee6668bf..c1c1ee4e6 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsActivity.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.settings; -import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; - import android.content.Context; import android.os.Bundle; import android.text.TextUtils; @@ -10,7 +8,6 @@ import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.EditText; - import androidx.annotation.IdRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -20,19 +17,13 @@ import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; - import com.jakewharton.rxbinding4.widget.RxTextView; - +import icepick.Icepick; +import icepick.State; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.SettingsLayoutBinding; -import org.schabi.newpipe.settings.preferencesearch.PreferenceParser; -import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchConfiguration; -import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchFragment; -import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchItem; -import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchResultHighlighter; -import org.schabi.newpipe.settings.preferencesearch.PreferenceSearchResultListener; -import org.schabi.newpipe.settings.preferencesearch.PreferenceSearcher; +import org.schabi.newpipe.settings.preferencesearch.*; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KeyboardUtil; import org.schabi.newpipe.util.ReleaseVersionUtil; @@ -41,8 +32,7 @@ import org.schabi.newpipe.views.FocusOverlayView; import java.util.concurrent.TimeUnit; -import icepick.Icepick; -import icepick.State; +import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; /* * Created by Christian Schabesberger on 31.08.15. @@ -72,20 +62,16 @@ public class SettingsActivity extends AppCompatActivity implements @IdRes private static final int FRAGMENT_HOLDER_ID = R.id.settings_fragment_holder; - - private PreferenceSearchFragment searchFragment; - - @Nullable - private MenuItem menuSearchItem; - - private View searchContainer; - private EditText searchEditText; - // State @State String searchText; @State boolean wasSearchActive; + private PreferenceSearchFragment searchFragment; + @Nullable + private MenuItem menuSearchItem; + private View searchContainer; + private EditText searchEditText; @Override protected void onCreate(final Bundle savedInstanceBundle) { @@ -290,6 +276,18 @@ public class SettingsActivity extends AppCompatActivity implements } } + private void hideSearchFragment() { + getSupportFragmentManager().beginTransaction().remove(searchFragment).commit(); + } + + private void resetSearchText() { + searchEditText.setText(""); + } + + private boolean isSearchActive() { + return searchContainer.getVisibility() == View.VISIBLE; + } + public void setSearchActive(final boolean active) { if (DEBUG) { Log.d(TAG, "setSearchActive called active=" + active); @@ -319,8 +317,8 @@ public class SettingsActivity extends AppCompatActivity implements hideSearchFragment(); getSupportFragmentManager() .popBackStack( - PreferenceSearchFragment.NAME, - FragmentManager.POP_BACK_STACK_INCLUSIVE); + PreferenceSearchFragment.NAME, + FragmentManager.POP_BACK_STACK_INCLUSIVE); KeyboardUtil.hideKeyboard(this, searchEditText); } @@ -328,18 +326,6 @@ public class SettingsActivity extends AppCompatActivity implements resetSearchText(); } - private void hideSearchFragment() { - getSupportFragmentManager().beginTransaction().remove(searchFragment).commit(); - } - - private void resetSearchText() { - searchEditText.setText(""); - } - - private boolean isSearchActive() { - return searchContainer.getVisibility() == View.VISIBLE; - } - private void onSearchChanged() { if (!isSearchActive()) { return; diff --git a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java index 78ddb3786..ab2f97618 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java +++ b/app/src/main/java/org/schabi/newpipe/settings/SettingsResourceRegistry.java @@ -3,7 +3,6 @@ package org.schabi.newpipe.settings; import androidx.annotation.NonNull; import androidx.annotation.XmlRes; import androidx.fragment.app.Fragment; - import org.schabi.newpipe.R; import java.util.HashSet; @@ -19,7 +18,7 @@ import java.util.Set; *

  • XML-Resource
  • *
  • ...
  • * - * + *

    * E.g. used by the preference search. */ public final class SettingsResourceRegistry { @@ -42,6 +41,10 @@ public final class SettingsResourceRegistry { add(VideoAudioSettingsFragment.class, R.xml.video_audio_settings); } + public static SettingsResourceRegistry getInstance() { + return INSTANCE; + } + private SettingRegistryEntry add( @NonNull final Class fragmentClass, @XmlRes final int preferencesResId @@ -89,11 +92,6 @@ public final class SettingsResourceRegistry { return new HashSet<>(registeredEntries); } - public static SettingsResourceRegistry getInstance() { - return INSTANCE; - } - - public static class SettingRegistryEntry { @NonNull private final Class fragmentClass; @@ -110,12 +108,6 @@ public final class SettingsResourceRegistry { this.preferencesResId = preferencesResId; } - @SuppressWarnings("HiddenField") - public SettingRegistryEntry setSearchable(final boolean searchable) { - this.searchable = searchable; - return this; - } - @NonNull public Class getFragmentClass() { return fragmentClass; @@ -129,6 +121,12 @@ public final class SettingsResourceRegistry { return searchable; } + @SuppressWarnings("HiddenField") + public SettingRegistryEntry setSearchable(final boolean searchable) { + this.searchable = searchable; + return this; + } + @Override public boolean equals(final Object o) { if (this == o) { diff --git a/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java index d1a379e66..bc04f43cd 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/UpdateSettingsFragment.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.settings; import android.os.Bundle; import android.widget.Toast; - import androidx.preference.Preference; - import org.schabi.newpipe.NewVersionWorker; import org.schabi.newpipe.R; diff --git a/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java index aae9cfca5..46f67a996 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/VideoAudioSettingsFragment.java @@ -7,11 +7,8 @@ import android.os.Bundle; import android.provider.Settings; import android.text.format.DateUtils; import android.widget.Toast; - import androidx.preference.ListPreference; - import com.google.android.material.snackbar.Snackbar; - import org.schabi.newpipe.R; import org.schabi.newpipe.util.PermissionHelper; @@ -40,7 +37,7 @@ public class VideoAudioSettingsFragment extends BasePreferenceFragment { && !Settings.canDrawOverlays(getContext())) { Snackbar.make(getListView(), R.string.permission_display_over_apps, - Snackbar.LENGTH_INDEFINITE) + Snackbar.LENGTH_INDEFINITE) .setAction(R.string.settings, view -> PermissionHelper.checkSystemAlertWindowPermission(getContext())) .show(); diff --git a/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java index 3e92f297e..e6840ba89 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java +++ b/app/src/main/java/org/schabi/newpipe/settings/custom/NotificationActionsPreference.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.settings.custom; -import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION; - import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -10,13 +8,7 @@ import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.RadioButton; -import android.widget.RadioGroup; -import android.widget.TextView; -import android.widget.Toast; - +import android.widget.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -24,7 +16,6 @@ import androidx.appcompat.content.res.AppCompatResources; import androidx.core.widget.TextViewCompat; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; - import org.schabi.newpipe.App; import org.schabi.newpipe.R; import org.schabi.newpipe.databinding.ListRadioIconItemBinding; @@ -37,19 +28,41 @@ import org.schabi.newpipe.views.FocusOverlayView; import java.util.List; import java.util.stream.IntStream; +import static org.schabi.newpipe.player.notification.NotificationConstants.ACTION_RECREATE_NOTIFICATION; + public class NotificationActionsPreference extends Preference { + private static final int[] SLOT_ITEMS = { + R.id.notificationAction0, + R.id.notificationAction1, + R.id.notificationAction2, + R.id.notificationAction3, + R.id.notificationAction4, + }; + private static final int[] SLOT_TITLES = { + R.string.notification_action_0_title, + R.string.notification_action_1_title, + R.string.notification_action_2_title, + R.string.notification_action_3_title, + R.string.notification_action_4_title, + }; + @Nullable + private NotificationSlot[] notificationSlots = null; + + //////////////////////////////////////////////////////////////////////////// + // Lifecycle + //////////////////////////////////////////////////////////////////////////// + @Nullable + private List compactSlots = null; + public NotificationActionsPreference(final Context context, final AttributeSet attrs) { super(context, attrs); setLayoutResource(R.layout.settings_notification); } - @Nullable private NotificationSlot[] notificationSlots = null; - @Nullable private List compactSlots = null; - //////////////////////////////////////////////////////////////////////////// - // Lifecycle + // Setup //////////////////////////////////////////////////////////////////////////// @Override @@ -60,6 +73,11 @@ public class NotificationActionsPreference extends Preference { setupActions(holder.itemView); } + + //////////////////////////////////////////////////////////////////////////// + // Saving + //////////////////////////////////////////////////////////////////////////// + @Override public void onDetached() { super.onDetached(); @@ -71,7 +89,7 @@ public class NotificationActionsPreference extends Preference { //////////////////////////////////////////////////////////////////////////// - // Setup + // Notification action //////////////////////////////////////////////////////////////////////////// private void setupActions(@NonNull final View view) { @@ -82,18 +100,13 @@ public class NotificationActionsPreference extends Preference { .toArray(NotificationSlot[]::new); } - - //////////////////////////////////////////////////////////////////////////// - // Saving - //////////////////////////////////////////////////////////////////////////// - private void saveChanges() { if (compactSlots != null && notificationSlots != null) { final SharedPreferences.Editor editor = getSharedPreferences().edit(); for (int i = 0; i < 3; i++) { editor.putInt(getContext().getString( - NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]), + NotificationConstants.SLOT_COMPACT_PREF_KEYS[i]), (i < compactSlots.size() ? compactSlots.get(i) : -1)); } @@ -106,31 +119,11 @@ public class NotificationActionsPreference extends Preference { } } - - //////////////////////////////////////////////////////////////////////////// - // Notification action - //////////////////////////////////////////////////////////////////////////// - - private static final int[] SLOT_ITEMS = { - R.id.notificationAction0, - R.id.notificationAction1, - R.id.notificationAction2, - R.id.notificationAction3, - R.id.notificationAction4, - }; - - private static final int[] SLOT_TITLES = { - R.string.notification_action_0_title, - R.string.notification_action_1_title, - R.string.notification_action_2_title, - R.string.notification_action_3_title, - R.string.notification_action_4_title, - }; - private class NotificationSlot { final int i; - @NotificationConstants.Action int selectedAction; + @NotificationConstants.Action + int selectedAction; ImageView icon; TextView summary; diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java index 68b0010c4..4416f97ed 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceFuzzySearchFunction.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.settings.preferencesearch; import android.text.TextUtils; import android.util.Pair; - import org.apache.commons.text.similarity.FuzzyScore; import java.util.Comparator; diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java index b925e8b5f..80b16774a 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceParser.java @@ -3,12 +3,10 @@ package org.schabi.newpipe.settings.preferencesearch; import android.content.Context; import android.text.TextUtils; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.XmlRes; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.util.Localization; import org.xmlpull.v1.XmlPullParser; @@ -35,7 +33,7 @@ public class PreferenceParser { final PreferenceSearchConfiguration searchConfiguration ) { this.context = context; - this.allPreferences = PreferenceManager.getDefaultSharedPreferences(context).getAll(); + this.allPreferences = PreferenceManager.getDefaultSharedPreferences(context).getAll(); this.searchConfiguration = searchConfiguration; } @@ -70,7 +68,7 @@ public class PreferenceParser { } } else if (xpp.getEventType() == XmlPullParser.END_TAG && searchConfiguration.getParserContainerElements() - .contains(xpp.getName())) { + .contains(xpp.getName())) { breadcrumbs.remove(breadcrumbs.size() - 1); } @@ -111,20 +109,20 @@ public class PreferenceParser { final String[] entryValues = readStringArray(getAttribute(xpp, "entryValues")); return new PreferenceSearchItem( - key, - tryFillInPreferenceValue( - readString(getAttribute(xpp, "title")), key, - entries, - entryValues), - tryFillInPreferenceValue( - readString(getAttribute(xpp, "summary")), - key, - entries, - entryValues), - TextUtils.join(",", entries), - breadcrumbs, - searchIndexItemResId + tryFillInPreferenceValue( + readString(getAttribute(xpp, "title")), + key, + entries, + entryValues), + tryFillInPreferenceValue( + readString(getAttribute(xpp, "summary")), + key, + entries, + entryValues), + TextUtils.join(",", entries), + breadcrumbs, + searchIndexItemResId ); } diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java index d6e2021a1..83125462d 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchAdapter.java @@ -3,12 +3,10 @@ package org.schabi.newpipe.settings.preferencesearch; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.recyclerview.widget.DiffUtil; import androidx.recyclerview.widget.ListAdapter; import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.databinding.SettingsPreferencesearchListItemResultBinding; import java.util.function.Consumer; diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java index 1ded181c8..7a95da201 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchConfiguration.java @@ -8,23 +8,21 @@ import java.util.Objects; import java.util.stream.Stream; public class PreferenceSearchConfiguration { - private PreferenceSearchFunction searcher = new PreferenceFuzzySearchFunction(); - private final List parserIgnoreElements = List.of( PreferenceCategory.class.getSimpleName()); private final List parserContainerElements = List.of( PreferenceCategory.class.getSimpleName(), PreferenceScreen.class.getSimpleName()); - - - public void setSearcher(final PreferenceSearchFunction searcher) { - this.searcher = Objects.requireNonNull(searcher); - } + private PreferenceSearchFunction searcher = new PreferenceFuzzySearchFunction(); public PreferenceSearchFunction getSearcher() { return searcher; } + public void setSearcher(final PreferenceSearchFunction searcher) { + this.searcher = Objects.requireNonNull(searcher); + } + public List getParserIgnoreElements() { return parserIgnoreElements; } diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java index 9d169d660..23041b309 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchFragment.java @@ -4,12 +4,10 @@ import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; - import org.schabi.newpipe.databinding.SettingsPreferencesearchFragmentBinding; import java.util.List; @@ -65,7 +63,7 @@ public class PreferenceSearchFragment extends Fragment { public void onItemClicked(final PreferenceSearchItem item) { if (!(getActivity() instanceof PreferenceSearchResultListener)) { throw new ClassCastException( - getActivity().toString() + " must implement SearchPreferenceResultListener"); + getActivity().toString() + " must implement SearchPreferenceResultListener"); } ((PreferenceSearchResultListener) getActivity()).onSearchResultClicked(item); diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java index 33856326c..27b4d5d10 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchItem.java @@ -31,7 +31,7 @@ public class PreferenceSearchItem { @NonNull private final String entries; /** - * Breadcrumbs - a hint where the setting is located e.g. 'Video and Audio > Player' + * Breadcrumbs - a hint where the setting is located e.g. 'Video and Audio > Player' */ @NonNull private final String breadcrumbs; diff --git a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java index 7eae5c128..0c7bb07d3 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java +++ b/app/src/main/java/org/schabi/newpipe/settings/preferencesearch/PreferenceSearchResultHighlighter.java @@ -10,13 +10,11 @@ import android.os.Handler; import android.os.Looper; import android.util.Log; import android.util.TypedValue; - import androidx.appcompat.content.res.AppCompatResources; import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; import androidx.preference.PreferenceGroup; import androidx.recyclerview.widget.RecyclerView; - import org.schabi.newpipe.R; @@ -31,7 +29,7 @@ public final class PreferenceSearchResultHighlighter { *
    * Note: This function is Thread independent (can be called from outside of the main thread). * - * @param item The item to highlight + * @param item The item to highlight * @param prefsFragment The fragment where the items is located on */ public static void highlight( diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/AddTabDialog.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/AddTabDialog.java index e2e833fee..5e1c32856 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/AddTabDialog.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/AddTabDialog.java @@ -7,12 +7,10 @@ import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.TextView; - import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.widget.AppCompatImageView; - import org.schabi.newpipe.R; public final class AddTabDialog { diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java index 289c824ba..1769b1227 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/ChooseTabsFragment.java @@ -1,21 +1,12 @@ package org.schabi.newpipe.settings.tabs; -import static org.schabi.newpipe.settings.tabs.Tab.typeFrom; -import static org.schabi.newpipe.util.ServiceHelper.getNameOfServiceById; - import android.annotation.SuppressLint; import android.app.Dialog; import android.content.Context; import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MotionEvent; -import android.view.View; -import android.view.ViewGroup; +import android.view.*; import android.widget.ImageView; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -24,9 +15,7 @@ import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; - import com.google.android.material.floatingactionbutton.FloatingActionButton; - import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorInfo; import org.schabi.newpipe.error.ErrorUtil; @@ -41,10 +30,12 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class ChooseTabsFragment extends Fragment { - private TabsManager tabsManager; +import static org.schabi.newpipe.settings.tabs.Tab.typeFrom; +import static org.schabi.newpipe.util.ServiceHelper.getNameOfServiceById; +public class ChooseTabsFragment extends Fragment { private final List tabList = new ArrayList<>(); + private TabsManager tabsManager; private ChooseTabsFragment.SelectedTabsAdapter selectedTabsAdapter; /*////////////////////////////////////////////////////////////////////////// diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java index 7e3f5d0c8..303e6e5b7 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/Tab.java @@ -1,15 +1,12 @@ package org.schabi.newpipe.settings.tabs; import android.content.Context; - import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.fragment.app.Fragment; - import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonStringWriter; - import org.schabi.newpipe.R; import org.schabi.newpipe.database.LocalItem.LocalItemType; import org.schabi.newpipe.error.ErrorInfo; @@ -116,10 +113,9 @@ public abstract class Tab { @Override public boolean equals(final Object obj) { - if (!(obj instanceof Tab)) { + if (!(obj instanceof final Tab other)) { return false; } - final Tab other = (Tab) obj; return getTabId() == other.getTabId(); } @@ -368,10 +364,9 @@ public abstract class Tab { @Override public boolean equals(final Object obj) { - if (!(obj instanceof KioskTab)) { + if (!(obj instanceof final KioskTab other)) { return false; } - final KioskTab other = (KioskTab) obj; return super.equals(obj) && kioskServiceId == other.kioskServiceId && kioskId.equals(other.kioskId); @@ -452,10 +447,9 @@ public abstract class Tab { @Override public boolean equals(final Object obj) { - if (!(obj instanceof ChannelTab)) { + if (!(obj instanceof final ChannelTab other)) { return false; } - final ChannelTab other = (ChannelTab) obj; return super.equals(obj) && channelServiceId == other.channelServiceId && channelUrl.equals(other.channelName) @@ -606,12 +600,10 @@ public abstract class Tab { @Override public boolean equals(final Object obj) { - if (!(obj instanceof PlaylistTab)) { + if (!(obj instanceof final PlaylistTab other)) { return false; } - final PlaylistTab other = (PlaylistTab) obj; - return super.equals(obj) && playlistServiceId == other.playlistServiceId // Remote && playlistId == other.playlistId // Local diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsJsonHelper.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsJsonHelper.java index 30676477c..d5bbe637c 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsJsonHelper.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsJsonHelper.java @@ -1,13 +1,7 @@ package org.schabi.newpipe.settings.tabs; import androidx.annotation.Nullable; - -import com.grack.nanojson.JsonArray; -import com.grack.nanojson.JsonObject; -import com.grack.nanojson.JsonParser; -import com.grack.nanojson.JsonParserException; -import com.grack.nanojson.JsonStringWriter; -import com.grack.nanojson.JsonWriter; +import com.grack.nanojson.*; import java.util.ArrayList; import java.util.List; @@ -24,7 +18,8 @@ public final class TabsJsonHelper { Tab.Type.SUBSCRIPTIONS.getTab(), Tab.Type.BOOKMARKS.getTab()); - private TabsJsonHelper() { } + private TabsJsonHelper() { + } /** * Try to reads the passed JSON and returns the list of tabs if no error were encountered. diff --git a/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsManager.java b/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsManager.java index c885b803c..a56b53ce2 100644 --- a/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsManager.java +++ b/app/src/main/java/org/schabi/newpipe/settings/tabs/TabsManager.java @@ -3,9 +3,7 @@ package org.schabi.newpipe.settings.tabs; import android.content.Context; import android.content.SharedPreferences; import android.widget.Toast; - import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import java.util.List; diff --git a/app/src/main/java/org/schabi/newpipe/streams/DataReader.java b/app/src/main/java/org/schabi/newpipe/streams/DataReader.java index 68225fbab..c5637ff07 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/DataReader.java +++ b/app/src/main/java/org/schabi/newpipe/streams/DataReader.java @@ -16,12 +16,14 @@ public class DataReader { public static final int FLOAT_SIZE = 4; private static final int BUFFER_SIZE = 128 * 1024; // 128 KiB - - private long position = 0; private final SharpStream stream; - + private final short[] primitive = new short[LONG_SIZE]; + private final byte[] readBuffer = new byte[BUFFER_SIZE]; + private long position = 0; private InputStream view; private int viewSize; + private int readOffset; + private int readCount; public DataReader(final SharpStream stream) { this.stream = stream; @@ -69,12 +71,11 @@ public class DataReader { return primitive[0] << 24 | primitive[1] << 16 | primitive[2] << 8 | primitive[3]; } - public long readUnsignedInt() throws IOException { + public long readUnsignedInt() throws IOException { final long value = readInt(); return value & 0xffffffffL; } - public short readShort() throws IOException { primitiveRead(SHORT_SIZE); return (short) (primitive[0] << 8 | primitive[1]); @@ -83,8 +84,8 @@ public class DataReader { public long readLong() throws IOException { primitiveRead(LONG_SIZE); final long high = - primitive[0] << 24 | primitive[1] << 16 | primitive[2] << 8 | primitive[3]; - final long low = primitive[4] << 24 | primitive[5] << 16 | primitive[6] << 8 | primitive[7]; + (long) primitive[0] << 24 | primitive[1] << 16 | primitive[2] << 8 | primitive[3]; + final long low = (long) primitive[4] << 24 | primitive[5] << 16 | primitive[6] << 8 | primitive[7]; return high << 32 | low; } @@ -228,8 +229,6 @@ public class DataReader { return view; } - private final short[] primitive = new short[LONG_SIZE]; - private void primitiveRead(final int amount) throws IOException { final byte[] buffer = new byte[amount]; final int read = read(buffer, 0, amount); @@ -245,10 +244,6 @@ public class DataReader { } } - private final byte[] readBuffer = new byte[BUFFER_SIZE]; - private int readOffset; - private int readCount; - private boolean fillBuffer() throws IOException { if (readCount < 0) { return true; diff --git a/app/src/main/java/org/schabi/newpipe/streams/Mp4DashReader.java b/app/src/main/java/org/schabi/newpipe/streams/Mp4DashReader.java index de327fba1..296a230df 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/Mp4DashReader.java +++ b/app/src/main/java/org/schabi/newpipe/streams/Mp4DashReader.java @@ -62,14 +62,14 @@ public class Mp4DashReader { private int selectedTrack = -1; private Box backupBox = null; - public enum TrackKind { - Audio, Video, Subtitles, Other - } - public Mp4DashReader(final SharpStream source) { this.stream = new DataReader(source); } + public static boolean hasFlag(final int flags, final int mask) { + return (flags & mask) == mask; + } + public void parse() throws IOException, NoSuchElementException { if (selectedTrack > -1) { return; @@ -250,10 +250,6 @@ public class Mp4DashReader { return null; } - public static boolean hasFlag(final int flags, final int mask) { - return (flags & mask) == mask; - } - private String boxName(final Box ref) { return boxName(ref.type); } @@ -743,6 +739,10 @@ public class Mp4DashReader { return readFullBox(b); } + public enum TrackKind { + Audio, Video, Subtitles, Other + } + static class Box { int type; long offset; @@ -750,19 +750,19 @@ public class Mp4DashReader { } public static class Moof { - int mfhdSequenceNumber; public Traf traf; + int mfhdSequenceNumber; } public static class Traf { public Tfhd tfhd; - long tfdt; public Trun trun; + long tfdt; } public static class Tfhd { - int bFlags; public int trackId; + int bFlags; int defaultSampleDuration; int defaultSampleSize; int defaultSampleFlags; @@ -784,10 +784,9 @@ public class Mp4DashReader { public int chunkSize; public int bFlags; + public int entryCount; int bFirstSampleFlags; int dataOffset; - - public int entryCount; byte[] bEntries; int entriesRowSize; @@ -867,11 +866,11 @@ public class Mp4DashReader { } public static class Trex { - private int trackId; int defaultSampleDescriptionIndex; int defaultSampleDuration; int defaultSampleSize; int defaultSampleFlags; + private int trackId; } public static class Elst { diff --git a/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java b/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java index 807f190b4..04c22fdbb 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java +++ b/app/src/main/java/org/schabi/newpipe/streams/Mp4FromDashWriter.java @@ -1,12 +1,6 @@ package org.schabi.newpipe.streams; -import org.schabi.newpipe.streams.Mp4DashReader.Hdlr; -import org.schabi.newpipe.streams.Mp4DashReader.Mdia; -import org.schabi.newpipe.streams.Mp4DashReader.Mp4DashChunk; -import org.schabi.newpipe.streams.Mp4DashReader.Mp4DashSample; -import org.schabi.newpipe.streams.Mp4DashReader.Mp4Track; -import org.schabi.newpipe.streams.Mp4DashReader.TrackKind; -import org.schabi.newpipe.streams.Mp4DashReader.TrunEntry; +import org.schabi.newpipe.streams.Mp4DashReader.*; import org.schabi.newpipe.streams.io.SharpStream; import java.io.IOException; @@ -28,28 +22,20 @@ public class Mp4FromDashWriter { private static final int THRESHOLD_MOOV_LENGTH = (256 * 1024) + (2048 * 1024); private final long time; - + private final ArrayList compatibleBrands = new ArrayList<>(5); private ByteBuffer auxBuffer; private SharpStream outStream; - private long lastWriteOffset = -1; private long writeOffset; - private boolean moovSimulation = true; - private boolean done = false; private boolean parsed = false; - private Mp4Track[] tracks; private SharpStream[] sourceTracks; - private Mp4DashReader[] readers; private Mp4DashChunk[] readersChunks; - private int overrideMainBrand = 0x00; - private final ArrayList compatibleBrands = new ArrayList<>(5); - public Mp4FromDashWriter(final SharpStream... sources) throws IOException { for (final SharpStream src : sources) { if (!src.canRewind() && !src.canRead()) { @@ -715,7 +701,7 @@ public class Mp4FromDashWriter { for (int i = 0; i < tracks.length; i++) { if (tracks[i].trak.tkhd.matrix.length != 36) { throw - new RuntimeException("bad track matrix length (expected 36) in track n°" + i); + new RuntimeException("bad track matrix length (expected 36) in track n°" + i); } makeTrak(i, durations[i], defaultMediaTime[i], tablesInfo[i], is64); } @@ -858,7 +844,7 @@ public class Mp4FromDashWriter { private int makeSbgp() throws IOException { final int offset = auxOffset(); - auxWrite(new byte[] { + auxWrite(new byte[]{ 0x00, 0x00, 0x00, 0x1C, // box size 0x73, 0x62, 0x67, 0x70, // "sbpg" 0x00, 0x00, 0x00, 0x00, // default box flags @@ -885,7 +871,7 @@ public class Mp4FromDashWriter { * most of m4a encoders and ffmpeg uses this box with dummy values (same values) */ - final ByteBuffer buffer = ByteBuffer.wrap(new byte[] { + final ByteBuffer buffer = ByteBuffer.wrap(new byte[]{ 0x00, 0x00, 0x00, 0x1A, // box size 0x73, 0x67, 0x70, 0x64, // "sgpd" 0x01, 0x00, 0x00, 0x00, // box flags (unknown flag sets) diff --git a/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java b/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java index 266cec24a..5718dcf50 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java +++ b/app/src/main/java/org/schabi/newpipe/streams/OggFromWebMWriter.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.streams; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import org.schabi.newpipe.streams.WebMReader.Cluster; import org.schabi.newpipe.streams.WebMReader.Segment; import org.schabi.newpipe.streams.WebMReader.SimpleBlock; @@ -27,32 +26,25 @@ public class OggFromWebMWriter implements Closeable { private static final byte HEADER_SIZE = 27; private static final int TIME_SCALE_NS = 1000000000; - - private boolean done = false; - private boolean parsed = false; - private final SharpStream source; private final SharpStream output; - - private int sequenceCount = 0; private final int streamId; + private final byte[] segmentTable = new byte[255]; + private final int[] crc32Table = new int[256]; + private boolean done = false; + private boolean parsed = false; + private int sequenceCount = 0; private byte packetFlag = FLAG_FIRST; - private WebMReader webm = null; private WebMTrack webmTrack = null; private Segment webmSegment = null; private Cluster webmCluster = null; private SimpleBlock webmBlock = null; - private long webmBlockLastTimecode = 0; private long webmBlockNearDuration = 0; - private short segmentTableSize = 0; - private final byte[] segmentTable = new byte[255]; private long segmentTableNextTimestamp = TIME_SCALE_NS; - private final int[] crc32Table = new int[256]; - public OggFromWebMWriter(@NonNull final SharpStream source, @NonNull final SharpStream target) { if (!source.canRead() || !source.canRewind()) { throw new IllegalArgumentException("source stream must be readable and allows seeking"); diff --git a/app/src/main/java/org/schabi/newpipe/streams/WebMReader.java b/app/src/main/java/org/schabi/newpipe/streams/WebMReader.java index 678974cce..715a4a0e9 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/WebMReader.java +++ b/app/src/main/java/org/schabi/newpipe/streams/WebMReader.java @@ -10,7 +10,6 @@ import java.util.ArrayList; import java.util.NoSuchElementException; /** - * * @author kapodamy */ public class WebMReader { @@ -43,19 +42,12 @@ public class WebMReader { private static final int ID_SIMPLE_BLOCK = 0x23; private static final int ID_BLOCK = 0x21; private static final int ID_GROUP_BLOCK = 0x20; - - - public enum TrackKind { - Audio/*2*/, Video/*1*/, Other - } - private final DataReader stream; private Segment segment; private WebMTrack[] tracks; private int selectedTrack; private boolean done; private boolean firstSegment; - public WebMReader(final SharpStream source) { this.stream = new DataReader(source); } @@ -396,6 +388,10 @@ public class WebMReader { return obj; } + public enum TrackKind { + Audio/*2*/, Video/*1*/, Other + } + static class Element { int type; long offset; @@ -410,7 +406,6 @@ public class WebMReader { public static class WebMTrack { public long trackNumber; - protected int trackType; public String codecId; public byte[] codecPrivate; public byte[] bMetadata; @@ -418,20 +413,38 @@ public class WebMReader { public long defaultDuration = -1; public long codecDelay = -1; public long seekPreRoll = -1; + protected int trackType; + } + + public static class SimpleBlock { + private final Element ref; + public InputStream data; + public boolean createdFromBlock; + public long trackNumber; + public short relativeTimeCode; + public long absoluteTimeCodeNs; + public byte flags; + public int dataSize; + SimpleBlock(final Element ref) { + this.ref = ref; + } + + public boolean isKeyframe() { + return (flags & 0x80) == 0x80; + } } public class Segment { + private final Element ref; + public Info info; + WebMTrack[] tracks; + boolean firstClusterInSegment; + private Element currentCluster; Segment(final Element ref) { this.ref = ref; this.firstClusterInSegment = true; } - public Info info; - WebMTrack[] tracks; - private Element currentCluster; - private final Element ref; - boolean firstClusterInSegment; - public Cluster getNextCluster() throws IOException { if (done) { return null; @@ -453,31 +466,11 @@ public class WebMReader { } } - public static class SimpleBlock { - public InputStream data; - public boolean createdFromBlock; - - SimpleBlock(final Element ref) { - this.ref = ref; - } - - public long trackNumber; - public short relativeTimeCode; - public long absoluteTimeCodeNs; - public byte flags; - public int dataSize; - private final Element ref; - - public boolean isKeyframe() { - return (flags & 0x80) == 0x80; - } - } - public class Cluster { + public long timecode; Element ref; SimpleBlock currentSimpleBlock = null; Element currentBlockGroup = null; - public long timecode; Cluster(final Element ref) { this.ref = ref; diff --git a/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java b/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java index 530959d96..bfb5d2f2e 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java +++ b/app/src/main/java/org/schabi/newpipe/streams/WebMWriter.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.streams; import androidx.annotation.NonNull; - import org.schabi.newpipe.streams.WebMReader.Cluster; import org.schabi.newpipe.streams.WebMReader.Segment; import org.schabi.newpipe.streams.WebMReader.SimpleBlock; @@ -724,15 +723,14 @@ public class WebMWriter implements Closeable { } static class KeyFrame { + final long clusterPosition; + final int relativePosition; + final long duration; KeyFrame(final long segment, final long cluster, final long block, final long timecode) { clusterPosition = cluster - segment; relativePosition = (int) (block - cluster - CLUSTER_HEADER_SIZE); duration = timecode; } - - final long clusterPosition; - final int relativePosition; - final long duration; } static class Block { diff --git a/app/src/main/java/org/schabi/newpipe/streams/io/NoFileManagerSafeGuard.java b/app/src/main/java/org/schabi/newpipe/streams/io/NoFileManagerSafeGuard.java index df43c34c1..0e2b9f5ee 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/io/NoFileManagerSafeGuard.java +++ b/app/src/main/java/org/schabi/newpipe/streams/io/NoFileManagerSafeGuard.java @@ -4,10 +4,8 @@ import android.content.ActivityNotFoundException; import android.content.Context; import android.os.Build; import android.util.Log; - import androidx.activity.result.ActivityResultLauncher; import androidx.appcompat.app.AlertDialog; - import org.schabi.newpipe.R; /** @@ -20,6 +18,7 @@ public final class NoFileManagerSafeGuard { /** * Shows an alert dialog when no file-manager is found. + * * @param context Context */ private static void showActivityNotFoundAlert(final Context context) { @@ -48,16 +47,16 @@ public final class NoFileManagerSafeGuard { /** * Launches the file manager safely. - * + *

    * If no file manager is found (which is normally only the case when the user uninstalled * the default file manager or the OS lacks one) an alert dialog shows up, asking the user * to fix the situation. * * @param activityResultLauncher see {@link ActivityResultLauncher#launch(Object)} - * @param input see {@link ActivityResultLauncher#launch(Object)} - * @param tag Tag used for logging - * @param context Context - * @param see {@link ActivityResultLauncher#launch(Object)} + * @param input see {@link ActivityResultLauncher#launch(Object)} + * @param tag Tag used for logging + * @param context Context + * @param see {@link ActivityResultLauncher#launch(Object)} */ public static void launchSafe( final ActivityResultLauncher activityResultLauncher, diff --git a/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java b/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java index 48ae54284..5e34456cc 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java +++ b/app/src/main/java/org/schabi/newpipe/streams/io/StoredDirectoryHelper.java @@ -6,11 +6,9 @@ import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.DocumentsContract; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.documentfile.provider.DocumentFile; - import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.util.FilePickerActivityHelper; @@ -27,14 +25,11 @@ import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; public class StoredDirectoryHelper { public static final int PERMISSION_FLAGS = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; - + private final String tag; private File ioTree; private DocumentFile docTree; - private Context context; - private final String tag; - public StoredDirectoryHelper(@NonNull final Context context, @NonNull final Uri path, final String tag) throws IOException { this.tag = tag; @@ -59,6 +54,99 @@ public class StoredDirectoryHelper { } } + private static void addIfStartWith(final ArrayList list, @NonNull final String base, + final String str) { + if (isNullOrEmpty(str)) { + return; + } + final String lowerStr = str.toLowerCase(); + if (lowerStr.startsWith(base)) { + list.add(lowerStr); + } + } + + private static String[] splitFilename(@NonNull final String filename) { + final int dotIndex = filename.lastIndexOf('.'); + + if (dotIndex < 0 || (dotIndex == filename.length() - 1)) { + return new String[]{filename, ""}; + } + + return new String[]{filename.substring(0, dotIndex), filename.substring(dotIndex)}; + } + + private static String makeFileName(final String name, final int idx, final String ext) { + return name.concat(" (").concat(String.valueOf(idx)).concat(")").concat(ext); + } + + /** + * Fast (but not enough) file/directory finder under the storage access framework. + * + * @param context The context + * @param tree Directory where search + * @param filename Target filename + * @return A {@link DocumentFile} contain the reference, otherwise, null + */ + static DocumentFile findFileSAFHelper(@Nullable final Context context, final DocumentFile tree, + final String filename) { + if (context == null) { + return tree.findFile(filename); // warning: this is very slow + } + + if (!tree.canRead()) { + return null; // missing read permission + } + + final int name = 0; + final int documentId = 1; + + // LOWER() SQL function is not supported + final String selection = COLUMN_DISPLAY_NAME + " = ?"; + //final String selection = COLUMN_DISPLAY_NAME + " LIKE ?%"; + + final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(tree.getUri(), + DocumentsContract.getDocumentId(tree.getUri())); + final String[] projection = {COLUMN_DISPLAY_NAME, COLUMN_DOCUMENT_ID}; + final ContentResolver contentResolver = context.getContentResolver(); + + final String lowerFilename = filename.toLowerCase(); + + try (Cursor cursor = contentResolver.query(childrenUri, projection, selection, + new String[]{lowerFilename}, null)) { + if (cursor == null) { + return null; + } + + while (cursor.moveToNext()) { + if (cursor.isNull(name) + || !cursor.getString(name).toLowerCase().startsWith(lowerFilename)) { + continue; + } + + return DocumentFile.fromSingleUri(context, + DocumentsContract.buildDocumentUriUsingTree(tree.getUri(), + cursor.getString(documentId))); + } + } + + return null; + } + + public static Intent getPicker(final Context ctx) { + if (NewPipeSettings.useStorageAccessFramework(ctx)) { + return new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) + .putExtra("android.content.extra.SHOW_ADVANCED", true) + .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | StoredDirectoryHelper.PERMISSION_FLAGS); + } else { + return new Intent(ctx, FilePickerActivityHelper.class) + .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false) + .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true) + .putExtra(FilePickerActivityHelper.EXTRA_MODE, + FilePickerActivityHelper.MODE_DIR); + } + } + public StoredFileHelper createFile(final String filename, final String mime) { return createFile(filename, mime, false); } @@ -200,6 +288,10 @@ public class StoredDirectoryHelper { return false; } + //////////////////// + // Utils + /////////////////// + public String getTag() { return tag; } @@ -232,101 +324,4 @@ public class StoredDirectoryHelper { public String toString() { return (docTree == null ? Uri.fromFile(ioTree) : docTree.getUri()).toString(); } - - //////////////////// - // Utils - /////////////////// - - private static void addIfStartWith(final ArrayList list, @NonNull final String base, - final String str) { - if (isNullOrEmpty(str)) { - return; - } - final String lowerStr = str.toLowerCase(); - if (lowerStr.startsWith(base)) { - list.add(lowerStr); - } - } - - private static String[] splitFilename(@NonNull final String filename) { - final int dotIndex = filename.lastIndexOf('.'); - - if (dotIndex < 0 || (dotIndex == filename.length() - 1)) { - return new String[]{filename, ""}; - } - - return new String[]{filename.substring(0, dotIndex), filename.substring(dotIndex)}; - } - - private static String makeFileName(final String name, final int idx, final String ext) { - return name.concat(" (").concat(String.valueOf(idx)).concat(")").concat(ext); - } - - /** - * Fast (but not enough) file/directory finder under the storage access framework. - * - * @param context The context - * @param tree Directory where search - * @param filename Target filename - * @return A {@link DocumentFile} contain the reference, otherwise, null - */ - static DocumentFile findFileSAFHelper(@Nullable final Context context, final DocumentFile tree, - final String filename) { - if (context == null) { - return tree.findFile(filename); // warning: this is very slow - } - - if (!tree.canRead()) { - return null; // missing read permission - } - - final int name = 0; - final int documentId = 1; - - // LOWER() SQL function is not supported - final String selection = COLUMN_DISPLAY_NAME + " = ?"; - //final String selection = COLUMN_DISPLAY_NAME + " LIKE ?%"; - - final Uri childrenUri = DocumentsContract.buildChildDocumentsUriUsingTree(tree.getUri(), - DocumentsContract.getDocumentId(tree.getUri())); - final String[] projection = {COLUMN_DISPLAY_NAME, COLUMN_DOCUMENT_ID}; - final ContentResolver contentResolver = context.getContentResolver(); - - final String lowerFilename = filename.toLowerCase(); - - try (Cursor cursor = contentResolver.query(childrenUri, projection, selection, - new String[]{lowerFilename}, null)) { - if (cursor == null) { - return null; - } - - while (cursor.moveToNext()) { - if (cursor.isNull(name) - || !cursor.getString(name).toLowerCase().startsWith(lowerFilename)) { - continue; - } - - return DocumentFile.fromSingleUri(context, - DocumentsContract.buildDocumentUriUsingTree(tree.getUri(), - cursor.getString(documentId))); - } - } - - return null; - } - - public static Intent getPicker(final Context ctx) { - if (NewPipeSettings.useStorageAccessFramework(ctx)) { - return new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) - .putExtra("android.content.extra.SHOW_ADVANCED", true) - .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | StoredDirectoryHelper.PERMISSION_FLAGS); - } else { - return new Intent(ctx, FilePickerActivityHelper.class) - .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false) - .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true) - .putExtra(FilePickerActivityHelper.EXTRA_MODE, - FilePickerActivityHelper.MODE_DIR); - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java b/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java index 1f0c91456..c9559af1e 100644 --- a/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java +++ b/app/src/main/java/org/schabi/newpipe/streams/io/StoredFileHelper.java @@ -8,42 +8,33 @@ import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.documentfile.provider.DocumentFile; - import com.nononsenseapps.filepicker.Utils; - import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.settings.NewPipeSettings; import org.schabi.newpipe.util.FilePickerActivityHelper; +import us.shandian.giga.io.FileStream; +import us.shandian.giga.io.FileStreamSAF; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.net.URI; -import us.shandian.giga.io.FileStream; -import us.shandian.giga.io.FileStreamSAF; - public class StoredFileHelper implements Serializable { + public static final String DEFAULT_MIME = "application/octet-stream"; private static final boolean DEBUG = MainActivity.DEBUG; private static final String TAG = StoredFileHelper.class.getSimpleName(); - private static final long serialVersionUID = 0L; - public static final String DEFAULT_MIME = "application/octet-stream"; - + protected String source; + protected String tag; private transient DocumentFile docFile; private transient DocumentFile docTree; private transient File ioFile; private transient Context context; - - protected String source; private String sourceTree; - - protected String tag; - private String srcName; private String srcType; @@ -183,6 +174,113 @@ public class StoredFileHelper implements Serializable { return instance; } + public static Intent getPicker(@NonNull final Context ctx, + @NonNull final String mimeType) { + if (NewPipeSettings.useStorageAccessFramework(ctx)) { + return new Intent(Intent.ACTION_OPEN_DOCUMENT) + .putExtra("android.content.extra.SHOW_ADVANCED", true) + .setType(mimeType) + .addCategory(Intent.CATEGORY_OPENABLE) + .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | StoredDirectoryHelper.PERMISSION_FLAGS); + } else { + return new Intent(ctx, FilePickerActivityHelper.class) + .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false) + .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true) + .putExtra(FilePickerActivityHelper.EXTRA_SINGLE_CLICK, true) + .putExtra(FilePickerActivityHelper.EXTRA_MODE, + FilePickerActivityHelper.MODE_FILE); + } + } + + public static Intent getPicker(@NonNull final Context ctx, + @NonNull final String mimeType, + @Nullable final Uri initialPath) { + return applyInitialPathToPickerIntent(ctx, getPicker(ctx, mimeType), initialPath, null); + } + + public static Intent getNewPicker(@NonNull final Context ctx, + @Nullable final String filename, + @NonNull final String mimeType, + @Nullable final Uri initialPath) { + final Intent i; + if (NewPipeSettings.useStorageAccessFramework(ctx)) { + i = new Intent(Intent.ACTION_CREATE_DOCUMENT) + .putExtra("android.content.extra.SHOW_ADVANCED", true) + .setType(mimeType) + .addCategory(Intent.CATEGORY_OPENABLE) + .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION + | StoredDirectoryHelper.PERMISSION_FLAGS); + if (filename != null) { + i.putExtra(Intent.EXTRA_TITLE, filename); + } + } else { + i = new Intent(ctx, FilePickerActivityHelper.class) + .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false) + .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true) + .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_EXISTING_FILE, true) + .putExtra(FilePickerActivityHelper.EXTRA_MODE, + FilePickerActivityHelper.MODE_NEW_FILE); + } + return applyInitialPathToPickerIntent(ctx, i, initialPath, filename); + } + + private static Intent applyInitialPathToPickerIntent(@NonNull final Context ctx, + @NonNull final Intent intent, + @Nullable final Uri initialPath, + @Nullable final String filename) { + + if (NewPipeSettings.useStorageAccessFramework(ctx)) { + if (initialPath == null) { + return intent; // nothing to do, no initial path provided + } + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + return intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialPath); + } else { + return intent; // can't set initial path on API < 26 + } + + } else { + if (initialPath == null && filename == null) { + return intent; // nothing to do, no initial path and no file name provided + } + + File file; + if (initialPath == null) { + // The only way to set the previewed filename in non-SAF FilePicker is to set a + // starting path ending with that filename. So when the initialPath is null but + // filename isn't just default to the external storage directory. + file = Environment.getExternalStorageDirectory(); + } else { + try { + file = Utils.getFileForUri(initialPath); + } catch (final Throwable ignored) { + // getFileForUri() can't decode paths to 'storage', fallback to this + file = new File(initialPath.toString()); + } + } + + // remove any filename at the end of the path (get the parent directory in that case) + if (!file.exists() || !file.isDirectory()) { + file = file.getParentFile(); + if (file == null || !file.exists()) { + // default to the external storage directory in case of an invalid path + file = Environment.getExternalStorageDirectory(); + } + // else: file is surely a directory + } + + if (filename != null) { + // append a filename so that the non-SAF FilePicker shows it as the preview + file = new File(file, filename); + } + + return intent + .putExtra(FilePickerActivityHelper.EXTRA_START_PATH, file.getAbsolutePath()); + } + } + public SharpStream getStream() throws IOException { assertValid(); @@ -401,7 +499,6 @@ public class StoredFileHelper implements Serializable { } } - private void assertValid() { if (source == null) { throw new IllegalStateException("In invalid state"); @@ -455,111 +552,4 @@ public class StoredFileHelper implements Serializable { return !str1.equals(str2); } - - public static Intent getPicker(@NonNull final Context ctx, - @NonNull final String mimeType) { - if (NewPipeSettings.useStorageAccessFramework(ctx)) { - return new Intent(Intent.ACTION_OPEN_DOCUMENT) - .putExtra("android.content.extra.SHOW_ADVANCED", true) - .setType(mimeType) - .addCategory(Intent.CATEGORY_OPENABLE) - .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | StoredDirectoryHelper.PERMISSION_FLAGS); - } else { - return new Intent(ctx, FilePickerActivityHelper.class) - .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false) - .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true) - .putExtra(FilePickerActivityHelper.EXTRA_SINGLE_CLICK, true) - .putExtra(FilePickerActivityHelper.EXTRA_MODE, - FilePickerActivityHelper.MODE_FILE); - } - } - - public static Intent getPicker(@NonNull final Context ctx, - @NonNull final String mimeType, - @Nullable final Uri initialPath) { - return applyInitialPathToPickerIntent(ctx, getPicker(ctx, mimeType), initialPath, null); - } - - public static Intent getNewPicker(@NonNull final Context ctx, - @Nullable final String filename, - @NonNull final String mimeType, - @Nullable final Uri initialPath) { - final Intent i; - if (NewPipeSettings.useStorageAccessFramework(ctx)) { - i = new Intent(Intent.ACTION_CREATE_DOCUMENT) - .putExtra("android.content.extra.SHOW_ADVANCED", true) - .setType(mimeType) - .addCategory(Intent.CATEGORY_OPENABLE) - .addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION - | StoredDirectoryHelper.PERMISSION_FLAGS); - if (filename != null) { - i.putExtra(Intent.EXTRA_TITLE, filename); - } - } else { - i = new Intent(ctx, FilePickerActivityHelper.class) - .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_MULTIPLE, false) - .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_CREATE_DIR, true) - .putExtra(FilePickerActivityHelper.EXTRA_ALLOW_EXISTING_FILE, true) - .putExtra(FilePickerActivityHelper.EXTRA_MODE, - FilePickerActivityHelper.MODE_NEW_FILE); - } - return applyInitialPathToPickerIntent(ctx, i, initialPath, filename); - } - - private static Intent applyInitialPathToPickerIntent(@NonNull final Context ctx, - @NonNull final Intent intent, - @Nullable final Uri initialPath, - @Nullable final String filename) { - - if (NewPipeSettings.useStorageAccessFramework(ctx)) { - if (initialPath == null) { - return intent; // nothing to do, no initial path provided - } - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - return intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, initialPath); - } else { - return intent; // can't set initial path on API < 26 - } - - } else { - if (initialPath == null && filename == null) { - return intent; // nothing to do, no initial path and no file name provided - } - - File file; - if (initialPath == null) { - // The only way to set the previewed filename in non-SAF FilePicker is to set a - // starting path ending with that filename. So when the initialPath is null but - // filename isn't just default to the external storage directory. - file = Environment.getExternalStorageDirectory(); - } else { - try { - file = Utils.getFileForUri(initialPath); - } catch (final Throwable ignored) { - // getFileForUri() can't decode paths to 'storage', fallback to this - file = new File(initialPath.toString()); - } - } - - // remove any filename at the end of the path (get the parent directory in that case) - if (!file.exists() || !file.isDirectory()) { - file = file.getParentFile(); - if (file == null || !file.exists()) { - // default to the external storage directory in case of an invalid path - file = Environment.getExternalStorageDirectory(); - } - // else: file is surely a directory - } - - if (filename != null) { - // append a filename so that the non-SAF FilePicker shows it as the preview - file = new File(file, filename); - } - - return intent - .putExtra(FilePickerActivityHelper.EXTRA_START_PATH, file.getAbsolutePath()); - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java index 4b08cfcb5..1385e0812 100644 --- a/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/DeviceUtils.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.util; -import static android.content.Context.INPUT_SERVICE; - import android.annotation.SuppressLint; import android.app.UiModeManager; import android.content.Context; @@ -17,25 +15,22 @@ import android.view.InputDevice; import android.view.KeyEvent; import android.view.WindowInsets; import android.view.WindowManager; - import androidx.annotation.Dimension; import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.App; import org.schabi.newpipe.R; import java.lang.reflect.Method; +import static android.content.Context.INPUT_SERVICE; + public final class DeviceUtils { private static final String AMAZON_FEATURE_FIRE_TV = "amazon.hardware.fire_tv"; private static final boolean SAMSUNG = Build.MANUFACTURER.equals("samsung"); - private static Boolean isTV = null; - private static Boolean isFireTV = null; - /* * Devices that do not support media tunneling */ @@ -51,6 +46,8 @@ public final class DeviceUtils { // Philips QM16XE private static final boolean QM16XE_U = Build.VERSION.SDK_INT == 23 && Build.DEVICE.equals("QM16XE_U"); + private static Boolean isTV = null; + private static Boolean isFireTV = null; private DeviceUtils() { } @@ -95,6 +92,7 @@ public final class DeviceUtils { /** * Checks if the device is in desktop or DeX mode. This function should only * be invoked once on view load as it is using reflection for the DeX checks. + * * @param context the context to use for services and config. * @return true if the Android device is in desktop mode or using DeX. */ @@ -214,6 +212,7 @@ public final class DeviceUtils { /** * Some devices have broken tunneled video playback but claim to support it. * See https://github.com/TeamNewPipe/NewPipe/issues/5911 + * * @return false if affected device */ public static boolean shouldSupportMediaTunneling() { diff --git a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java index d5d472d6f..4adb21499 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ExtractorHelper.java @@ -19,28 +19,20 @@ package org.schabi.newpipe.util; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; -import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; - import android.content.Context; import android.util.Log; import android.view.View; import android.widget.TextView; - import androidx.annotation.Nullable; import androidx.core.text.HtmlCompat; import androidx.preference.PreferenceManager; - +import io.reactivex.rxjava3.core.Maybe; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; -import org.schabi.newpipe.extractor.Info; -import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.*; import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage; -import org.schabi.newpipe.extractor.ListInfo; -import org.schabi.newpipe.extractor.MetaInfo; -import org.schabi.newpipe.extractor.NewPipe; -import org.schabi.newpipe.extractor.Page; -import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.channel.ChannelInfo; import org.schabi.newpipe.extractor.comments.CommentsInfo; import org.schabi.newpipe.extractor.comments.CommentsInfoItem; @@ -57,9 +49,8 @@ import org.schabi.newpipe.util.text.TextLinkifier; import java.util.Collections; import java.util.List; -import io.reactivex.rxjava3.core.Maybe; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; +import static org.schabi.newpipe.util.text.TextLinkifier.SET_LINK_MOVEMENT_METHOD; public final class ExtractorHelper { private static final String TAG = ExtractorHelper.class.getSimpleName(); @@ -229,7 +220,7 @@ public final class ExtractorHelper { load = actualLoadFromNetwork; } else { load = Maybe.concat(ExtractorHelper.loadFromCache(serviceId, url, infoType), - actualLoadFromNetwork.toMaybe()) + actualLoadFromNetwork.toMaybe()) .firstElement() // Take the first valid .toSingle(); } @@ -240,10 +231,10 @@ public final class ExtractorHelper { /** * Default implementation uses the {@link InfoCache} to get cached results. * - * @param the item type's class that extends {@link Info} - * @param serviceId the service to load from - * @param url the URL to load - * @param infoType the {@link InfoItem.InfoType} of the item + * @param the item type's class that extends {@link Info} + * @param serviceId the service to load from + * @param url the URL to load + * @param infoType the {@link InfoItem.InfoType} of the item * @return a {@link Single} that loads the item */ private static Maybe loadFromCache(final int serviceId, final String url, @@ -274,11 +265,12 @@ public final class ExtractorHelper { * Formats the text contained in the meta info list as HTML and puts it into the text view, * while also making the separator visible. If the list is null or empty, or the user chose not * to see meta information, both the text view and the separator are hidden - * @param metaInfos a list of meta information, can be null or empty - * @param metaInfoTextView the text view in which to show the formatted HTML + * + * @param metaInfos a list of meta information, can be null or empty + * @param metaInfoTextView the text view in which to show the formatted HTML * @param metaInfoSeparator another view to be shown or hidden accordingly to the text view - * @param disposables disposables created by the method are added here and their lifecycle - * should be handled by the calling class + * @param disposables disposables created by the method are added here and their lifecycle + * should be handled by the calling class */ public static void showMetaInfoInTextView(@Nullable final List metaInfos, final TextView metaInfoTextView, @@ -287,7 +279,7 @@ public final class ExtractorHelper { final Context context = metaInfoTextView.getContext(); if (metaInfos == null || metaInfos.isEmpty() || !PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - context.getString(R.string.show_meta_info_key), true)) { + context.getString(R.string.show_meta_info_key), true)) { metaInfoTextView.setVisibility(View.GONE); metaInfoSeparator.setVisibility(View.GONE); diff --git a/app/src/main/java/org/schabi/newpipe/util/FallbackViewHolder.java b/app/src/main/java/org/schabi/newpipe/util/FallbackViewHolder.java index 967a54f0a..0f4a5134d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/FallbackViewHolder.java +++ b/app/src/main/java/org/schabi/newpipe/util/FallbackViewHolder.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.util; import android.view.View; - import androidx.recyclerview.widget.RecyclerView; public class FallbackViewHolder extends RecyclerView.ViewHolder { diff --git a/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java b/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java index d7fb39651..64793c101 100644 --- a/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/FilePickerActivityHelper.java @@ -10,16 +10,13 @@ import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.loader.content.Loader; import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.SortedList; - import com.nononsenseapps.filepicker.AbstractFilePickerFragment; import com.nononsenseapps.filepicker.FilePickerFragment; - import org.schabi.newpipe.R; import java.io.File; diff --git a/app/src/main/java/org/schabi/newpipe/util/FilenameUtils.java b/app/src/main/java/org/schabi/newpipe/util/FilenameUtils.java index edcb565a0..2817d056f 100644 --- a/app/src/main/java/org/schabi/newpipe/util/FilenameUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/FilenameUtils.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.util; import android.content.Context; import android.content.SharedPreferences; - import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import java.util.regex.Pattern; @@ -13,7 +11,8 @@ public final class FilenameUtils { private static final String CHARSET_MOST_SPECIAL = "[\\n\\r|?*<\":\\\\>/']+"; private static final String CHARSET_ONLY_LETTERS_AND_DIGITS = "[^\\w\\d]+"; - private FilenameUtils() { } + private FilenameUtils() { + } /** * #143 #44 #42 #22: make sure that the filename does not contain illegal chars. diff --git a/app/src/main/java/org/schabi/newpipe/util/InfoCache.java b/app/src/main/java/org/schabi/newpipe/util/InfoCache.java index a07f05828..8ea189de6 100644 --- a/app/src/main/java/org/schabi/newpipe/util/InfoCache.java +++ b/app/src/main/java/org/schabi/newpipe/util/InfoCache.java @@ -20,11 +20,9 @@ package org.schabi.newpipe.util; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.LruCache; - import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.extractor.Info; import org.schabi.newpipe.extractor.InfoItem; @@ -32,17 +30,15 @@ import org.schabi.newpipe.extractor.InfoItem; import java.util.Map; public final class InfoCache { - private final String TAG = getClass().getSimpleName(); private static final boolean DEBUG = MainActivity.DEBUG; - private static final InfoCache INSTANCE = new InfoCache(); private static final int MAX_ITEMS_ON_CACHE = 60; /** * Trim the cache to this size. */ private static final int TRIM_CACHE_TO = 30; - private static final LruCache LRU_CACHE = new LruCache<>(MAX_ITEMS_ON_CACHE); + private final String TAG = getClass().getSimpleName(); private InfoCache() { // no instance diff --git a/app/src/main/java/org/schabi/newpipe/util/KeyboardUtil.java b/app/src/main/java/org/schabi/newpipe/util/KeyboardUtil.java index a709dc32e..ccd315d0c 100644 --- a/app/src/main/java/org/schabi/newpipe/util/KeyboardUtil.java +++ b/app/src/main/java/org/schabi/newpipe/util/KeyboardUtil.java @@ -3,7 +3,6 @@ package org.schabi.newpipe.util; import android.app.Activity; import android.view.inputmethod.InputMethodManager; import android.widget.EditText; - import androidx.core.content.ContextCompat; /** diff --git a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java index c05057fb3..474c3ff5d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java +++ b/app/src/main/java/org/schabi/newpipe/util/KioskTranslator.java @@ -1,7 +1,6 @@ package org.schabi.newpipe.util; import android.content.Context; - import org.schabi.newpipe.R; /** @@ -26,11 +25,14 @@ import org.schabi.newpipe.R; */ public final class KioskTranslator { - private KioskTranslator() { } + private KioskTranslator() { + } public static String getTranslatedKioskName(final String kioskId, final Context c) { // patch - if (kioskId == null) { return "" } + if (kioskId == null) { + return "" + } switch (kioskId) { case "Trending": return c.getString(R.string.trending); diff --git a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java index b3b7c1792..bff95fd78 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ListHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ListHelper.java @@ -3,13 +3,11 @@ package org.schabi.newpipe.util; import android.content.Context; import android.content.SharedPreferences; import android.net.ConnectivityManager; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.AudioStream; @@ -17,14 +15,7 @@ import org.schabi.newpipe.extractor.stream.DeliveryMethod; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.VideoStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.function.Predicate; import java.util.stream.Collectors; @@ -42,13 +33,14 @@ public final class ListHelper { // Use a Set for better performance private static final Set HIGH_RESOLUTION_LIST = Set.of("1440p", "2160p"); - private ListHelper() { } + private ListHelper() { + } /** - * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) * @param context Android app context * @param videoStreams list of the video streams to check * @return index of the video stream with the default index + * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) */ public static int getDefaultResolutionIndex(final Context context, final List videoStreams) { @@ -58,11 +50,11 @@ public final class ListHelper { } /** - * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) * @param context Android app context * @param videoStreams list of the video streams to check * @param defaultResolution the default resolution to look for * @return index of the video stream with the default index + * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) */ public static int getResolutionIndex(final Context context, final List videoStreams, @@ -71,10 +63,10 @@ public final class ListHelper { } /** - * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) - * @param context Android app context - * @param videoStreams list of the video streams to check + * @param context Android app context + * @param videoStreams list of the video streams to check * @return index of the video stream with the default index + * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) */ public static int getPopupDefaultResolutionIndex(final Context context, final List videoStreams) { @@ -84,11 +76,11 @@ public final class ListHelper { } /** - * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) * @param context Android app context * @param videoStreams list of the video streams to check * @param defaultResolution the default resolution to look for * @return index of the video stream with the default index + * @see #getDefaultResolutionIndex(String, String, MediaFormat, List) */ public static int getPopupResolutionIndex(final Context context, final List videoStreams, @@ -300,8 +292,8 @@ public final class ListHelper { // Filter out higher resolutions (or not if high resolutions should always be shown) .filter(stream -> showHigherResolutions || !HIGH_RESOLUTION_LIST.contains(stream.getResolution() - // Replace any frame rate with nothing - .replaceAll("p\\d+$", "p"))) + // Replace any frame rate with nothing + .replaceAll("p\\d+$", "p"))) .collect(Collectors.toList()); final HashMap hashMap = new HashMap<>(); diff --git a/app/src/main/java/org/schabi/newpipe/util/Localization.java b/app/src/main/java/org/schabi/newpipe/util/Localization.java index 916b902f0..13bf7b472 100644 --- a/app/src/main/java/org/schabi/newpipe/util/Localization.java +++ b/app/src/main/java/org/schabi/newpipe/util/Localization.java @@ -9,13 +9,11 @@ import android.icu.text.CompactDecimalFormat; import android.os.Build; import android.text.TextUtils; import android.util.DisplayMetrics; - import androidx.annotation.NonNull; import androidx.annotation.PluralsRes; import androidx.annotation.StringRes; import androidx.core.math.MathUtils; import androidx.preference.PreferenceManager; - import org.ocpsoft.prettytime.PrettyTime; import org.ocpsoft.prettytime.units.Decade; import org.schabi.newpipe.R; @@ -59,7 +57,8 @@ public final class Localization { public static final String DOT_SEPARATOR = " • "; private static PrettyTime prettyTime; - private Localization() { } + private Localization() { + } @NonNull public static String concatenateStrings(final String... strings) { diff --git a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java index b4556507c..d47dbabce 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NavigationHelper.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.util; -import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp; - import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -10,7 +8,6 @@ import android.net.Uri; import android.os.Build; import android.util.Log; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AlertDialog; @@ -19,9 +16,7 @@ import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; - import com.jakewharton.processphoenix.ProcessPhoenix; - import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; @@ -32,12 +27,7 @@ import org.schabi.newpipe.download.DownloadActivity; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; -import org.schabi.newpipe.extractor.stream.AudioStream; -import org.schabi.newpipe.extractor.stream.DeliveryMethod; -import org.schabi.newpipe.extractor.stream.Stream; -import org.schabi.newpipe.extractor.stream.StreamInfo; -import org.schabi.newpipe.extractor.stream.StreamInfoItem; -import org.schabi.newpipe.extractor.stream.VideoStream; +import org.schabi.newpipe.extractor.stream.*; import org.schabi.newpipe.fragments.MainFragment; import org.schabi.newpipe.fragments.detail.VideoDetailFragment; import org.schabi.newpipe.fragments.list.channel.ChannelFragment; @@ -50,9 +40,9 @@ import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; import org.schabi.newpipe.local.subscription.SubscriptionFragment; import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment; -import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.PlayQueueActivity; import org.schabi.newpipe.player.Player; +import org.schabi.newpipe.player.PlayerService; import org.schabi.newpipe.player.PlayerType; import org.schabi.newpipe.player.helper.PlayerHelper; import org.schabi.newpipe.player.helper.PlayerHolder; @@ -64,6 +54,7 @@ import org.schabi.newpipe.util.external_communication.ShareUtils; import java.util.List; import static org.schabi.newpipe.util.ListHelper.getUrlAndNonTorrentStreams; +import static org.schabi.newpipe.util.external_communication.ShareUtils.installApp; public final class NavigationHelper { public static final String MAIN_FRAGMENT_TAG = "main_fragment_tag"; @@ -404,10 +395,6 @@ public final class NavigationHelper { .commitAllowingStateLoss(); } - private interface RunnableWithVideoDetailFragment { - void run(VideoDetailFragment detailFragment); - } - public static void openVideoDetailFragment(@NonNull final Context context, @NonNull final FragmentManager fragmentManager, final int serviceId, @@ -547,10 +534,6 @@ public final class NavigationHelper { .commit(); } - /*////////////////////////////////////////////////////////////////////////// - // Through Intents - //////////////////////////////////////////////////////////////////////////*/ - public static void openSearch(final Context context, final int serviceId, final String searchString) { final Intent mIntent = new Intent(context, MainActivity.class); @@ -560,6 +543,10 @@ public final class NavigationHelper { context.startActivity(mIntent); } + /*////////////////////////////////////////////////////////////////////////// + // Through Intents + //////////////////////////////////////////////////////////////////////////*/ + public static void openVideoDetail(final Context context, final int serviceId, final String url, @@ -586,6 +573,7 @@ public final class NavigationHelper { * Opens {@link ChannelFragment}. * Use this instead of {@link #openChannelFragment(FragmentManager, int, String, String)} * when no fragments are used / no FragmentManager is available. + * * @param context * @param serviceId * @param url @@ -647,10 +635,6 @@ public final class NavigationHelper { context.startActivity(intent); } - /*////////////////////////////////////////////////////////////////////////// - // Link handling - //////////////////////////////////////////////////////////////////////////*/ - private static Intent getOpenIntent(final Context context, final String url, final int serviceId, final StreamingService.LinkType type) { final Intent mIntent = new Intent(context, MainActivity.class); @@ -660,6 +644,10 @@ public final class NavigationHelper { return mIntent; } + /*////////////////////////////////////////////////////////////////////////// + // Link handling + //////////////////////////////////////////////////////////////////////////*/ + public static Intent getIntentByLink(final Context context, final String url) throws ExtractionException { return getIntentByLink(context, NewPipe.getServiceByUrl(url), url); @@ -723,4 +711,8 @@ public final class NavigationHelper { ProcessPhoenix.triggerRebirth(activity.getApplicationContext()); } + + private interface RunnableWithVideoDetailFragment { + void run(VideoDetailFragment detailFragment); + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/NewPipeTextViewHelper.java b/app/src/main/java/org/schabi/newpipe/util/NewPipeTextViewHelper.java index cf1a9a03a..e8911ed47 100644 --- a/app/src/main/java/org/schabi/newpipe/util/NewPipeTextViewHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/NewPipeTextViewHelper.java @@ -4,10 +4,8 @@ import android.content.Context; import android.text.Selection; import android.text.Spannable; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import org.schabi.newpipe.util.external_communication.ShareUtils; import org.schabi.newpipe.views.NewPipeEditText; import org.schabi.newpipe.views.NewPipeTextView; diff --git a/app/src/main/java/org/schabi/newpipe/util/PeertubeHelper.java b/app/src/main/java/org/schabi/newpipe/util/PeertubeHelper.java index 34f99d262..a34b934ea 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PeertubeHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PeertubeHelper.java @@ -2,16 +2,8 @@ package org.schabi.newpipe.util; import android.content.Context; import android.content.SharedPreferences; - import androidx.preference.PreferenceManager; - -import com.grack.nanojson.JsonArray; -import com.grack.nanojson.JsonObject; -import com.grack.nanojson.JsonParser; -import com.grack.nanojson.JsonParserException; -import com.grack.nanojson.JsonStringWriter; -import com.grack.nanojson.JsonWriter; - +import com.grack.nanojson.*; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance; @@ -20,7 +12,8 @@ import java.util.ArrayList; import java.util.List; public final class PeertubeHelper { - private PeertubeHelper() { } + private PeertubeHelper() { + } public static List getInstanceList(final Context context) { final SharedPreferences sharedPreferences = PreferenceManager @@ -35,8 +28,7 @@ public final class PeertubeHelper { final JsonArray array = JsonParser.object().from(savedJson).getArray("instances"); final List result = new ArrayList<>(); for (final Object o : array) { - if (o instanceof JsonObject) { - final JsonObject instance = (JsonObject) o; + if (o instanceof final JsonObject instance) { final String name = instance.getString("name"); final String url = instance.getString("url"); result.add(new PeertubeInstance(url, name)); diff --git a/app/src/main/java/org/schabi/newpipe/util/PendingIntentCompat.java b/app/src/main/java/org/schabi/newpipe/util/PendingIntentCompat.java index 6b9c36eab..66e531e7e 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PendingIntentCompat.java +++ b/app/src/main/java/org/schabi/newpipe/util/PendingIntentCompat.java @@ -4,7 +4,6 @@ import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; - import androidx.annotation.NonNull; public final class PendingIntentCompat { diff --git a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java index 55193599e..a3468f89d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PermissionHelper.java @@ -10,11 +10,9 @@ import android.net.Uri; import android.os.Build; import android.provider.Settings; import android.widget.Toast; - import androidx.annotation.RequiresApi; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; - import org.schabi.newpipe.R; import org.schabi.newpipe.settings.NewPipeSettings; @@ -23,7 +21,8 @@ public final class PermissionHelper { public static final int DOWNLOAD_DIALOG_REQUEST_CODE = 778; public static final int DOWNLOADS_REQUEST_CODE = 777; - private PermissionHelper() { } + private PermissionHelper() { + } public static boolean checkStoragePermissions(final Activity activity, final int requestCode) { if (NewPipeSettings.useStorageAccessFramework(activity)) { @@ -88,7 +87,7 @@ public final class PermissionHelper { Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(activity, - new String[] {Manifest.permission.POST_NOTIFICATIONS}, requestCode); + new String[]{Manifest.permission.POST_NOTIFICATIONS}, requestCode); return false; } return true; diff --git a/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java index ece0c7e87..5176ba06d 100644 --- a/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/PicassoHelper.java @@ -1,48 +1,36 @@ package org.schabi.newpipe.util; -import static org.schabi.newpipe.MainActivity.DEBUG; -import static org.schabi.newpipe.extractor.utils.Utils.isBlank; - import android.annotation.SuppressLint; import android.content.Context; import android.graphics.Bitmap; import android.util.Log; - import androidx.annotation.Nullable; import androidx.core.graphics.BitmapCompat; - -import com.squareup.picasso.Cache; -import com.squareup.picasso.LruCache; -import com.squareup.picasso.OkHttp3Downloader; -import com.squareup.picasso.Picasso; -import com.squareup.picasso.RequestCreator; -import com.squareup.picasso.Transformation; - +import com.squareup.picasso.*; +import okhttp3.OkHttpClient; import org.schabi.newpipe.R; import java.io.File; import java.io.IOException; import java.util.concurrent.TimeUnit; -import okhttp3.OkHttpClient; +import static org.schabi.newpipe.MainActivity.DEBUG; +import static org.schabi.newpipe.extractor.utils.Utils.isBlank; public final class PicassoHelper { private static final String TAG = PicassoHelper.class.getSimpleName(); private static final String PLAYER_THUMBNAIL_TRANSFORMATION_KEY = "PICASSO_PLAYER_THUMBNAIL_TRANSFORMATION_KEY"; - - private PicassoHelper() { - } - private static Cache picassoCache; private static OkHttpClient picassoDownloaderClient; - // suppress because terminate() is called in App.onTerminate(), preventing leaks @SuppressLint("StaticFieldLeak") private static Picasso picassoInstance; - private static boolean shouldLoadImages; + private PicassoHelper() { + } + public static void init(final Context context) { picassoCache = new LruCache(10 * 1024 * 1024); picassoDownloaderClient = new OkHttpClient.Builder() @@ -87,14 +75,13 @@ public final class PicassoHelper { picassoInstance.setIndicatorsEnabled(enabled); // useful for debugging } - public static void setShouldLoadImages(final boolean shouldLoadImages) { - PicassoHelper.shouldLoadImages = shouldLoadImages; - } - public static boolean getShouldLoadImages() { return shouldLoadImages; } + public static void setShouldLoadImages(final boolean shouldLoadImages) { + PicassoHelper.shouldLoadImages = shouldLoadImages; + } public static RequestCreator loadAvatar(final String url) { return loadImageDefault(url, R.drawable.placeholder_person); diff --git a/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java b/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java index e7fd2d4a4..a5a73c33f 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/SecondaryStreamHelper.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.util; import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import org.schabi.newpipe.extractor.MediaFormat; import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; diff --git a/app/src/main/java/org/schabi/newpipe/util/SerializedCache.java b/app/src/main/java/org/schabi/newpipe/util/SerializedCache.java index b4c196ce4..a413a3011 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SerializedCache.java +++ b/app/src/main/java/org/schabi/newpipe/util/SerializedCache.java @@ -1,18 +1,12 @@ package org.schabi.newpipe.util; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.LruCache; - import org.schabi.newpipe.MainActivity; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; +import java.io.*; import java.util.UUID; public final class SerializedCache { diff --git a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java index acd019ba0..3c640cabe 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ServiceHelper.java @@ -1,20 +1,15 @@ package org.schabi.newpipe.util; -import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; - import android.content.Context; import android.content.SharedPreferences; - import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.StringRes; import androidx.preference.PreferenceManager; - import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParserException; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.ServiceList; @@ -25,10 +20,13 @@ import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance; import java.util.Optional; import java.util.concurrent.TimeUnit; +import static org.schabi.newpipe.extractor.ServiceList.SoundCloud; + public final class ServiceHelper { private static final StreamingService DEFAULT_FALLBACK_SERVICE = ServiceList.YouTube; - private ServiceHelper() { } + private ServiceHelper() { + } @DrawableRes public static int getIcon(final int serviceId) { @@ -107,12 +105,10 @@ public final class ServiceHelper { */ @StringRes public static int getImportInstructionsHint(final int serviceId) { - switch (serviceId) { - case 1: - return R.string.import_soundcloud_instructions_hint; - default: - return -1; + if (serviceId == 1) { + return R.string.import_soundcloud_instructions_hint; } + return -1; } public static int getSelectedServiceId(final Context context) { diff --git a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java index 6e9ea7a47..c0100a165 100644 --- a/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java +++ b/app/src/main/java/org/schabi/newpipe/util/SparseItemUtil.java @@ -1,13 +1,12 @@ package org.schabi.newpipe.util; -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; - import android.content.Context; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Completable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.NewPipeDatabase; import org.schabi.newpipe.R; import org.schabi.newpipe.database.stream.model.StreamEntity; @@ -20,9 +19,7 @@ import org.schabi.newpipe.player.playqueue.SinglePlayQueue; import java.util.function.Consumer; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Completable; -import io.reactivex.rxjava3.schedulers.Schedulers; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; /** * Utility class for fetching additional data for stream items when needed. @@ -108,7 +105,7 @@ public final class SparseItemUtil { .subscribe(result -> { // save to database in the background (not on main thread) Completable.fromAction(() -> NewPipeDatabase.getInstance(context) - .streamDAO().upsert(new StreamEntity(result))) + .streamDAO().upsert(new StreamEntity(result))) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .doOnError(throwable -> diff --git a/app/src/main/java/org/schabi/newpipe/util/StateSaver.java b/app/src/main/java/org/schabi/newpipe/util/StateSaver.java index 3c901aacb..6ef365d07 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StateSaver.java +++ b/app/src/main/java/org/schabi/newpipe/util/StateSaver.java @@ -24,18 +24,12 @@ import android.content.Context; import android.os.Bundle; import android.text.TextUtils; import android.util.Log; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.MainActivity; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; +import java.io.*; import java.util.LinkedList; import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; diff --git a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java index 74de45720..137ca60c9 100644 --- a/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java +++ b/app/src/main/java/org/schabi/newpipe/util/StreamItemAdapter.java @@ -8,11 +8,12 @@ import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.Spinner; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.collection.SparseArrayCompat; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.DownloaderImpl; import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.MediaFormat; @@ -20,6 +21,7 @@ import org.schabi.newpipe.extractor.stream.AudioStream; import org.schabi.newpipe.extractor.stream.Stream; import org.schabi.newpipe.extractor.stream.SubtitlesStream; import org.schabi.newpipe.extractor.stream.VideoStream; +import us.shandian.giga.util.Utility; import java.io.Serializable; import java.util.Arrays; @@ -27,11 +29,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.Callable; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.schedulers.Schedulers; -import us.shandian.giga.util.Utility; - /** * A list adapter for a list of {@link Stream streams}. * It currently supports {@link VideoStream}, {@link AudioStream} and {@link SubtitlesStream}. @@ -126,8 +123,7 @@ public class StreamItemAdapter extends BaseA int woSoundIconVisibility = View.GONE; String qualityString; - if (stream instanceof VideoStream) { - final VideoStream videoStream = ((VideoStream) stream); + if (stream instanceof final VideoStream videoStream) { qualityString = videoStream.getResolution(); if (hasAnyVideoOnlyStreamWithNoSecondaryStream) { @@ -143,8 +139,7 @@ public class StreamItemAdapter extends BaseA woSoundIconVisibility = View.INVISIBLE; } } - } else if (stream instanceof AudioStream) { - final AudioStream audioStream = ((AudioStream) stream); + } else if (stream instanceof final AudioStream audioStream) { if (audioStream.getAverageBitrate() > 0) { qualityString = audioStream.getAverageBitrate() + "kbps"; } else if (mediaFormat != null) { @@ -241,7 +236,7 @@ public class StreamItemAdapter extends BaseA /** * Helper method to fetch the sizes of all the streams in a wrapper. * - * @param the stream type's class extending {@link Stream} + * @param the stream type's class extending {@link Stream} * @param streamsWrapper the wrapper * @return a {@link Single} that returns a boolean indicating if any elements were changed */ diff --git a/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java b/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java index ab74e0305..6a4d973d3 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ThemeHelper.java @@ -25,7 +25,6 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.util.TypedValue; - import androidx.annotation.AttrRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -36,7 +35,6 @@ import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.content.res.AppCompatResources; import androidx.core.content.ContextCompat; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; @@ -50,7 +48,7 @@ public final class ThemeHelper { /** * Apply the selected theme (on NewPipe settings) in the context * with the default style (see {@link #setTheme(Context, int)}). - * + *

    * ThemeHelper.setDayNightMode should be called before * the applying theme for the first time in session * @@ -63,7 +61,7 @@ public final class ThemeHelper { /** * Apply the selected theme (on NewPipe settings) in the context, * themed according with the styles defined for the service . - * + *

    * ThemeHelper.setDayNightMode should be called before * the applying theme for the first time in session * @@ -358,6 +356,7 @@ public final class ThemeHelper { /** * Returns item view mode. + * * @param context to read preference and parse string * @return Returns one of ItemViewMode */ @@ -405,7 +404,7 @@ public final class ThemeHelper { * Calculates the number of grid items that can fit horizontally on the screen based on the * minimum width. * - * @param context the context to use + * @param context the context to use * @param minWidth the minimum width of items in the grid * @return the span count of grid list items */ diff --git a/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java b/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java index bc08e6197..63ee4ab60 100644 --- a/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java +++ b/app/src/main/java/org/schabi/newpipe/util/ZipHelper.java @@ -1,18 +1,13 @@ package org.schabi.newpipe.util; import org.schabi.newpipe.streams.io.SharpInputStream; +import org.schabi.newpipe.streams.io.StoredFileHelper; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; +import java.io.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; -import org.schabi.newpipe.streams.io.StoredFileHelper; - /** * Created by Christian Schabesberger on 28.01.18. * Copyright 2018 Christian Schabesberger @@ -34,10 +29,11 @@ import org.schabi.newpipe.streams.io.StoredFileHelper; */ public final class ZipHelper { - private ZipHelper() { } - private static final int BUFFER_SIZE = 2048; + private ZipHelper() { + } + /** * This function helps to create zip files. * Caution this will override the original file. @@ -66,8 +62,8 @@ public final class ZipHelper { * Caution this will override the original file. * * @param zipFile The zip file - * @param file The path of the file on the disk where the data should be extracted to. - * @param name The path of the file inside the zip. + * @param file The path of the file on the disk where the data should be extracted to. + * @param name The path of the file inside the zip. * @return will return true if the file was found within the zip file * @throws Exception */ diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java index 0df579d88..36c67c025 100644 --- a/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/KoreUtils.java @@ -1,21 +1,21 @@ package org.schabi.newpipe.util.external_communication; import android.content.Context; - import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.preference.PreferenceManager; - import org.schabi.newpipe.R; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.util.NavigationHelper; /** * Util class that provides methods which are related to the Kodi Media Center and its Kore app. + * * @see Kodi website */ public final class KoreUtils { - private KoreUtils() { } + private KoreUtils() { + } public static boolean isServiceSupportedByKore(final int serviceId) { return (serviceId == ServiceList.YouTube.getServiceId() diff --git a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java index 06dd3f945..b5a5425ad 100644 --- a/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/external_communication/ShareUtils.java @@ -1,12 +1,6 @@ package org.schabi.newpipe.util.external_communication; -import static org.schabi.newpipe.MainActivity.DEBUG; - -import android.content.ActivityNotFoundException; -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; +import android.content.*; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.graphics.Bitmap; @@ -15,12 +9,10 @@ import android.os.Build; import android.text.TextUtils; import android.util.Log; import android.widget.Toast; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; - import org.schabi.newpipe.BuildConfig; import org.schabi.newpipe.R; import org.schabi.newpipe.util.PicassoHelper; @@ -28,6 +20,8 @@ import org.schabi.newpipe.util.PicassoHelper; import java.io.File; import java.io.FileOutputStream; +import static org.schabi.newpipe.MainActivity.DEBUG; + public final class ShareUtils { private static final String TAG = ShareUtils.class.getSimpleName(); @@ -192,9 +186,9 @@ public final class ShareUtils { final ClipData.Item item = new ClipData.Item(intent.getData()); final String[] mimeTypes; if (intent.getType() != null) { - mimeTypes = new String[] {intent.getType()}; + mimeTypes = new String[]{intent.getType()}; } else { - mimeTypes = new String[] {}; + mimeTypes = new String[]{}; } targetClipData = new ClipData(null, mimeTypes, item); } @@ -384,9 +378,9 @@ public final class ShareUtils { fileOutputStream.close(); final ClipData clipData = ClipData.newUri(applicationContext.getContentResolver(), "", - FileProvider.getUriForFile(applicationContext, - BuildConfig.APPLICATION_ID + ".provider", - thumbnailPreviewFile)); + FileProvider.getUriForFile(applicationContext, + BuildConfig.APPLICATION_ID + ".provider", + thumbnailPreviewFile)); if (DEBUG) { Log.d(TAG, "ClipData successfully generated for Android share sheet: " + clipData); diff --git a/app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java b/app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java index 5018a6120..173096b46 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/CommentTextOnTouchListener.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.util.text; -import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine; - import android.annotation.SuppressLint; import android.text.Spanned; import android.text.style.ClickableSpan; @@ -9,19 +7,19 @@ import android.view.MotionEvent; import android.view.View; import android.widget.TextView; +import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine; + public class CommentTextOnTouchListener implements View.OnTouchListener { public static final CommentTextOnTouchListener INSTANCE = new CommentTextOnTouchListener(); @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(final View v, final MotionEvent event) { - if (!(v instanceof TextView)) { + if (!(v instanceof final TextView widget)) { return false; } - final TextView widget = (TextView) v; final CharSequence text = widget.getText(); - if (text instanceof Spanned) { - final Spanned buffer = (Spanned) text; + if (text instanceof final Spanned buffer) { final int action = event.getAction(); if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) { diff --git a/app/src/main/java/org/schabi/newpipe/util/text/HashtagLongPressClickableSpan.java b/app/src/main/java/org/schabi/newpipe/util/text/HashtagLongPressClickableSpan.java index 8a0363ecb..d931c054c 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/HashtagLongPressClickableSpan.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/HashtagLongPressClickableSpan.java @@ -2,9 +2,7 @@ package org.schabi.newpipe.util.text; import android.content.Context; import android.view.View; - import androidx.annotation.NonNull; - import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; diff --git a/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java b/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java index b87618922..d17101c90 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/InternalUrlsHandler.java @@ -2,10 +2,12 @@ package org.schabi.newpipe.util.text; import android.content.Context; import android.util.Log; - import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; - +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.MainActivity; import org.schabi.newpipe.R; import org.schabi.newpipe.error.ErrorPanelHelper; @@ -23,11 +25,6 @@ import org.schabi.newpipe.util.NavigationHelper; import java.util.regex.Matcher; import java.util.regex.Pattern; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.schedulers.Schedulers; - public final class InternalUrlsHandler { private static final String TAG = InternalUrlsHandler.class.getSimpleName(); private static final boolean DEBUG = MainActivity.DEBUG; @@ -169,7 +166,8 @@ public final class InternalUrlsHandler { .setTitle(R.string.player_stream_failure) .setMessage( ErrorPanelHelper.Companion.getExceptionDescription(throwable)) - .setPositiveButton(R.string.ok, (v, b) -> { }) + .setPositiveButton(R.string.ok, (v, b) -> { + }) .show(); })); return true; diff --git a/app/src/main/java/org/schabi/newpipe/util/text/LongPressClickableSpan.java b/app/src/main/java/org/schabi/newpipe/util/text/LongPressClickableSpan.java index 5c94a5850..b0140b161 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/LongPressClickableSpan.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/LongPressClickableSpan.java @@ -2,7 +2,6 @@ package org.schabi.newpipe.util.text; import android.text.style.ClickableSpan; import android.view.View; - import androidx.annotation.NonNull; public abstract class LongPressClickableSpan extends ClickableSpan { diff --git a/app/src/main/java/org/schabi/newpipe/util/text/LongPressLinkMovementMethod.java b/app/src/main/java/org/schabi/newpipe/util/text/LongPressLinkMovementMethod.java index bd57621cb..4c374a8a0 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/LongPressLinkMovementMethod.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/LongPressLinkMovementMethod.java @@ -1,7 +1,5 @@ package org.schabi.newpipe.util.text; -import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine; - import android.os.Handler; import android.os.Looper; import android.text.Selection; @@ -11,9 +9,10 @@ import android.text.method.MovementMethod; import android.view.MotionEvent; import android.view.ViewConfiguration; import android.widget.TextView; - import androidx.annotation.NonNull; +import static org.schabi.newpipe.util.text.TouchUtils.getOffsetForHorizontalLine; + // Class adapted from https://stackoverflow.com/a/31786969 public class LongPressLinkMovementMethod extends LinkMovementMethod { @@ -25,6 +24,15 @@ public class LongPressLinkMovementMethod extends LinkMovementMethod { private Handler longClickHandler; private boolean isLongPressed = false; + public static MovementMethod getInstance() { + if (instance == null) { + instance = new LongPressLinkMovementMethod(); + instance.longClickHandler = new Handler(Looper.myLooper()); + } + + return instance; + } + @Override public boolean onTouchEvent(@NonNull final TextView widget, @NonNull final Spannable buffer, @@ -65,13 +73,4 @@ public class LongPressLinkMovementMethod extends LinkMovementMethod { return super.onTouchEvent(widget, buffer, event); } - - public static MovementMethod getInstance() { - if (instance == null) { - instance = new LongPressLinkMovementMethod(); - instance.longClickHandler = new Handler(Looper.myLooper()); - } - - return instance; - } } diff --git a/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java b/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java index e59a3dc05..e85cb7a27 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/TextLinkifier.java @@ -7,11 +7,15 @@ import android.text.util.Linkify; import android.util.Log; import android.view.View; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.text.HtmlCompat; - +import io.noties.markwon.Markwon; +import io.noties.markwon.linkify.LinkifyPlugin; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.disposables.CompositeDisposable; +import io.reactivex.rxjava3.schedulers.Schedulers; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.stream.Description; import org.schabi.newpipe.util.NavigationHelper; @@ -21,21 +25,12 @@ import java.util.function.Consumer; import java.util.regex.Matcher; import java.util.regex.Pattern; -import io.noties.markwon.Markwon; -import io.noties.markwon.linkify.LinkifyPlugin; -import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; -import io.reactivex.rxjava3.core.Single; -import io.reactivex.rxjava3.disposables.CompositeDisposable; -import io.reactivex.rxjava3.schedulers.Schedulers; - public final class TextLinkifier { public static final String TAG = TextLinkifier.class.getSimpleName(); - - // Looks for hashtags with characters from any language (\p{L}), numbers, or underscores - private static final Pattern HASHTAGS_PATTERN = Pattern.compile("(#[\\p{L}0-9_]+)"); - public static final Consumer SET_LINK_MOVEMENT_METHOD = v -> v.setMovementMethod(LongPressLinkMovementMethod.getInstance()); + // Looks for hashtags with characters from any language (\p{L}), numbers, or underscores + private static final Pattern HASHTAGS_PATTERN = Pattern.compile("(#[\\p{L}0-9_]+)"); private TextLinkifier() { } @@ -76,7 +71,8 @@ public final class TextLinkifier { TextLinkifier.fromMarkdown(textView, description.getContent(), relatedInfoService, relatedStreamUrl, disposables, onCompletion); break; - case Description.PLAIN_TEXT: default: + case Description.PLAIN_TEXT: + default: TextLinkifier.fromPlainText(textView, description.getContent(), relatedInfoService, relatedStreamUrl, disposables, onCompletion); break; diff --git a/app/src/main/java/org/schabi/newpipe/util/text/TimestampLongPressClickableSpan.java b/app/src/main/java/org/schabi/newpipe/util/text/TimestampLongPressClickableSpan.java index f5864794a..4c9fc4647 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/TimestampLongPressClickableSpan.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/TimestampLongPressClickableSpan.java @@ -1,17 +1,14 @@ package org.schabi.newpipe.util.text; -import static org.schabi.newpipe.util.text.InternalUrlsHandler.playOnPopup; - import android.content.Context; import android.view.View; - import androidx.annotation.NonNull; - +import io.reactivex.rxjava3.disposables.CompositeDisposable; import org.schabi.newpipe.extractor.ServiceList; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.util.external_communication.ShareUtils; -import io.reactivex.rxjava3.disposables.CompositeDisposable; +import static org.schabi.newpipe.util.text.InternalUrlsHandler.playOnPopup; final class TimestampLongPressClickableSpan extends LongPressClickableSpan { @@ -43,18 +40,6 @@ final class TimestampLongPressClickableSpan extends LongPressClickableSpan { this.timestampMatchDTO = timestampMatchDTO; } - @Override - public void onClick(@NonNull final View view) { - playOnPopup(context, relatedStreamUrl, relatedInfoService, - timestampMatchDTO.seconds(), disposables); - } - - @Override - public void onLongClick(@NonNull final View view) { - ShareUtils.copyToClipboard(context, getTimestampTextToCopy( - relatedInfoService, relatedStreamUrl, descriptionText, timestampMatchDTO)); - } - @NonNull private static String getTimestampTextToCopy( @NonNull final StreamingService relatedInfoService, @@ -75,4 +60,16 @@ final class TimestampLongPressClickableSpan extends LongPressClickableSpan { return descriptionText.subSequence(timestampMatchDTO.timestampStart(), timestampMatchDTO.timestampEnd()).toString(); } + + @Override + public void onClick(@NonNull final View view) { + playOnPopup(context, relatedStreamUrl, relatedInfoService, + timestampMatchDTO.seconds(), disposables); + } + + @Override + public void onLongClick(@NonNull final View view) { + ShareUtils.copyToClipboard(context, getTimestampTextToCopy( + relatedInfoService, relatedStreamUrl, descriptionText, timestampMatchDTO)); + } } diff --git a/app/src/main/java/org/schabi/newpipe/util/text/TouchUtils.java b/app/src/main/java/org/schabi/newpipe/util/text/TouchUtils.java index 5c0db20a3..22e6ac585 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/TouchUtils.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/TouchUtils.java @@ -3,7 +3,6 @@ package org.schabi.newpipe.util.text; import android.text.Layout; import android.view.MotionEvent; import android.widget.TextView; - import androidx.annotation.NonNull; public final class TouchUtils { diff --git a/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java b/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java index eb0d7425e..808c21d3f 100644 --- a/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java +++ b/app/src/main/java/org/schabi/newpipe/util/text/UrlLongPressClickableSpan.java @@ -2,12 +2,9 @@ package org.schabi.newpipe.util.text; import android.content.Context; import android.view.View; - import androidx.annotation.NonNull; - -import org.schabi.newpipe.util.external_communication.ShareUtils; - import io.reactivex.rxjava3.disposables.CompositeDisposable; +import org.schabi.newpipe.util.external_communication.ShareUtils; final class UrlLongPressClickableSpan extends LongPressClickableSpan { diff --git a/app/src/main/java/org/schabi/newpipe/util/urlfinder/PatternsCompat.java b/app/src/main/java/org/schabi/newpipe/util/urlfinder/PatternsCompat.java index 49be86ae0..b3185d454 100644 --- a/app/src/main/java/org/schabi/newpipe/util/urlfinder/PatternsCompat.java +++ b/app/src/main/java/org/schabi/newpipe/util/urlfinder/PatternsCompat.java @@ -67,30 +67,24 @@ public final class PatternsCompat { */ private static final String IRI_LABEL = "[" + LABEL_CHAR + "](?:[" + LABEL_CHAR + "_\\-]{0,61}[" + LABEL_CHAR + "]){0,1}"; - - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // CHANGED: Removed rtsp from supported protocols // - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - private static final String PROTOCOL = "(?i:http|https)://"; - - /* A word boundary or end of input. This is to stop foo.sure from matching as foo.su */ - private static final String WORD_BOUNDARY = "(?:\\b|$|^)"; - - private static final String USER_INFO = "(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" - + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" - + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@"; - - private static final String PORT_NUMBER = "\\:\\d{1,5}"; - - private static final String PATH_AND_QUERY = "[/\\?](?:(?:[" + LABEL_CHAR - + ";/\\?:@&=#~" // plus optional query params - + "\\-\\.\\+!\\*'\\(\\),_\\$])|(?:%[a-fA-F0-9]{2}))*"; - /** * Regular expression that matches domain names without a TLD. */ private static final String RELAXED_DOMAIN_NAME = "(?:" + "(?:" + IRI_LABEL + "(?:\\.(?=\\S))" + "?)+" + "|" + IP_ADDRESS + ")"; + private static final String PATH_AND_QUERY = "[/\\?](?:(?:[" + LABEL_CHAR + + ";/\\?:@&=#~" // plus optional query params + + "\\-\\.\\+!\\*'\\(\\),_\\$])|(?:%[a-fA-F0-9]{2}))*"; + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // CHANGED: Removed rtsp from supported protocols // + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + private static final String PROTOCOL = "(?i:http|https)://"; + /* A word boundary or end of input. This is to stop foo.sure from matching as foo.su */ + private static final String WORD_BOUNDARY = "(?:\\b|$|^)"; + private static final String USER_INFO = "(?:[a-zA-Z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)" + + "\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,64}(?:\\:(?:[a-zA-Z0-9\\$\\-\\_" + + "\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-fA-F0-9]{2})){1,25})?\\@"; + private static final String PORT_NUMBER = "\\:\\d{1,5}"; //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // CHANGED: Field visibility was modified // @@ -113,5 +107,6 @@ public final class PatternsCompat { /** * Do not create this static utility class. */ - private PatternsCompat() { } + private PatternsCompat() { + } } diff --git a/app/src/main/java/org/schabi/newpipe/views/AnimatedProgressBar.java b/app/src/main/java/org/schabi/newpipe/views/AnimatedProgressBar.java index b1fabe715..d7493301b 100644 --- a/app/src/main/java/org/schabi/newpipe/views/AnimatedProgressBar.java +++ b/app/src/main/java/org/schabi/newpipe/views/AnimatedProgressBar.java @@ -6,7 +6,6 @@ import android.view.animation.AccelerateDecelerateInterpolator; import android.view.animation.Animation; import android.view.animation.Transformation; import android.widget.ProgressBar; - import androidx.annotation.Nullable; public final class AnimatedProgressBar extends ProgressBar { diff --git a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java index f79e1e3a3..518024d5d 100644 --- a/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java +++ b/app/src/main/java/org/schabi/newpipe/views/CollapsibleView.java @@ -25,19 +25,16 @@ import android.os.Parcelable; import android.util.AttributeSet; import android.util.Log; import android.widget.LinearLayout; - import androidx.annotation.IntDef; import androidx.annotation.Nullable; - +import icepick.Icepick; +import icepick.State; import org.schabi.newpipe.ktx.ViewUtils; import java.lang.annotation.Retention; import java.util.ArrayList; import java.util.List; -import icepick.Icepick; -import icepick.State; - import static java.lang.annotation.RetentionPolicy.SOURCE; import static org.schabi.newpipe.MainActivity.DEBUG; @@ -45,21 +42,17 @@ import static org.schabi.newpipe.MainActivity.DEBUG; * A view that can be fully collapsed and expanded. */ public class CollapsibleView extends LinearLayout { - private static final String TAG = CollapsibleView.class.getSimpleName(); - - private static final int ANIMATION_DURATION = 420; - public static final int COLLAPSED = 0; public static final int EXPANDED = 1; - + private static final String TAG = CollapsibleView.class.getSimpleName(); + private static final int ANIMATION_DURATION = 420; + private final List listeners = new ArrayList<>(); @State @ViewMode int currentState = COLLAPSED; private boolean readyToChangeState; - private int targetHeight = -1; private ValueAnimator currentAnimator; - private final List listeners = new ArrayList<>(); public CollapsibleView(final Context context) { super(context); @@ -182,6 +175,7 @@ public class CollapsibleView extends LinearLayout { /** * Add a listener which will be listening for changes in this view (i.e. collapsed or expanded). + * * @param listener {@link StateListener} to be added */ public void addListener(final StateListener listener) { @@ -194,6 +188,7 @@ public class CollapsibleView extends LinearLayout { /** * Remove a listener so it doesn't receive more state changes. + * * @param listener {@link StateListener} to be removed */ public void removeListener(final StateListener listener) { @@ -232,7 +227,8 @@ public class CollapsibleView extends LinearLayout { @Retention(SOURCE) @IntDef({COLLAPSED, EXPANDED}) - public @interface ViewMode { } + public @interface ViewMode { + } /** * Simple interface used for listening state changes of the {@link CollapsibleView}. diff --git a/app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java b/app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java index dc667b22a..636b1d768 100644 --- a/app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java +++ b/app/src/main/java/org/schabi/newpipe/views/CustomCollapsingToolbarLayout.java @@ -2,12 +2,10 @@ package org.schabi.newpipe.views; import android.content.Context; import android.util.AttributeSet; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.view.ViewCompat; import androidx.core.view.WindowInsetsCompat; - import com.google.android.material.appbar.CollapsingToolbarLayout; public class CustomCollapsingToolbarLayout extends CollapsingToolbarLayout { @@ -34,7 +32,7 @@ public class CustomCollapsingToolbarLayout extends CollapsingToolbarLayout { * system insets {@link CollapsingToolbarLayout#onWindowInsetChanged(WindowInsetsCompat)} * so we will not receive them in subviews with fitsSystemWindows = true. * Override Google's behavior - * */ + */ public void overrideListener() { ViewCompat.setOnApplyWindowInsetsListener(this, (v, insets) -> insets); } diff --git a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java index 175c81e46..f053c4b1b 100644 --- a/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java +++ b/app/src/main/java/org/schabi/newpipe/views/ExpandableSurfaceView.java @@ -3,7 +3,6 @@ package org.schabi.newpipe.views; import android.content.Context; import android.util.AttributeSet; import android.view.SurfaceView; - import com.google.android.exoplayer2.ui.AspectRatioFrameLayout; import static com.google.android.exoplayer2.ui.AspectRatioFrameLayout.RESIZE_MODE_FIT; @@ -85,6 +84,11 @@ public class ExpandableSurfaceView extends SurfaceView { requestLayout(); } + @AspectRatioFrameLayout.ResizeMode + public int getResizeMode() { + return resizeMode; + } + public void setResizeMode(@AspectRatioFrameLayout.ResizeMode final int newResizeMode) { if (resizeMode == newResizeMode) { return; @@ -94,11 +98,6 @@ public class ExpandableSurfaceView extends SurfaceView { requestLayout(); } - @AspectRatioFrameLayout.ResizeMode - public int getResizeMode() { - return resizeMode; - } - public void setAspectRatio(final float aspectRatio) { if (videoAspectRatio == aspectRatio) { return; diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusAwareCoordinator.java b/app/src/main/java/org/schabi/newpipe/views/FocusAwareCoordinator.java index d4fafc31a..f95feddb4 100644 --- a/app/src/main/java/org/schabi/newpipe/views/FocusAwareCoordinator.java +++ b/app/src/main/java/org/schabi/newpipe/views/FocusAwareCoordinator.java @@ -23,12 +23,10 @@ import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.view.WindowInsets; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.coordinatorlayout.widget.CoordinatorLayout; import androidx.core.view.WindowInsetsCompat; - import org.schabi.newpipe.R; public final class FocusAwareCoordinator extends CoordinatorLayout { diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusAwareDrawerLayout.java b/app/src/main/java/org/schabi/newpipe/views/FocusAwareDrawerLayout.java index 5c694c3a9..ab3617d02 100644 --- a/app/src/main/java/org/schabi/newpipe/views/FocusAwareDrawerLayout.java +++ b/app/src/main/java/org/schabi/newpipe/views/FocusAwareDrawerLayout.java @@ -22,7 +22,6 @@ import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.drawerlayout.widget.DrawerLayout; @@ -46,8 +45,8 @@ public final class FocusAwareDrawerLayout extends DrawerLayout { } @Override - protected boolean onRequestFocusInDescendants(final int direction, - final Rect previouslyFocusedRect) { + private boolean onRequestFocusInDescendants(final int direction, + final Rect previouslyFocusedRect) { // SDK implementation of this method picks whatever visible View takes the focus first // without regard to addFocusables. If the open drawer is temporarily empty, the focus // escapes outside of it, which can be confusing diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java b/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java index 8176a9aef..fca7576c9 100644 --- a/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java +++ b/app/src/main/java/org/schabi/newpipe/views/FocusAwareSeekBar.java @@ -23,19 +23,21 @@ import android.util.AttributeSet; import android.view.KeyEvent; import android.view.ViewTreeObserver; import android.widget.SeekBar; - import androidx.appcompat.widget.AppCompatSeekBar; - import org.schabi.newpipe.util.DeviceUtils; /** * SeekBar, adapted for directional navigation. It emulates touch-related callbacks * (onStartTrackingTouch/onStopTrackingTouch), so existing code does not need to be changed to * work with it. - */ + */ public final class FocusAwareSeekBar extends AppCompatSeekBar { private NestedListener listener; - + private final ViewTreeObserver.OnTouchModeChangeListener touchModeListener = isInTouchMode -> { + if (isInTouchMode) { + releaseTrack(); + } + }; private ViewTreeObserver treeObserver; public FocusAwareSeekBar(final Context context) { @@ -68,8 +70,8 @@ public final class FocusAwareSeekBar extends AppCompatSeekBar { } @Override - protected void onFocusChanged(final boolean gainFocus, final int direction, - final Rect previouslyFocusedRect) { + private void onFocusChanged(final boolean gainFocus, final int direction, + final Rect previouslyFocusedRect) { super.onFocusChanged(gainFocus, direction, previouslyFocusedRect); if (!isInTouchMode() && !gainFocus) { @@ -77,14 +79,8 @@ public final class FocusAwareSeekBar extends AppCompatSeekBar { } } - private final ViewTreeObserver.OnTouchModeChangeListener touchModeListener = isInTouchMode -> { - if (isInTouchMode) { - releaseTrack(); - } - }; - @Override - protected void onAttachedToWindow() { + private void onAttachedToWindow() { super.onAttachedToWindow(); treeObserver = getViewTreeObserver(); @@ -92,7 +88,7 @@ public final class FocusAwareSeekBar extends AppCompatSeekBar { } @Override - protected void onDetachedFromWindow() { + private void onDetachedFromWindow() { if (treeObserver == null || !treeObserver.isAlive()) { treeObserver = getViewTreeObserver(); } diff --git a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java index 29c38511c..93afe15a5 100644 --- a/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java +++ b/app/src/main/java/org/schabi/newpipe/views/FocusOverlayView.java @@ -21,27 +21,17 @@ package org.schabi.newpipe.views; import android.app.Activity; import android.app.Dialog; import android.content.Context; -import android.graphics.Canvas; -import android.graphics.ColorFilter; -import android.graphics.Paint; -import android.graphics.PixelFormat; -import android.graphics.Rect; +import android.graphics.*; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Handler; import android.os.Looper; import android.os.Message; -import android.view.KeyEvent; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; -import android.view.Window; - +import android.view.*; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import androidx.appcompat.view.WindowCallbackWrapper; - import org.schabi.newpipe.R; import java.lang.ref.WeakReference; @@ -52,12 +42,10 @@ public final class FocusOverlayView extends Drawable implements ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnScrollChangedListener, ViewTreeObserver.OnTouchModeChangeListener { - private boolean isInTouchMode; - private final Rect focusRect = new Rect(); - private final Paint rectPaint = new Paint(); - + private boolean isInTouchMode; + private WeakReference focused; private final Handler animator = new Handler(Looper.getMainLooper()) { @Override public void handleMessage(final Message msg) { @@ -65,14 +53,110 @@ public final class FocusOverlayView extends Drawable implements } }; - private WeakReference focused; - public FocusOverlayView(final Context context) { rectPaint.setStyle(Paint.Style.STROKE); rectPaint.setStrokeWidth(2); rectPaint.setColor(context.getResources().getColor(R.color.white)); } + public static void setupFocusObserver(final Dialog dialog) { + final Rect displayRect = new Rect(); + + final Window window = dialog.getWindow(); + assert window != null; + + final View decor = window.getDecorView(); + decor.getWindowVisibleDisplayFrame(displayRect); + + final FocusOverlayView overlay = new FocusOverlayView(dialog.getContext()); + overlay.setBounds(0, 0, displayRect.width(), displayRect.height()); + + setupOverlay(window, overlay); + } + + public static void setupFocusObserver(final Activity activity) { + final Rect displayRect = new Rect(); + + final Window window = activity.getWindow(); + final View decor = window.getDecorView(); + decor.getWindowVisibleDisplayFrame(displayRect); + + final FocusOverlayView overlay = new FocusOverlayView(activity); + overlay.setBounds(0, 0, displayRect.width(), displayRect.height()); + + setupOverlay(window, overlay); + } + + private static void setupOverlay(final Window window, final FocusOverlayView overlay) { + final ViewGroup decor = (ViewGroup) window.getDecorView(); + decor.getOverlay().add(overlay); + + fixFocusHierarchy(decor); + + final ViewTreeObserver observer = decor.getViewTreeObserver(); + observer.addOnScrollChangedListener(overlay); + observer.addOnGlobalFocusChangeListener(overlay); + observer.addOnGlobalLayoutListener(overlay); + observer.addOnTouchModeChangeListener(overlay); + observer.addOnDrawListener(overlay); + + overlay.setCurrentFocus(decor.getFocusedChild()); + + // Some key presses don't actually move focus, but still result in movement on screen. + // For example, MovementMethod of TextView may cause requestRectangleOnScreen() due to + // some "focusable" spans, which in turn causes CoordinatorLayout to "scroll" it's children. + // Unfortunately many such forms of "scrolling" do not count as scrolling for purpose + // of dispatching ViewTreeObserver callbacks, so we have to intercept them by directly + // receiving keys from Window. + window.setCallback(new WindowCallbackWrapper(window.getCallback()) { + @Override + public boolean dispatchKeyEvent(final KeyEvent event) { + final boolean res = super.dispatchKeyEvent(event); + overlay.onKey(event); + return res; + } + }); + } + + private static void fixFocusHierarchy(final View decor) { + // During Android 8 development some dumb ass decided, that action bar has to be + // a keyboard focus cluster. Unfortunately, keyboard clusters do not work for primary + // auditory of key navigation — Android TV users (Android TV remotes do not have + // keyboard META key for moving between clusters). We have to fix this unfortunate accident + // While we are at it, let's deal with touchscreenBlocksFocus too. + + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + return; + } + + if (!(decor instanceof ViewGroup)) { + return; + } + + clearFocusObstacles((ViewGroup) decor); + } + + @RequiresApi(api = Build.VERSION_CODES.O) + private static void clearFocusObstacles(final ViewGroup viewGroup) { + viewGroup.setTouchscreenBlocksFocus(false); + + if (viewGroup.isKeyboardNavigationCluster()) { + viewGroup.setKeyboardNavigationCluster(false); + + return; // clusters aren't supposed to nest + } + + final int childCount = viewGroup.getChildCount(); + + for (int i = 0; i < childCount; ++i) { + final View view = viewGroup.getChildAt(i); + + if (view instanceof ViewGroup) { + clearFocusObstacles((ViewGroup) view); + } + } + } + @Override public void onGlobalFocusChanged(final View oldFocus, final View newFocus) { if (newFocus != null) { @@ -183,65 +267,6 @@ public final class FocusOverlayView extends Drawable implements return focusedView == null || focusedRect.equals(getBounds()); } - public static void setupFocusObserver(final Dialog dialog) { - final Rect displayRect = new Rect(); - - final Window window = dialog.getWindow(); - assert window != null; - - final View decor = window.getDecorView(); - decor.getWindowVisibleDisplayFrame(displayRect); - - final FocusOverlayView overlay = new FocusOverlayView(dialog.getContext()); - overlay.setBounds(0, 0, displayRect.width(), displayRect.height()); - - setupOverlay(window, overlay); - } - - public static void setupFocusObserver(final Activity activity) { - final Rect displayRect = new Rect(); - - final Window window = activity.getWindow(); - final View decor = window.getDecorView(); - decor.getWindowVisibleDisplayFrame(displayRect); - - final FocusOverlayView overlay = new FocusOverlayView(activity); - overlay.setBounds(0, 0, displayRect.width(), displayRect.height()); - - setupOverlay(window, overlay); - } - - private static void setupOverlay(final Window window, final FocusOverlayView overlay) { - final ViewGroup decor = (ViewGroup) window.getDecorView(); - decor.getOverlay().add(overlay); - - fixFocusHierarchy(decor); - - final ViewTreeObserver observer = decor.getViewTreeObserver(); - observer.addOnScrollChangedListener(overlay); - observer.addOnGlobalFocusChangeListener(overlay); - observer.addOnGlobalLayoutListener(overlay); - observer.addOnTouchModeChangeListener(overlay); - observer.addOnDrawListener(overlay); - - overlay.setCurrentFocus(decor.getFocusedChild()); - - // Some key presses don't actually move focus, but still result in movement on screen. - // For example, MovementMethod of TextView may cause requestRectangleOnScreen() due to - // some "focusable" spans, which in turn causes CoordinatorLayout to "scroll" it's children. - // Unfortunately many such forms of "scrolling" do not count as scrolling for purpose - // of dispatching ViewTreeObserver callbacks, so we have to intercept them by directly - // receiving keys from Window. - window.setCallback(new WindowCallbackWrapper(window.getCallback()) { - @Override - public boolean dispatchKeyEvent(final KeyEvent event) { - final boolean res = super.dispatchKeyEvent(event); - overlay.onKey(event); - return res; - } - }); - } - private void onKey(final KeyEvent event) { if (event.getAction() != KeyEvent.ACTION_DOWN) { return; @@ -251,43 +276,4 @@ public final class FocusOverlayView extends Drawable implements animator.sendEmptyMessageDelayed(0, 100); } - - private static void fixFocusHierarchy(final View decor) { - // During Android 8 development some dumb ass decided, that action bar has to be - // a keyboard focus cluster. Unfortunately, keyboard clusters do not work for primary - // auditory of key navigation — Android TV users (Android TV remotes do not have - // keyboard META key for moving between clusters). We have to fix this unfortunate accident - // While we are at it, let's deal with touchscreenBlocksFocus too. - - if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { - return; - } - - if (!(decor instanceof ViewGroup)) { - return; - } - - clearFocusObstacles((ViewGroup) decor); - } - - @RequiresApi(api = Build.VERSION_CODES.O) - private static void clearFocusObstacles(final ViewGroup viewGroup) { - viewGroup.setTouchscreenBlocksFocus(false); - - if (viewGroup.isKeyboardNavigationCluster()) { - viewGroup.setKeyboardNavigationCluster(false); - - return; // clusters aren't supposed to nest - } - - final int childCount = viewGroup.getChildCount(); - - for (int i = 0; i < childCount; ++i) { - final View view = viewGroup.getChildAt(i); - - if (view instanceof ViewGroup) { - clearFocusObstacles((ViewGroup) view); - } - } - } } diff --git a/app/src/main/java/org/schabi/newpipe/views/NewPipeEditText.java b/app/src/main/java/org/schabi/newpipe/views/NewPipeEditText.java index f0993055e..b626075c7 100644 --- a/app/src/main/java/org/schabi/newpipe/views/NewPipeEditText.java +++ b/app/src/main/java/org/schabi/newpipe/views/NewPipeEditText.java @@ -2,11 +2,9 @@ package org.schabi.newpipe.views; import android.content.Context; import android.util.AttributeSet; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatEditText; - import org.schabi.newpipe.util.NewPipeTextViewHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; diff --git a/app/src/main/java/org/schabi/newpipe/views/NewPipeRecyclerView.java b/app/src/main/java/org/schabi/newpipe/views/NewPipeRecyclerView.java index 23b961297..b7f149489 100644 --- a/app/src/main/java/org/schabi/newpipe/views/NewPipeRecyclerView.java +++ b/app/src/main/java/org/schabi/newpipe/views/NewPipeRecyclerView.java @@ -25,7 +25,6 @@ import android.util.Log; import android.view.FocusFinder; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; diff --git a/app/src/main/java/org/schabi/newpipe/views/NewPipeTextView.java b/app/src/main/java/org/schabi/newpipe/views/NewPipeTextView.java index dd3f20f40..ecb0468a6 100644 --- a/app/src/main/java/org/schabi/newpipe/views/NewPipeTextView.java +++ b/app/src/main/java/org/schabi/newpipe/views/NewPipeTextView.java @@ -3,11 +3,9 @@ package org.schabi.newpipe.views; import android.content.Context; import android.text.method.MovementMethod; import android.util.AttributeSet; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.AppCompatTextView; - import org.schabi.newpipe.util.NewPipeTextViewHelper; import org.schabi.newpipe.util.external_communication.ShareUtils; diff --git a/app/src/main/java/org/schabi/newpipe/views/ScrollableTabLayout.java b/app/src/main/java/org/schabi/newpipe/views/ScrollableTabLayout.java index fb21a8083..db4072ffd 100644 --- a/app/src/main/java/org/schabi/newpipe/views/ScrollableTabLayout.java +++ b/app/src/main/java/org/schabi/newpipe/views/ScrollableTabLayout.java @@ -3,9 +3,7 @@ package org.schabi.newpipe.views; import android.content.Context; import android.util.AttributeSet; import android.view.View; - import androidx.annotation.NonNull; - import com.google.android.material.tabs.TabLayout; /** diff --git a/app/src/main/java/org/schabi/newpipe/views/SuperScrollLayoutManager.java b/app/src/main/java/org/schabi/newpipe/views/SuperScrollLayoutManager.java index 62465d2a4..5601d554c 100644 --- a/app/src/main/java/org/schabi/newpipe/views/SuperScrollLayoutManager.java +++ b/app/src/main/java/org/schabi/newpipe/views/SuperScrollLayoutManager.java @@ -21,7 +21,6 @@ import android.content.Context; import android.graphics.Rect; import android.view.View; import android.view.ViewGroup; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.LinearLayoutManager; diff --git a/app/src/main/res/animator/custom_fade_in.xml b/app/src/main/res/animator/custom_fade_in.xml index f8df118cc..23bd08a32 100644 --- a/app/src/main/res/animator/custom_fade_in.xml +++ b/app/src/main/res/animator/custom_fade_in.xml @@ -1,9 +1,9 @@ + android:duration="120" + android:interpolator="@android:interpolator/accelerate_decelerate" + android:propertyName="alpha" + android:valueFrom="0.0f" + android:valueTo="1.0f"/> diff --git a/app/src/main/res/animator/custom_fade_out.xml b/app/src/main/res/animator/custom_fade_out.xml index 3f71e5c58..4a83447b9 100644 --- a/app/src/main/res/animator/custom_fade_out.xml +++ b/app/src/main/res/animator/custom_fade_out.xml @@ -1,9 +1,9 @@ + android:duration="120" + android:interpolator="@android:interpolator/accelerate_decelerate" + android:propertyName="alpha" + android:valueFrom="1.0f" + android:valueTo="0.0f"/> diff --git a/app/src/main/res/drawable/background_oval_black_transparent.xml b/app/src/main/res/drawable/background_oval_black_transparent.xml index e665f5299..b4c9600a1 100644 --- a/app/src/main/res/drawable/background_oval_black_transparent.xml +++ b/app/src/main/res/drawable/background_oval_black_transparent.xml @@ -1,5 +1,5 @@ - + android:shape="oval"> + diff --git a/app/src/main/res/drawable/dashed_border_black.xml b/app/src/main/res/drawable/dashed_border_black.xml index 137184b86..c641a40ce 100644 --- a/app/src/main/res/drawable/dashed_border_black.xml +++ b/app/src/main/res/drawable/dashed_border_black.xml @@ -1,8 +1,8 @@ + android:width="1dp" + android:color="@color/black_border_color" + android:dashWidth="4dp" + android:dashGap="4dp"/> diff --git a/app/src/main/res/drawable/dashed_border_dark.xml b/app/src/main/res/drawable/dashed_border_dark.xml index ff714a448..5c37540f4 100644 --- a/app/src/main/res/drawable/dashed_border_dark.xml +++ b/app/src/main/res/drawable/dashed_border_dark.xml @@ -1,8 +1,8 @@ + android:width="1dp" + android:color="@color/dark_border_color" + android:dashWidth="4dp" + android:dashGap="4dp"/> diff --git a/app/src/main/res/drawable/dashed_border_light.xml b/app/src/main/res/drawable/dashed_border_light.xml index cc71acb72..d0525da6c 100644 --- a/app/src/main/res/drawable/dashed_border_light.xml +++ b/app/src/main/res/drawable/dashed_border_light.xml @@ -1,8 +1,8 @@ + android:width="1dp" + android:color="@color/light_border_color" + android:dashWidth="4dp" + android:dashGap="4dp"/> diff --git a/app/src/main/res/drawable/drawer_header_bottom_background.xml b/app/src/main/res/drawable/drawer_header_bottom_background.xml index 9f9792340..9a7b30b64 100644 --- a/app/src/main/res/drawable/drawer_header_bottom_background.xml +++ b/app/src/main/res/drawable/drawer_header_bottom_background.xml @@ -1,7 +1,7 @@ + android:angle="90" + android:endColor="#00000000" + android:startColor="#4D000000"/> diff --git a/app/src/main/res/drawable/ic_add.xml b/app/src/main/res/drawable/ic_add.xml index fc2163f43..a954e2c5a 100644 --- a/app/src/main/res/drawable/ic_add.xml +++ b/app/src/main/res/drawable/ic_add.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/> diff --git a/app/src/main/res/drawable/ic_add_circle_outline.xml b/app/src/main/res/drawable/ic_add_circle_outline.xml index 0d79d6918..9357e7251 100644 --- a/app/src/main/res/drawable/ic_add_circle_outline.xml +++ b/app/src/main/res/drawable/ic_add_circle_outline.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M13,7h-2v4L7,11v2h4v4h2v-4h4v-2h-4L13,7zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> diff --git a/app/src/main/res/drawable/ic_apps.xml b/app/src/main/res/drawable/ic_apps.xml index b800b1743..557974be1 100644 --- a/app/src/main/res/drawable/ic_apps.xml +++ b/app/src/main/res/drawable/ic_apps.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M4,8h4L8,4L4,4v4zM10,20h4v-4h-4v4zM4,20h4v-4L4,16v4zM4,14h4v-4L4,10v4zM10,14h4v-4h-4v4zM16,4v4h4L20,4h-4zM10,8h4L14,4h-4v4zM16,14h4v-4h-4v4zM16,20h4v-4h-4v4z"/> diff --git a/app/src/main/res/drawable/ic_arrow_back.xml b/app/src/main/res/drawable/ic_arrow_back.xml index 5ed19d5fd..524b62a30 100644 --- a/app/src/main/res/drawable/ic_arrow_back.xml +++ b/app/src/main/res/drawable/ic_arrow_back.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,11H7.83l5.59,-5.59L12,4l-8,8 8,8 1.41,-1.41L7.83,13H20v-2z"/> diff --git a/app/src/main/res/drawable/ic_arrow_drop_down.xml b/app/src/main/res/drawable/ic_arrow_drop_down.xml index da5d30807..489c01400 100644 --- a/app/src/main/res/drawable/ic_arrow_drop_down.xml +++ b/app/src/main/res/drawable/ic_arrow_drop_down.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M7,10l5,5 5,-5z"/> diff --git a/app/src/main/res/drawable/ic_arrow_drop_up.xml b/app/src/main/res/drawable/ic_arrow_drop_up.xml index df4199d18..a96415b76 100644 --- a/app/src/main/res/drawable/ic_arrow_drop_up.xml +++ b/app/src/main/res/drawable/ic_arrow_drop_up.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M7,14l5,-5 5,5z"/> diff --git a/app/src/main/res/drawable/ic_art_track.xml b/app/src/main/res/drawable/ic_art_track.xml index 7e61e1044..9cf92f68f 100644 --- a/app/src/main/res/drawable/ic_art_track.xml +++ b/app/src/main/res/drawable/ic_art_track.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M22,13h-8v-2h8v2zM22,7h-8v2h8L22,7zM14,17h8v-2h-8v2zM12,9v6c0,1.1 -0.9,2 -2,2L4,17c-1.1,0 -2,-0.9 -2,-2L2,9c0,-1.1 0.9,-2 2,-2h6c1.1,0 2,0.9 2,2zM10.5,15l-2.25,-3 -1.75,2.26 -1.25,-1.51L3.5,15h7z"/> diff --git a/app/src/main/res/drawable/ic_asterisk.xml b/app/src/main/res/drawable/ic_asterisk.xml index df7c4b32c..64f89655e 100644 --- a/app/src/main/res/drawable/ic_asterisk.xml +++ b/app/src/main/res/drawable/ic_asterisk.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M10,2H14L13.21,9.91L19.66,5.27L21.66,8.73L14.42,12L21.66,15.27L19.66,18.73L13.21,14.09L14,22H10L10.79,14.09L4.34,18.73L2.34,15.27L9.58,12L2.34,8.73L4.34,5.27L10.79,9.91L10,2Z"/> diff --git a/app/src/main/res/drawable/ic_attach_money.xml b/app/src/main/res/drawable/ic_attach_money.xml index b2c0f5c36..05a4fe563 100644 --- a/app/src/main/res/drawable/ic_attach_money.xml +++ b/app/src/main/res/drawable/ic_attach_money.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M11.8,10.9c-2.27,-0.59 -3,-1.2 -3,-2.15 0,-1.09 1.01,-1.85 2.7,-1.85 1.78,0 2.44,0.85 2.5,2.1h2.21c-0.07,-1.72 -1.12,-3.3 -3.21,-3.81V3h-3v2.16c-1.94,0.42 -3.5,1.68 -3.5,3.61 0,2.31 1.91,3.46 4.7,4.13 2.5,0.6 3,1.48 3,2.41 0,0.69 -0.49,1.79 -2.7,1.79 -2.06,0 -2.87,-0.92 -2.98,-2.1h-2.2c0.12,2.19 1.76,3.42 3.68,3.83V21h3v-2.15c1.95,-0.37 3.5,-1.5 3.5,-3.55 0,-2.84 -2.43,-3.81 -4.7,-4.4z"/> diff --git a/app/src/main/res/drawable/ic_backup.xml b/app/src/main/res/drawable/ic_backup.xml index cf996d197..951e3023f 100644 --- a/app/src/main/res/drawable/ic_backup.xml +++ b/app/src/main/res/drawable/ic_backup.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM14,13v4h-4v-4H7l5,-5 5,5h-3z"/> diff --git a/app/src/main/res/drawable/ic_bookmark.xml b/app/src/main/res/drawable/ic_bookmark.xml index 32cd107f7..3a5d8000f 100644 --- a/app/src/main/res/drawable/ic_bookmark.xml +++ b/app/src/main/res/drawable/ic_bookmark.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M17,3H7c-1.1,0 -1.99,0.9 -1.99,2L5,21l7,-3 7,3V5c0,-1.1 -0.9,-2 -2,-2z"/> diff --git a/app/src/main/res/drawable/ic_brightness_high.xml b/app/src/main/res/drawable/ic_brightness_high.xml index d613ed523..2fc223c51 100644 --- a/app/src/main/res/drawable/ic_brightness_high.xml +++ b/app/src/main/res/drawable/ic_brightness_high.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,8.69L20,4h-4.69L12,0.69 8.69,4L4,4v4.69L0.69,12 4,15.31L4,20h4.69L12,23.31 15.31,20L20,20v-4.69L23.31,12 20,8.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4z"/> diff --git a/app/src/main/res/drawable/ic_brightness_low.xml b/app/src/main/res/drawable/ic_brightness_low.xml index 498a67ec0..f25fc114b 100644 --- a/app/src/main/res/drawable/ic_brightness_low.xml +++ b/app/src/main/res/drawable/ic_brightness_low.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18c-3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6 6,2.69 6,6 -2.69,6 -6,6z"/> diff --git a/app/src/main/res/drawable/ic_brightness_medium.xml b/app/src/main/res/drawable/ic_brightness_medium.xml index 1f3952586..44b36c7ce 100644 --- a/app/src/main/res/drawable/ic_brightness_medium.xml +++ b/app/src/main/res/drawable/ic_brightness_medium.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,15.31L23.31,12 20,8.69V4h-4.69L12,0.69 8.69,4H4v4.69L0.69,12 4,15.31V20h4.69L12,23.31 15.31,20H20v-4.69zM12,18V6c3.31,0 6,2.69 6,6s-2.69,6 -6,6z"/> diff --git a/app/src/main/res/drawable/ic_bug_report.xml b/app/src/main/res/drawable/ic_bug_report.xml index c7c44ccb2..74266e2c3 100644 --- a/app/src/main/res/drawable/ic_bug_report.xml +++ b/app/src/main/res/drawable/ic_bug_report.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"/> diff --git a/app/src/main/res/drawable/ic_campaign.xml b/app/src/main/res/drawable/ic_campaign.xml index a368f50f6..878a6d48e 100644 --- a/app/src/main/res/drawable/ic_campaign.xml +++ b/app/src/main/res/drawable/ic_campaign.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M18,11v2h4v-2h-4zM16,17.61c0.96,0.71 2.21,1.65 3.2,2.39 0.4,-0.53 0.8,-1.07 1.2,-1.6 -0.99,-0.74 -2.24,-1.68 -3.2,-2.4 -0.4,0.54 -0.8,1.08 -1.2,1.61zM20.4,5.6c-0.4,-0.53 -0.8,-1.07 -1.2,-1.6 -0.99,0.74 -2.24,1.68 -3.2,2.4 0.4,0.53 0.8,1.07 1.2,1.6 0.96,-0.72 2.21,-1.65 3.2,-2.4zM4,9c-1.1,0 -2,0.9 -2,2v2c0,1.1 0.9,2 2,2h1v4h2v-4h1l5,3L13,6L8,9L4,9zM15.5,12c0,-1.33 -0.58,-2.53 -1.5,-3.35v6.69c0.92,-0.81 1.5,-2.01 1.5,-3.34z"/> diff --git a/app/src/main/res/drawable/ic_cast.xml b/app/src/main/res/drawable/ic_cast.xml index 321dfcfc2..e6244195c 100644 --- a/app/src/main/res/drawable/ic_cast.xml +++ b/app/src/main/res/drawable/ic_cast.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M21,3L3,3c-1.1,0 -2,0.9 -2,2v3h2L3,5h18v14h-7v2h7c1.1,0 2,-0.9 2,-2L23,5c0,-1.1 -0.9,-2 -2,-2zM1,18v3h3c0,-1.66 -1.34,-3 -3,-3zM1,14v2c2.76,0 5,2.24 5,5h2c0,-3.87 -3.13,-7 -7,-7zM1,10v2c4.97,0 9,4.03 9,9h2c0,-6.08 -4.93,-11 -11,-11z"/> diff --git a/app/src/main/res/drawable/ic_checklist.xml b/app/src/main/res/drawable/ic_checklist.xml index 27bed183f..3f8b56bce 100644 --- a/app/src/main/res/drawable/ic_checklist.xml +++ b/app/src/main/res/drawable/ic_checklist.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M22,7h-9v2h9V7zM22,15h-9v2h9V15zM5.54,11L2,7.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,11zM5.54,19L2,15.46l1.41,-1.41l2.12,2.12l4.24,-4.24l1.41,1.41L5.54,19z"/> diff --git a/app/src/main/res/drawable/ic_child_care.xml b/app/src/main/res/drawable/ic_child_care.xml index 5d2ac1665..49faec2b0 100644 --- a/app/src/main/res/drawable/ic_child_care.xml +++ b/app/src/main/res/drawable/ic_child_care.xml @@ -1,16 +1,16 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M14.5,10.5m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0"/> + android:fillColor="#FF000000" + android:pathData="M9.5,10.5m-1.25,0a1.25,1.25 0,1 1,2.5 0a1.25,1.25 0,1 1,-2.5 0"/> + android:fillColor="#FF000000" + android:pathData="M22.94,12.66c0.04,-0.21 0.06,-0.43 0.06,-0.66s-0.02,-0.45 -0.06,-0.66c-0.25,-1.51 -1.36,-2.74 -2.81,-3.17 -0.53,-1.12 -1.28,-2.1 -2.19,-2.91C16.36,3.85 14.28,3 12,3s-4.36,0.85 -5.94,2.26c-0.92,0.81 -1.67,1.8 -2.19,2.91 -1.45,0.43 -2.56,1.65 -2.81,3.17 -0.04,0.21 -0.06,0.43 -0.06,0.66s0.02,0.45 0.06,0.66c0.25,1.51 1.36,2.74 2.81,3.17 0.52,1.11 1.27,2.09 2.17,2.89C7.62,20.14 9.71,21 12,21s4.38,-0.86 5.97,-2.28c0.9,-0.8 1.65,-1.79 2.17,-2.89 1.44,-0.43 2.55,-1.65 2.8,-3.17zM19,14c-0.1,0 -0.19,-0.02 -0.29,-0.03 -0.2,0.67 -0.49,1.29 -0.86,1.86C16.6,17.74 14.45,19 12,19s-4.6,-1.26 -5.85,-3.17c-0.37,-0.57 -0.66,-1.19 -0.86,-1.86 -0.1,0.01 -0.19,0.03 -0.29,0.03 -1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2c0.1,0 0.19,0.02 0.29,0.03 0.2,-0.67 0.49,-1.29 0.86,-1.86C7.4,6.26 9.55,5 12,5s4.6,1.26 5.85,3.17c0.37,0.57 0.66,1.19 0.86,1.86 0.1,-0.01 0.19,-0.03 0.29,-0.03 1.1,0 2,0.9 2,2s-0.9,2 -2,2zM7.5,14c0.76,1.77 2.49,3 4.5,3s3.74,-1.23 4.5,-3h-9z"/> diff --git a/app/src/main/res/drawable/ic_circle.xml b/app/src/main/res/drawable/ic_circle.xml index dc0a218b8..c69fb0410 100644 --- a/app/src/main/res/drawable/ic_circle.xml +++ b/app/src/main/res/drawable/ic_circle.xml @@ -1,10 +1,10 @@ + android:height="24dp" + android:width="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:tint="@color/defaultIconTint"> + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10s10,-4.49 10,-10S17.52,2 12,2L12,2z" + android:fillColor="#FF000000"/> diff --git a/app/src/main/res/drawable/ic_close.xml b/app/src/main/res/drawable/ic_close.xml index 1d5133364..67d762ffb 100644 --- a/app/src/main/res/drawable/ic_close.xml +++ b/app/src/main/res/drawable/ic_close.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/> diff --git a/app/src/main/res/drawable/ic_cloud.xml b/app/src/main/res/drawable/ic_cloud.xml index 15a682b76..c9211aa88 100644 --- a/app/src/main/res/drawable/ic_cloud.xml +++ b/app/src/main/res/drawable/ic_cloud.xml @@ -1,10 +1,10 @@ + android:height="24dp" + android:width="24dp" + android:viewportWidth="24" + android:viewportHeight="24" + android:tint="@color/defaultIconTint"> + android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96z" + android:fillColor="#FF000000"/> diff --git a/app/src/main/res/drawable/ic_cloud_download.xml b/app/src/main/res/drawable/ic_cloud_download.xml index 79c7db8e3..28dade195 100644 --- a/app/src/main/res/drawable/ic_cloud_download.xml +++ b/app/src/main/res/drawable/ic_cloud_download.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19.35,10.04C18.67,6.59 15.64,4 12,4 9.11,4 6.6,5.64 5.35,8.04 2.34,8.36 0,10.91 0,14c0,3.31 2.69,6 6,6h13c2.76,0 5,-2.24 5,-5 0,-2.64 -2.05,-4.78 -4.65,-4.96zM17,13l-5,5 -5,-5h3V9h4v4h3z"/> diff --git a/app/src/main/res/drawable/ic_comment.xml b/app/src/main/res/drawable/ic_comment.xml index 4bc124a81..65af57591 100644 --- a/app/src/main/res/drawable/ic_comment.xml +++ b/app/src/main/res/drawable/ic_comment.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M21.99,4c0,-1.1 -0.89,-2 -1.99,-2L4,2c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h14l4,4 -0.01,-18zM18,14L6,14v-2h12v2zM18,11L6,11L6,9h12v2zM18,8L6,8L6,6h12v2z"/> diff --git a/app/src/main/res/drawable/ic_computer.xml b/app/src/main/res/drawable/ic_computer.xml index 6b0e79313..41ebbbf62 100644 --- a/app/src/main/res/drawable/ic_computer.xml +++ b/app/src/main/res/drawable/ic_computer.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,18c1.1,0 1.99,-0.9 1.99,-2L22,6c0,-1.1 -0.9,-2 -2,-2H4c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2H0v2h24v-2h-4zM4,6h16v10H4V6z"/> diff --git a/app/src/main/res/drawable/ic_crop_portrait.xml b/app/src/main/res/drawable/ic_crop_portrait.xml index 50ce52f91..2848f56c0 100644 --- a/app/src/main/res/drawable/ic_crop_portrait.xml +++ b/app/src/main/res/drawable/ic_crop_portrait.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M17,3L7,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,5c0,-1.1 -0.9,-2 -2,-2zM17,19L7,19L7,5h10v14z"/> diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml index f38c5f130..438ecdf76 100644 --- a/app/src/main/res/drawable/ic_delete.xml +++ b/app/src/main/res/drawable/ic_delete.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/> diff --git a/app/src/main/res/drawable/ic_description.xml b/app/src/main/res/drawable/ic_description.xml index 5b80cbefd..d27a7209e 100644 --- a/app/src/main/res/drawable/ic_description.xml +++ b/app/src/main/res/drawable/ic_description.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M14,2L6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6zM16,18L8,18v-2h8v2zM16,14L8,14v-2h8v2zM13,9L13,3.5L18.5,9L13,9z"/> diff --git a/app/src/main/res/drawable/ic_directions_bike.xml b/app/src/main/res/drawable/ic_directions_bike.xml index b5580ee8d..e84f868fe 100644 --- a/app/src/main/res/drawable/ic_directions_bike.xml +++ b/app/src/main/res/drawable/ic_directions_bike.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M15.5,5.5c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM5,12c-2.8,0 -5,2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,-5 -5,-5zM5,20.5c-1.9,0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,1.6 3.5,3.5 -1.6,3.5 -3.5,3.5zM10.8,10.5l2.4,-2.4 0.8,0.8c1.3,1.3 3,2.1 5.1,2.1L19.1,9c-1.5,0 -2.7,-0.6 -3.6,-1.5l-1.9,-1.9c-0.5,-0.4 -1,-0.6 -1.6,-0.6s-1.1,0.2 -1.4,0.6L7.8,8.4c-0.4,0.4 -0.6,0.9 -0.6,1.4 0,0.6 0.2,1.1 0.6,1.4L11,14v5h2v-6.2l-2.2,-2.3zM19,12c-2.8,0 -5,2.2 -5,5s2.2,5 5,5 5,-2.2 5,-5 -2.2,-5 -5,-5zM19,20.5c-1.9,0 -3.5,-1.6 -3.5,-3.5s1.6,-3.5 3.5,-3.5 3.5,1.6 3.5,3.5 -1.6,3.5 -3.5,3.5z"/> diff --git a/app/src/main/res/drawable/ic_directions_car.xml b/app/src/main/res/drawable/ic_directions_car.xml index 3bfd9b4c3..4b4643887 100644 --- a/app/src/main/res/drawable/ic_directions_car.xml +++ b/app/src/main/res/drawable/ic_directions_car.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M18.92,6.01C18.72,5.42 18.16,5 17.5,5h-11c-0.66,0 -1.21,0.42 -1.42,1.01L3,12v8c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-1h12v1c0,0.55 0.45,1 1,1h1c0.55,0 1,-0.45 1,-1v-8l-2.08,-5.99zM6.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5S5.67,13 6.5,13s1.5,0.67 1.5,1.5S7.33,16 6.5,16zM17.5,16c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM5,11l1.5,-4.5h11L19,11L5,11z"/> diff --git a/app/src/main/res/drawable/ic_done.xml b/app/src/main/res/drawable/ic_done.xml index 43b77a9cd..63cbe0eaa 100644 --- a/app/src/main/res/drawable/ic_done.xml +++ b/app/src/main/res/drawable/ic_done.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M9,16.2L4.8,12l-1.4,1.4L9,19 21,7l-1.4,-1.4L9,16.2z"/> diff --git a/app/src/main/res/drawable/ic_drag_handle.xml b/app/src/main/res/drawable/ic_drag_handle.xml index c08695e98..9b0b41e2f 100644 --- a/app/src/main/res/drawable/ic_drag_handle.xml +++ b/app/src/main/res/drawable/ic_drag_handle.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,9H4v2h16V9zM4,15h16v-2H4V15z"/> diff --git a/app/src/main/res/drawable/ic_expand_more.xml b/app/src/main/res/drawable/ic_expand_more.xml index 2e6d23792..f95bf9101 100644 --- a/app/src/main/res/drawable/ic_expand_more.xml +++ b/app/src/main/res/drawable/ic_expand_more.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/> diff --git a/app/src/main/res/drawable/ic_explore.xml b/app/src/main/res/drawable/ic_explore.xml index 2b974c69f..9f85d0e5f 100644 --- a/app/src/main/res/drawable/ic_explore.xml +++ b/app/src/main/res/drawable/ic_explore.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,10.9c-0.61,0 -1.1,0.49 -1.1,1.1s0.49,1.1 1.1,1.1c0.61,0 1.1,-0.49 1.1,-1.1s-0.49,-1.1 -1.1,-1.1zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM14.19,14.19L6,18l3.81,-8.19L18,6l-3.81,8.19z"/> diff --git a/app/src/main/res/drawable/ic_fast_forward.xml b/app/src/main/res/drawable/ic_fast_forward.xml index 4edc96a9b..de7d3c914 100644 --- a/app/src/main/res/drawable/ic_fast_forward.xml +++ b/app/src/main/res/drawable/ic_fast_forward.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M4,18l8.5,-6L4,6v12zM13,6v12l8.5,-6L13,6z"/> diff --git a/app/src/main/res/drawable/ic_fast_rewind.xml b/app/src/main/res/drawable/ic_fast_rewind.xml index 33d9f56ef..b30f5c1f4 100644 --- a/app/src/main/res/drawable/ic_fast_rewind.xml +++ b/app/src/main/res/drawable/ic_fast_rewind.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M11,18L11,6l-8.5,6 8.5,6zM11.5,12l8.5,6L20,6l-8.5,6z"/> diff --git a/app/src/main/res/drawable/ic_fastfood.xml b/app/src/main/res/drawable/ic_fastfood.xml index b2a1abdf3..ef3fea8af 100644 --- a/app/src/main/res/drawable/ic_fastfood.xml +++ b/app/src/main/res/drawable/ic_fastfood.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M18.06,22.99h1.66c0.84,0 1.53,-0.64 1.63,-1.46L23,5.05h-5L18,1h-1.97v4.05h-4.97l0.3,2.34c1.71,0.47 3.31,1.32 4.27,2.26 1.44,1.42 2.43,2.89 2.43,5.29v8.05zM1,21.99L1,21h15.03v0.99c0,0.55 -0.45,1 -1.01,1L2.01,22.99c-0.56,0 -1.01,-0.45 -1.01,-1zM16.03,14.99c0,-8 -15.03,-8 -15.03,0h15.03zM1.02,17h15v2h-15z"/> diff --git a/app/src/main/res/drawable/ic_favorite.xml b/app/src/main/res/drawable/ic_favorite.xml index 87d14880f..22b8b3ec0 100644 --- a/app/src/main/res/drawable/ic_favorite.xml +++ b/app/src/main/res/drawable/ic_favorite.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/> diff --git a/app/src/main/res/drawable/ic_file_download.xml b/app/src/main/res/drawable/ic_file_download.xml index b4d9e15e9..1dfefe98b 100644 --- a/app/src/main/res/drawable/ic_file_download.xml +++ b/app/src/main/res/drawable/ic_file_download.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/> diff --git a/app/src/main/res/drawable/ic_filter_list.xml b/app/src/main/res/drawable/ic_filter_list.xml index e1a2b236b..ad6109e3f 100644 --- a/app/src/main/res/drawable/ic_filter_list.xml +++ b/app/src/main/res/drawable/ic_filter_list.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M10,18h4v-2h-4v2zM3,6v2h18L21,6L3,6zM6,13h12v-2L6,11v2z"/> diff --git a/app/src/main/res/drawable/ic_fitness_center.xml b/app/src/main/res/drawable/ic_fitness_center.xml index 56670cba6..b04cf57c6 100644 --- a/app/src/main/res/drawable/ic_fitness_center.xml +++ b/app/src/main/res/drawable/ic_fitness_center.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20.57,14.86L22,13.43 20.57,12 17,15.57 8.43,7 12,3.43 10.57,2 9.14,3.43 7.71,2 5.57,4.14 4.14,2.71 2.71,4.14l1.43,1.43L2,7.71l1.43,1.43L2,10.57 3.43,12 7,8.43 15.57,17 12,20.57 13.43,22l1.43,-1.43L16.29,22l2.14,-2.14 1.43,1.43 1.43,-1.43 -1.43,-1.43L22,16.29z"/> diff --git a/app/src/main/res/drawable/ic_fullscreen.xml b/app/src/main/res/drawable/ic_fullscreen.xml index 35e1bbbac..a59d815a9 100644 --- a/app/src/main/res/drawable/ic_fullscreen.xml +++ b/app/src/main/res/drawable/ic_fullscreen.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M7,14L5,14v5h5v-2L7,17v-3zM5,10h2L7,7h3L10,5L5,5v5zM17,17h-3v2h5v-5h-2v3zM14,5v2h3v3h2L19,5h-5z"/> diff --git a/app/src/main/res/drawable/ic_fullscreen_exit.xml b/app/src/main/res/drawable/ic_fullscreen_exit.xml index a497da742..b9cb59ab2 100644 --- a/app/src/main/res/drawable/ic_fullscreen_exit.xml +++ b/app/src/main/res/drawable/ic_fullscreen_exit.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M5,16h3v3h2v-5L5,14v2zM8,8L5,8v2h5L10,5L8,5v3zM14,19h2v-3h3v-2h-5v5zM16,8L16,5h-2v5h5L19,8h-3z"/> diff --git a/app/src/main/res/drawable/ic_headset.xml b/app/src/main/res/drawable/ic_headset.xml index 3eff4b7dd..f1db701a1 100644 --- a/app/src/main/res/drawable/ic_headset.xml +++ b/app/src/main/res/drawable/ic_headset.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z"/> diff --git a/app/src/main/res/drawable/ic_headset_shadow.xml b/app/src/main/res/drawable/ic_headset_shadow.xml index 2d6f61eee..afc8d55ac 100644 --- a/app/src/main/res/drawable/ic_headset_shadow.xml +++ b/app/src/main/res/drawable/ic_headset_shadow.xml @@ -1,17 +1,17 @@ + android:width="24dp" + android:height="24dp" + android:alpha="0.73333333" + android:viewportWidth="24" + android:viewportHeight="24"> + android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z" + android:strokeWidth="1" + android:strokeAlpha="0.34090909" + android:strokeColor="#000000" + android:strokeLineCap="butt" + android:strokeLineJoin="miter"/> + android:fillColor="#ffffff" + android:pathData="M12,1c-4.97,0 -9,4.03 -9,9v7c0,1.66 1.34,3 3,3h3v-8H5v-2c0,-3.87 3.13,-7 7,-7s7,3.13 7,7v2h-4v8h3c1.66,0 3,-1.34 3,-3v-7c0,-4.97 -4.03,-9 -9,-9z"/> diff --git a/app/src/main/res/drawable/ic_heart.xml b/app/src/main/res/drawable/ic_heart.xml index 248f9788b..94274c1ab 100644 --- a/app/src/main/res/drawable/ic_heart.xml +++ b/app/src/main/res/drawable/ic_heart.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="#E53935" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,21.35l-1.45,-1.32C5.4,15.36 2,12.28 2,8.5 2,5.42 4.42,3 7.5,3c1.74,0 3.41,0.81 4.5,2.09C13.09,3.81 14.76,3 16.5,3 19.58,3 22,5.42 22,8.5c0,3.78 -3.4,6.86 -8.55,11.54L12,21.35z"/> diff --git a/app/src/main/res/drawable/ic_help.xml b/app/src/main/res/drawable/ic_help.xml index 45955eae7..55c78d520 100644 --- a/app/src/main/res/drawable/ic_help.xml +++ b/app/src/main/res/drawable/ic_help.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,19h-2v-2h2v2zM15.07,11.25l-0.9,0.92C13.45,12.9 13,13.5 13,15h-2v-0.5c0,-1.1 0.45,-2.1 1.17,-2.83l1.24,-1.26c0.37,-0.36 0.59,-0.86 0.59,-1.41 0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2L8,9c0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,0.88 -0.36,1.68 -0.93,2.25z"/> diff --git a/app/src/main/res/drawable/ic_history.xml b/app/src/main/res/drawable/ic_history.xml index 4e21de19d..ebca7bd37 100644 --- a/app/src/main/res/drawable/ic_history.xml +++ b/app/src/main/res/drawable/ic_history.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/> diff --git a/app/src/main/res/drawable/ic_history_future.xml b/app/src/main/res/drawable/ic_history_future.xml index db6f2acbf..9403d4a2e 100644 --- a/app/src/main/res/drawable/ic_history_future.xml +++ b/app/src/main/res/drawable/ic_history_future.xml @@ -1,15 +1,15 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:name="flip" + android:pivotX="12" + android:scaleX="-1"> + android:fillColor="#FF000000" + android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/> diff --git a/app/src/main/res/drawable/ic_home.xml b/app/src/main/res/drawable/ic_home.xml index 48f968b4c..74aada02d 100644 --- a/app/src/main/res/drawable/ic_home.xml +++ b/app/src/main/res/drawable/ic_home.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M10,20v-6h4v6h5v-8h3L12,3 2,12h3v8z"/> diff --git a/app/src/main/res/drawable/ic_hourglass_top.xml b/app/src/main/res/drawable/ic_hourglass_top.xml index f92496779..739d66866 100644 --- a/app/src/main/res/drawable/ic_hourglass_top.xml +++ b/app/src/main/res/drawable/ic_hourglass_top.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M6,2l0.01,6L10,12l-3.99,4.01L6,22h12v-6l-4,-4l4,-3.99V2H6zM16,16.5V20H8v-3.5l4,-4L16,16.5z"/> diff --git a/app/src/main/res/drawable/ic_info_outline.xml b/app/src/main/res/drawable/ic_info_outline.xml index 3bbe51917..c94d7afe9 100644 --- a/app/src/main/res/drawable/ic_info_outline.xml +++ b/app/src/main/res/drawable/ic_info_outline.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M11,7h2v2h-2zM11,11h2v6h-2zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8z"/> diff --git a/app/src/main/res/drawable/ic_insert_emoticon.xml b/app/src/main/res/drawable/ic_insert_emoticon.xml index 2ddb7120c..8eca99410 100644 --- a/app/src/main/res/drawable/ic_insert_emoticon.xml +++ b/app/src/main/res/drawable/ic_insert_emoticon.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8zM15.5,11c0.83,0 1.5,-0.67 1.5,-1.5S16.33,8 15.5,8 14,8.67 14,9.5s0.67,1.5 1.5,1.5zM8.5,11c0.83,0 1.5,-0.67 1.5,-1.5S9.33,8 8.5,8 7,8.67 7,9.5 7.67,11 8.5,11zM12,17.5c2.33,0 4.31,-1.46 5.11,-3.5L6.89,14c0.8,2.04 2.78,3.5 5.11,3.5z"/> diff --git a/app/src/main/res/drawable/ic_language.xml b/app/src/main/res/drawable/ic_language.xml index 8bc821acc..660260abd 100644 --- a/app/src/main/res/drawable/ic_language.xml +++ b/app/src/main/res/drawable/ic_language.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM18.92,8h-2.95c-0.32,-1.25 -0.78,-2.45 -1.38,-3.56 1.84,0.63 3.37,1.91 4.33,3.56zM12,4.04c0.83,1.2 1.48,2.53 1.91,3.96h-3.82c0.43,-1.43 1.08,-2.76 1.91,-3.96zM4.26,14C4.1,13.36 4,12.69 4,12s0.1,-1.36 0.26,-2h3.38c-0.08,0.66 -0.14,1.32 -0.14,2 0,0.68 0.06,1.34 0.14,2L4.26,14zM5.08,16h2.95c0.32,1.25 0.78,2.45 1.38,3.56 -1.84,-0.63 -3.37,-1.9 -4.33,-3.56zM8.03,8L5.08,8c0.96,-1.66 2.49,-2.93 4.33,-3.56C8.81,5.55 8.35,6.75 8.03,8zM12,19.96c-0.83,-1.2 -1.48,-2.53 -1.91,-3.96h3.82c-0.43,1.43 -1.08,2.76 -1.91,3.96zM14.34,14L9.66,14c-0.09,-0.66 -0.16,-1.32 -0.16,-2 0,-0.68 0.07,-1.35 0.16,-2h4.68c0.09,0.65 0.16,1.32 0.16,2 0,0.68 -0.07,1.34 -0.16,2zM14.59,19.56c0.6,-1.11 1.06,-2.31 1.38,-3.56h2.95c-0.96,1.65 -2.49,2.93 -4.33,3.56zM16.36,14c0.08,-0.66 0.14,-1.32 0.14,-2 0,-0.68 -0.06,-1.34 -0.14,-2h3.38c0.16,0.64 0.26,1.31 0.26,2s-0.1,1.36 -0.26,2h-3.38z"/> diff --git a/app/src/main/res/drawable/ic_list.xml b/app/src/main/res/drawable/ic_list.xml index f6538e875..031ab8075 100644 --- a/app/src/main/res/drawable/ic_list.xml +++ b/app/src/main/res/drawable/ic_list.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M3,13h2v-2L3,11v2zM3,17h2v-2L3,15v2zM3,9h2L5,7L3,7v2zM7,13h14v-2L7,11v2zM7,17h14v-2L7,15v2zM7,7v2h14L21,7L7,7z"/> diff --git a/app/src/main/res/drawable/ic_live_tv.xml b/app/src/main/res/drawable/ic_live_tv.xml index 80fb172aa..d968cdb35 100644 --- a/app/src/main/res/drawable/ic_live_tv.xml +++ b/app/src/main/res/drawable/ic_live_tv.xml @@ -1,11 +1,11 @@ + android:width="24dp" + android:height="24dp" + android:autoMirrored="true" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="@android:color/black" + android:pathData="M21,6h-7.59l3.29,-3.29L16,2l-4,4 -4,-4 -0.71,0.71L10.59,6L3,6c-1.1,0 -2,0.89 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.11 -0.9,-2 -2,-2zM21,20L3,20L3,8h18v12zM9,10v8l7,-4z"/> diff --git a/app/src/main/res/drawable/ic_menu_book.xml b/app/src/main/res/drawable/ic_menu_book.xml index 4cd4fb3a4..683b3ae56 100644 --- a/app/src/main/res/drawable/ic_menu_book.xml +++ b/app/src/main/res/drawable/ic_menu_book.xml @@ -1,12 +1,16 @@ - - - - + android:autoMirrored="true" + android:height="24dp" + android:tint="#000000" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp"> + + + + diff --git a/app/src/main/res/drawable/ic_mic.xml b/app/src/main/res/drawable/ic_mic.xml index 9da90f5a9..e210bb207 100644 --- a/app/src/main/res/drawable/ic_mic.xml +++ b/app/src/main/res/drawable/ic_mic.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,14c1.66,0 2.99,-1.34 2.99,-3L15,5c0,-1.66 -1.34,-3 -3,-3S9,3.34 9,5v6c0,1.66 1.34,3 3,3zM17.3,11c0,3 -2.54,5.1 -5.3,5.1S6.7,14 6.7,11L5,11c0,3.41 2.72,6.23 6,6.72L11,21h2v-3.28c3.28,-0.48 6,-3.3 6,-6.72h-1.7z"/> diff --git a/app/src/main/res/drawable/ic_more_vert.xml b/app/src/main/res/drawable/ic_more_vert.xml index 1a873cf8b..d2fa47cc5 100644 --- a/app/src/main/res/drawable/ic_more_vert.xml +++ b/app/src/main/res/drawable/ic_more_vert.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,8c1.1,0 2,-0.9 2,-2s-0.9,-2 -2,-2 -2,0.9 -2,2 0.9,2 2,2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,16c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/> diff --git a/app/src/main/res/drawable/ic_motorcycle.xml b/app/src/main/res/drawable/ic_motorcycle.xml index 7684b0673..c3176bd7a 100644 --- a/app/src/main/res/drawable/ic_motorcycle.xml +++ b/app/src/main/res/drawable/ic_motorcycle.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M17.42,10L13.41,6H9V8H12.59L14.59,10H6.5C4,10 2,12 2,14.5C2,17 4,19 6.5,19C8.72,19 10.56,17.38 10.92,15.27L13.04,14C13,14.17 13,14.33 13,14.5C13,17 15,19 17.5,19C20,19 22,17 22,14.5C22,12 20,10 17.5,10M8.84,15.26C8.5,16.27 7.58,17 6.47,17C5.09,17 3.97,15.88 3.97,14.5C3.97,13.12 5.09,12 6.47,12C7.59,12 8.5,12.74 8.84,13.75H6V15.25L8.84,15.26M17.47,17C16.09,17 14.97,15.88 14.97,14.5C14.97,13.12 16.09,12 17.47,12A2.5,2.5 0 0,1 19.97,14.5A2.5,2.5 0 0,1 17.47,17Z"/> diff --git a/app/src/main/res/drawable/ic_movie.xml b/app/src/main/res/drawable/ic_movie.xml index 49eaf7174..8c4513596 100644 --- a/app/src/main/res/drawable/ic_movie.xml +++ b/app/src/main/res/drawable/ic_movie.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M18,4l2,4h-3l-2,-4h-2l2,4h-3l-2,-4H8l2,4H7L5,4H4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2V4h-4z"/> diff --git a/app/src/main/res/drawable/ic_music_note.xml b/app/src/main/res/drawable/ic_music_note.xml index cc4e5bd10..6f0258ef1 100644 --- a/app/src/main/res/drawable/ic_music_note.xml +++ b/app/src/main/res/drawable/ic_music_note.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z"/> diff --git a/app/src/main/res/drawable/ic_next.xml b/app/src/main/res/drawable/ic_next.xml index 2805ebb26..5abae0e69 100644 --- a/app/src/main/res/drawable/ic_next.xml +++ b/app/src/main/res/drawable/ic_next.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#ffffff" + android:pathData="M6,18l8.5,-6L6,6v12zM16,6v12h2V6h-2z"/> diff --git a/app/src/main/res/drawable/ic_notifications.xml b/app/src/main/res/drawable/ic_notifications.xml index f87cac524..66691075d 100644 --- a/app/src/main/res/drawable/ic_notifications.xml +++ b/app/src/main/res/drawable/ic_notifications.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z"/> diff --git a/app/src/main/res/drawable/ic_palette.xml b/app/src/main/res/drawable/ic_palette.xml index 0356bfe8f..f4b91ff9f 100644 --- a/app/src/main/res/drawable/ic_palette.xml +++ b/app/src/main/res/drawable/ic_palette.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,2C6.49,2 2,6.49 2,12s4.49,10 10,10c1.38,0 2.5,-1.12 2.5,-2.5c0,-0.61 -0.23,-1.2 -0.64,-1.67c-0.08,-0.1 -0.13,-0.21 -0.13,-0.33c0,-0.28 0.22,-0.5 0.5,-0.5H16c3.31,0 6,-2.69 6,-6C22,6.04 17.51,2 12,2zM17.5,13c-0.83,0 -1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5s1.5,0.67 1.5,1.5C19,12.33 18.33,13 17.5,13zM14.5,9C13.67,9 13,8.33 13,7.5C13,6.67 13.67,6 14.5,6S16,6.67 16,7.5C16,8.33 15.33,9 14.5,9zM5,11.5C5,10.67 5.67,10 6.5,10S8,10.67 8,11.5C8,12.33 7.33,13 6.5,13S5,12.33 5,11.5zM11,7.5C11,8.33 10.33,9 9.5,9S8,8.33 8,7.5C8,6.67 8.67,6 9.5,6S11,6.67 11,7.5z"/> diff --git a/app/src/main/res/drawable/ic_pause.xml b/app/src/main/res/drawable/ic_pause.xml index d8f1e440e..9255d3d8b 100644 --- a/app/src/main/res/drawable/ic_pause.xml +++ b/app/src/main/res/drawable/ic_pause.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z"/> diff --git a/app/src/main/res/drawable/ic_people.xml b/app/src/main/res/drawable/ic_people.xml index 9cd3ad3fb..9cf836c87 100644 --- a/app/src/main/res/drawable/ic_people.xml +++ b/app/src/main/res/drawable/ic_people.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/> diff --git a/app/src/main/res/drawable/ic_person.xml b/app/src/main/res/drawable/ic_person.xml index db64734ae..c2a964c9f 100644 --- a/app/src/main/res/drawable/ic_person.xml +++ b/app/src/main/res/drawable/ic_person.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/> diff --git a/app/src/main/res/drawable/ic_pets.xml b/app/src/main/res/drawable/ic_pets.xml index 0aadab03d..29c221497 100644 --- a/app/src/main/res/drawable/ic_pets.xml +++ b/app/src/main/res/drawable/ic_pets.xml @@ -1,22 +1,22 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M4.5,9.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/> + android:fillColor="#FF000000" + android:pathData="M9,5.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/> + android:fillColor="#FF000000" + android:pathData="M15,5.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/> + android:fillColor="#FF000000" + android:pathData="M19.5,9.5m-2.5,0a2.5,2.5 0,1 1,5 0a2.5,2.5 0,1 1,-5 0"/> + android:fillColor="#FF000000" + android:pathData="M17.34,14.86c-0.87,-1.02 -1.6,-1.89 -2.48,-2.91 -0.46,-0.54 -1.05,-1.08 -1.75,-1.32 -0.11,-0.04 -0.22,-0.07 -0.33,-0.09 -0.25,-0.04 -0.52,-0.04 -0.78,-0.04s-0.53,0 -0.79,0.05c-0.11,0.02 -0.22,0.05 -0.33,0.09 -0.7,0.24 -1.28,0.78 -1.75,1.32 -0.87,1.02 -1.6,1.89 -2.48,2.91 -1.31,1.31 -2.92,2.76 -2.62,4.79 0.29,1.02 1.02,2.03 2.33,2.32 0.73,0.15 3.06,-0.44 5.54,-0.44h0.18c2.48,0 4.81,0.58 5.54,0.44 1.31,-0.29 2.04,-1.31 2.33,-2.32 0.31,-2.04 -1.3,-3.49 -2.61,-4.8z"/> diff --git a/app/src/main/res/drawable/ic_picture_in_picture.xml b/app/src/main/res/drawable/ic_picture_in_picture.xml index 91fd52413..31eda61c2 100644 --- a/app/src/main/res/drawable/ic_picture_in_picture.xml +++ b/app/src/main/res/drawable/ic_picture_in_picture.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19,7h-8v6h8L19,7zM21,3L3,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,1.98 2,1.98h18c1.1,0 2,-0.88 2,-1.98L23,5c0,-1.1 -0.9,-2 -2,-2zM21,19.01L3,19.01L3,4.98h18v14.03z"/> diff --git a/app/src/main/res/drawable/ic_pin.xml b/app/src/main/res/drawable/ic_pin.xml index e41fd7f12..090886077 100644 --- a/app/src/main/res/drawable/ic_pin.xml +++ b/app/src/main/res/drawable/ic_pin.xml @@ -1,19 +1,19 @@ - - - - + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + + + + diff --git a/app/src/main/res/drawable/ic_placeholder_bandcamp.xml b/app/src/main/res/drawable/ic_placeholder_bandcamp.xml index 411e69854..ac4c2b9dd 100644 --- a/app/src/main/res/drawable/ic_placeholder_bandcamp.xml +++ b/app/src/main/res/drawable/ic_placeholder_bandcamp.xml @@ -1,10 +1,10 @@ + android:height="24dp" + android:width="24dp" + android:viewportHeight="97.75" + android:viewportWidth="97.75" + android:tint="@color/defaultIconTint"> + android:pathData="M48.875,0C21.882,0 0,21.882 0,48.875S21.882,97.75 48.875,97.75 97.75,75.868 97.75,48.875 75.868,0 48.875,0zM64.835,70.857L12.593,70.857l20.32,-43.965h52.244L64.835,70.857z" + android:fillColor="#FF000000"/> diff --git a/app/src/main/res/drawable/ic_placeholder_media_ccc.xml b/app/src/main/res/drawable/ic_placeholder_media_ccc.xml index cdc743cb2..187c0f291 100644 --- a/app/src/main/res/drawable/ic_placeholder_media_ccc.xml +++ b/app/src/main/res/drawable/ic_placeholder_media_ccc.xml @@ -1,10 +1,10 @@ - + android:height="24dp" + android:width="24dp" + android:viewportWidth="26.458319" + android:viewportHeight="26.458332" + android:tint="@color/defaultIconTint"> + diff --git a/app/src/main/res/drawable/ic_placeholder_peertube.xml b/app/src/main/res/drawable/ic_placeholder_peertube.xml index 263d92d70..7accc9897 100644 --- a/app/src/main/res/drawable/ic_placeholder_peertube.xml +++ b/app/src/main/res/drawable/ic_placeholder_peertube.xml @@ -1,9 +1,12 @@ - - - + android:width="24dp" + android:height="24dp" + android:viewportWidth="400" + android:viewportHeight="400"> + + + diff --git a/app/src/main/res/drawable/ic_play_arrow.xml b/app/src/main/res/drawable/ic_play_arrow.xml index a70a4ddbb..574e063bf 100644 --- a/app/src/main/res/drawable/ic_play_arrow.xml +++ b/app/src/main/res/drawable/ic_play_arrow.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M8,5v14l11,-7z"/> diff --git a/app/src/main/res/drawable/ic_play_arrow_shadow.xml b/app/src/main/res/drawable/ic_play_arrow_shadow.xml index bf4b895b0..d96557a2e 100644 --- a/app/src/main/res/drawable/ic_play_arrow_shadow.xml +++ b/app/src/main/res/drawable/ic_play_arrow_shadow.xml @@ -1,25 +1,25 @@ + android:width="24dp" + android:height="24dp" + android:autoMirrored="true" + android:viewportWidth="80" + android:viewportHeight="80"> + android:fillColor="#00000000" + android:fillType="evenOdd" + android:pathData="M12.0447,6.5087 L12.0447,72.7367 69.3276,38.8486Z" + android:strokeWidth="7" + android:strokeAlpha="0.25" + android:strokeColor="#000000" + android:strokeLineCap="butt" + android:strokeLineJoin="miter"/> + android:fillColor="#00000000" + android:fillType="evenOdd" + android:pathData="M12.0447,6.5087 L12.0447,72.7367 69.3276,38.8486Z" + android:strokeWidth="5" + android:strokeAlpha="0.73333333" + android:strokeColor="#ffffff" + android:strokeLineCap="butt" + android:strokeLineJoin="miter"/> diff --git a/app/src/main/res/drawable/ic_play_seek_triangle.xml b/app/src/main/res/drawable/ic_play_seek_triangle.xml index 9c257c423..e5face0eb 100644 --- a/app/src/main/res/drawable/ic_play_seek_triangle.xml +++ b/app/src/main/res/drawable/ic_play_seek_triangle.xml @@ -1,9 +1,9 @@ + android:width="16dp" + android:height="20dp" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FFFFFF" + android:pathData="M3,2 L22,12 L3,22 Z"/> diff --git a/app/src/main/res/drawable/ic_playlist_add.xml b/app/src/main/res/drawable/ic_playlist_add.xml index 144f123b1..ecc169cfb 100644 --- a/app/src/main/res/drawable/ic_playlist_add.xml +++ b/app/src/main/res/drawable/ic_playlist_add.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M14,10H3v2h11V10zM14,6H3v2h11V6zM18,14v-4h-2v4h-4v2h4v4h2v-4h4v-2H18zM3,16h7v-2H3V16z"/> diff --git a/app/src/main/res/drawable/ic_playlist_add_check.xml b/app/src/main/res/drawable/ic_playlist_add_check.xml index 54bf2fb56..4833f7504 100644 --- a/app/src/main/res/drawable/ic_playlist_add_check.xml +++ b/app/src/main/res/drawable/ic_playlist_add_check.xml @@ -1,19 +1,19 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M3,10h11v2h-11z"/> + android:fillColor="#FF000000" + android:pathData="M3,6h11v2h-11z"/> + android:fillColor="#FF000000" + android:pathData="M3,14h7v2h-7z"/> + android:fillColor="#FF000000" + android:pathData="M20.59,11.93l-4.25,4.24l-2.12,-2.12l-1.41,1.41l3.53,3.54l5.66,-5.66z"/> diff --git a/app/src/main/res/drawable/ic_playlist_play.xml b/app/src/main/res/drawable/ic_playlist_play.xml index b9c64946a..8dae42040 100644 --- a/app/src/main/res/drawable/ic_playlist_play.xml +++ b/app/src/main/res/drawable/ic_playlist_play.xml @@ -1,19 +1,19 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M3,10h11v2h-11z"/> + android:fillColor="#FF000000" + android:pathData="M3,6h11v2h-11z"/> + android:fillColor="#FF000000" + android:pathData="M3,14h7v2h-7z"/> + android:fillColor="#FF000000" + android:pathData="M16,13l0,8l6,-4z"/> diff --git a/app/src/main/res/drawable/ic_previous.xml b/app/src/main/res/drawable/ic_previous.xml index 4af0d2178..694e6753b 100644 --- a/app/src/main/res/drawable/ic_previous.xml +++ b/app/src/main/res/drawable/ic_previous.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#ffffff" + android:pathData="M6,6h2v12L6,18zM9.5,12l8.5,6L18,6z"/> diff --git a/app/src/main/res/drawable/ic_public.xml b/app/src/main/res/drawable/ic_public.xml index 796f37812..2b1bb25a4 100644 --- a/app/src/main/res/drawable/ic_public.xml +++ b/app/src/main/res/drawable/ic_public.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM11,19.93c-3.95,-0.49 -7,-3.85 -7,-7.93 0,-0.62 0.08,-1.21 0.21,-1.79L9,15v1c0,1.1 0.9,2 2,2v1.93zM17.9,17.39c-0.26,-0.81 -1,-1.39 -1.9,-1.39h-1v-3c0,-0.55 -0.45,-1 -1,-1L8,12v-2h2c0.55,0 1,-0.45 1,-1L11,7h2c1.1,0 2,-0.9 2,-2v-0.41c2.93,1.19 5,4.06 5,7.41 0,2.08 -0.8,3.97 -2.1,5.39z"/> diff --git a/app/src/main/res/drawable/ic_radio.xml b/app/src/main/res/drawable/ic_radio.xml index f009ff54e..8ef53f319 100644 --- a/app/src/main/res/drawable/ic_radio.xml +++ b/app/src/main/res/drawable/ic_radio.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M3.24,6.15C2.51,6.43 2,7.17 2,8v12c0,1.1 0.89,2 2,2h16c1.11,0 2,-0.9 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2L8.3,6l8.26,-3.34L15.88,1 3.24,6.15zM7,20c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM20,12h-2v-2h-2v2L4,12L4,8h16v4z"/> diff --git a/app/src/main/res/drawable/ic_refresh.xml b/app/src/main/res/drawable/ic_refresh.xml index 20af23dde..20ea8e9e0 100644 --- a/app/src/main/res/drawable/ic_refresh.xml +++ b/app/src/main/res/drawable/ic_refresh.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/> diff --git a/app/src/main/res/drawable/ic_repeat.xml b/app/src/main/res/drawable/ic_repeat.xml index fb9ef820b..e171ae4a4 100644 --- a/app/src/main/res/drawable/ic_repeat.xml +++ b/app/src/main/res/drawable/ic_repeat.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/> diff --git a/app/src/main/res/drawable/ic_replay.xml b/app/src/main/res/drawable/ic_replay.xml index 987710fc7..82774984d 100644 --- a/app/src/main/res/drawable/ic_replay.xml +++ b/app/src/main/res/drawable/ic_replay.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FFFFFFFF" + android:pathData="M12,5V1L7,6l5,5V7c3.31,0 6,2.69 6,6s-2.69,6 -6,6 -6,-2.69 -6,-6H4c0,4.42 3.58,8 8,8s8,-3.58 8,-8 -3.58,-8 -8,-8z"/> diff --git a/app/src/main/res/drawable/ic_restaurant.xml b/app/src/main/res/drawable/ic_restaurant.xml index 9dccc8ee7..9260883a8 100644 --- a/app/src/main/res/drawable/ic_restaurant.xml +++ b/app/src/main/res/drawable/ic_restaurant.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M11,9L9,9L9,2L7,2v7L5,9L5,2L3,2v7c0,2.12 1.66,3.84 3.75,3.97L6.75,22h2.5v-9.03C11.34,12.84 13,11.12 13,9L13,2h-2v7zM16,6v8h2.5v8L21,22L21,2c-2.76,0 -5,2.24 -5,4z"/> diff --git a/app/src/main/res/drawable/ic_rss_feed.xml b/app/src/main/res/drawable/ic_rss_feed.xml index a73eff527..b299be44f 100644 --- a/app/src/main/res/drawable/ic_rss_feed.xml +++ b/app/src/main/res/drawable/ic_rss_feed.xml @@ -1,13 +1,13 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M6.18,17.82m-2.18,0a2.18,2.18 0,1 1,4.36 0a2.18,2.18 0,1 1,-4.36 0"/> + android:fillColor="#FF000000" + android:pathData="M4,4.44v2.83c7.03,0 12.73,5.7 12.73,12.73h2.83c0,-8.59 -6.97,-15.56 -15.56,-15.56zM4,10.1v2.83c3.9,0 7.07,3.17 7.07,7.07h2.83c0,-5.47 -4.43,-9.9 -9.9,-9.9z"/> diff --git a/app/src/main/res/drawable/ic_save.xml b/app/src/main/res/drawable/ic_save.xml index 26e664589..81630e54d 100644 --- a/app/src/main/res/drawable/ic_save.xml +++ b/app/src/main/res/drawable/ic_save.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M17,3L5,3c-1.11,0 -2,0.9 -2,2v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2L21,7l-4,-4zM12,19c-1.66,0 -3,-1.34 -3,-3s1.34,-3 3,-3 3,1.34 3,3 -1.34,3 -3,3zM15,9L5,9L5,5h10v4z"/> diff --git a/app/src/main/res/drawable/ic_school.xml b/app/src/main/res/drawable/ic_school.xml index 6d7e2f0e9..a67120e27 100644 --- a/app/src/main/res/drawable/ic_school.xml +++ b/app/src/main/res/drawable/ic_school.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M5,13.18v4L12,21l7,-3.82v-4L12,17l-7,-3.82zM12,3L1,9l11,6 9,-4.91V17h2V9L12,3z"/> diff --git a/app/src/main/res/drawable/ic_search.xml b/app/src/main/res/drawable/ic_search.xml index a889b09e5..7b9417555 100644 --- a/app/src/main/res/drawable/ic_search.xml +++ b/app/src/main/res/drawable/ic_search.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/> diff --git a/app/src/main/res/drawable/ic_search_add.xml b/app/src/main/res/drawable/ic_search_add.xml index 449115e3a..820af18c1 100644 --- a/app/src/main/res/drawable/ic_search_add.xml +++ b/app/src/main/res/drawable/ic_search_add.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19,17.59L17.59,19L7,8.41V15H5V5H15V7H8.41L19,17.59Z"/> diff --git a/app/src/main/res/drawable/ic_select_all.xml b/app/src/main/res/drawable/ic_select_all.xml index 19c050773..d7687d247 100644 --- a/app/src/main/res/drawable/ic_select_all.xml +++ b/app/src/main/res/drawable/ic_select_all.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M3,5h2L5,3c-1.1,0 -2,0.9 -2,2zM3,13h2v-2L3,11v2zM7,21h2v-2L7,19v2zM3,9h2L5,7L3,7v2zM13,3h-2v2h2L13,3zM19,3v2h2c0,-1.1 -0.9,-2 -2,-2zM5,21v-2L3,19c0,1.1 0.9,2 2,2zM3,17h2v-2L3,15v2zM9,3L7,3v2h2L9,3zM11,21h2v-2h-2v2zM19,13h2v-2h-2v2zM19,21c1.1,0 2,-0.9 2,-2h-2v2zM19,9h2L21,7h-2v2zM19,17h2v-2h-2v2zM15,21h2v-2h-2v2zM15,5h2L17,3h-2v2zM7,17h10L17,7L7,7v10zM9,9h6v6L9,15L9,9z"/> diff --git a/app/src/main/res/drawable/ic_settings.xml b/app/src/main/res/drawable/ic_settings.xml index 1e259c6ad..e460d110b 100644 --- a/app/src/main/res/drawable/ic_settings.xml +++ b/app/src/main/res/drawable/ic_settings.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6s3.6,1.62 3.6,3.6S13.98,15.6 12,15.6z"/> diff --git a/app/src/main/res/drawable/ic_settings_backup_restore.xml b/app/src/main/res/drawable/ic_settings_backup_restore.xml index 580025971..ea6901328 100644 --- a/app/src/main/res/drawable/ic_settings_backup_restore.xml +++ b/app/src/main/res/drawable/ic_settings_backup_restore.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M14,12c0,-1.1 -0.9,-2 -2,-2s-2,0.9 -2,2 0.9,2 2,2 2,-0.9 2,-2zM12,3c-4.97,0 -9,4.03 -9,9L0,12l4,4 4,-4L5,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.51,0 -2.91,-0.49 -4.06,-1.3l-1.42,1.44C8.04,20.3 9.94,21 12,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9z"/> diff --git a/app/src/main/res/drawable/ic_share.xml b/app/src/main/res/drawable/ic_share.xml index 40971e408..0c177f210 100644 --- a/app/src/main/res/drawable/ic_share.xml +++ b/app/src/main/res/drawable/ic_share.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/> diff --git a/app/src/main/res/drawable/ic_shopping_cart.xml b/app/src/main/res/drawable/ic_shopping_cart.xml index 9e361b60d..565de7f82 100644 --- a/app/src/main/res/drawable/ic_shopping_cart.xml +++ b/app/src/main/res/drawable/ic_shopping_cart.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M7,18c-1.1,0 -1.99,0.9 -1.99,2S5.9,22 7,22s2,-0.9 2,-2 -0.9,-2 -2,-2zM1,2v2h2l3.6,7.59 -1.35,2.45c-0.16,0.28 -0.25,0.61 -0.25,0.96 0,1.1 0.9,2 2,2h12v-2L7.42,15c-0.14,0 -0.25,-0.11 -0.25,-0.25l0.03,-0.12 0.9,-1.63h7.45c0.75,0 1.41,-0.41 1.75,-1.03l3.58,-6.49c0.08,-0.14 0.12,-0.31 0.12,-0.48 0,-0.55 -0.45,-1 -1,-1L5.21,4l-0.94,-2L1,2zM17,18c-1.1,0 -1.99,0.9 -1.99,2s0.89,2 1.99,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/> diff --git a/app/src/main/res/drawable/ic_shuffle.xml b/app/src/main/res/drawable/ic_shuffle.xml index 86717de36..e5c7578eb 100644 --- a/app/src/main/res/drawable/ic_shuffle.xml +++ b/app/src/main/res/drawable/ic_shuffle.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M10.59,9.17L5.41,4 4,5.41l5.17,5.17 1.42,-1.41zM14.5,4l2.04,2.04L4,18.59 5.41,20 17.96,7.46 20,9.5L20,4h-5.5zM14.83,13.41l-1.41,1.41 3.13,3.13L14.5,20L20,20v-5.5l-2.04,2.04 -3.13,-3.13z"/> diff --git a/app/src/main/res/drawable/ic_smart_display.xml b/app/src/main/res/drawable/ic_smart_display.xml index d666a3b37..c601c0b42 100644 --- a/app/src/main/res/drawable/ic_smart_display.xml +++ b/app/src/main/res/drawable/ic_smart_display.xml @@ -1,10 +1,10 @@ + android:height="24dp" + android:width="24dp" + android:viewportHeight="24" + android:viewportWidth="24" + android:tint="@color/defaultIconTint"> + android:pathData="M20 4H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zM9.5 16.5v-9l7 4.5-7 4.5z" + android:fillColor="#FF000000"/> diff --git a/app/src/main/res/drawable/ic_sort.xml b/app/src/main/res/drawable/ic_sort.xml index a97bebd87..2c93dc2ff 100644 --- a/app/src/main/res/drawable/ic_sort.xml +++ b/app/src/main/res/drawable/ic_sort.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M3,18h6v-2L3,16v2zM3,6v2h18L21,6L3,6zM3,13h12v-2L3,11v2z"/> diff --git a/app/src/main/res/drawable/ic_stars.xml b/app/src/main/res/drawable/ic_stars.xml index ac5b9dd19..ace080db9 100644 --- a/app/src/main/res/drawable/ic_stars.xml +++ b/app/src/main/res/drawable/ic_stars.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM16.23,18L12,15.45 7.77,18l1.12,-4.81 -3.73,-3.23 4.92,-0.42L12,5l1.92,4.53 4.92,0.42 -3.73,3.23L16.23,18z"/> diff --git a/app/src/main/res/drawable/ic_subscriptions.xml b/app/src/main/res/drawable/ic_subscriptions.xml index f2ac7bec2..8fe0e107e 100644 --- a/app/src/main/res/drawable/ic_subscriptions.xml +++ b/app/src/main/res/drawable/ic_subscriptions.xml @@ -1,10 +1,10 @@ + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportHeight="24" + android:viewportWidth="24" + android:width="24dp"> + android:fillColor="@android:color/white" + android:pathData="M20,8L4,8L4,6h16v2zM18,2L6,2v2h12L18,2zM22,12v8c0,1.1 -0.9,2 -2,2L4,22c-1.1,0 -2,-0.9 -2,-2v-8c0,-1.1 0.9,-2 2,-2h16c1.1,0 2,0.9 2,2zM16,16l-6,-3.27v6.53L16,16z"/> diff --git a/app/src/main/res/drawable/ic_subtitles.xml b/app/src/main/res/drawable/ic_subtitles.xml index 43bf3e16b..fdc66f3a6 100644 --- a/app/src/main/res/drawable/ic_subtitles.xml +++ b/app/src/main/res/drawable/ic_subtitles.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM4,12h4v2L4,14v-2zM14,18L4,18v-2h10v2zM20,18h-4v-2h4v2zM20,14L10,14v-2h10v2z"/> diff --git a/app/src/main/res/drawable/ic_telescope.xml b/app/src/main/res/drawable/ic_telescope.xml index e3d5ea33b..5d3761381 100644 --- a/app/src/main/res/drawable/ic_telescope.xml +++ b/app/src/main/res/drawable/ic_telescope.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M21.9,8.9L20.2,9.9L16.2,3L17.9,2L21.9,8.9M9.8,7.9L12.8,13.1L18.9,9.6L15.9,4.4L9.8,7.9M11.4,12.7L9.4,9.2L5.1,11.7L7.1,15.2L11.4,12.7M2.1,14.6L3.1,16.3L5.7,14.8L4.7,13.1L2.1,14.6M12.1,14L11.8,13.6L7.5,16.1L7.8,16.5C8,16.8 8.3,17.1 8.6,17.3L7,22H9L10.4,17.7H10.5L12,22H14L12.1,16.4C12.6,15.7 12.6,14.8 12.1,14Z"/> diff --git a/app/src/main/res/drawable/ic_thumb_down.xml b/app/src/main/res/drawable/ic_thumb_down.xml index aa828aa50..e5d27134b 100644 --- a/app/src/main/res/drawable/ic_thumb_down.xml +++ b/app/src/main/res/drawable/ic_thumb_down.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M15,3L6,3c-0.83,0 -1.54,0.5 -1.84,1.22l-3.02,7.05c-0.09,0.23 -0.14,0.47 -0.14,0.73v2c0,1.1 0.9,2 2,2h6.31l-0.95,4.57 -0.03,0.32c0,0.41 0.17,0.79 0.44,1.06L9.83,23l6.59,-6.59c0.36,-0.36 0.58,-0.86 0.58,-1.41L17,5c0,-1.1 -0.9,-2 -2,-2zM19,3v12h4L23,3h-4z"/> diff --git a/app/src/main/res/drawable/ic_thumb_up.xml b/app/src/main/res/drawable/ic_thumb_up.xml index 65d7f78ce..876b6bbc6 100644 --- a/app/src/main/res/drawable/ic_thumb_up.xml +++ b/app/src/main/res/drawable/ic_thumb_up.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M1,21h4L5,9L1,9v12zM23,10c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-2z"/> diff --git a/app/src/main/res/drawable/ic_trending_up.xml b/app/src/main/res/drawable/ic_trending_up.xml index e7fd8e4ae..d37f9abd3 100644 --- a/app/src/main/res/drawable/ic_trending_up.xml +++ b/app/src/main/res/drawable/ic_trending_up.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M16,6l2.29,2.29 -4.88,4.88 -4,-4L2,16.59 3.41,18l6,-6 4,4 6.3,-6.29L22,12V6z"/> diff --git a/app/src/main/res/drawable/ic_tv.xml b/app/src/main/res/drawable/ic_tv.xml index 91d860eaf..833e864f1 100644 --- a/app/src/main/res/drawable/ic_tv.xml +++ b/app/src/main/res/drawable/ic_tv.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M21,6h-7.59l3.29,-3.29L16,2l-4,4 -4,-4 -0.71,0.71L10.59,6L3,6c-1.1,0 -2,0.89 -2,2v12c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.11 -0.9,-2 -2,-2zM21,20L3,20L3,8h18v12z"/> diff --git a/app/src/main/res/drawable/ic_videogame_asset.xml b/app/src/main/res/drawable/ic_videogame_asset.xml index 01a91b053..afad3977b 100644 --- a/app/src/main/res/drawable/ic_videogame_asset.xml +++ b/app/src/main/res/drawable/ic_videogame_asset.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M21,6L3,6c-1.1,0 -2,0.9 -2,2v8c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,8c0,-1.1 -0.9,-2 -2,-2zM11,13L8,13v3L6,16v-3L3,13v-2h3L6,8h2v3h3v2zM15.5,15c-0.83,0 -1.5,-0.67 -1.5,-1.5s0.67,-1.5 1.5,-1.5 1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5zM19.5,12c-0.83,0 -1.5,-0.67 -1.5,-1.5S18.67,9 19.5,9s1.5,0.67 1.5,1.5 -0.67,1.5 -1.5,1.5z"/> diff --git a/app/src/main/res/drawable/ic_visibility_off.xml b/app/src/main/res/drawable/ic_visibility_off.xml index f833d5e06..74af53fa6 100644 --- a/app/src/main/res/drawable/ic_visibility_off.xml +++ b/app/src/main/res/drawable/ic_visibility_off.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,7c2.76,0 5,2.24 5,5 0,0.65 -0.13,1.26 -0.36,1.83l2.92,2.92c1.51,-1.26 2.7,-2.89 3.43,-4.75 -1.73,-4.39 -6,-7.5 -11,-7.5 -1.4,0 -2.74,0.25 -3.98,0.7l2.16,2.16C10.74,7.13 11.35,7 12,7zM2,4.27l2.28,2.28 0.46,0.46C3.08,8.3 1.78,10.02 1,12c1.73,4.39 6,7.5 11,7.5 1.55,0 3.03,-0.3 4.38,-0.84l0.42,0.42L19.73,22 21,20.73 3.27,3 2,4.27zM7.53,9.8l1.55,1.55c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.66 1.34,3 3,3 0.22,0 0.44,-0.03 0.65,-0.08l1.55,1.55c-0.67,0.33 -1.41,0.53 -2.2,0.53 -2.76,0 -5,-2.24 -5,-5 0,-0.79 0.2,-1.53 0.53,-2.2zM11.84,9.02l3.15,3.15 0.02,-0.16c0,-1.66 -1.34,-3 -3,-3l-0.17,0.01z"/> diff --git a/app/src/main/res/drawable/ic_visibility_on.xml b/app/src/main/res/drawable/ic_visibility_on.xml index 06e530961..9687cb1a2 100644 --- a/app/src/main/res/drawable/ic_visibility_on.xml +++ b/app/src/main/res/drawable/ic_visibility_on.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/> diff --git a/app/src/main/res/drawable/ic_volume_down.xml b/app/src/main/res/drawable/ic_volume_down.xml index 0fe36fad3..8a50d0e8b 100644 --- a/app/src/main/res/drawable/ic_volume_down.xml +++ b/app/src/main/res/drawable/ic_volume_down.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M18.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM5,9v6h4l5,5V4L9,9H5z"/> diff --git a/app/src/main/res/drawable/ic_volume_mute.xml b/app/src/main/res/drawable/ic_volume_mute.xml index b18f6337c..fb61719db 100644 --- a/app/src/main/res/drawable/ic_volume_mute.xml +++ b/app/src/main/res/drawable/ic_volume_mute.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M7,9v6h4l5,5V4l-5,5H7z"/> diff --git a/app/src/main/res/drawable/ic_volume_off.xml b/app/src/main/res/drawable/ic_volume_off.xml index 420593e04..a78a4f66b 100644 --- a/app/src/main/res/drawable/ic_volume_off.xml +++ b/app/src/main/res/drawable/ic_volume_off.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v2.21l2.45,2.45c0.03,-0.2 0.05,-0.41 0.05,-0.63zM19,12c0,0.94 -0.2,1.82 -0.54,2.64l1.51,1.51C20.63,14.91 21,13.5 21,12c0,-4.28 -2.99,-7.86 -7,-8.77v2.06c2.89,0.86 5,3.54 5,6.71zM4.27,3L3,4.27 7.73,9L3,9v6h4l5,5v-6.73l4.25,4.25c-0.67,0.52 -1.42,0.93 -2.25,1.18v2.06c1.38,-0.31 2.63,-0.95 3.69,-1.81L19.73,21 21,19.73l-9,-9L4.27,3zM12,4L9.91,6.09 12,8.18L12,4z"/> diff --git a/app/src/main/res/drawable/ic_volume_up.xml b/app/src/main/res/drawable/ic_volume_up.xml index b5a47789b..e1b08d4dc 100644 --- a/app/src/main/res/drawable/ic_volume_up.xml +++ b/app/src/main/res/drawable/ic_volume_up.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M3,9v6h4l5,5L12,4L7,9L3,9zM16.5,12c0,-1.77 -1.02,-3.29 -2.5,-4.03v8.05c1.48,-0.73 2.5,-2.25 2.5,-4.02zM14,3.23v2.06c2.89,0.86 5,3.54 5,6.71s-2.11,5.85 -5,6.71v2.06c4.01,-0.91 7,-4.49 7,-8.77s-2.99,-7.86 -7,-8.77z"/> diff --git a/app/src/main/res/drawable/ic_watch_later.xml b/app/src/main/res/drawable/ic_watch_later.xml index 34ecad214..74b9bd1e9 100644 --- a/app/src/main/res/drawable/ic_watch_later.xml +++ b/app/src/main/res/drawable/ic_watch_later.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M12,2C6.5,2 2,6.5 2,12s4.5,10 10,10s10,-4.5 10,-10S17.5,2 12,2zM16.2,16.2L11,13V7h1.5v5.2l4.5,2.7L16.2,16.2z"/> diff --git a/app/src/main/res/drawable/ic_wb_sunny.xml b/app/src/main/res/drawable/ic_wb_sunny.xml index 922cf72e0..ad4892e3f 100644 --- a/app/src/main/res/drawable/ic_wb_sunny.xml +++ b/app/src/main/res/drawable/ic_wb_sunny.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M6.76,4.84l-1.8,-1.79 -1.41,1.41 1.79,1.79 1.42,-1.41zM4,10.5L1,10.5v2h3v-2zM13,0.55h-2L11,3.5h2L13,0.55zM20.45,4.46l-1.41,-1.41 -1.79,1.79 1.41,1.41 1.79,-1.79zM17.24,18.16l1.79,1.8 1.41,-1.41 -1.8,-1.79 -1.4,1.4zM20,10.5v2h3v-2h-3zM12,5.5c-3.31,0 -6,2.69 -6,6s2.69,6 6,6 6,-2.69 6,-6 -2.69,-6 -6,-6zM11,22.45h2L13,19.5h-2v2.95zM3.55,18.54l1.41,1.41 1.79,-1.8 -1.41,-1.41 -1.79,1.8z"/> diff --git a/app/src/main/res/drawable/ic_whatshot.xml b/app/src/main/res/drawable/ic_whatshot.xml index 84260ffe4..3769503a9 100644 --- a/app/src/main/res/drawable/ic_whatshot.xml +++ b/app/src/main/res/drawable/ic_whatshot.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M13.5,0.67s0.74,2.65 0.74,4.8c0,2.06 -1.35,3.73 -3.41,3.73 -2.07,0 -3.63,-1.67 -3.63,-3.73l0.03,-0.36C5.21,7.51 4,10.62 4,14c0,4.42 3.58,8 8,8s8,-3.58 8,-8C20,8.61 17.41,3.8 13.5,0.67zM11.71,19c-1.78,0 -3.22,-1.4 -3.22,-3.14 0,-1.62 1.05,-2.76 2.81,-3.12 1.77,-0.36 3.6,-1.21 4.62,-2.58 0.39,1.29 0.59,2.65 0.59,4.04 0,2.65 -2.15,4.8 -4.8,4.8z"/> diff --git a/app/src/main/res/drawable/ic_work.xml b/app/src/main/res/drawable/ic_work.xml index 014718e60..a4b4acb89 100644 --- a/app/src/main/res/drawable/ic_work.xml +++ b/app/src/main/res/drawable/ic_work.xml @@ -1,10 +1,10 @@ + android:width="24dp" + android:height="24dp" + android:tint="@color/defaultIconTint" + android:viewportWidth="24" + android:viewportHeight="24"> + android:fillColor="#FF000000" + android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM14,6h-4L10,4h4v2z"/> diff --git a/app/src/main/res/drawable/not_available_monkey.xml b/app/src/main/res/drawable/not_available_monkey.xml index b15a381c5..ae08264f3 100644 --- a/app/src/main/res/drawable/not_available_monkey.xml +++ b/app/src/main/res/drawable/not_available_monkey.xml @@ -1,27 +1,27 @@ + android:width="2608dp" + android:height="1469dp" + android:tint="?attr/colorAccent" + android:viewportWidth="2608" + android:viewportHeight="1469"> + android:pathData="M1495.4,309.4c-12.7,3.1 -29.4,14 -39.4,25.7 -41.7,49 -61.3,150.9 -47.4,246.9 10.5,72.3 39,127 74.9,143.5 27.6,12.8 56.5,4.5 79.5,-22.7 57.5,-68.1 68.2,-234.7 21.4,-331.8 -16.8,-34.8 -37.4,-55.3 -61.9,-61.5 -7,-1.8 -20,-1.8 -27.1,-0.1z" + android:fillColor="#000000" + android:strokeColor="#00000000"/> + android:pathData="M1112.6,357.6c-45.9,11.1 -80.9,78.7 -89.6,172.9 -1.6,17.4 -1.4,60.1 0.4,78.6 7,71.1 30.1,129 61.8,154.5 13.9,11.3 26,15.9 41.3,15.7 18.6,-0.1 33.5,-7.3 49.3,-23.8 51.8,-53.8 70.8,-187.9 41.7,-293.8 -18.1,-66.3 -52.6,-106.1 -91.6,-105.6 -4.1,-0 -10,0.7 -13.3,1.5z" + android:fillColor="#000000" + android:strokeColor="#00000000"/> + android:pathData="M1339.05,750c-3.8,4.5 -5.2,9.6 -5.1,19.1 0,7 0.5,9.4 2.5,13.5 4.5,9.1 10.2,9.5 15,1 2.1,-3.6 2.4,-5.3 2.4,-15.1 0,-9.5 -0.3,-11.6 -2.3,-15.2 -3.7,-7 -8.4,-8.2 -12.5,-3.3z" + android:fillColor="#000000" + android:strokeColor="#00000000"/> + android:pathData="M1296.95,756c-3.2,3.9 -4.7,9.5 -4.8,17.5 0,7.8 0.8,12 3.5,16.8 4,7.1 10.3,6.4 14.2,-1.7 4.6,-9.5 3.5,-25.8 -2.1,-32.6 -3.5,-4 -7.3,-4 -10.8,-0z" + android:fillColor="#000000" + android:strokeColor="#00000000"/> + android:pathData="M1304.5,840.6c-34,3.6 -54.9,7.4 -80.5,14.8 -90.8,26.1 -187.2,87.1 -296,187.4 -15.3,14.1 -36,34.3 -36,35.1 0,0.4 3.3,4 7.4,8l7.5,7.4 4.3,-3.8c2.4,-2.1 11.5,-10.7 20.3,-19.1 112,-106.4 215,-171.7 310,-196.5 16.2,-4.2 37.1,-8 55.5,-10 14.3,-1.6 52,-1.6 66,-0 91.9,10.4 176.9,53.2 273.5,137.7 17.4,15.2 65.3,62.3 85.6,84.2l11.4,12.2 7.8,-7.1c4.3,-3.9 7.7,-7.7 7.5,-8.4 -0.5,-2.1 -34.4,-37.9 -56.4,-59.5 -109.9,-107.8 -212,-166 -316.4,-180.2 -14,-1.8 -60.8,-3.3 -71.5,-2.2z" + android:fillColor="#000000" + android:strokeColor="#00000000"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/placeholder_person.xml b/app/src/main/res/drawable/placeholder_person.xml index 2b3229e8f..f37cdf330 100644 --- a/app/src/main/res/drawable/placeholder_person.xml +++ b/app/src/main/res/drawable/placeholder_person.xml @@ -1,20 +1,20 @@ + android:width="24dp" + android:height="24dp" + android:viewportWidth="24" + android:viewportHeight="24"> - - - - + android:pathData="M0,0 L24,0 L24,24 L0,24 z" + android:fillColor="@color/placeholder_background"/> + + + + diff --git a/app/src/main/res/drawable/placeholder_thumbnail_playlist.xml b/app/src/main/res/drawable/placeholder_thumbnail_playlist.xml index de286d860..84b11c51b 100644 --- a/app/src/main/res/drawable/placeholder_thumbnail_playlist.xml +++ b/app/src/main/res/drawable/placeholder_thumbnail_playlist.xml @@ -1,15 +1,15 @@ - - - + android:width="16dp" + android:height="9dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + + diff --git a/app/src/main/res/drawable/placeholder_thumbnail_video.xml b/app/src/main/res/drawable/placeholder_thumbnail_video.xml index 0b262f923..1d1faa6bc 100644 --- a/app/src/main/res/drawable/placeholder_thumbnail_video.xml +++ b/app/src/main/res/drawable/placeholder_thumbnail_video.xml @@ -1,12 +1,12 @@ - - + android:width="16dp" + android:height="9dp" + android:viewportWidth="24" + android:viewportHeight="24"> + + diff --git a/app/src/main/res/drawable/player_controls_background.xml b/app/src/main/res/drawable/player_controls_background.xml index cd25f2b04..fabb3e9a9 100644 --- a/app/src/main/res/drawable/player_controls_background.xml +++ b/app/src/main/res/drawable/player_controls_background.xml @@ -1,7 +1,7 @@ + android:angle="90" + android:endColor="#00000000" + android:startColor="@color/video_overlay_color"/> diff --git a/app/src/main/res/drawable/player_controls_top_background.xml b/app/src/main/res/drawable/player_controls_top_background.xml index 92f9fddca..eb226a9d3 100644 --- a/app/src/main/res/drawable/player_controls_top_background.xml +++ b/app/src/main/res/drawable/player_controls_top_background.xml @@ -1,7 +1,7 @@ + android:angle="-90" + android:endColor="#00000000" + android:startColor="@color/video_overlay_color"/> diff --git a/app/src/main/res/drawable/progress_circular_white.xml b/app/src/main/res/drawable/progress_circular_white.xml index 79e6f54a6..ab97cd7b1 100644 --- a/app/src/main/res/drawable/progress_circular_white.xml +++ b/app/src/main/res/drawable/progress_circular_white.xml @@ -1,12 +1,12 @@ + android:fromDegrees="-90" + android:toDegrees="-90"> - + android:innerRadiusRatio="2.25" + android:shape="ring" + android:thicknessRatio="17.75" + android:useLevel="true"> + diff --git a/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml b/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml index 54a850125..3f938096d 100644 --- a/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml +++ b/app/src/main/res/drawable/progress_soundcloud_horizontal_dark.xml @@ -2,13 +2,13 @@ - + - + diff --git a/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml b/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml index 3fb6651fa..bf83987ab 100644 --- a/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml +++ b/app/src/main/res/drawable/progress_soundcloud_horizontal_light.xml @@ -2,13 +2,13 @@ - + - + diff --git a/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml b/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml index 4815aec7c..7609b5ed2 100644 --- a/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml +++ b/app/src/main/res/drawable/progress_youtube_horizontal_dark.xml @@ -2,13 +2,13 @@ - + - + diff --git a/app/src/main/res/drawable/progress_youtube_horizontal_light.xml b/app/src/main/res/drawable/progress_youtube_horizontal_light.xml index 4c85370d5..6d6060698 100644 --- a/app/src/main/res/drawable/progress_youtube_horizontal_light.xml +++ b/app/src/main/res/drawable/progress_youtube_horizontal_light.xml @@ -2,13 +2,13 @@ - + - + diff --git a/app/src/main/res/drawable/selector_checked_dark.xml b/app/src/main/res/drawable/selector_checked_dark.xml index da05e96c6..d1d1e3ff9 100644 --- a/app/src/main/res/drawable/selector_checked_dark.xml +++ b/app/src/main/res/drawable/selector_checked_dark.xml @@ -1,7 +1,8 @@ - - - - - + + + + + diff --git a/app/src/main/res/drawable/selector_checked_light.xml b/app/src/main/res/drawable/selector_checked_light.xml index e64b8d083..12dd2c473 100644 --- a/app/src/main/res/drawable/selector_checked_light.xml +++ b/app/src/main/res/drawable/selector_checked_light.xml @@ -1,7 +1,8 @@ - - - - - + + + + + diff --git a/app/src/main/res/drawable/selector_dark.xml b/app/src/main/res/drawable/selector_dark.xml index 0c79be292..a220bdc97 100644 --- a/app/src/main/res/drawable/selector_dark.xml +++ b/app/src/main/res/drawable/selector_dark.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/selector_focused_dark.xml b/app/src/main/res/drawable/selector_focused_dark.xml index 508083fcd..7c07044bf 100644 --- a/app/src/main/res/drawable/selector_focused_dark.xml +++ b/app/src/main/res/drawable/selector_focused_dark.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/selector_focused_light.xml b/app/src/main/res/drawable/selector_focused_light.xml index 508083fcd..7c07044bf 100644 --- a/app/src/main/res/drawable/selector_focused_light.xml +++ b/app/src/main/res/drawable/selector_focused_light.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/selector_light.xml b/app/src/main/res/drawable/selector_light.xml index 0c79be292..a220bdc97 100644 --- a/app/src/main/res/drawable/selector_light.xml +++ b/app/src/main/res/drawable/selector_light.xml @@ -1,5 +1,5 @@ - - + + diff --git a/app/src/main/res/drawable/splash_background.xml b/app/src/main/res/drawable/splash_background.xml index c9b018add..d3f821ea9 100644 --- a/app/src/main/res/drawable/splash_background.xml +++ b/app/src/main/res/drawable/splash_background.xml @@ -2,11 +2,11 @@ + android:drawable="@color/light_youtube_primary_color"/> + android:src="@drawable/ic_newpipe_triangle_white" + android:gravity="center_vertical|center_horizontal"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/splash_foreground.xml b/app/src/main/res/drawable/splash_foreground.xml index 63fd0351f..a5bcfc499 100644 --- a/app/src/main/res/drawable/splash_foreground.xml +++ b/app/src/main/res/drawable/splash_foreground.xml @@ -1,10 +1,10 @@ + android:width="100dp" + android:height="100dp" + android:viewportWidth="100" + android:viewportHeight="100"> + android:fillColor="#ffffff" + android:pathData="m23.909,10.211v78.869c0,0 7.7,-4.556 12.4,-7.337V67.477,56.739 31.686c0,0 3.707,2.173 8.948,5.24 6.263,3.579 14.57,8.565 21.473,12.655 -9.358,5.483 -16.8,9.876 -22.496,13.234V77.053C57.974,68.927 75.176,58.762 90.762,49.581 75.551,40.634 57.144,29.768 43.467,21.715 31.963,14.94 23.909,10.211 23.909,10.211Z" + android:strokeWidth="1.2782383"/> diff --git a/app/src/main/res/drawable/toolbar_shadow_dark.xml b/app/src/main/res/drawable/toolbar_shadow_dark.xml index b9419029f..dd9cf14f5 100644 --- a/app/src/main/res/drawable/toolbar_shadow_dark.xml +++ b/app/src/main/res/drawable/toolbar_shadow_dark.xml @@ -1,7 +1,7 @@ + android:angle="-90" + android:endColor="#00000000" + android:startColor="@color/dark_shadow_start_color"/> diff --git a/app/src/main/res/drawable/toolbar_shadow_light.xml b/app/src/main/res/drawable/toolbar_shadow_light.xml index 6546bdd20..dfb02409a 100644 --- a/app/src/main/res/drawable/toolbar_shadow_light.xml +++ b/app/src/main/res/drawable/toolbar_shadow_light.xml @@ -1,7 +1,7 @@ + android:angle="-90" + android:endColor="#00000000" + android:startColor="@color/light_shadow_start_color"/> diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 661c4affc..c099d382e 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -1,39 +1,39 @@ + xmlns:app="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fitsSystemWindows="true" + tools:context="org.schabi.newpipe.about.AboutActivity"> - - - - + android:paddingTop="@dimen/appbar_padding_top" + android:theme="@style/ThemeOverlay.AppCompat.DayNight.ActionBar" + app:popupTheme="@style/ThemeOverlay.AppCompat.DayNight.ActionBar"> + + + + + android:id="@+id/about_viewPager2" + android:layout_width="match_parent" + android:layout_height="wrap_content" + app:layout_behavior="@string/appbar_scrolling_view_behavior"/> diff --git a/app/src/main/res/layout/activity_downloader.xml b/app/src/main/res/layout/activity_downloader.xml index e3b56e282..ffb796a64 100644 --- a/app/src/main/res/layout/activity_downloader.xml +++ b/app/src/main/res/layout/activity_downloader.xml @@ -1,15 +1,15 @@ + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> + layout="@layout/toolbar_layout" + android:id="@+id/toolbar_layout"/> + android:id="@+id/frame" + android:layout_width="match_parent" + android:layout_height="match_parent"/> diff --git a/app/src/main/res/layout/activity_error.xml b/app/src/main/res/layout/activity_error.xml index 45101c1a1..78e01ab79 100644 --- a/app/src/main/res/layout/activity_error.xml +++ b/app/src/main/res/layout/activity_error.xml @@ -1,145 +1,145 @@ + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context=".error.ErrorActivity"> + layout="@layout/toolbar_layout" + android:id="@+id/toolbar_layout"/> + android:id="@+id/scrollView" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginTop="?attr/actionBarSize"> - - - - + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingBottom="@dimen/activity_vertical_margin"> + android:id="@+id/errorSorryView" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:gravity="center" + android:text="@string/sorry_string" + android:textAppearance="?android:attr/textAppearanceLarge" + android:textStyle="bold"/> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="@dimen/activity_vertical_margin" + android:text="@string/what_happened_headline" + android:textAppearance="?android:attr/textAppearanceMedium"/> - - - + android:textColor="?attr/colorAccent"/> - + + + android:orientation="horizontal"> + + + + + android:id="@+id/errorInfosView" + android:layout_width="wrap_content" + android:layout_height="wrap_content"/> - - - - + android:paddingTop="@dimen/activity_vertical_margin" + android:text="@string/error_details_headline" + android:textAppearance="?android:attr/textAppearanceMedium"/> + + + + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingTop="@dimen/activity_vertical_margin" + android:text="@string/your_comment" + android:textAppearance="?android:attr/textAppearanceMedium"/> + android:id="@+id/errorCommentBox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:inputType=""/>