Repository: yuliskov/SmartTube Branch: master Commit: ce402b5cf741 Files: 2868 Total size: 30.3 MB Directory structure: gitextract_iykr0_hr/ ├── .github/ │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── 1-bug_report.yml │ │ ├── 2-feature-request.yml │ │ └── config.yml │ └── workflows/ │ ├── CI.yml │ ├── cleanup.yml │ ├── stale.yml │ └── virustotal_scan.yml ├── .gitignore ├── .gitmodules ├── .reuse/ │ └── dep5 ├── LICENSE ├── README.md ├── build.gradle ├── chatkit/ │ ├── .gitignore │ ├── LICENSE │ ├── build.gradle │ ├── proguard.txt │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── stfalcon/ │ │ └── chatkit/ │ │ ├── commons/ │ │ │ ├── DebouncedOnClickListener.java │ │ │ ├── ImageLoader.java │ │ │ ├── InputTrackingRecyclerViewAdapter.java │ │ │ ├── Style.java │ │ │ ├── ViewHolder.java │ │ │ ├── models/ │ │ │ │ ├── IDialog.java │ │ │ │ ├── IMessage.java │ │ │ │ ├── IUser.java │ │ │ │ └── MessageContentType.java │ │ │ └── widgets/ │ │ │ ├── FocusFixRelativeLayout.java │ │ │ └── WrapWidthTextView.java │ │ ├── dialogs/ │ │ │ ├── DialogListStyle.java │ │ │ ├── DialogsList.java │ │ │ └── DialogsListAdapter.java │ │ ├── messages/ │ │ │ ├── MessageHolders.java │ │ │ ├── MessageInput.java │ │ │ ├── MessageInputStyle.java │ │ │ ├── MessagesList.java │ │ │ ├── MessagesListAdapter.java │ │ │ ├── MessagesListStyle.java │ │ │ └── RecyclerScrollMoreListener.java │ │ └── utils/ │ │ ├── DateFormatter.java │ │ ├── RoundedImageView.java │ │ └── ShapeImageView.java │ └── res/ │ ├── color/ │ │ └── textchange.xml │ ├── drawable/ │ │ ├── bgchange.xml │ │ ├── bubble_circle.xml │ │ ├── shape_incoming_message.xml │ │ ├── shape_incoming_message_focused.xml │ │ └── shape_outcoming_message.xml │ ├── layout/ │ │ ├── item_date_header.xml │ │ ├── item_dialog.xml │ │ ├── item_incoming_image_message.xml │ │ ├── item_incoming_text_message.xml │ │ ├── item_outcoming_image_message.xml │ │ ├── item_outcoming_text_message.xml │ │ └── view_message_input.xml │ ├── values/ │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── fonts.xml │ │ ├── ids.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── values-v21/ │ └── fonts.xml ├── common/ │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src/ │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── liskovsoft/ │ │ │ └── smartyoutubetv2/ │ │ │ └── common/ │ │ │ ├── app/ │ │ │ │ ├── models/ │ │ │ │ │ ├── data/ │ │ │ │ │ │ ├── BrowseSection.java │ │ │ │ │ │ ├── Playlist.java │ │ │ │ │ │ ├── SettingsGroup.java │ │ │ │ │ │ ├── SettingsItem.java │ │ │ │ │ │ ├── SimpleMediaItem.java │ │ │ │ │ │ ├── Video.java │ │ │ │ │ │ └── VideoGroup.java │ │ │ │ │ ├── errors/ │ │ │ │ │ │ ├── CategoryEmptyError.java │ │ │ │ │ │ ├── ErrorFragmentData.java │ │ │ │ │ │ ├── PasswordError.java │ │ │ │ │ │ └── SignInError.java │ │ │ │ │ ├── playback/ │ │ │ │ │ │ ├── BasePlayerController.java │ │ │ │ │ │ ├── controllers/ │ │ │ │ │ │ │ ├── AutoFrameRateController.java │ │ │ │ │ │ │ ├── ChatController.java │ │ │ │ │ │ │ ├── CommentsController.java │ │ │ │ │ │ │ ├── HQDialogController.java │ │ │ │ │ │ │ ├── PlayerUIController.java │ │ │ │ │ │ │ ├── RemoteController.java │ │ │ │ │ │ │ ├── SponsorBlockController.java │ │ │ │ │ │ │ ├── SuggestionsController.java │ │ │ │ │ │ │ ├── VideoLoaderController.java │ │ │ │ │ │ │ └── VideoStateController.java │ │ │ │ │ │ ├── listener/ │ │ │ │ │ │ │ ├── PlayerEngineEventListener.java │ │ │ │ │ │ │ ├── PlayerEventListener.java │ │ │ │ │ │ │ ├── PlayerUiEventListener.java │ │ │ │ │ │ │ └── ViewEventListener.java │ │ │ │ │ │ ├── manager/ │ │ │ │ │ │ │ ├── PlayerConstants.java │ │ │ │ │ │ │ ├── PlayerEngine.java │ │ │ │ │ │ │ ├── PlayerManager.java │ │ │ │ │ │ │ └── PlayerUI.java │ │ │ │ │ │ ├── service/ │ │ │ │ │ │ │ └── VideoStateService.java │ │ │ │ │ │ └── ui/ │ │ │ │ │ │ ├── AbstractCommentsReceiver.java │ │ │ │ │ │ ├── ChatReceiver.java │ │ │ │ │ │ ├── ChatReceiverImpl.java │ │ │ │ │ │ ├── CommentsReceiver.java │ │ │ │ │ │ ├── OptionCallback.java │ │ │ │ │ │ ├── OptionCategory.java │ │ │ │ │ │ ├── OptionItem.java │ │ │ │ │ │ ├── SeekBarSegment.java │ │ │ │ │ │ └── UiOptionItem.java │ │ │ │ │ └── search/ │ │ │ │ │ ├── MediaServiceSearchTagProvider.java │ │ │ │ │ ├── PrefsSearchTagsProvider.java │ │ │ │ │ ├── SearchTagsProvider.java │ │ │ │ │ └── vineyard/ │ │ │ │ │ ├── Option.java │ │ │ │ │ ├── Tag.java │ │ │ │ │ └── User.java │ │ │ │ ├── presenters/ │ │ │ │ │ ├── AddDevicePresenter.java │ │ │ │ │ ├── AppDialogPresenter.java │ │ │ │ │ ├── BrowsePresenter.java │ │ │ │ │ ├── ChannelPresenter.java │ │ │ │ │ ├── ChannelUploadsPresenter.java │ │ │ │ │ ├── DetailsPresenter.java │ │ │ │ │ ├── GoogleSignInPresenter.java │ │ │ │ │ ├── PlaybackPresenter.java │ │ │ │ │ ├── SearchPresenter.java │ │ │ │ │ ├── SignInPresenter.java │ │ │ │ │ ├── SplashPresenter.java │ │ │ │ │ ├── WebBrowserPresenter.java │ │ │ │ │ ├── YTSignInPresenter.java │ │ │ │ │ ├── base/ │ │ │ │ │ │ └── BasePresenter.java │ │ │ │ │ ├── dialogs/ │ │ │ │ │ │ ├── ATVBridgePresenter.java │ │ │ │ │ │ ├── AccountSelectionPresenter.java │ │ │ │ │ │ ├── AmazonBridgePresenter.java │ │ │ │ │ │ ├── AppUpdatePresenter.java │ │ │ │ │ │ ├── BootDialogPresenter.java │ │ │ │ │ │ ├── BridgePresenter.java │ │ │ │ │ │ ├── QuickRestorePresenter.java │ │ │ │ │ │ ├── StableRestorePresenter.java │ │ │ │ │ │ ├── VideoActionPresenter.java │ │ │ │ │ │ └── menu/ │ │ │ │ │ │ ├── BaseMenuPresenter.java │ │ │ │ │ │ ├── ChannelUploadsMenuPresenter.java │ │ │ │ │ │ ├── SectionMenuPresenter.java │ │ │ │ │ │ ├── VideoMenuPresenter.java │ │ │ │ │ │ └── providers/ │ │ │ │ │ │ ├── ContextMenuManager.java │ │ │ │ │ │ ├── ContextMenuProvider.java │ │ │ │ │ │ └── channelgroup/ │ │ │ │ │ │ ├── ChannelGroupMenuProvider.java │ │ │ │ │ │ ├── ChannelGroupServiceWrapper.java │ │ │ │ │ │ ├── RemoveGroupMenuProvider.java │ │ │ │ │ │ └── RenameGroupMenuProvider.java │ │ │ │ │ ├── interfaces/ │ │ │ │ │ │ ├── Presenter.java │ │ │ │ │ │ ├── SectionPresenter.java │ │ │ │ │ │ └── VideoGroupPresenter.java │ │ │ │ │ ├── service/ │ │ │ │ │ │ └── SidebarService.java │ │ │ │ │ └── settings/ │ │ │ │ │ ├── AboutSettingsPresenter.java │ │ │ │ │ ├── AboutSimpleSettingsPresenter.java │ │ │ │ │ ├── AccountSettingsPresenter.java │ │ │ │ │ ├── AutoFrameRateSettingsPresenter.java │ │ │ │ │ ├── BackupSettingsPresenter.java │ │ │ │ │ ├── DeArrowSettingsPresenter.java │ │ │ │ │ ├── GeneralSettingsPresenter.java │ │ │ │ │ ├── LanguageSettingsPresenter.java │ │ │ │ │ ├── MainUISettingsPresenter.java │ │ │ │ │ ├── PlayerSettingsPresenter.java │ │ │ │ │ ├── RemoteControlSettingsPresenter.java │ │ │ │ │ ├── SearchSettingsPresenter.java │ │ │ │ │ ├── SponsorBlockSettingsPresenter.java │ │ │ │ │ ├── SubtitleSettingsPresenter.java │ │ │ │ │ └── UIScaleSettingsPresenter.java │ │ │ │ └── views/ │ │ │ │ ├── AddDeviceView.java │ │ │ │ ├── AppDialogView.java │ │ │ │ ├── AppUpdateView.java │ │ │ │ ├── BrowseView.java │ │ │ │ ├── ChannelUploadsView.java │ │ │ │ ├── ChannelView.java │ │ │ │ ├── DetailsView.java │ │ │ │ ├── PlaybackView.java │ │ │ │ ├── SearchView.java │ │ │ │ ├── SignInView.java │ │ │ │ ├── SplashView.java │ │ │ │ ├── ViewManager.java │ │ │ │ └── WebBrowserView.java │ │ │ ├── autoframerate/ │ │ │ │ ├── AutoFrameRateHelper.java │ │ │ │ ├── ModeSyncManager.java │ │ │ │ └── internal/ │ │ │ │ ├── DisplayHolder.java │ │ │ │ ├── DisplaySyncHelper.java │ │ │ │ ├── UhdHelper.java │ │ │ │ └── UhdHelperListener.java │ │ │ ├── exoplayer/ │ │ │ │ ├── ExoMediaSourceFactory.java │ │ │ │ ├── LiveDashManifestParser.java │ │ │ │ ├── controller/ │ │ │ │ │ ├── ExoPlayerController.java │ │ │ │ │ └── PlayerView.java │ │ │ │ ├── errors/ │ │ │ │ │ ├── DashDefaultLoadErrorHandlingPolicy.java │ │ │ │ │ ├── SabrDefaultLoadErrorHandlingPolicy.java │ │ │ │ │ └── TrackErrorFixer.java │ │ │ │ ├── other/ │ │ │ │ │ ├── DebugInfoManager.java │ │ │ │ │ ├── ExoPlayerInitializer.java │ │ │ │ │ ├── SubtitleManager.java │ │ │ │ │ ├── VideoZoomManager.java │ │ │ │ │ └── VolumeBooster.java │ │ │ │ ├── selector/ │ │ │ │ │ ├── ExoFormatItem.java │ │ │ │ │ ├── FormatItem.java │ │ │ │ │ ├── TrackInfoFormatter2.java │ │ │ │ │ ├── TrackSelectorManager.java │ │ │ │ │ ├── TrackSelectorUtil.java │ │ │ │ │ └── track/ │ │ │ │ │ ├── AudioTrack.java │ │ │ │ │ ├── MediaTrack.java │ │ │ │ │ ├── SubtitleTrack.java │ │ │ │ │ └── VideoTrack.java │ │ │ │ └── versions/ │ │ │ │ ├── ExoUtils.java │ │ │ │ ├── renderer/ │ │ │ │ │ ├── CustomOverridesRenderersFactory.java │ │ │ │ │ ├── CustomRenderersFactoryBase.java │ │ │ │ │ ├── DebugInfoMediaCodecVideoRenderer.java │ │ │ │ │ ├── DelayMediaCodecAudioRenderer.java │ │ │ │ │ └── TweaksMediaCodecVideoRenderer.java │ │ │ │ └── selector/ │ │ │ │ ├── BlacklistMediaCodecSelector.java │ │ │ │ ├── RestoreTrackSelector.java │ │ │ │ └── backport/ │ │ │ │ └── Definition.java │ │ │ ├── misc/ │ │ │ │ ├── AppDataSourceManager.java │ │ │ │ ├── BackgroundPlaybackService.java │ │ │ │ ├── BackupAndRestoreHelper.java │ │ │ │ ├── BackupAndRestoreManager.java │ │ │ │ ├── BackupReceiverActivity.java │ │ │ │ ├── BrowseProcessor.java │ │ │ │ ├── BrowseProcessorManager.java │ │ │ │ ├── CrashRestorer.java │ │ │ │ ├── DeArrowProcessor.java │ │ │ │ ├── GDriveBackupManager.java │ │ │ │ ├── GDriveBackupManagerOld.java │ │ │ │ ├── GDriveBackupWorker.java │ │ │ │ ├── GlobalKeyTranslator.java │ │ │ │ ├── KeyTranslator.java │ │ │ │ ├── LocalDriveBackupWorker.java │ │ │ │ ├── MediaServiceManager.java │ │ │ │ ├── MotherActivity.java │ │ │ │ ├── PlayerKeyTranslator.java │ │ │ │ ├── RemoteControlReceiver.java │ │ │ │ ├── RemoteControlService.java │ │ │ │ ├── RemoteControlWorker.java │ │ │ │ ├── ScreensaverManager.java │ │ │ │ ├── SharedPreferencesHelper.java │ │ │ │ ├── StreamReminderService.java │ │ │ │ ├── TickleManager.java │ │ │ │ ├── UnlocalizedTitleProcessor.java │ │ │ │ ├── ZipHelper.java │ │ │ │ └── ZipHelper2.java │ │ │ ├── prefs/ │ │ │ │ ├── AccountsData.java │ │ │ │ ├── AppPrefs.java │ │ │ │ ├── BlockedChannelData.java │ │ │ │ ├── DeArrowData.java │ │ │ │ ├── GeneralData.java │ │ │ │ ├── HiddenPrefs.java │ │ │ │ ├── MainUIData.java │ │ │ │ ├── PlayerData.java │ │ │ │ ├── PlayerTweaksData.java │ │ │ │ ├── RemoteControlData.java │ │ │ │ ├── SearchData.java │ │ │ │ ├── SponsorBlockData.java │ │ │ │ └── common/ │ │ │ │ ├── DataChangeBase.java │ │ │ │ └── DataSaverBase.java │ │ │ ├── proxy/ │ │ │ │ ├── PasswdInetSocketAddress.java │ │ │ │ ├── PasswdURI.java │ │ │ │ ├── Proxy.java │ │ │ │ ├── ProxyManager.java │ │ │ │ └── WebProxyDialog.java │ │ │ └── utils/ │ │ │ ├── AppDialogUtil.java │ │ │ ├── ClickbaitRemover.java │ │ │ ├── CopyOnWriteHashList.java │ │ │ ├── HttpURLConnectionUtils.java │ │ │ ├── IntentExtractor.java │ │ │ ├── LoadingManager.java │ │ │ ├── SimpleEditDialog.java │ │ │ ├── TvQuickActions.java │ │ │ ├── UserAgentManager.java │ │ │ └── Utils.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── debug_view.xml │ │ │ ├── dim_container.xml │ │ │ ├── simple_edit_dialog.xml │ │ │ └── web_proxy_dialog.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── countries.xml │ │ │ ├── dimens.xml │ │ │ ├── donations.xml │ │ │ ├── feedback.xml │ │ │ ├── ids.xml │ │ │ ├── languages.xml │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ ├── unlocalized-strings.xml │ │ │ └── update_urls.xml │ │ ├── values-ar/ │ │ │ └── strings.xml │ │ ├── values-az/ │ │ │ └── strings.xml │ │ ├── values-bg/ │ │ │ └── strings.xml │ │ ├── values-ca/ │ │ │ └── strings.xml │ │ ├── values-cs/ │ │ │ └── strings.xml │ │ ├── values-da/ │ │ │ └── strings.xml │ │ ├── values-de/ │ │ │ └── strings.xml │ │ ├── values-el/ │ │ │ └── strings.xml │ │ ├── values-es/ │ │ │ └── strings.xml │ │ ├── values-et/ │ │ │ └── strings.xml │ │ ├── values-fa-rIR/ │ │ │ └── strings.xml │ │ ├── values-fi/ │ │ │ └── strings.xml │ │ ├── values-fr/ │ │ │ └── strings.xml │ │ ├── values-gl-rES/ │ │ │ └── strings.xml │ │ ├── values-he/ │ │ │ └── strings.xml │ │ ├── values-hi/ │ │ │ └── strings.xml │ │ ├── values-hr/ │ │ │ └── strings.xml │ │ ├── values-hu/ │ │ │ └── strings.xml │ │ ├── values-hy/ │ │ │ └── strings.xml │ │ ├── values-in/ │ │ │ └── strings.xml │ │ ├── values-it/ │ │ │ └── strings.xml │ │ ├── values-iw/ │ │ │ └── strings.xml │ │ ├── values-ja/ │ │ │ └── strings.xml │ │ ├── values-ko/ │ │ │ └── strings.xml │ │ ├── values-lt/ │ │ │ └── strings.xml │ │ ├── values-lv/ │ │ │ └── strings.xml │ │ ├── values-mo/ │ │ │ └── strings.xml │ │ ├── values-nb/ │ │ │ └── strings.xml │ │ ├── values-nl/ │ │ │ └── strings.xml │ │ ├── values-pl/ │ │ │ └── strings.xml │ │ ├── values-pt-rBR/ │ │ │ └── strings.xml │ │ ├── values-pt-rPT/ │ │ │ └── strings.xml │ │ ├── values-ro/ │ │ │ └── strings.xml │ │ ├── values-ru/ │ │ │ └── strings.xml │ │ ├── values-sk/ │ │ │ └── strings.xml │ │ ├── values-sl/ │ │ │ └── strings.xml │ │ ├── values-sq/ │ │ │ └── strings.xml │ │ ├── values-sr/ │ │ │ └── strings.xml │ │ ├── values-sv/ │ │ │ └── strings.xml │ │ ├── values-te/ │ │ │ └── strings.xml │ │ ├── values-th/ │ │ │ └── strings.xml │ │ ├── values-tr/ │ │ │ └── strings.xml │ │ ├── values-uk/ │ │ │ └── strings.xml │ │ ├── values-vi/ │ │ │ └── strings.xml │ │ ├── values-zh/ │ │ │ └── strings.xml │ │ ├── values-zh-rTW/ │ │ │ └── strings.xml │ │ ├── volume-fa/ │ │ │ └── strings.xml │ │ └── xml/ │ │ └── app_prefs.xml │ ├── stbeta/ │ │ └── res/ │ │ └── values/ │ │ └── update_urls.xml │ └── ststable/ │ └── res/ │ └── values/ │ └── update_urls.xml ├── crowdin.yml ├── doubletapplayerview/ │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── github/ │ │ └── vkay94/ │ │ └── dtpv/ │ │ ├── DoubleTapPlayerAdapter.kt │ │ ├── DoubleTapPlayerView.kt │ │ ├── DoubleTapPlayerViewImpl.kt │ │ ├── PlayerDoubleTapListener.java │ │ ├── SeekListener.kt │ │ └── youtube/ │ │ ├── YouTubeOverlay.kt │ │ └── views/ │ │ ├── CircleClipTapView.kt │ │ └── YouTubeSecondsView.kt │ └── res/ │ ├── drawable/ │ │ └── ic_play_triangle.xml │ ├── layout/ │ │ ├── yt_overlay.xml │ │ └── yt_seconds_view.xml │ ├── values/ │ │ ├── dtpv.xml │ │ ├── plurals.xml │ │ ├── public.xml │ │ └── yt_overlay.xml │ ├── values-af/ │ │ └── plurals.xml │ ├── values-am/ │ │ └── plurals.xml │ ├── values-ar/ │ │ └── plurals.xml │ ├── values-az/ │ │ └── plurals.xml │ ├── values-b+sr+Latn/ │ │ └── plurals.xml │ ├── values-be/ │ │ └── plurals.xml │ ├── values-bg/ │ │ └── plurals.xml │ ├── values-bn/ │ │ └── plurals.xml │ ├── values-bs/ │ │ └── plurals.xml │ ├── values-ca/ │ │ └── plurals.xml │ ├── values-cs/ │ │ └── plurals.xml │ ├── values-da/ │ │ └── plurals.xml │ ├── values-de/ │ │ └── plurals.xml │ ├── values-el/ │ │ └── plurals.xml │ ├── values-en-rGB/ │ │ └── plurals.xml │ ├── values-en-rIN/ │ │ └── plurals.xml │ ├── values-es/ │ │ └── plurals.xml │ ├── values-es-rUS/ │ │ └── plurals.xml │ ├── values-et/ │ │ └── plurals.xml │ ├── values-eu/ │ │ └── plurals.xml │ ├── values-fa/ │ │ └── plurals.xml │ ├── values-fi/ │ │ └── plurals.xml │ ├── values-fr/ │ │ └── plurals.xml │ ├── values-fr-rCA/ │ │ └── plurals.xml │ ├── values-gl/ │ │ └── plurals.xml │ ├── values-gu/ │ │ └── plurals.xml │ ├── values-hi/ │ │ └── plurals.xml │ ├── values-hr/ │ │ └── plurals.xml │ ├── values-hu/ │ │ └── plurals.xml │ ├── values-hy/ │ │ └── plurals.xml │ ├── values-in/ │ │ └── plurals.xml │ ├── values-is/ │ │ └── plurals.xml │ ├── values-it/ │ │ └── plurals.xml │ ├── values-iw/ │ │ └── plurals.xml │ ├── values-ja/ │ │ └── plurals.xml │ ├── values-ka/ │ │ └── plurals.xml │ ├── values-kk/ │ │ └── plurals.xml │ ├── values-km/ │ │ └── plurals.xml │ ├── values-kn/ │ │ └── plurals.xml │ ├── values-ko/ │ │ └── plurals.xml │ ├── values-ky/ │ │ └── plurals.xml │ ├── values-lo/ │ │ └── plurals.xml │ ├── values-lt/ │ │ └── plurals.xml │ ├── values-lv/ │ │ └── plurals.xml │ ├── values-mk/ │ │ └── plurals.xml │ ├── values-ml/ │ │ └── plurals.xml │ ├── values-mn/ │ │ └── plurals.xml │ ├── values-mr/ │ │ └── plurals.xml │ ├── values-ms/ │ │ └── plurals.xml │ ├── values-my/ │ │ └── plurals.xml │ ├── values-nb/ │ │ └── plurals.xml │ ├── values-ne/ │ │ └── plurals.xml │ ├── values-nl/ │ │ └── plurals.xml │ ├── values-pa/ │ │ └── plurals.xml │ ├── values-pl/ │ │ └── plurals.xml │ ├── values-pt-rBR/ │ │ └── plurals.xml │ ├── values-pt-rPT/ │ │ └── plurals.xml │ ├── values-ro/ │ │ └── plurals.xml │ ├── values-ru/ │ │ └── plurals.xml │ ├── values-si/ │ │ └── plurals.xml │ ├── values-sk/ │ │ └── plurals.xml │ ├── values-sl/ │ │ └── plurals.xml │ ├── values-sq/ │ │ └── plurals.xml │ ├── values-sr/ │ │ └── plurals.xml │ ├── values-sv/ │ │ └── plurals.xml │ ├── values-sw/ │ │ └── plurals.xml │ ├── values-ta/ │ │ └── plurals.xml │ ├── values-te/ │ │ └── plurals.xml │ ├── values-th/ │ │ └── plurals.xml │ ├── values-tl/ │ │ └── plurals.xml │ ├── values-tr/ │ │ └── plurals.xml │ ├── values-uk/ │ │ └── plurals.xml │ ├── values-ur/ │ │ └── plurals.xml │ ├── values-uz/ │ │ └── plurals.xml │ ├── values-vi/ │ │ └── plurals.xml │ ├── values-zh-rCN/ │ │ └── plurals.xml │ ├── values-zh-rHK/ │ │ └── plurals.xml │ ├── values-zh-rTW/ │ │ └── plurals.xml │ └── values-zu/ │ └── plurals.xml ├── exoplayer-amzn-2.10.6/ │ ├── .gitignore │ ├── .hgignore │ ├── CONTRIBUTING.md │ ├── LICENSE │ ├── README-ORIGINAL.md │ ├── README.md │ ├── RELEASENOTES.md │ ├── build.gradle │ ├── constants.gradle │ ├── core_settings.gradle │ ├── demos/ │ │ ├── README.md │ │ ├── cast/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── castdemo/ │ │ │ │ ├── DefaultReceiverPlayerManager.java │ │ │ │ ├── DemoUtil.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── PlayerManager.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── ic_plus.xml │ │ │ ├── layout/ │ │ │ │ ├── cast_context_error.xml │ │ │ │ ├── main_activity.xml │ │ │ │ └── sample_list.xml │ │ │ ├── menu/ │ │ │ │ └── menu.xml │ │ │ └── values/ │ │ │ └── strings.xml │ │ ├── ima/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── imademo/ │ │ │ │ ├── MainActivity.java │ │ │ │ └── PlayerManager.java │ │ │ └── res/ │ │ │ ├── layout/ │ │ │ │ └── main_activity.xml │ │ │ └── values/ │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── main/ │ │ ├── README.md │ │ ├── build.gradle │ │ ├── proguard-rules.txt │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ ├── assets/ │ │ │ └── media.exolist.json │ │ ├── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── demo/ │ │ │ ├── DemoApplication.java │ │ │ ├── DemoDownloadService.java │ │ │ ├── DownloadTracker.java │ │ │ ├── PlayerActivity.java │ │ │ ├── SampleChooserActivity.java │ │ │ └── TrackSelectionDialog.java │ │ └── res/ │ │ ├── layout/ │ │ │ ├── player_activity.xml │ │ │ ├── sample_chooser_activity.xml │ │ │ ├── sample_list_item.xml │ │ │ └── track_selection_dialog.xml │ │ ├── menu/ │ │ │ └── sample_chooser_menu.xml │ │ └── values/ │ │ ├── strings.xml │ │ └── styles.xml │ ├── extensions/ │ │ ├── README.md │ │ ├── cast/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── cast/ │ │ │ │ ├── CastPlayer.java │ │ │ │ ├── CastTimeline.java │ │ │ │ ├── CastTimelineTracker.java │ │ │ │ ├── CastUtils.java │ │ │ │ ├── DefaultCastOptionsProvider.java │ │ │ │ ├── MediaItem.java │ │ │ │ ├── MediaItemQueue.java │ │ │ │ └── SessionAvailabilityListener.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── cast/ │ │ │ ├── CastTimelineTrackerTest.java │ │ │ └── MediaItemTest.java │ │ ├── cronet/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── cronet/ │ │ │ │ ├── ByteArrayUploadDataProvider.java │ │ │ │ ├── CronetDataSource.java │ │ │ │ ├── CronetDataSourceFactory.java │ │ │ │ └── CronetEngineWrapper.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── cronet/ │ │ │ ├── ByteArrayUploadDataProviderTest.java │ │ │ └── CronetDataSourceTest.java │ │ ├── ffmpeg/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── google/ │ │ │ │ │ └── android/ │ │ │ │ │ └── exoplayer2/ │ │ │ │ │ └── ext/ │ │ │ │ │ └── ffmpeg/ │ │ │ │ │ ├── FfmpegAudioRenderer.java │ │ │ │ │ ├── FfmpegDecoder.java │ │ │ │ │ ├── FfmpegDecoderException.java │ │ │ │ │ └── FfmpegLibrary.java │ │ │ │ └── jni/ │ │ │ │ ├── Android.mk │ │ │ │ ├── Application.mk │ │ │ │ └── ffmpeg_jni.cc │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── ffmpeg/ │ │ │ └── DefaultRenderersFactoryTest.java │ │ ├── flac/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ ├── androidTest/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── assets/ │ │ │ │ │ ├── bear-flac.mka │ │ │ │ │ ├── bear.flac │ │ │ │ │ ├── bear.flac.0.dump │ │ │ │ │ ├── bear.flac.1.dump │ │ │ │ │ ├── bear.flac.2.dump │ │ │ │ │ ├── bear.flac.3.dump │ │ │ │ │ ├── bear_no_seek.flac │ │ │ │ │ ├── bear_with_id3.flac │ │ │ │ │ ├── bear_with_id3.flac.0.dump │ │ │ │ │ ├── bear_with_id3.flac.1.dump │ │ │ │ │ ├── bear_with_id3.flac.2.dump │ │ │ │ │ └── bear_with_id3.flac.3.dump │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── flac/ │ │ │ │ ├── FlacBinarySearchSeekerTest.java │ │ │ │ ├── FlacExtractorSeekTest.java │ │ │ │ ├── FlacExtractorTest.java │ │ │ │ └── FlacPlaybackTest.java │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── google/ │ │ │ │ │ └── android/ │ │ │ │ │ └── exoplayer2/ │ │ │ │ │ └── ext/ │ │ │ │ │ └── flac/ │ │ │ │ │ ├── FlacBinarySearchSeeker.java │ │ │ │ │ ├── FlacDecoder.java │ │ │ │ │ ├── FlacDecoderException.java │ │ │ │ │ ├── FlacDecoderJni.java │ │ │ │ │ ├── FlacExtractor.java │ │ │ │ │ ├── FlacLibrary.java │ │ │ │ │ └── LibflacAudioRenderer.java │ │ │ │ └── jni/ │ │ │ │ ├── Android.mk │ │ │ │ ├── Application.mk │ │ │ │ ├── flac_jni.cc │ │ │ │ ├── flac_parser.cc │ │ │ │ ├── flac_sources.mk │ │ │ │ └── include/ │ │ │ │ ├── data_source.h │ │ │ │ └── flac_parser.h │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── flac/ │ │ │ ├── DefaultExtractorsFactoryTest.java │ │ │ └── DefaultRenderersFactoryTest.java │ │ ├── gvr/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── gvr/ │ │ │ │ ├── GvrAudioProcessor.java │ │ │ │ └── GvrPlayerActivity.java │ │ │ └── res/ │ │ │ ├── layout/ │ │ │ │ └── vr_ui.xml │ │ │ ├── values/ │ │ │ │ └── styles.xml │ │ │ └── values-v21/ │ │ │ └── styles.xml │ │ ├── ima/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── ima/ │ │ │ │ └── ImaAdsLoader.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── ima/ │ │ │ ├── FakeAd.java │ │ │ ├── FakeAdsLoader.java │ │ │ ├── FakeAdsRequest.java │ │ │ ├── FakePlayer.java │ │ │ ├── ImaAdsLoaderTest.java │ │ │ └── SingletonImaFactory.java │ │ ├── jobdispatcher/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── jobdispatcher/ │ │ │ └── JobDispatcherScheduler.java │ │ ├── leanback/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── leanback/ │ │ │ └── LeanbackPlayerAdapter.java │ │ ├── mediasession/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── mediasession/ │ │ │ │ ├── MediaSessionConnector.java │ │ │ │ ├── RepeatModeActionProvider.java │ │ │ │ ├── TimelineQueueEditor.java │ │ │ │ └── TimelineQueueNavigator.java │ │ │ └── res/ │ │ │ ├── drawable-anydpi-v21/ │ │ │ │ ├── exo_media_action_repeat_all.xml │ │ │ │ ├── exo_media_action_repeat_off.xml │ │ │ │ └── exo_media_action_repeat_one.xml │ │ │ ├── values/ │ │ │ │ └── strings.xml │ │ │ ├── values-af/ │ │ │ │ └── strings.xml │ │ │ ├── values-am/ │ │ │ │ └── strings.xml │ │ │ ├── values-ar/ │ │ │ │ └── strings.xml │ │ │ ├── values-az/ │ │ │ │ └── strings.xml │ │ │ ├── values-b+sr+Latn/ │ │ │ │ └── strings.xml │ │ │ ├── values-be/ │ │ │ │ └── strings.xml │ │ │ ├── values-bg/ │ │ │ │ └── strings.xml │ │ │ ├── values-bn/ │ │ │ │ └── strings.xml │ │ │ ├── values-bs/ │ │ │ │ └── strings.xml │ │ │ ├── values-ca/ │ │ │ │ └── strings.xml │ │ │ ├── values-cs/ │ │ │ │ └── strings.xml │ │ │ ├── values-da/ │ │ │ │ └── strings.xml │ │ │ ├── values-de/ │ │ │ │ └── strings.xml │ │ │ ├── values-el/ │ │ │ │ └── strings.xml │ │ │ ├── values-en-rAU/ │ │ │ │ └── strings.xml │ │ │ ├── values-en-rGB/ │ │ │ │ └── strings.xml │ │ │ ├── values-en-rIN/ │ │ │ │ └── strings.xml │ │ │ ├── values-es/ │ │ │ │ └── strings.xml │ │ │ ├── values-es-rUS/ │ │ │ │ └── strings.xml │ │ │ ├── values-et/ │ │ │ │ └── strings.xml │ │ │ ├── values-eu/ │ │ │ │ └── strings.xml │ │ │ ├── values-fa/ │ │ │ │ └── strings.xml │ │ │ ├── values-fi/ │ │ │ │ └── strings.xml │ │ │ ├── values-fr/ │ │ │ │ └── strings.xml │ │ │ ├── values-fr-rCA/ │ │ │ │ └── strings.xml │ │ │ ├── values-gl/ │ │ │ │ └── strings.xml │ │ │ ├── values-gu/ │ │ │ │ └── strings.xml │ │ │ ├── values-hi/ │ │ │ │ └── strings.xml │ │ │ ├── values-hr/ │ │ │ │ └── strings.xml │ │ │ ├── values-hu/ │ │ │ │ └── strings.xml │ │ │ ├── values-hy/ │ │ │ │ └── strings.xml │ │ │ ├── values-in/ │ │ │ │ └── strings.xml │ │ │ ├── values-is/ │ │ │ │ └── strings.xml │ │ │ ├── values-it/ │ │ │ │ └── strings.xml │ │ │ ├── values-iw/ │ │ │ │ └── strings.xml │ │ │ ├── values-ja/ │ │ │ │ └── strings.xml │ │ │ ├── values-ka/ │ │ │ │ └── strings.xml │ │ │ ├── values-kk/ │ │ │ │ └── strings.xml │ │ │ ├── values-km/ │ │ │ │ └── strings.xml │ │ │ ├── values-kn/ │ │ │ │ └── strings.xml │ │ │ ├── values-ko/ │ │ │ │ └── strings.xml │ │ │ ├── values-ky/ │ │ │ │ └── strings.xml │ │ │ ├── values-lo/ │ │ │ │ └── strings.xml │ │ │ ├── values-lt/ │ │ │ │ └── strings.xml │ │ │ ├── values-lv/ │ │ │ │ └── strings.xml │ │ │ ├── values-mk/ │ │ │ │ └── strings.xml │ │ │ ├── values-ml/ │ │ │ │ └── strings.xml │ │ │ ├── values-mn/ │ │ │ │ └── strings.xml │ │ │ ├── values-mr/ │ │ │ │ └── strings.xml │ │ │ ├── values-ms/ │ │ │ │ └── strings.xml │ │ │ ├── values-my/ │ │ │ │ └── strings.xml │ │ │ ├── values-nb/ │ │ │ │ └── strings.xml │ │ │ ├── values-ne/ │ │ │ │ └── strings.xml │ │ │ ├── values-nl/ │ │ │ │ └── strings.xml │ │ │ ├── values-pa/ │ │ │ │ └── strings.xml │ │ │ ├── values-pl/ │ │ │ │ └── strings.xml │ │ │ ├── values-pt/ │ │ │ │ └── strings.xml │ │ │ ├── values-pt-rPT/ │ │ │ │ └── strings.xml │ │ │ ├── values-ro/ │ │ │ │ └── strings.xml │ │ │ ├── values-ru/ │ │ │ │ └── strings.xml │ │ │ ├── values-si/ │ │ │ │ └── strings.xml │ │ │ ├── values-sk/ │ │ │ │ └── strings.xml │ │ │ ├── values-sl/ │ │ │ │ └── strings.xml │ │ │ ├── values-sq/ │ │ │ │ └── strings.xml │ │ │ ├── values-sr/ │ │ │ │ └── strings.xml │ │ │ ├── values-sv/ │ │ │ │ └── strings.xml │ │ │ ├── values-sw/ │ │ │ │ └── strings.xml │ │ │ ├── values-ta/ │ │ │ │ └── strings.xml │ │ │ ├── values-te/ │ │ │ │ └── strings.xml │ │ │ ├── values-th/ │ │ │ │ └── strings.xml │ │ │ ├── values-tl/ │ │ │ │ └── strings.xml │ │ │ ├── values-tr/ │ │ │ │ └── strings.xml │ │ │ ├── values-uk/ │ │ │ │ └── strings.xml │ │ │ ├── values-ur/ │ │ │ │ └── strings.xml │ │ │ ├── values-uz/ │ │ │ │ └── strings.xml │ │ │ ├── values-vi/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rCN/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rHK/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rTW/ │ │ │ │ └── strings.xml │ │ │ └── values-zu/ │ │ │ └── strings.xml │ │ ├── okhttp/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── okhttp/ │ │ │ ├── OkHttpDataSource.java │ │ │ └── OkHttpDataSourceFactory.java │ │ ├── opus/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ ├── androidTest/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── assets/ │ │ │ │ │ └── bear-opus.webm │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── opus/ │ │ │ │ └── OpusPlaybackTest.java │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── google/ │ │ │ │ │ └── android/ │ │ │ │ │ └── exoplayer2/ │ │ │ │ │ └── ext/ │ │ │ │ │ └── opus/ │ │ │ │ │ ├── LibopusAudioRenderer.java │ │ │ │ │ ├── OpusDecoder.java │ │ │ │ │ ├── OpusDecoderException.java │ │ │ │ │ └── OpusLibrary.java │ │ │ │ └── jni/ │ │ │ │ ├── Android.mk │ │ │ │ ├── Application.mk │ │ │ │ ├── convert_android_asm.sh │ │ │ │ ├── libopus.mk │ │ │ │ └── opus_jni.cc │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── opus/ │ │ │ └── DefaultRenderersFactoryTest.java │ │ ├── rtmp/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── rtmp/ │ │ │ │ ├── RtmpDataSource.java │ │ │ │ └── RtmpDataSourceFactory.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── rtmp/ │ │ │ └── DefaultDataSourceTest.java │ │ ├── vp9/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ ├── androidTest/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── assets/ │ │ │ │ │ ├── bear-vp9-odd-dimensions.webm │ │ │ │ │ ├── bear-vp9.webm │ │ │ │ │ ├── invalid-bitstream.webm │ │ │ │ │ └── roadtrip-vp92-10bit.webm │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ext/ │ │ │ │ └── vp9/ │ │ │ │ └── VpxPlaybackTest.java │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ ├── java/ │ │ │ │ │ └── com/ │ │ │ │ │ └── google/ │ │ │ │ │ └── android/ │ │ │ │ │ └── exoplayer2/ │ │ │ │ │ └── ext/ │ │ │ │ │ └── vp9/ │ │ │ │ │ ├── LibvpxVideoRenderer.java │ │ │ │ │ ├── VpxDecoder.java │ │ │ │ │ ├── VpxDecoderException.java │ │ │ │ │ ├── VpxInputBuffer.java │ │ │ │ │ ├── VpxLibrary.java │ │ │ │ │ ├── VpxOutputBuffer.java │ │ │ │ │ ├── VpxOutputBufferRenderer.java │ │ │ │ │ ├── VpxRenderer.java │ │ │ │ │ └── VpxVideoSurfaceView.java │ │ │ │ └── jni/ │ │ │ │ ├── Android.mk │ │ │ │ ├── Application.mk │ │ │ │ ├── generate_libvpx_android_configs.sh │ │ │ │ ├── libvpx.mk │ │ │ │ └── vpx_jni.cc │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── ext/ │ │ │ └── vp9/ │ │ │ └── DefaultRenderersFactoryTest.java │ │ └── workmanager/ │ │ ├── README.md │ │ ├── build.gradle │ │ └── src/ │ │ └── main/ │ │ ├── AndroidManifest.xml │ │ └── java/ │ │ └── com/ │ │ └── google/ │ │ └── android/ │ │ └── exoplayer2/ │ │ └── ext/ │ │ └── workmanager/ │ │ └── WorkManagerScheduler.java │ ├── gradle/ │ │ └── wrapper/ │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradle.properties │ ├── gradlew │ ├── gradlew.bat │ ├── issues/ │ │ └── player-accessed-on-wrong-thread.md │ ├── javadoc_combined.gradle │ ├── javadoc_library.gradle │ ├── javadoc_util.gradle │ ├── library/ │ │ ├── README.md │ │ ├── all/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ └── AndroidManifest.xml │ │ ├── core/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ ├── proguard-rules.txt │ │ │ └── src/ │ │ │ ├── androidTest/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── upstream/ │ │ │ │ └── ContentDataSourceTest.java │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ ├── BasePlayer.java │ │ │ │ ├── BaseRenderer.java │ │ │ │ ├── C.java │ │ │ │ ├── ControlDispatcher.java │ │ │ │ ├── DefaultControlDispatcher.java │ │ │ │ ├── DefaultLoadControl.java │ │ │ │ ├── DefaultMediaClock.java │ │ │ │ ├── DefaultRenderersFactory.java │ │ │ │ ├── ExoPlaybackException.java │ │ │ │ ├── ExoPlayer.java │ │ │ │ ├── ExoPlayerFactory.java │ │ │ │ ├── ExoPlayerImpl.java │ │ │ │ ├── ExoPlayerImplInternal.java │ │ │ │ ├── ExoPlayerLibraryInfo.java │ │ │ │ ├── Format.java │ │ │ │ ├── FormatHolder.java │ │ │ │ ├── IllegalSeekPositionException.java │ │ │ │ ├── LoadControl.java │ │ │ │ ├── MediaPeriodHolder.java │ │ │ │ ├── MediaPeriodInfo.java │ │ │ │ ├── MediaPeriodQueue.java │ │ │ │ ├── NoSampleRenderer.java │ │ │ │ ├── ParserException.java │ │ │ │ ├── PlaybackInfo.java │ │ │ │ ├── PlaybackParameters.java │ │ │ │ ├── PlaybackPreparer.java │ │ │ │ ├── Player.java │ │ │ │ ├── PlayerMessage.java │ │ │ │ ├── Renderer.java │ │ │ │ ├── RendererCapabilities.java │ │ │ │ ├── RendererConfiguration.java │ │ │ │ ├── RenderersFactory.java │ │ │ │ ├── SeekParameters.java │ │ │ │ ├── SimpleExoPlayer.java │ │ │ │ ├── Timeline.java │ │ │ │ ├── analytics/ │ │ │ │ │ ├── AnalyticsCollector.java │ │ │ │ │ ├── AnalyticsListener.java │ │ │ │ │ └── DefaultAnalyticsListener.java │ │ │ │ ├── audio/ │ │ │ │ │ ├── Ac3Util.java │ │ │ │ │ ├── Ac4Util.java │ │ │ │ │ ├── AudioAttributes.java │ │ │ │ │ ├── AudioCapabilities.java │ │ │ │ │ ├── AudioCapabilitiesReceiver.java │ │ │ │ │ ├── AudioDecoderException.java │ │ │ │ │ ├── AudioFocusManager.java │ │ │ │ │ ├── AudioListener.java │ │ │ │ │ ├── AudioProcessor.java │ │ │ │ │ ├── AudioRendererEventListener.java │ │ │ │ │ ├── AudioSink.java │ │ │ │ │ ├── AudioTimestampPoller.java │ │ │ │ │ ├── AudioTrackPositionTracker.java │ │ │ │ │ ├── AuxEffectInfo.java │ │ │ │ │ ├── BaseAudioProcessor.java │ │ │ │ │ ├── ChannelMappingAudioProcessor.java │ │ │ │ │ ├── DefaultAudioSink.java │ │ │ │ │ ├── DolbyPassthroughAudioTrack.java │ │ │ │ │ ├── DtsUtil.java │ │ │ │ │ ├── FloatResamplingAudioProcessor.java │ │ │ │ │ ├── MediaCodecAudioRenderer.java │ │ │ │ │ ├── ResamplingAudioProcessor.java │ │ │ │ │ ├── SilenceSkippingAudioProcessor.java │ │ │ │ │ ├── SimpleDecoderAudioRenderer.java │ │ │ │ │ ├── Sonic.java │ │ │ │ │ ├── SonicAudioProcessor.java │ │ │ │ │ ├── TeeAudioProcessor.java │ │ │ │ │ ├── TrimmingAudioProcessor.java │ │ │ │ │ └── WavUtil.java │ │ │ │ ├── database/ │ │ │ │ │ ├── DatabaseIOException.java │ │ │ │ │ ├── DatabaseProvider.java │ │ │ │ │ ├── DefaultDatabaseProvider.java │ │ │ │ │ ├── ExoDatabaseProvider.java │ │ │ │ │ └── VersionTable.java │ │ │ │ ├── decoder/ │ │ │ │ │ ├── Buffer.java │ │ │ │ │ ├── CryptoInfo.java │ │ │ │ │ ├── Decoder.java │ │ │ │ │ ├── DecoderCounters.java │ │ │ │ │ ├── DecoderInputBuffer.java │ │ │ │ │ ├── OutputBuffer.java │ │ │ │ │ ├── SimpleDecoder.java │ │ │ │ │ └── SimpleOutputBuffer.java │ │ │ │ ├── drm/ │ │ │ │ │ ├── ClearKeyUtil.java │ │ │ │ │ ├── DecryptionException.java │ │ │ │ │ ├── DecryptionResource.java │ │ │ │ │ ├── DefaultDrmSession.java │ │ │ │ │ ├── DefaultDrmSessionEventListener.java │ │ │ │ │ ├── DefaultDrmSessionManager.java │ │ │ │ │ ├── DrmInitData.java │ │ │ │ │ ├── DrmSession.java │ │ │ │ │ ├── DrmSessionManager.java │ │ │ │ │ ├── ErrorStateDrmSession.java │ │ │ │ │ ├── ExoMediaCrypto.java │ │ │ │ │ ├── ExoMediaDrm.java │ │ │ │ │ ├── FrameworkMediaCrypto.java │ │ │ │ │ ├── FrameworkMediaDrm.java │ │ │ │ │ ├── HttpMediaDrmCallback.java │ │ │ │ │ ├── KeysExpiredException.java │ │ │ │ │ ├── LocalMediaDrmCallback.java │ │ │ │ │ ├── MediaDrmCallback.java │ │ │ │ │ ├── OfflineLicenseHelper.java │ │ │ │ │ ├── UnsupportedDrmException.java │ │ │ │ │ └── WidevineUtil.java │ │ │ │ ├── extractor/ │ │ │ │ │ ├── BinarySearchSeeker.java │ │ │ │ │ ├── ChunkIndex.java │ │ │ │ │ ├── ConstantBitrateSeekMap.java │ │ │ │ │ ├── DefaultExtractorInput.java │ │ │ │ │ ├── DefaultExtractorsFactory.java │ │ │ │ │ ├── DummyExtractorOutput.java │ │ │ │ │ ├── DummyTrackOutput.java │ │ │ │ │ ├── Extractor.java │ │ │ │ │ ├── ExtractorInput.java │ │ │ │ │ ├── ExtractorOutput.java │ │ │ │ │ ├── ExtractorsFactory.java │ │ │ │ │ ├── GaplessInfoHolder.java │ │ │ │ │ ├── Id3Peeker.java │ │ │ │ │ ├── MpegAudioHeader.java │ │ │ │ │ ├── PositionHolder.java │ │ │ │ │ ├── SeekMap.java │ │ │ │ │ ├── SeekPoint.java │ │ │ │ │ ├── TrackOutput.java │ │ │ │ │ ├── amr/ │ │ │ │ │ │ └── AmrExtractor.java │ │ │ │ │ ├── flv/ │ │ │ │ │ │ ├── AudioTagPayloadReader.java │ │ │ │ │ │ ├── FlvExtractor.java │ │ │ │ │ │ ├── ScriptTagPayloadReader.java │ │ │ │ │ │ ├── TagPayloadReader.java │ │ │ │ │ │ └── VideoTagPayloadReader.java │ │ │ │ │ ├── mkv/ │ │ │ │ │ │ ├── DefaultEbmlReader.java │ │ │ │ │ │ ├── EbmlProcessor.java │ │ │ │ │ │ ├── EbmlReader.java │ │ │ │ │ │ ├── MatroskaExtractor.java │ │ │ │ │ │ ├── Sniffer.java │ │ │ │ │ │ └── VarintReader.java │ │ │ │ │ ├── mp3/ │ │ │ │ │ │ ├── ConstantBitrateSeeker.java │ │ │ │ │ │ ├── MlltSeeker.java │ │ │ │ │ │ ├── Mp3Extractor.java │ │ │ │ │ │ ├── Seeker.java │ │ │ │ │ │ ├── VbriSeeker.java │ │ │ │ │ │ └── XingSeeker.java │ │ │ │ │ ├── mp4/ │ │ │ │ │ │ ├── Atom.java │ │ │ │ │ │ ├── AtomParsers.java │ │ │ │ │ │ ├── DefaultSampleValues.java │ │ │ │ │ │ ├── FixedSampleSizeRechunker.java │ │ │ │ │ │ ├── FragmentedMp4Extractor.java │ │ │ │ │ │ ├── MdtaMetadataEntry.java │ │ │ │ │ │ ├── MetadataUtil.java │ │ │ │ │ │ ├── Mp4Extractor.java │ │ │ │ │ │ ├── PsshAtomUtil.java │ │ │ │ │ │ ├── Sniffer.java │ │ │ │ │ │ ├── Track.java │ │ │ │ │ │ ├── TrackEncryptionBox.java │ │ │ │ │ │ ├── TrackFragment.java │ │ │ │ │ │ └── TrackSampleTable.java │ │ │ │ │ ├── ogg/ │ │ │ │ │ │ ├── DefaultOggSeeker.java │ │ │ │ │ │ ├── FlacReader.java │ │ │ │ │ │ ├── OggExtractor.java │ │ │ │ │ │ ├── OggPacket.java │ │ │ │ │ │ ├── OggPageHeader.java │ │ │ │ │ │ ├── OggSeeker.java │ │ │ │ │ │ ├── OpusReader.java │ │ │ │ │ │ ├── StreamReader.java │ │ │ │ │ │ ├── VorbisBitArray.java │ │ │ │ │ │ ├── VorbisReader.java │ │ │ │ │ │ └── VorbisUtil.java │ │ │ │ │ ├── rawcc/ │ │ │ │ │ │ └── RawCcExtractor.java │ │ │ │ │ ├── ts/ │ │ │ │ │ │ ├── Ac3Extractor.java │ │ │ │ │ │ ├── Ac3Reader.java │ │ │ │ │ │ ├── Ac4Extractor.java │ │ │ │ │ │ ├── Ac4Reader.java │ │ │ │ │ │ ├── AdtsExtractor.java │ │ │ │ │ │ ├── AdtsReader.java │ │ │ │ │ │ ├── DefaultTsPayloadReaderFactory.java │ │ │ │ │ │ ├── DtsReader.java │ │ │ │ │ │ ├── DvbSubtitleReader.java │ │ │ │ │ │ ├── ElementaryStreamReader.java │ │ │ │ │ │ ├── H262Reader.java │ │ │ │ │ │ ├── H264Reader.java │ │ │ │ │ │ ├── H265Reader.java │ │ │ │ │ │ ├── Id3Reader.java │ │ │ │ │ │ ├── LatmReader.java │ │ │ │ │ │ ├── MpegAudioReader.java │ │ │ │ │ │ ├── NalUnitTargetBuffer.java │ │ │ │ │ │ ├── PesReader.java │ │ │ │ │ │ ├── PsBinarySearchSeeker.java │ │ │ │ │ │ ├── PsDurationReader.java │ │ │ │ │ │ ├── PsExtractor.java │ │ │ │ │ │ ├── SectionPayloadReader.java │ │ │ │ │ │ ├── SectionReader.java │ │ │ │ │ │ ├── SeiReader.java │ │ │ │ │ │ ├── SpliceInfoSectionReader.java │ │ │ │ │ │ ├── TsBinarySearchSeeker.java │ │ │ │ │ │ ├── TsDurationReader.java │ │ │ │ │ │ ├── TsExtractor.java │ │ │ │ │ │ ├── TsPayloadReader.java │ │ │ │ │ │ ├── TsUtil.java │ │ │ │ │ │ └── UserDataReader.java │ │ │ │ │ └── wav/ │ │ │ │ │ ├── WavExtractor.java │ │ │ │ │ ├── WavHeader.java │ │ │ │ │ └── WavHeaderReader.java │ │ │ │ ├── mediacodec/ │ │ │ │ │ ├── MediaCodecInfo.java │ │ │ │ │ ├── MediaCodecRenderer.java │ │ │ │ │ ├── MediaCodecSelector.java │ │ │ │ │ ├── MediaCodecUtil.java │ │ │ │ │ └── MediaFormatUtil.java │ │ │ │ ├── metadata/ │ │ │ │ │ ├── Metadata.java │ │ │ │ │ ├── MetadataDecoder.java │ │ │ │ │ ├── MetadataDecoderFactory.java │ │ │ │ │ ├── MetadataInputBuffer.java │ │ │ │ │ ├── MetadataOutput.java │ │ │ │ │ ├── MetadataRenderer.java │ │ │ │ │ ├── emsg/ │ │ │ │ │ │ ├── EventMessage.java │ │ │ │ │ │ ├── EventMessageDecoder.java │ │ │ │ │ │ └── EventMessageEncoder.java │ │ │ │ │ ├── flac/ │ │ │ │ │ │ ├── PictureFrame.java │ │ │ │ │ │ └── VorbisComment.java │ │ │ │ │ ├── icy/ │ │ │ │ │ │ ├── IcyDecoder.java │ │ │ │ │ │ ├── IcyHeaders.java │ │ │ │ │ │ └── IcyInfo.java │ │ │ │ │ ├── id3/ │ │ │ │ │ │ ├── ApicFrame.java │ │ │ │ │ │ ├── BinaryFrame.java │ │ │ │ │ │ ├── ChapterFrame.java │ │ │ │ │ │ ├── ChapterTocFrame.java │ │ │ │ │ │ ├── CommentFrame.java │ │ │ │ │ │ ├── GeobFrame.java │ │ │ │ │ │ ├── Id3Decoder.java │ │ │ │ │ │ ├── Id3Frame.java │ │ │ │ │ │ ├── InternalFrame.java │ │ │ │ │ │ ├── MlltFrame.java │ │ │ │ │ │ ├── PrivFrame.java │ │ │ │ │ │ ├── TextInformationFrame.java │ │ │ │ │ │ └── UrlLinkFrame.java │ │ │ │ │ └── scte35/ │ │ │ │ │ ├── PrivateCommand.java │ │ │ │ │ ├── SpliceCommand.java │ │ │ │ │ ├── SpliceInfoDecoder.java │ │ │ │ │ ├── SpliceInsertCommand.java │ │ │ │ │ ├── SpliceNullCommand.java │ │ │ │ │ ├── SpliceScheduleCommand.java │ │ │ │ │ └── TimeSignalCommand.java │ │ │ │ ├── offline/ │ │ │ │ │ ├── ActionFile.java │ │ │ │ │ ├── ActionFileUpgradeUtil.java │ │ │ │ │ ├── DefaultDownloadIndex.java │ │ │ │ │ ├── DefaultDownloaderFactory.java │ │ │ │ │ ├── Download.java │ │ │ │ │ ├── DownloadCursor.java │ │ │ │ │ ├── DownloadException.java │ │ │ │ │ ├── DownloadHelper.java │ │ │ │ │ ├── DownloadIndex.java │ │ │ │ │ ├── DownloadManager.java │ │ │ │ │ ├── DownloadProgress.java │ │ │ │ │ ├── DownloadRequest.java │ │ │ │ │ ├── DownloadService.java │ │ │ │ │ ├── Downloader.java │ │ │ │ │ ├── DownloaderConstructorHelper.java │ │ │ │ │ ├── DownloaderFactory.java │ │ │ │ │ ├── FilterableManifest.java │ │ │ │ │ ├── FilteringManifestParser.java │ │ │ │ │ ├── ProgressiveDownloader.java │ │ │ │ │ ├── SegmentDownloader.java │ │ │ │ │ ├── StreamKey.java │ │ │ │ │ └── WritableDownloadIndex.java │ │ │ │ ├── scheduler/ │ │ │ │ │ ├── PlatformScheduler.java │ │ │ │ │ ├── Requirements.java │ │ │ │ │ ├── RequirementsWatcher.java │ │ │ │ │ └── Scheduler.java │ │ │ │ ├── source/ │ │ │ │ │ ├── AbstractConcatenatedTimeline.java │ │ │ │ │ ├── AdaptiveMediaSourceEventListener.java │ │ │ │ │ ├── BaseMediaSource.java │ │ │ │ │ ├── BehindLiveWindowException.java │ │ │ │ │ ├── ClippingMediaPeriod.java │ │ │ │ │ ├── ClippingMediaSource.java │ │ │ │ │ ├── CompositeMediaSource.java │ │ │ │ │ ├── CompositeSequenceableLoader.java │ │ │ │ │ ├── CompositeSequenceableLoaderFactory.java │ │ │ │ │ ├── ConcatenatingMediaSource.java │ │ │ │ │ ├── DefaultCompositeSequenceableLoaderFactory.java │ │ │ │ │ ├── DefaultMediaSourceEventListener.java │ │ │ │ │ ├── DeferredMediaPeriod.java │ │ │ │ │ ├── DynamicConcatenatingMediaSource.java │ │ │ │ │ ├── EmptySampleStream.java │ │ │ │ │ ├── ExtractorMediaSource.java │ │ │ │ │ ├── ForwardingTimeline.java │ │ │ │ │ ├── IcyDataSource.java │ │ │ │ │ ├── LoopingMediaSource.java │ │ │ │ │ ├── MediaPeriod.java │ │ │ │ │ ├── MediaSource.java │ │ │ │ │ ├── MediaSourceEventListener.java │ │ │ │ │ ├── MergingMediaPeriod.java │ │ │ │ │ ├── MergingMediaSource.java │ │ │ │ │ ├── ProgressiveMediaPeriod.java │ │ │ │ │ ├── ProgressiveMediaSource.java │ │ │ │ │ ├── SampleMetadataQueue.java │ │ │ │ │ ├── SampleQueue.java │ │ │ │ │ ├── SampleStream.java │ │ │ │ │ ├── SequenceableLoader.java │ │ │ │ │ ├── ShuffleOrder.java │ │ │ │ │ ├── SilenceMediaSource.java │ │ │ │ │ ├── SinglePeriodTimeline.java │ │ │ │ │ ├── SingleSampleMediaPeriod.java │ │ │ │ │ ├── SingleSampleMediaSource.java │ │ │ │ │ ├── TrackGroup.java │ │ │ │ │ ├── TrackGroupArray.java │ │ │ │ │ ├── UnrecognizedInputFormatException.java │ │ │ │ │ ├── ads/ │ │ │ │ │ │ ├── AdPlaybackState.java │ │ │ │ │ │ ├── AdsLoader.java │ │ │ │ │ │ ├── AdsMediaSource.java │ │ │ │ │ │ └── SinglePeriodAdTimeline.java │ │ │ │ │ └── chunk/ │ │ │ │ │ ├── BaseMediaChunk.java │ │ │ │ │ ├── BaseMediaChunkIterator.java │ │ │ │ │ ├── BaseMediaChunkOutput.java │ │ │ │ │ ├── Chunk.java │ │ │ │ │ ├── ChunkExtractorWrapper.java │ │ │ │ │ ├── ChunkHolder.java │ │ │ │ │ ├── ChunkSampleStream.java │ │ │ │ │ ├── ChunkSource.java │ │ │ │ │ ├── ContainerMediaChunk.java │ │ │ │ │ ├── DataChunk.java │ │ │ │ │ ├── InitializationChunk.java │ │ │ │ │ ├── MediaChunk.java │ │ │ │ │ ├── MediaChunkIterator.java │ │ │ │ │ ├── MediaChunkListIterator.java │ │ │ │ │ └── SingleSampleMediaChunk.java │ │ │ │ ├── text/ │ │ │ │ │ ├── CaptionStyleCompat.java │ │ │ │ │ ├── Cue.java │ │ │ │ │ ├── SimpleSubtitleDecoder.java │ │ │ │ │ ├── SimpleSubtitleOutputBuffer.java │ │ │ │ │ ├── Subtitle.java │ │ │ │ │ ├── SubtitleDecoder.java │ │ │ │ │ ├── SubtitleDecoderException.java │ │ │ │ │ ├── SubtitleDecoderFactory.java │ │ │ │ │ ├── SubtitleInputBuffer.java │ │ │ │ │ ├── SubtitleOutputBuffer.java │ │ │ │ │ ├── TextOutput.java │ │ │ │ │ ├── TextRenderer.java │ │ │ │ │ ├── cea/ │ │ │ │ │ │ ├── Cea608Decoder.java │ │ │ │ │ │ ├── Cea708Cue.java │ │ │ │ │ │ ├── Cea708Decoder.java │ │ │ │ │ │ ├── Cea708InitializationData.java │ │ │ │ │ │ ├── CeaDecoder.java │ │ │ │ │ │ ├── CeaSubtitle.java │ │ │ │ │ │ └── CeaUtil.java │ │ │ │ │ ├── dvb/ │ │ │ │ │ │ ├── DvbDecoder.java │ │ │ │ │ │ ├── DvbParser.java │ │ │ │ │ │ └── DvbSubtitle.java │ │ │ │ │ ├── pgs/ │ │ │ │ │ │ ├── PgsDecoder.java │ │ │ │ │ │ └── PgsSubtitle.java │ │ │ │ │ ├── ssa/ │ │ │ │ │ │ ├── SsaDecoder.java │ │ │ │ │ │ └── SsaSubtitle.java │ │ │ │ │ ├── subrip/ │ │ │ │ │ │ ├── SubripDecoder.java │ │ │ │ │ │ └── SubripSubtitle.java │ │ │ │ │ ├── ttml/ │ │ │ │ │ │ ├── TtmlDecoder.java │ │ │ │ │ │ ├── TtmlNode.java │ │ │ │ │ │ ├── TtmlRegion.java │ │ │ │ │ │ ├── TtmlRenderUtil.java │ │ │ │ │ │ ├── TtmlStyle.java │ │ │ │ │ │ └── TtmlSubtitle.java │ │ │ │ │ ├── tx3g/ │ │ │ │ │ │ ├── Tx3gDecoder.java │ │ │ │ │ │ └── Tx3gSubtitle.java │ │ │ │ │ └── webvtt/ │ │ │ │ │ ├── CssParser.java │ │ │ │ │ ├── Mp4WebvttDecoder.java │ │ │ │ │ ├── Mp4WebvttSubtitle.java │ │ │ │ │ ├── WebvttCssStyle.java │ │ │ │ │ ├── WebvttCue.java │ │ │ │ │ ├── WebvttCueParser.java │ │ │ │ │ ├── WebvttDecoder.java │ │ │ │ │ ├── WebvttParserUtil.java │ │ │ │ │ └── WebvttSubtitle.java │ │ │ │ ├── trackselection/ │ │ │ │ │ ├── AdaptiveTrackSelection.java │ │ │ │ │ ├── BaseTrackSelection.java │ │ │ │ │ ├── BufferSizeAdaptationBuilder.java │ │ │ │ │ ├── DefaultTrackSelector.java │ │ │ │ │ ├── FixedTrackSelection.java │ │ │ │ │ ├── MappingTrackSelector.java │ │ │ │ │ ├── RandomTrackSelection.java │ │ │ │ │ ├── TrackBitrateEstimator.java │ │ │ │ │ ├── TrackSelection.java │ │ │ │ │ ├── TrackSelectionArray.java │ │ │ │ │ ├── TrackSelectionParameters.java │ │ │ │ │ ├── TrackSelectionUtil.java │ │ │ │ │ ├── TrackSelector.java │ │ │ │ │ ├── TrackSelectorResult.java │ │ │ │ │ └── WindowedTrackBitrateEstimator.java │ │ │ │ ├── upstream/ │ │ │ │ │ ├── Allocation.java │ │ │ │ │ ├── Allocator.java │ │ │ │ │ ├── AssetDataSource.java │ │ │ │ │ ├── BandwidthMeter.java │ │ │ │ │ ├── BaseDataSource.java │ │ │ │ │ ├── ByteArrayDataSink.java │ │ │ │ │ ├── ByteArrayDataSource.java │ │ │ │ │ ├── ContentDataSource.java │ │ │ │ │ ├── DataSchemeDataSource.java │ │ │ │ │ ├── DataSink.java │ │ │ │ │ ├── DataSource.java │ │ │ │ │ ├── DataSourceException.java │ │ │ │ │ ├── DataSourceInputStream.java │ │ │ │ │ ├── DataSpec.java │ │ │ │ │ ├── DefaultAllocator.java │ │ │ │ │ ├── DefaultBandwidthMeter.java │ │ │ │ │ ├── DefaultDataSource.java │ │ │ │ │ ├── DefaultDataSourceFactory.java │ │ │ │ │ ├── DefaultHttpDataSource.java │ │ │ │ │ ├── DefaultHttpDataSourceFactory.java │ │ │ │ │ ├── DefaultLoadErrorHandlingPolicy.java │ │ │ │ │ ├── DummyDataSource.java │ │ │ │ │ ├── FileDataSource.java │ │ │ │ │ ├── FileDataSourceFactory.java │ │ │ │ │ ├── HttpDataSource.java │ │ │ │ │ ├── LoadErrorHandlingPolicy.java │ │ │ │ │ ├── Loader.java │ │ │ │ │ ├── LoaderErrorThrower.java │ │ │ │ │ ├── ParsingLoadable.java │ │ │ │ │ ├── PriorityDataSource.java │ │ │ │ │ ├── PriorityDataSourceFactory.java │ │ │ │ │ ├── RawResourceDataSource.java │ │ │ │ │ ├── ResolvingDataSource.java │ │ │ │ │ ├── StatsDataSource.java │ │ │ │ │ ├── TeeDataSource.java │ │ │ │ │ ├── TransferListener.java │ │ │ │ │ ├── UdpDataSource.java │ │ │ │ │ ├── cache/ │ │ │ │ │ │ ├── Cache.java │ │ │ │ │ │ ├── CacheDataSink.java │ │ │ │ │ │ ├── CacheDataSinkFactory.java │ │ │ │ │ │ ├── CacheDataSource.java │ │ │ │ │ │ ├── CacheDataSourceFactory.java │ │ │ │ │ │ ├── CacheEvictor.java │ │ │ │ │ │ ├── CacheFileMetadata.java │ │ │ │ │ │ ├── CacheFileMetadataIndex.java │ │ │ │ │ │ ├── CacheKeyFactory.java │ │ │ │ │ │ ├── CacheSpan.java │ │ │ │ │ │ ├── CacheUtil.java │ │ │ │ │ │ ├── CachedContent.java │ │ │ │ │ │ ├── CachedContentIndex.java │ │ │ │ │ │ ├── CachedRegionTracker.java │ │ │ │ │ │ ├── ContentMetadata.java │ │ │ │ │ │ ├── ContentMetadataMutations.java │ │ │ │ │ │ ├── DefaultContentMetadata.java │ │ │ │ │ │ ├── LeastRecentlyUsedCacheEvictor.java │ │ │ │ │ │ ├── NoOpCacheEvictor.java │ │ │ │ │ │ ├── SimpleCache.java │ │ │ │ │ │ └── SimpleCacheSpan.java │ │ │ │ │ └── crypto/ │ │ │ │ │ ├── AesCipherDataSink.java │ │ │ │ │ ├── AesCipherDataSource.java │ │ │ │ │ ├── AesFlushingCipher.java │ │ │ │ │ └── CryptoUtil.java │ │ │ │ ├── util/ │ │ │ │ │ ├── AmazonQuirks.java │ │ │ │ │ ├── Assertions.java │ │ │ │ │ ├── AtomicFile.java │ │ │ │ │ ├── Clock.java │ │ │ │ │ ├── CodecSpecificDataUtil.java │ │ │ │ │ ├── ColorParser.java │ │ │ │ │ ├── ConditionVariable.java │ │ │ │ │ ├── EGLSurfaceTexture.java │ │ │ │ │ ├── ErrorMessageProvider.java │ │ │ │ │ ├── EventDispatcher.java │ │ │ │ │ ├── EventLogger.java │ │ │ │ │ ├── FlacStreamMetadata.java │ │ │ │ │ ├── GlUtil.java │ │ │ │ │ ├── HandlerWrapper.java │ │ │ │ │ ├── LibraryLoader.java │ │ │ │ │ ├── Log.java │ │ │ │ │ ├── Logger.java │ │ │ │ │ ├── LongArray.java │ │ │ │ │ ├── MediaClock.java │ │ │ │ │ ├── MimeTypes.java │ │ │ │ │ ├── NalUnitUtil.java │ │ │ │ │ ├── NotificationUtil.java │ │ │ │ │ ├── ParsableBitArray.java │ │ │ │ │ ├── ParsableByteArray.java │ │ │ │ │ ├── ParsableNalUnitBitArray.java │ │ │ │ │ ├── Predicate.java │ │ │ │ │ ├── PriorityTaskManager.java │ │ │ │ │ ├── RepeatModeUtil.java │ │ │ │ │ ├── ReusableBufferedOutputStream.java │ │ │ │ │ ├── SlidingPercentile.java │ │ │ │ │ ├── StandaloneMediaClock.java │ │ │ │ │ ├── SystemClock.java │ │ │ │ │ ├── SystemHandlerWrapper.java │ │ │ │ │ ├── TimedValueQueue.java │ │ │ │ │ ├── TimestampAdjuster.java │ │ │ │ │ ├── TraceUtil.java │ │ │ │ │ ├── UriUtil.java │ │ │ │ │ ├── Util.java │ │ │ │ │ └── XmlPullParserUtil.java │ │ │ │ └── video/ │ │ │ │ ├── AvcConfig.java │ │ │ │ ├── ColorInfo.java │ │ │ │ ├── DolbyVisionConfig.java │ │ │ │ ├── DummySurface.java │ │ │ │ ├── HevcConfig.java │ │ │ │ ├── MediaCodecVideoRenderer.java │ │ │ │ ├── VideoFrameMetadataListener.java │ │ │ │ ├── VideoFrameReleaseTimeHelper.java │ │ │ │ ├── VideoListener.java │ │ │ │ ├── VideoRendererEventListener.java │ │ │ │ └── spherical/ │ │ │ │ ├── CameraMotionListener.java │ │ │ │ ├── CameraMotionRenderer.java │ │ │ │ ├── FrameRotationQueue.java │ │ │ │ ├── Projection.java │ │ │ │ └── ProjectionDecoder.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets/ │ │ │ │ ├── amr/ │ │ │ │ │ ├── sample_nb.amr │ │ │ │ │ ├── sample_nb.amr.0.dump │ │ │ │ │ ├── sample_nb_cbr.amr │ │ │ │ │ ├── sample_nb_cbr.amr.0.dump │ │ │ │ │ ├── sample_nb_cbr.amr.1.dump │ │ │ │ │ ├── sample_nb_cbr.amr.2.dump │ │ │ │ │ ├── sample_nb_cbr.amr.3.dump │ │ │ │ │ ├── sample_nb_cbr.amr.unklen.dump │ │ │ │ │ ├── sample_wb.amr │ │ │ │ │ ├── sample_wb.amr.0.dump │ │ │ │ │ ├── sample_wb_cbr.amr │ │ │ │ │ ├── sample_wb_cbr.amr.0.dump │ │ │ │ │ ├── sample_wb_cbr.amr.1.dump │ │ │ │ │ ├── sample_wb_cbr.amr.2.dump │ │ │ │ │ ├── sample_wb_cbr.amr.3.dump │ │ │ │ │ └── sample_wb_cbr.amr.unklen.dump │ │ │ │ ├── download-actions/ │ │ │ │ │ ├── dash-download-v0 │ │ │ │ │ ├── dash-remove-v0 │ │ │ │ │ ├── hls-download-v0 │ │ │ │ │ ├── hls-download-v1 │ │ │ │ │ ├── hls-remove-v0 │ │ │ │ │ ├── hls-remove-v1 │ │ │ │ │ ├── progressive-download-v0 │ │ │ │ │ ├── progressive-remove-v0 │ │ │ │ │ ├── ss-download-v0 │ │ │ │ │ ├── ss-download-v1 │ │ │ │ │ ├── ss-remove-v0 │ │ │ │ │ └── ss-remove-v1 │ │ │ │ ├── flv/ │ │ │ │ │ ├── sample.flv │ │ │ │ │ └── sample.flv.0.dump │ │ │ │ ├── mkv/ │ │ │ │ │ ├── sample.mkv │ │ │ │ │ ├── sample.mkv.0.dump │ │ │ │ │ ├── sample.mkv.1.dump │ │ │ │ │ ├── sample.mkv.2.dump │ │ │ │ │ ├── sample.mkv.3.dump │ │ │ │ │ ├── subsample_encrypted_altref.webm │ │ │ │ │ ├── subsample_encrypted_altref.webm.0.dump │ │ │ │ │ ├── subsample_encrypted_noaltref.webm │ │ │ │ │ └── subsample_encrypted_noaltref.webm.0.dump │ │ │ │ ├── mp3/ │ │ │ │ │ ├── bear.mp3.0.dump │ │ │ │ │ ├── bear.mp3.1.dump │ │ │ │ │ ├── bear.mp3.2.dump │ │ │ │ │ ├── bear.mp3.3.dump │ │ │ │ │ ├── play-trimmed.mp3.0.dump │ │ │ │ │ ├── play-trimmed.mp3.1.dump │ │ │ │ │ ├── play-trimmed.mp3.2.dump │ │ │ │ │ ├── play-trimmed.mp3.3.dump │ │ │ │ │ └── play-trimmed.mp3.unklen.dump │ │ │ │ ├── mp4/ │ │ │ │ │ ├── sample.mp4.0.dump │ │ │ │ │ ├── sample.mp4.1.dump │ │ │ │ │ ├── sample.mp4.2.dump │ │ │ │ │ ├── sample.mp4.3.dump │ │ │ │ │ ├── sample_fragmented.mp4.0.dump │ │ │ │ │ ├── sample_fragmented_seekable.mp4.0.dump │ │ │ │ │ ├── sample_fragmented_seekable.mp4.1.dump │ │ │ │ │ ├── sample_fragmented_seekable.mp4.2.dump │ │ │ │ │ ├── sample_fragmented_seekable.mp4.3.dump │ │ │ │ │ └── sample_fragmented_sei.mp4.0.dump │ │ │ │ ├── offline/ │ │ │ │ │ ├── action_file_for_download_index_upgrade.exi │ │ │ │ │ ├── action_file_incomplete_header.exi │ │ │ │ │ ├── action_file_no_data.exi │ │ │ │ │ ├── action_file_one_action.exi │ │ │ │ │ ├── action_file_two_actions.exi │ │ │ │ │ ├── action_file_unsupported_version.exi │ │ │ │ │ └── action_file_zero_actions.exi │ │ │ │ ├── ogg/ │ │ │ │ │ ├── bear.opus │ │ │ │ │ ├── bear.opus.0.dump │ │ │ │ │ ├── bear.opus.1.dump │ │ │ │ │ ├── bear.opus.2.dump │ │ │ │ │ ├── bear.opus.3.dump │ │ │ │ │ ├── bear.opus.unklen.dump │ │ │ │ │ ├── bear_flac.ogg │ │ │ │ │ ├── bear_flac.ogg.0.dump │ │ │ │ │ ├── bear_flac.ogg.1.dump │ │ │ │ │ ├── bear_flac.ogg.2.dump │ │ │ │ │ ├── bear_flac.ogg.3.dump │ │ │ │ │ ├── bear_flac.ogg.unklen.dump │ │ │ │ │ ├── bear_flac_noseektable.ogg │ │ │ │ │ ├── bear_flac_noseektable.ogg.0.dump │ │ │ │ │ ├── bear_flac_noseektable.ogg.1.dump │ │ │ │ │ ├── bear_flac_noseektable.ogg.2.dump │ │ │ │ │ ├── bear_flac_noseektable.ogg.3.dump │ │ │ │ │ ├── bear_flac_noseektable.ogg.unklen.dump │ │ │ │ │ ├── bear_vorbis.ogg │ │ │ │ │ ├── bear_vorbis.ogg.0.dump │ │ │ │ │ ├── bear_vorbis.ogg.1.dump │ │ │ │ │ ├── bear_vorbis.ogg.2.dump │ │ │ │ │ ├── bear_vorbis.ogg.3.dump │ │ │ │ │ └── bear_vorbis.ogg.unklen.dump │ │ │ │ ├── rawcc/ │ │ │ │ │ ├── sample.rawcc │ │ │ │ │ └── sample.rawcc.0.dump │ │ │ │ ├── ssa/ │ │ │ │ │ ├── empty │ │ │ │ │ ├── invalid_timecodes │ │ │ │ │ ├── no_end_timecodes │ │ │ │ │ ├── typical │ │ │ │ │ ├── typical_dialogue │ │ │ │ │ ├── typical_format │ │ │ │ │ └── typical_header │ │ │ │ ├── subrip/ │ │ │ │ │ ├── empty │ │ │ │ │ ├── no_end_timecodes │ │ │ │ │ ├── typical │ │ │ │ │ ├── typical_extra_blank_line │ │ │ │ │ ├── typical_missing_sequence │ │ │ │ │ ├── typical_missing_timecode │ │ │ │ │ ├── typical_negative_timestamps │ │ │ │ │ ├── typical_unexpected_end │ │ │ │ │ ├── typical_with_byte_order_mark │ │ │ │ │ └── typical_with_tags │ │ │ │ ├── ts/ │ │ │ │ │ ├── bbb_2500ms.ts │ │ │ │ │ ├── elephants_dream.mpg │ │ │ │ │ ├── sample.ac3 │ │ │ │ │ ├── sample.ac3.0.dump │ │ │ │ │ ├── sample.ac4 │ │ │ │ │ ├── sample.ac4.0.dump │ │ │ │ │ ├── sample.adts │ │ │ │ │ ├── sample.adts.0.dump │ │ │ │ │ ├── sample.eac3 │ │ │ │ │ ├── sample.eac3.0.dump │ │ │ │ │ ├── sample.ps │ │ │ │ │ ├── sample.ps.0.dump │ │ │ │ │ ├── sample.ps.1.dump │ │ │ │ │ ├── sample.ps.2.dump │ │ │ │ │ ├── sample.ps.3.dump │ │ │ │ │ ├── sample.ps.unklen.dump │ │ │ │ │ ├── sample.ts │ │ │ │ │ ├── sample.ts.0.dump │ │ │ │ │ ├── sample.ts.1.dump │ │ │ │ │ ├── sample.ts.2.dump │ │ │ │ │ ├── sample.ts.3.dump │ │ │ │ │ ├── sample.ts.unklen.dump │ │ │ │ │ ├── sample_cbs.adts │ │ │ │ │ ├── sample_cbs.adts.0.dump │ │ │ │ │ ├── sample_cbs.adts.1.dump │ │ │ │ │ ├── sample_cbs.adts.2.dump │ │ │ │ │ ├── sample_cbs.adts.3.dump │ │ │ │ │ ├── sample_cbs.adts.unklen.dump │ │ │ │ │ └── sample_with_sdt.ts │ │ │ │ ├── ttml/ │ │ │ │ │ ├── bitmap_percentage_region.xml │ │ │ │ │ ├── bitmap_pixel_region.xml │ │ │ │ │ ├── bitmap_unsupported_region.xml │ │ │ │ │ ├── chain_multiple_styles.xml │ │ │ │ │ ├── font_size.xml │ │ │ │ │ ├── font_size_empty.xml │ │ │ │ │ ├── font_size_invalid.xml │ │ │ │ │ ├── font_size_no_unit.xml │ │ │ │ │ ├── frame_rate.xml │ │ │ │ │ ├── inherit_and_override_style.xml │ │ │ │ │ ├── inherit_global_and_parent.xml │ │ │ │ │ ├── inherit_multiple_styles.xml │ │ │ │ │ ├── inherit_style.xml │ │ │ │ │ ├── inline_style_attributes.xml │ │ │ │ │ ├── multiple_regions.xml │ │ │ │ │ └── no_underline_linethrough.xml │ │ │ │ ├── tx3g/ │ │ │ │ │ ├── initialization │ │ │ │ │ ├── initialization_all_defaults │ │ │ │ │ ├── no_subtitle │ │ │ │ │ ├── sample_just_text │ │ │ │ │ ├── sample_utf16_be_no_styl │ │ │ │ │ ├── sample_utf16_le_no_styl │ │ │ │ │ ├── sample_with_multiple_styl │ │ │ │ │ ├── sample_with_other_extension │ │ │ │ │ ├── sample_with_styl │ │ │ │ │ ├── sample_with_styl_all_defaults │ │ │ │ │ └── sample_with_tbox │ │ │ │ ├── wav/ │ │ │ │ │ ├── sample.wav.0.dump │ │ │ │ │ ├── sample.wav.1.dump │ │ │ │ │ ├── sample.wav.2.dump │ │ │ │ │ └── sample.wav.3.dump │ │ │ │ ├── webm/ │ │ │ │ │ └── vorbis_codec_private │ │ │ │ └── webvtt/ │ │ │ │ ├── empty │ │ │ │ ├── typical │ │ │ │ ├── typical_with_bad_timestamps │ │ │ │ ├── typical_with_comments │ │ │ │ ├── typical_with_identifiers │ │ │ │ ├── with_bad_cue_header │ │ │ │ ├── with_bom │ │ │ │ ├── with_css_complex_selectors │ │ │ │ ├── with_css_styles │ │ │ │ ├── with_positioning │ │ │ │ └── with_tags │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ ├── CTest.java │ │ │ ├── DefaultLoadControlTest.java │ │ │ ├── DefaultMediaClockTest.java │ │ │ ├── ExoPlayerTest.java │ │ │ ├── FormatTest.java │ │ │ ├── MediaPeriodQueueTest.java │ │ │ ├── TimelineTest.java │ │ │ ├── analytics/ │ │ │ │ └── AnalyticsCollectorTest.java │ │ │ ├── audio/ │ │ │ │ ├── Ac3UtilTest.java │ │ │ │ ├── AudioFocusManagerTest.java │ │ │ │ ├── DefaultAudioSinkTest.java │ │ │ │ ├── SilenceSkippingAudioProcessorTest.java │ │ │ │ ├── SimpleDecoderAudioRendererTest.java │ │ │ │ └── SonicAudioProcessorTest.java │ │ │ ├── database/ │ │ │ │ └── VersionTableTest.java │ │ │ ├── drm/ │ │ │ │ ├── ClearKeyUtilTest.java │ │ │ │ ├── DrmInitDataTest.java │ │ │ │ └── OfflineLicenseHelperTest.java │ │ │ ├── extractor/ │ │ │ │ ├── ConstantBitrateSeekMapTest.java │ │ │ │ ├── DefaultExtractorInputTest.java │ │ │ │ ├── DefaultExtractorsFactoryTest.java │ │ │ │ ├── ExtractorTest.java │ │ │ │ ├── Id3PeekerTest.java │ │ │ │ ├── amr/ │ │ │ │ │ ├── AmrExtractorSeekTest.java │ │ │ │ │ └── AmrExtractorTest.java │ │ │ │ ├── flv/ │ │ │ │ │ └── FlvExtractorTest.java │ │ │ │ ├── mkv/ │ │ │ │ │ ├── DefaultEbmlReaderTest.java │ │ │ │ │ ├── MatroskaExtractorTest.java │ │ │ │ │ └── VarintReaderTest.java │ │ │ │ ├── mp3/ │ │ │ │ │ ├── Mp3ExtractorTest.java │ │ │ │ │ └── XingSeekerTest.java │ │ │ │ ├── mp4/ │ │ │ │ │ ├── AtomParsersTest.java │ │ │ │ │ ├── FragmentedMp4ExtractorTest.java │ │ │ │ │ ├── MdtaMetadataEntryTest.java │ │ │ │ │ ├── Mp4ExtractorTest.java │ │ │ │ │ └── PsshAtomUtilTest.java │ │ │ │ ├── ogg/ │ │ │ │ │ ├── DefaultOggSeekerTest.java │ │ │ │ │ ├── OggExtractorTest.java │ │ │ │ │ ├── OggPacketTest.java │ │ │ │ │ ├── OggPageHeaderTest.java │ │ │ │ │ ├── OggTestFile.java │ │ │ │ │ ├── VorbisBitArrayTest.java │ │ │ │ │ ├── VorbisReaderTest.java │ │ │ │ │ └── VorbisUtilTest.java │ │ │ │ ├── rawcc/ │ │ │ │ │ └── RawCcExtractorTest.java │ │ │ │ ├── ts/ │ │ │ │ │ ├── Ac3ExtractorTest.java │ │ │ │ │ ├── Ac4ExtractorTest.java │ │ │ │ │ ├── AdtsExtractorSeekTest.java │ │ │ │ │ ├── AdtsExtractorTest.java │ │ │ │ │ ├── AdtsReaderTest.java │ │ │ │ │ ├── PsDurationReaderTest.java │ │ │ │ │ ├── PsExtractorSeekTest.java │ │ │ │ │ ├── PsExtractorTest.java │ │ │ │ │ ├── SectionReaderTest.java │ │ │ │ │ ├── TsDurationReaderTest.java │ │ │ │ │ ├── TsExtractorSeekTest.java │ │ │ │ │ └── TsExtractorTest.java │ │ │ │ └── wav/ │ │ │ │ └── WavExtractorTest.java │ │ │ ├── metadata/ │ │ │ │ ├── MetadataRendererTest.java │ │ │ │ ├── emsg/ │ │ │ │ │ ├── EventMessageDecoderTest.java │ │ │ │ │ ├── EventMessageEncoderTest.java │ │ │ │ │ └── EventMessageTest.java │ │ │ │ ├── flac/ │ │ │ │ │ ├── PictureFrameTest.java │ │ │ │ │ └── VorbisCommentTest.java │ │ │ │ ├── icy/ │ │ │ │ │ ├── IcyDecoderTest.java │ │ │ │ │ ├── IcyHeadersTest.java │ │ │ │ │ └── IcyInfoTest.java │ │ │ │ ├── id3/ │ │ │ │ │ ├── ChapterFrameTest.java │ │ │ │ │ ├── ChapterTocFrameTest.java │ │ │ │ │ ├── Id3DecoderTest.java │ │ │ │ │ └── MlltFrameTest.java │ │ │ │ └── scte35/ │ │ │ │ └── SpliceInfoDecoderTest.java │ │ │ ├── offline/ │ │ │ │ ├── ActionFileTest.java │ │ │ │ ├── ActionFileUpgradeUtilTest.java │ │ │ │ ├── DefaultDownloadIndexTest.java │ │ │ │ ├── DefaultDownloaderFactoryTest.java │ │ │ │ ├── DownloadBuilder.java │ │ │ │ ├── DownloadHelperTest.java │ │ │ │ ├── DownloadManagerTest.java │ │ │ │ ├── DownloadRequestTest.java │ │ │ │ └── StreamKeyTest.java │ │ │ ├── source/ │ │ │ │ ├── ClippingMediaSourceTest.java │ │ │ │ ├── CompositeSequenceableLoaderTest.java │ │ │ │ ├── ConcatenatingMediaSourceTest.java │ │ │ │ ├── LoopingMediaSourceTest.java │ │ │ │ ├── MergingMediaSourceTest.java │ │ │ │ ├── SampleQueueTest.java │ │ │ │ ├── ShuffleOrderTest.java │ │ │ │ ├── SinglePeriodTimelineTest.java │ │ │ │ ├── TrackGroupArrayTest.java │ │ │ │ ├── TrackGroupTest.java │ │ │ │ ├── ads/ │ │ │ │ │ └── AdPlaybackStateTest.java │ │ │ │ └── chunk/ │ │ │ │ └── MediaChunkListIteratorTest.java │ │ │ ├── text/ │ │ │ │ ├── ssa/ │ │ │ │ │ └── SsaDecoderTest.java │ │ │ │ ├── subrip/ │ │ │ │ │ └── SubripDecoderTest.java │ │ │ │ ├── ttml/ │ │ │ │ │ ├── TtmlDecoderTest.java │ │ │ │ │ ├── TtmlRenderUtilTest.java │ │ │ │ │ └── TtmlStyleTest.java │ │ │ │ ├── tx3g/ │ │ │ │ │ └── Tx3gDecoderTest.java │ │ │ │ └── webvtt/ │ │ │ │ ├── CssParserTest.java │ │ │ │ ├── Mp4WebvttDecoderTest.java │ │ │ │ ├── WebvttCueParserTest.java │ │ │ │ ├── WebvttDecoderTest.java │ │ │ │ └── WebvttSubtitleTest.java │ │ │ ├── trackselection/ │ │ │ │ ├── AdaptiveTrackSelectionTest.java │ │ │ │ ├── BufferSizeAdaptiveTrackSelectionTest.java │ │ │ │ ├── DefaultTrackSelectorTest.java │ │ │ │ ├── MappingTrackSelectorTest.java │ │ │ │ ├── TrackSelectionUtilTest.java │ │ │ │ ├── TrackSelectorTest.java │ │ │ │ └── WindowedTrackBitrateEstimatorTest.java │ │ │ ├── upstream/ │ │ │ │ ├── AssetDataSourceTest.java │ │ │ │ ├── BaseDataSourceTest.java │ │ │ │ ├── ByteArrayDataSourceTest.java │ │ │ │ ├── DataSchemeDataSourceTest.java │ │ │ │ ├── DataSourceAsserts.java │ │ │ │ ├── DataSourceInputStreamTest.java │ │ │ │ ├── DataSpecTest.java │ │ │ │ ├── DefaultBandwidthMeterTest.java │ │ │ │ ├── DefaultLoadErrorHandlingPolicyTest.java │ │ │ │ ├── cache/ │ │ │ │ │ ├── CacheDataSourceTest.java │ │ │ │ │ ├── CacheDataSourceTest2.java │ │ │ │ │ ├── CacheUtilTest.java │ │ │ │ │ ├── CachedContentIndexTest.java │ │ │ │ │ ├── CachedRegionTrackerTest.java │ │ │ │ │ ├── DefaultContentMetadataTest.java │ │ │ │ │ ├── LeastRecentlyUsedCacheEvictorTest.java │ │ │ │ │ ├── SimpleCacheSpanTest.java │ │ │ │ │ └── SimpleCacheTest.java │ │ │ │ └── crypto/ │ │ │ │ └── AesFlushingCipherTest.java │ │ │ ├── util/ │ │ │ │ ├── AtomicFileTest.java │ │ │ │ ├── ColorParserTest.java │ │ │ │ ├── FlacStreamMetadataTest.java │ │ │ │ ├── MimeTypesTest.java │ │ │ │ ├── NalUnitUtilTest.java │ │ │ │ ├── ParsableBitArrayTest.java │ │ │ │ ├── ParsableByteArrayTest.java │ │ │ │ ├── ParsableNalUnitBitArrayTest.java │ │ │ │ ├── ReusableBufferedOutputStreamTest.java │ │ │ │ ├── TimedValueQueueTest.java │ │ │ │ ├── UriUtilTest.java │ │ │ │ └── UtilTest.java │ │ │ └── video/ │ │ │ └── spherical/ │ │ │ ├── FrameRotationQueueTest.java │ │ │ ├── ProjectionDecoderTest.java │ │ │ └── ProjectionTest.java │ │ ├── dash/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── source/ │ │ │ │ └── dash/ │ │ │ │ ├── DashChunkSource.java │ │ │ │ ├── DashManifestStaleException.java │ │ │ │ ├── DashMediaPeriod.java │ │ │ │ ├── DashMediaSource.java │ │ │ │ ├── DashSegmentIndex.java │ │ │ │ ├── DashUtil.java │ │ │ │ ├── DashWrappingSegmentIndex.java │ │ │ │ ├── DefaultDashChunkSource.java │ │ │ │ ├── EventSampleStream.java │ │ │ │ ├── PlayerEmsgHandler.java │ │ │ │ ├── manifest/ │ │ │ │ │ ├── AdaptationSet.java │ │ │ │ │ ├── DashManifest.java │ │ │ │ │ ├── DashManifestParser.java │ │ │ │ │ ├── DashManifestParser2.java │ │ │ │ │ ├── Descriptor.java │ │ │ │ │ ├── EventStream.java │ │ │ │ │ ├── Period.java │ │ │ │ │ ├── ProgramInformation.java │ │ │ │ │ ├── RangedUri.java │ │ │ │ │ ├── Representation.java │ │ │ │ │ ├── SegmentBase.java │ │ │ │ │ ├── SingleSegmentIndex.java │ │ │ │ │ ├── UrlTemplate.java │ │ │ │ │ └── UtcTimingElement.java │ │ │ │ └── offline/ │ │ │ │ └── DashDownloader.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets/ │ │ │ │ ├── sample_mpd │ │ │ │ ├── sample_mpd_event_stream │ │ │ │ ├── sample_mpd_labels │ │ │ │ ├── sample_mpd_segment_template │ │ │ │ └── sample_mpd_unknown_mime_type │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── source/ │ │ │ └── dash/ │ │ │ ├── DashMediaPeriodTest.java │ │ │ ├── DashMediaSourceTest.java │ │ │ ├── DashUtilTest.java │ │ │ ├── EventSampleStreamTest.java │ │ │ ├── manifest/ │ │ │ │ ├── DashManifestParserTest.java │ │ │ │ ├── DashManifestTest.java │ │ │ │ ├── RangedUriTest.java │ │ │ │ └── UrlTemplateTest.java │ │ │ └── offline/ │ │ │ ├── DashDownloadTestData.java │ │ │ ├── DashDownloaderTest.java │ │ │ ├── DownloadHelperTest.java │ │ │ ├── DownloadManagerDashTest.java │ │ │ └── DownloadServiceDashTest.java │ │ ├── hls/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── source/ │ │ │ │ └── hls/ │ │ │ │ ├── Aes128DataSource.java │ │ │ │ ├── DefaultHlsDataSourceFactory.java │ │ │ │ ├── DefaultHlsExtractorFactory.java │ │ │ │ ├── HlsChunkSource.java │ │ │ │ ├── HlsDataSourceFactory.java │ │ │ │ ├── HlsExtractorFactory.java │ │ │ │ ├── HlsManifest.java │ │ │ │ ├── HlsMediaChunk.java │ │ │ │ ├── HlsMediaPeriod.java │ │ │ │ ├── HlsMediaSource.java │ │ │ │ ├── HlsMetadataType.java │ │ │ │ ├── HlsSampleStream.java │ │ │ │ ├── HlsSampleStreamWrapper.java │ │ │ │ ├── HlsTrackMetadataEntry.java │ │ │ │ ├── SampleQueueMappingException.java │ │ │ │ ├── TimestampAdjusterProvider.java │ │ │ │ ├── WebvttExtractor.java │ │ │ │ ├── offline/ │ │ │ │ │ └── HlsDownloader.java │ │ │ │ └── playlist/ │ │ │ │ ├── DefaultHlsPlaylistParserFactory.java │ │ │ │ ├── DefaultHlsPlaylistTracker.java │ │ │ │ ├── FilteringHlsPlaylistParserFactory.java │ │ │ │ ├── HlsMasterPlaylist.java │ │ │ │ ├── HlsMediaPlaylist.java │ │ │ │ ├── HlsPlaylist.java │ │ │ │ ├── HlsPlaylistParser.java │ │ │ │ ├── HlsPlaylistParserFactory.java │ │ │ │ └── HlsPlaylistTracker.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── source/ │ │ │ └── hls/ │ │ │ ├── Aes128DataSourceTest.java │ │ │ ├── HlsMediaPeriodTest.java │ │ │ ├── WebvttExtractorTest.java │ │ │ ├── offline/ │ │ │ │ ├── DownloadHelperTest.java │ │ │ │ ├── HlsDownloadTestData.java │ │ │ │ └── HlsDownloaderTest.java │ │ │ └── playlist/ │ │ │ ├── HlsMasterPlaylistParserTest.java │ │ │ └── HlsMediaPlaylistParserTest.java │ │ ├── sabr/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ └── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── source/ │ │ │ │ └── sabr/ │ │ │ │ ├── DefaultSabrChunkSource.java │ │ │ │ ├── EventSampleStream.java │ │ │ │ ├── PlayerEmsgHandler.java │ │ │ │ ├── SabrChunkSource.java │ │ │ │ ├── SabrMediaPeriod.java │ │ │ │ ├── SabrMediaSource.java │ │ │ │ ├── SabrSegmentIndex.java │ │ │ │ ├── SabrWrappingSegmentIndex.java │ │ │ │ ├── manifest/ │ │ │ │ │ ├── AdaptationSet.java │ │ │ │ │ ├── EventStream.java │ │ │ │ │ ├── Period.java │ │ │ │ │ ├── RangedUri.java │ │ │ │ │ ├── Representation.java │ │ │ │ │ ├── SabrManifest.java │ │ │ │ │ ├── SabrManifestParser.java │ │ │ │ │ ├── SegmentBase.java │ │ │ │ │ ├── SingleSegmentIndex.java │ │ │ │ │ └── UrlTemplate.java │ │ │ │ └── parser/ │ │ │ │ ├── SabrProcessor.java │ │ │ │ ├── SabrStream.java │ │ │ │ ├── adapter/ │ │ │ │ │ ├── SabrFragmentedMp4Adapter.java │ │ │ │ │ └── SabrMatroskaAdapter.java │ │ │ │ ├── exceptions/ │ │ │ │ │ ├── MediaSegmentMismatchError.java │ │ │ │ │ ├── PoTokenError.java │ │ │ │ │ ├── SabrStreamConsumedError.java │ │ │ │ │ └── SabrStreamError.java │ │ │ │ ├── frames/ │ │ │ │ │ ├── AACFrameExtractor.java │ │ │ │ │ ├── AVCFrameExtractor.java │ │ │ │ │ ├── BaseFrameExtractor.java │ │ │ │ │ ├── FrameExtractor.java │ │ │ │ │ ├── OpusFrameExtractor.java │ │ │ │ │ ├── VP9FrameExtractor.java │ │ │ │ │ └── VorbisFrameExtractor.java │ │ │ │ ├── misc/ │ │ │ │ │ ├── EnabledTrackTypes.java │ │ │ │ │ ├── SabrExtractorInput.java │ │ │ │ │ └── Utils.java │ │ │ │ ├── models/ │ │ │ │ │ ├── AudioSelector.java │ │ │ │ │ ├── CaptionSelector.java │ │ │ │ │ ├── ConsumedRange.java │ │ │ │ │ ├── FormatSelector.java │ │ │ │ │ ├── Segment.java │ │ │ │ │ ├── SelectedFormat.java │ │ │ │ │ └── VideoSelector.java │ │ │ │ ├── parts/ │ │ │ │ │ ├── FormatInitializedSabrPart.java │ │ │ │ │ ├── MediaSeekSabrPart.java │ │ │ │ │ ├── MediaSegmentDataSabrPart.java │ │ │ │ │ ├── MediaSegmentEndSabrPart.java │ │ │ │ │ ├── MediaSegmentInitSabrPart.java │ │ │ │ │ ├── PoTokenStatusSabrPart.java │ │ │ │ │ ├── RefreshPlayerResponseSabrPart.java │ │ │ │ │ └── SabrPart.java │ │ │ │ ├── results/ │ │ │ │ │ ├── ProcessFormatInitializationMetadataResult.java │ │ │ │ │ ├── ProcessLiveMetadataResult.java │ │ │ │ │ ├── ProcessMediaEndResult.java │ │ │ │ │ ├── ProcessMediaHeaderResult.java │ │ │ │ │ ├── ProcessMediaResult.java │ │ │ │ │ ├── ProcessSabrSeekResult.java │ │ │ │ │ └── ProcessStreamProtectionStatusResult.java │ │ │ │ └── ump/ │ │ │ │ ├── UMPDecoder.java │ │ │ │ ├── UMPEncoder.java │ │ │ │ ├── UMPInputStream.java │ │ │ │ ├── UMPPart.java │ │ │ │ └── UMPPartId.java │ │ │ └── proto/ │ │ │ └── sabr/ │ │ │ ├── misc/ │ │ │ │ └── common.proto │ │ │ ├── other/ │ │ │ │ └── ump_part_id.proto │ │ │ └── videostreaming/ │ │ │ ├── buffered_range.proto │ │ │ ├── client_abr_state.proto │ │ │ ├── crypto_params.proto │ │ │ ├── format_initialization_metadata.proto │ │ │ ├── innertube_request.proto │ │ │ ├── live_metadata.proto │ │ │ ├── media_capabilities.proto │ │ │ ├── media_header.proto │ │ │ ├── next_request_policy.proto │ │ │ ├── onesie_header.proto │ │ │ ├── onesie_header_type.proto │ │ │ ├── onesie_innertube_request.proto │ │ │ ├── onesie_innertube_response.proto │ │ │ ├── onesie_proxy_status.proto │ │ │ ├── onesie_request.proto │ │ │ ├── playback_cookie.proto │ │ │ ├── reload_player_response.proto │ │ │ ├── sabr_context_sending_policy.proto │ │ │ ├── sabr_context_update.proto │ │ │ ├── sabr_error.proto │ │ │ ├── sabr_redirect.proto │ │ │ ├── sabr_seek.proto │ │ │ ├── stream_protection_status.proto │ │ │ ├── streamer_context.proto │ │ │ ├── time_range.proto │ │ │ └── video_playback_abr_request.proto │ │ ├── smoothstreaming/ │ │ │ ├── README.md │ │ │ ├── build.gradle │ │ │ └── src/ │ │ │ ├── main/ │ │ │ │ ├── AndroidManifest.xml │ │ │ │ └── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── source/ │ │ │ │ └── smoothstreaming/ │ │ │ │ ├── DefaultSsChunkSource.java │ │ │ │ ├── SsChunkSource.java │ │ │ │ ├── SsMediaPeriod.java │ │ │ │ ├── SsMediaSource.java │ │ │ │ ├── manifest/ │ │ │ │ │ ├── SsManifest.java │ │ │ │ │ ├── SsManifestParser.java │ │ │ │ │ └── SsUtil.java │ │ │ │ └── offline/ │ │ │ │ └── SsDownloader.java │ │ │ └── test/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets/ │ │ │ │ ├── sample_ismc_1 │ │ │ │ └── sample_ismc_2 │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── source/ │ │ │ └── smoothstreaming/ │ │ │ ├── SsMediaPeriodTest.java │ │ │ ├── SsTestUtils.java │ │ │ ├── manifest/ │ │ │ │ ├── SsManifestParserTest.java │ │ │ │ └── SsManifestTest.java │ │ │ └── offline/ │ │ │ ├── DownloadHelperTest.java │ │ │ └── SsDownloaderTest.java │ │ └── ui/ │ │ ├── README.md │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── ui/ │ │ │ │ ├── AspectRatioFrameLayout.java │ │ │ │ ├── DebugTextViewHelper.java │ │ │ │ ├── DefaultTimeBar.java │ │ │ │ ├── DefaultTrackNameProvider.java │ │ │ │ ├── DownloadNotificationHelper.java │ │ │ │ ├── DownloadNotificationUtil.java │ │ │ │ ├── PlaybackControlView.java │ │ │ │ ├── PlayerControlView.java │ │ │ │ ├── PlayerNotificationManager.java │ │ │ │ ├── PlayerView.java │ │ │ │ ├── SimpleExoPlayerView.java │ │ │ │ ├── SubtitlePainter.java │ │ │ │ ├── SubtitleView.java │ │ │ │ ├── TimeBar.java │ │ │ │ ├── TrackNameProvider.java │ │ │ │ ├── TrackSelectionDialogBuilder.java │ │ │ │ ├── TrackSelectionView.java │ │ │ │ └── spherical/ │ │ │ │ ├── CanvasRenderer.java │ │ │ │ ├── GlViewGroup.java │ │ │ │ ├── OrientationListener.java │ │ │ │ ├── PointerRenderer.java │ │ │ │ ├── ProjectionRenderer.java │ │ │ │ ├── SceneRenderer.java │ │ │ │ ├── SingleTapListener.java │ │ │ │ ├── SphericalSurfaceView.java │ │ │ │ └── TouchTracker.java │ │ │ └── res/ │ │ │ ├── drawable/ │ │ │ │ └── exo_edit_mode_logo.xml │ │ │ ├── drawable-anydpi-v21/ │ │ │ │ ├── exo_controls_fullscreen_enter.xml │ │ │ │ ├── exo_controls_fullscreen_exit.xml │ │ │ │ ├── exo_controls_repeat_all.xml │ │ │ │ ├── exo_controls_repeat_off.xml │ │ │ │ ├── exo_controls_repeat_one.xml │ │ │ │ ├── exo_controls_shuffle_off.xml │ │ │ │ ├── exo_controls_shuffle_on.xml │ │ │ │ ├── exo_icon_fastforward.xml │ │ │ │ ├── exo_icon_next.xml │ │ │ │ ├── exo_icon_pause.xml │ │ │ │ ├── exo_icon_play.xml │ │ │ │ ├── exo_icon_previous.xml │ │ │ │ ├── exo_icon_rewind.xml │ │ │ │ └── exo_icon_stop.xml │ │ │ ├── layout/ │ │ │ │ ├── exo_list_divider.xml │ │ │ │ ├── exo_playback_control_view.xml │ │ │ │ ├── exo_player_control_view.xml │ │ │ │ ├── exo_player_view.xml │ │ │ │ ├── exo_simple_player_view.xml │ │ │ │ └── exo_track_selection_dialog.xml │ │ │ ├── values/ │ │ │ │ ├── attrs.xml │ │ │ │ ├── constants.xml │ │ │ │ ├── drawables.xml │ │ │ │ ├── ids.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── values-af/ │ │ │ │ └── strings.xml │ │ │ ├── values-am/ │ │ │ │ └── strings.xml │ │ │ ├── values-ar/ │ │ │ │ └── strings.xml │ │ │ ├── values-az/ │ │ │ │ └── strings.xml │ │ │ ├── values-b+sr+Latn/ │ │ │ │ └── strings.xml │ │ │ ├── values-be/ │ │ │ │ └── strings.xml │ │ │ ├── values-bg/ │ │ │ │ └── strings.xml │ │ │ ├── values-bn/ │ │ │ │ └── strings.xml │ │ │ ├── values-bs/ │ │ │ │ └── strings.xml │ │ │ ├── values-ca/ │ │ │ │ └── strings.xml │ │ │ ├── values-cs/ │ │ │ │ └── strings.xml │ │ │ ├── values-da/ │ │ │ │ └── strings.xml │ │ │ ├── values-de/ │ │ │ │ └── strings.xml │ │ │ ├── values-el/ │ │ │ │ └── strings.xml │ │ │ ├── values-en-rAU/ │ │ │ │ └── strings.xml │ │ │ ├── values-en-rGB/ │ │ │ │ └── strings.xml │ │ │ ├── values-en-rIN/ │ │ │ │ └── strings.xml │ │ │ ├── values-es/ │ │ │ │ └── strings.xml │ │ │ ├── values-es-rUS/ │ │ │ │ └── strings.xml │ │ │ ├── values-et/ │ │ │ │ └── strings.xml │ │ │ ├── values-eu/ │ │ │ │ └── strings.xml │ │ │ ├── values-fa/ │ │ │ │ └── strings.xml │ │ │ ├── values-fi/ │ │ │ │ └── strings.xml │ │ │ ├── values-fr/ │ │ │ │ └── strings.xml │ │ │ ├── values-fr-rCA/ │ │ │ │ └── strings.xml │ │ │ ├── values-gl/ │ │ │ │ └── strings.xml │ │ │ ├── values-gu/ │ │ │ │ └── strings.xml │ │ │ ├── values-hi/ │ │ │ │ └── strings.xml │ │ │ ├── values-hr/ │ │ │ │ └── strings.xml │ │ │ ├── values-hu/ │ │ │ │ └── strings.xml │ │ │ ├── values-hy/ │ │ │ │ └── strings.xml │ │ │ ├── values-in/ │ │ │ │ └── strings.xml │ │ │ ├── values-is/ │ │ │ │ └── strings.xml │ │ │ ├── values-it/ │ │ │ │ └── strings.xml │ │ │ ├── values-iw/ │ │ │ │ └── strings.xml │ │ │ ├── values-ja/ │ │ │ │ └── strings.xml │ │ │ ├── values-ka/ │ │ │ │ └── strings.xml │ │ │ ├── values-kk/ │ │ │ │ └── strings.xml │ │ │ ├── values-km/ │ │ │ │ └── strings.xml │ │ │ ├── values-kn/ │ │ │ │ └── strings.xml │ │ │ ├── values-ko/ │ │ │ │ └── strings.xml │ │ │ ├── values-ky/ │ │ │ │ └── strings.xml │ │ │ ├── values-lo/ │ │ │ │ └── strings.xml │ │ │ ├── values-lt/ │ │ │ │ └── strings.xml │ │ │ ├── values-lv/ │ │ │ │ └── strings.xml │ │ │ ├── values-mk/ │ │ │ │ └── strings.xml │ │ │ ├── values-ml/ │ │ │ │ └── strings.xml │ │ │ ├── values-mn/ │ │ │ │ └── strings.xml │ │ │ ├── values-mr/ │ │ │ │ └── strings.xml │ │ │ ├── values-ms/ │ │ │ │ └── strings.xml │ │ │ ├── values-my/ │ │ │ │ └── strings.xml │ │ │ ├── values-nb/ │ │ │ │ └── strings.xml │ │ │ ├── values-ne/ │ │ │ │ └── strings.xml │ │ │ ├── values-nl/ │ │ │ │ └── strings.xml │ │ │ ├── values-pa/ │ │ │ │ └── strings.xml │ │ │ ├── values-pl/ │ │ │ │ └── strings.xml │ │ │ ├── values-pt/ │ │ │ │ └── strings.xml │ │ │ ├── values-pt-rPT/ │ │ │ │ └── strings.xml │ │ │ ├── values-ro/ │ │ │ │ └── strings.xml │ │ │ ├── values-ru/ │ │ │ │ └── strings.xml │ │ │ ├── values-si/ │ │ │ │ └── strings.xml │ │ │ ├── values-sk/ │ │ │ │ └── strings.xml │ │ │ ├── values-sl/ │ │ │ │ └── strings.xml │ │ │ ├── values-sq/ │ │ │ │ └── strings.xml │ │ │ ├── values-sr/ │ │ │ │ └── strings.xml │ │ │ ├── values-sv/ │ │ │ │ └── strings.xml │ │ │ ├── values-sw/ │ │ │ │ └── strings.xml │ │ │ ├── values-ta/ │ │ │ │ └── strings.xml │ │ │ ├── values-te/ │ │ │ │ └── strings.xml │ │ │ ├── values-th/ │ │ │ │ └── strings.xml │ │ │ ├── values-tl/ │ │ │ │ └── strings.xml │ │ │ ├── values-tr/ │ │ │ │ └── strings.xml │ │ │ ├── values-uk/ │ │ │ │ └── strings.xml │ │ │ ├── values-ur/ │ │ │ │ └── strings.xml │ │ │ ├── values-uz/ │ │ │ │ └── strings.xml │ │ │ ├── values-vi/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rCN/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rHK/ │ │ │ │ └── strings.xml │ │ │ ├── values-zh-rTW/ │ │ │ │ └── strings.xml │ │ │ └── values-zu/ │ │ │ └── strings.xml │ │ └── test/ │ │ ├── AndroidManifest.xml │ │ └── java/ │ │ └── com/ │ │ └── google/ │ │ └── android/ │ │ └── exoplayer2/ │ │ └── ui/ │ │ └── spherical/ │ │ ├── CanvasRendererTest.java │ │ └── TouchTrackerTest.java │ ├── playbacktests/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── androidTest/ │ │ │ ├── AndroidManifest.xml │ │ │ └── java/ │ │ │ └── com/ │ │ │ └── google/ │ │ │ └── android/ │ │ │ └── exoplayer2/ │ │ │ └── playbacktests/ │ │ │ └── gts/ │ │ │ ├── CommonEncryptionDrmTest.java │ │ │ ├── DashDownloadTest.java │ │ │ ├── DashStreamingTest.java │ │ │ ├── DashTestData.java │ │ │ ├── DashTestRunner.java │ │ │ ├── DashWidevineOfflineTest.java │ │ │ └── EnumerateDecodersTest.java │ │ └── main/ │ │ └── AndroidManifest.xml │ ├── publish.gradle │ ├── settings.gradle │ ├── testutils/ │ │ ├── build.gradle │ │ └── src/ │ │ ├── main/ │ │ │ ├── AndroidManifest.xml │ │ │ ├── java/ │ │ │ │ └── com/ │ │ │ │ └── google/ │ │ │ │ └── android/ │ │ │ │ └── exoplayer2/ │ │ │ │ └── testutil/ │ │ │ │ ├── Action.java │ │ │ │ ├── ActionSchedule.java │ │ │ │ ├── AutoAdvancingFakeClock.java │ │ │ │ ├── DebugRenderersFactory.java │ │ │ │ ├── DecoderCountersUtil.java │ │ │ │ ├── DummyMainThread.java │ │ │ │ ├── Dumper.java │ │ │ │ ├── ExoHostedTest.java │ │ │ │ ├── ExoPlayerTestRunner.java │ │ │ │ ├── ExtractorAsserts.java │ │ │ │ ├── FakeAdaptiveDataSet.java │ │ │ │ ├── FakeAdaptiveMediaPeriod.java │ │ │ │ ├── FakeAdaptiveMediaSource.java │ │ │ │ ├── FakeChunkSource.java │ │ │ │ ├── FakeClock.java │ │ │ │ ├── FakeDataSet.java │ │ │ │ ├── FakeDataSource.java │ │ │ │ ├── FakeExtractorInput.java │ │ │ │ ├── FakeExtractorOutput.java │ │ │ │ ├── FakeMediaPeriod.java │ │ │ │ ├── FakeMediaSource.java │ │ │ │ ├── FakeRenderer.java │ │ │ │ ├── FakeSampleStream.java │ │ │ │ ├── FakeTimeline.java │ │ │ │ ├── FakeTrackOutput.java │ │ │ │ ├── HostActivity.java │ │ │ │ ├── LogcatMetricsLogger.java │ │ │ │ ├── MetricsLogger.java │ │ │ │ └── TestUtil.java │ │ │ └── res/ │ │ │ └── layout/ │ │ │ └── exo_testutils_host_activity.xml │ │ └── test/ │ │ ├── AndroidManifest.xml │ │ └── java/ │ │ └── com/ │ │ └── google/ │ │ └── android/ │ │ └── exoplayer2/ │ │ └── testutil/ │ │ ├── FakeAdaptiveDataSetTest.java │ │ ├── FakeClockTest.java │ │ ├── FakeDataSetTest.java │ │ └── FakeDataSourceTest.java │ └── testutils_robolectric/ │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── com/ │ └── google/ │ └── android/ │ └── exoplayer2/ │ └── testutil/ │ ├── CacheAsserts.java │ ├── DefaultRenderersFactoryAsserts.java │ ├── FakeMediaChunk.java │ ├── FakeMediaChunkIterator.java │ ├── FakeMediaClockRenderer.java │ ├── FakeShuffleOrder.java │ ├── FakeTrackSelection.java │ ├── FakeTrackSelector.java │ ├── MediaPeriodAsserts.java │ ├── MediaSourceTestRunner.java │ ├── OggTestData.java │ ├── RobolectricUtil.java │ ├── StubExoPlayer.java │ ├── TestDownloadManagerListener.java │ └── TimelineAsserts.java ├── fastlane/ │ └── metadata/ │ └── android/ │ └── en-US/ │ ├── full_description.txt │ └── short_description.txt ├── filepicker-lib/ │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── arte/ │ │ └── programar/ │ │ └── materialfile/ │ │ ├── MaterialFilePicker.java │ │ ├── filter/ │ │ │ ├── CompositeFilter.java │ │ │ ├── FileFilter.java │ │ │ ├── HiddenFilter.java │ │ │ └── PatternFilter.java │ │ ├── ui/ │ │ │ ├── DirectoryAdapter.java │ │ │ ├── DirectoryFragment.java │ │ │ ├── FilePickerActivity.java │ │ │ ├── OnItemClickListener.java │ │ │ └── ThrottleClickListener.java │ │ ├── utils/ │ │ │ ├── FileComparator.java │ │ │ ├── FileTypeUtils.java │ │ │ └── FileUtils.java │ │ └── widget/ │ │ └── EmptyRecyclerView.java │ └── res/ │ ├── drawable/ │ │ ├── bg_clickable.xml │ │ ├── ic_app_apk.xml │ │ ├── ic_app_certificate.xml │ │ ├── ic_app_compress.xml │ │ ├── ic_app_database.xml │ │ ├── ic_app_directory.xml │ │ ├── ic_app_document.xml │ │ ├── ic_app_drawing.xml │ │ ├── ic_app_file.xml │ │ ├── ic_app_image.xml │ │ ├── ic_app_json.xml │ │ ├── ic_app_music.xml │ │ ├── ic_app_pdf.xml │ │ ├── ic_app_presentation.xml │ │ ├── ic_app_spreadsheet.xml │ │ ├── ic_app_video.xml │ │ └── ic_close.xml │ ├── drawable-night/ │ │ └── ic_close.xml │ ├── drawable-v21/ │ │ └── bg_clickable.xml │ ├── layout/ │ │ ├── activity_file_picker.xml │ │ ├── fragment_directory.xml │ │ └── item_file.xml │ ├── values/ │ │ ├── attr.xml │ │ ├── colors.xml │ │ ├── dimen.xml │ │ ├── strings.xml │ │ └── styles.xml │ ├── values-cs/ │ │ └── strings.xml │ ├── values-de/ │ │ └── strings.xml │ ├── values-es/ │ │ └── strings.xml │ ├── values-night/ │ │ ├── colors.xml │ │ └── styles.xml │ ├── values-night-v21/ │ │ └── styles.xml │ ├── values-night-v23/ │ │ └── styles.xml │ ├── values-night-v27/ │ │ └── styles.xml │ ├── values-night-v29/ │ │ └── styles.xml │ ├── values-ru/ │ │ └── strings.xml │ ├── values-sk/ │ │ └── strings.xml │ ├── values-v19/ │ │ └── styles.xml │ ├── values-v21/ │ │ └── styles.xml │ ├── values-v23/ │ │ └── styles.xml │ ├── values-v27/ │ │ └── styles.xml │ ├── values-v29/ │ │ └── styles.xml │ └── values-zh/ │ └── strings.xml ├── fragment-1.1.0/ │ ├── .gitignore │ ├── LICENSE │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ └── java/ │ └── androidx/ │ └── fragment/ │ └── app/ │ ├── BackStackRecord.java │ ├── BackStackState.java │ ├── DialogFragment.java │ ├── Fragment.java │ ├── FragmentActivity.java │ ├── FragmentContainer.java │ ├── FragmentController.java │ ├── FragmentFactory.java │ ├── FragmentHostCallback.java │ ├── FragmentManager.java │ ├── FragmentManagerImpl.java │ ├── FragmentManagerNonConfig.java │ ├── FragmentManagerState.java │ ├── FragmentManagerViewModel.java │ ├── FragmentPagerAdapter.java │ ├── FragmentState.java │ ├── FragmentStatePagerAdapter.java │ ├── FragmentTabHost.java │ ├── FragmentTransaction.java │ ├── FragmentTransition.java │ ├── FragmentTransitionCompat21.java │ ├── FragmentTransitionImpl.java │ ├── FragmentViewLifecycleOwner.java │ ├── ListFragment.java │ └── SuperNotCalledException.java ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── leanback-1.0.0/ │ ├── .gitignore │ ├── LICENSE │ ├── build.gradle │ └── src/ │ └── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── androidx/ │ │ └── leanback/ │ │ ├── animation/ │ │ │ ├── LogAccelerateInterpolator.java │ │ │ └── LogDecelerateInterpolator.java │ │ ├── app/ │ │ │ ├── BackgroundFragment.java │ │ │ ├── BackgroundManager.java │ │ │ ├── BaseFragment.java │ │ │ ├── BaseRowFragment.java │ │ │ ├── BaseRowSupportFragment.java │ │ │ ├── BaseSupportFragment.java │ │ │ ├── BrandedFragment.java │ │ │ ├── BrandedSupportFragment.java │ │ │ ├── BrowseFragment.java │ │ │ ├── BrowseSupportFragment.java │ │ │ ├── DetailsBackgroundVideoHelper.java │ │ │ ├── DetailsFragment.java │ │ │ ├── DetailsFragmentBackgroundController.java │ │ │ ├── DetailsSupportFragment.java │ │ │ ├── DetailsSupportFragmentBackgroundController.java │ │ │ ├── ErrorFragment.java │ │ │ ├── ErrorSupportFragment.java │ │ │ ├── FragmentUtil.java │ │ │ ├── GuidedStepFragment.java │ │ │ ├── GuidedStepRootLayout.java │ │ │ ├── GuidedStepSupportFragment.java │ │ │ ├── HeadersFragment.java │ │ │ ├── HeadersSupportFragment.java │ │ │ ├── ListRowDataAdapter.java │ │ │ ├── OnboardingFragment.java │ │ │ ├── OnboardingSupportFragment.java │ │ │ ├── PermissionHelper.java │ │ │ ├── PlaybackFragment.java │ │ │ ├── PlaybackFragmentGlueHost.java │ │ │ ├── PlaybackSupportFragment.java │ │ │ ├── PlaybackSupportFragmentGlueHost.java │ │ │ ├── ProgressBarManager.java │ │ │ ├── RowsFragment.java │ │ │ ├── RowsSupportFragment.java │ │ │ ├── SearchFragment.java │ │ │ ├── SearchSupportFragment.java │ │ │ ├── VerticalGridFragment.java │ │ │ ├── VerticalGridSupportFragment.java │ │ │ ├── VideoFragment.java │ │ │ ├── VideoFragmentGlueHost.java │ │ │ ├── VideoSupportFragment.java │ │ │ ├── VideoSupportFragmentGlueHost.java │ │ │ └── package-info.java │ │ ├── database/ │ │ │ └── CursorMapper.java │ │ ├── graphics/ │ │ │ ├── BoundsRule.java │ │ │ ├── ColorFilterCache.java │ │ │ ├── ColorFilterDimmer.java │ │ │ ├── ColorOverlayDimmer.java │ │ │ ├── CompositeDrawable.java │ │ │ └── FitWidthBitmapDrawable.java │ │ ├── media/ │ │ │ ├── MediaControllerAdapter.java │ │ │ ├── MediaControllerGlue.java │ │ │ ├── MediaPlayerAdapter.java │ │ │ ├── MediaPlayerGlue.java │ │ │ ├── PlaybackBannerControlGlue.java │ │ │ ├── PlaybackBaseControlGlue.java │ │ │ ├── PlaybackControlGlue.java │ │ │ ├── PlaybackGlue.java │ │ │ ├── PlaybackGlueHost.java │ │ │ ├── PlaybackTransportControlGlue.java │ │ │ ├── PlayerAdapter.java │ │ │ └── SurfaceHolderGlueHost.java │ │ ├── package-info.java │ │ ├── system/ │ │ │ └── Settings.java │ │ ├── transition/ │ │ │ ├── CustomChangeBounds.java │ │ │ ├── FadeAndShortSlide.java │ │ │ ├── LeanbackTransitionHelper.java │ │ │ ├── ParallaxTransition.java │ │ │ ├── Scale.java │ │ │ ├── SlideKitkat.java │ │ │ ├── SlideNoPropagation.java │ │ │ ├── TransitionEpicenterCallback.java │ │ │ ├── TransitionHelper.java │ │ │ ├── TransitionListener.java │ │ │ └── TranslationAnimationCreator.java │ │ ├── util/ │ │ │ ├── MathUtil.java │ │ │ └── StateMachine.java │ │ └── widget/ │ │ ├── AbstractDetailsDescriptionPresenter.java │ │ ├── AbstractMediaItemPresenter.java │ │ ├── AbstractMediaListHeaderPresenter.java │ │ ├── Action.java │ │ ├── ActionPresenterSelector.java │ │ ├── ArrayObjectAdapter.java │ │ ├── BackgroundHelper.java │ │ ├── BaseCardView.java │ │ ├── BaseGridView.java │ │ ├── BaseOnItemViewClickedListener.java │ │ ├── BaseOnItemViewSelectedListener.java │ │ ├── BrowseFrameLayout.java │ │ ├── BrowseRowsFrameLayout.java │ │ ├── CheckableImageView.java │ │ ├── ClassPresenterSelector.java │ │ ├── ControlBar.java │ │ ├── ControlBarPresenter.java │ │ ├── ControlButtonPresenterSelector.java │ │ ├── CursorObjectAdapter.java │ │ ├── DetailsOverviewLogoPresenter.java │ │ ├── DetailsOverviewRow.java │ │ ├── DetailsOverviewRowPresenter.java │ │ ├── DetailsOverviewSharedElementHelper.java │ │ ├── DetailsParallax.java │ │ ├── DetailsParallaxDrawable.java │ │ ├── DiffCallback.java │ │ ├── DividerPresenter.java │ │ ├── DividerRow.java │ │ ├── FacetProvider.java │ │ ├── FacetProviderAdapter.java │ │ ├── FocusHighlight.java │ │ ├── FocusHighlightHandler.java │ │ ├── FocusHighlightHelper.java │ │ ├── ForegroundHelper.java │ │ ├── FragmentAnimationProvider.java │ │ ├── FullWidthDetailsOverviewRowPresenter.java │ │ ├── FullWidthDetailsOverviewSharedElementHelper.java │ │ ├── Grid.java │ │ ├── GridLayoutManager.java │ │ ├── GuidanceStylingRelativeLayout.java │ │ ├── GuidanceStylist.java │ │ ├── GuidedAction.java │ │ ├── GuidedActionAdapter.java │ │ ├── GuidedActionAdapterGroup.java │ │ ├── GuidedActionAutofillSupport.java │ │ ├── GuidedActionDiffCallback.java │ │ ├── GuidedActionEditText.java │ │ ├── GuidedActionItemContainer.java │ │ ├── GuidedActionsRelativeLayout.java │ │ ├── GuidedActionsStylist.java │ │ ├── GuidedDatePickerAction.java │ │ ├── HeaderItem.java │ │ ├── HorizontalGridView.java │ │ ├── HorizontalHoverCardSwitcher.java │ │ ├── ImageCardView.java │ │ ├── ImeKeyMonitor.java │ │ ├── InvisibleRowPresenter.java │ │ ├── ItemAlignment.java │ │ ├── ItemAlignmentFacet.java │ │ ├── ItemAlignmentFacetHelper.java │ │ ├── ItemBridgeAdapter.java │ │ ├── ItemBridgeAdapterShadowOverlayWrapper.java │ │ ├── ListRow.java │ │ ├── ListRowHoverCardView.java │ │ ├── ListRowPresenter.java │ │ ├── ListRowView.java │ │ ├── MediaItemActionPresenter.java │ │ ├── MediaNowPlayingView.java │ │ ├── MediaRowFocusView.java │ │ ├── MultiActionsProvider.java │ │ ├── NonOverlappingFrameLayout.java │ │ ├── NonOverlappingLinearLayout.java │ │ ├── NonOverlappingLinearLayoutWithForeground.java │ │ ├── NonOverlappingRelativeLayout.java │ │ ├── NonOverlappingView.java │ │ ├── ObjectAdapter.java │ │ ├── OnActionClickedListener.java │ │ ├── OnChildLaidOutListener.java │ │ ├── OnChildSelectedListener.java │ │ ├── OnChildViewHolderSelectedListener.java │ │ ├── OnItemViewClickedListener.java │ │ ├── OnItemViewSelectedListener.java │ │ ├── PageRow.java │ │ ├── PagingIndicator.java │ │ ├── Parallax.java │ │ ├── ParallaxEffect.java │ │ ├── ParallaxTarget.java │ │ ├── PersistentFocusWrapper.java │ │ ├── PlaybackControlsPresenter.java │ │ ├── PlaybackControlsRow.java │ │ ├── PlaybackControlsRowPresenter.java │ │ ├── PlaybackControlsRowView.java │ │ ├── PlaybackRowPresenter.java │ │ ├── PlaybackSeekDataProvider.java │ │ ├── PlaybackSeekUi.java │ │ ├── PlaybackTransportRowPresenter.java │ │ ├── PlaybackTransportRowView.java │ │ ├── Presenter.java │ │ ├── PresenterSelector.java │ │ ├── PresenterSwitcher.java │ │ ├── RecyclerViewParallax.java │ │ ├── ResizingTextView.java │ │ ├── RoundedRectHelper.java │ │ ├── RoundedRectHelperApi21.java │ │ ├── Row.java │ │ ├── RowContainerView.java │ │ ├── RowHeaderPresenter.java │ │ ├── RowHeaderView.java │ │ ├── RowPresenter.java │ │ ├── ScaleFrameLayout.java │ │ ├── SearchBar.java │ │ ├── SearchEditText.java │ │ ├── SearchOrbView.java │ │ ├── SectionRow.java │ │ ├── SeekBar.java │ │ ├── ShadowHelper.java │ │ ├── ShadowHelperApi21.java │ │ ├── ShadowOverlayContainer.java │ │ ├── ShadowOverlayHelper.java │ │ ├── SinglePresenterSelector.java │ │ ├── SingleRow.java │ │ ├── SparseArrayObjectAdapter.java │ │ ├── SpeechOrbView.java │ │ ├── SpeechRecognitionCallback.java │ │ ├── StaggeredGrid.java │ │ ├── StaggeredGridDefault.java │ │ ├── StaticShadowHelper.java │ │ ├── StreamingTextView.java │ │ ├── ThumbsBar.java │ │ ├── TitleHelper.java │ │ ├── TitleView.java │ │ ├── TitleViewAdapter.java │ │ ├── Util.java │ │ ├── VerticalGridPresenter.java │ │ ├── VerticalGridView.java │ │ ├── VideoSurfaceView.java │ │ ├── ViewHolderTask.java │ │ ├── ViewsStateBundle.java │ │ ├── Visibility.java │ │ ├── WindowAlignment.java │ │ ├── package-info.java │ │ └── picker/ │ │ ├── DatePicker.java │ │ ├── Picker.java │ │ ├── PickerColumn.java │ │ ├── PickerUtility.java │ │ └── TimePicker.java │ └── res/ │ ├── anim/ │ │ ├── lb_decelerator_2.xml │ │ └── lb_decelerator_4.xml │ ├── animator/ │ │ ├── lb_guidedactions_item_pressed.xml │ │ ├── lb_guidedactions_item_unpressed.xml │ │ ├── lb_guidedstep_slide_down.xml │ │ ├── lb_guidedstep_slide_up.xml │ │ ├── lb_onboarding_description_enter.xml │ │ ├── lb_onboarding_logo_enter.xml │ │ ├── lb_onboarding_logo_exit.xml │ │ ├── lb_onboarding_page_indicator_enter.xml │ │ ├── lb_onboarding_page_indicator_fade_in.xml │ │ ├── lb_onboarding_page_indicator_fade_out.xml │ │ ├── lb_onboarding_start_button_fade_in.xml │ │ ├── lb_onboarding_start_button_fade_out.xml │ │ ├── lb_onboarding_title_enter.xml │ │ ├── lb_playback_bg_fade_in.xml │ │ ├── lb_playback_bg_fade_out.xml │ │ ├── lb_playback_controls_fade_in.xml │ │ ├── lb_playback_controls_fade_out.xml │ │ ├── lb_playback_description_fade_in.xml │ │ ├── lb_playback_description_fade_out.xml │ │ ├── lb_playback_rows_fade_in.xml │ │ └── lb_playback_rows_fade_out.xml │ ├── animator-v21/ │ │ ├── lb_onboarding_description_enter.xml │ │ ├── lb_onboarding_logo_enter.xml │ │ ├── lb_onboarding_logo_exit.xml │ │ ├── lb_onboarding_page_indicator_enter.xml │ │ ├── lb_onboarding_title_enter.xml │ │ ├── lb_playback_bg_fade_in.xml │ │ ├── lb_playback_bg_fade_out.xml │ │ └── lb_playback_description_fade_out.xml │ ├── drawable/ │ │ ├── lb_background.xml │ │ ├── lb_card_foreground.xml │ │ ├── lb_control_button_primary.xml │ │ ├── lb_control_button_secondary.xml │ │ ├── lb_headers_right_fading.xml │ │ ├── lb_onboarding_start_button_background.xml │ │ ├── lb_playback_now_playing_bar.xml │ │ ├── lb_playback_progress_bar.xml │ │ ├── lb_search_orb.xml │ │ └── lb_speech_orb.xml │ ├── drawable-v21/ │ │ ├── lb_action_bg.xml │ │ ├── lb_card_foreground.xml │ │ ├── lb_control_button_primary.xml │ │ ├── lb_control_button_secondary.xml │ │ └── lb_selectable_item_rounded_rect.xml │ ├── layout/ │ │ ├── lb_action_1_line.xml │ │ ├── lb_action_2_lines.xml │ │ ├── lb_background_window.xml │ │ ├── lb_browse_fragment.xml │ │ ├── lb_browse_title.xml │ │ ├── lb_control_bar.xml │ │ ├── lb_control_button_primary.xml │ │ ├── lb_control_button_secondary.xml │ │ ├── lb_details_description.xml │ │ ├── lb_details_fragment.xml │ │ ├── lb_details_overview.xml │ │ ├── lb_divider.xml │ │ ├── lb_error_fragment.xml │ │ ├── lb_fullwidth_details_overview.xml │ │ ├── lb_fullwidth_details_overview_logo.xml │ │ ├── lb_guidance.xml │ │ ├── lb_guidedactions.xml │ │ ├── lb_guidedactions_datepicker_item.xml │ │ ├── lb_guidedactions_item.xml │ │ ├── lb_guidedbuttonactions.xml │ │ ├── lb_guidedstep_background.xml │ │ ├── lb_guidedstep_fragment.xml │ │ ├── lb_header.xml │ │ ├── lb_headers_fragment.xml │ │ ├── lb_image_card_view.xml │ │ ├── lb_image_card_view_themed_badge_left.xml │ │ ├── lb_image_card_view_themed_badge_right.xml │ │ ├── lb_image_card_view_themed_content.xml │ │ ├── lb_image_card_view_themed_title.xml │ │ ├── lb_list_row.xml │ │ ├── lb_list_row_hovercard.xml │ │ ├── lb_media_item_number_view_flipper.xml │ │ ├── lb_media_list_header.xml │ │ ├── lb_onboarding_fragment.xml │ │ ├── lb_picker.xml │ │ ├── lb_picker_column.xml │ │ ├── lb_picker_item.xml │ │ ├── lb_picker_separator.xml │ │ ├── lb_playback_controls.xml │ │ ├── lb_playback_controls_row.xml │ │ ├── lb_playback_fragment.xml │ │ ├── lb_playback_now_playing_bars.xml │ │ ├── lb_playback_transport_controls.xml │ │ ├── lb_playback_transport_controls_row.xml │ │ ├── lb_row_container.xml │ │ ├── lb_row_header.xml │ │ ├── lb_row_media_item.xml │ │ ├── lb_row_media_item_action.xml │ │ ├── lb_rows_fragment.xml │ │ ├── lb_search_bar.xml │ │ ├── lb_search_fragment.xml │ │ ├── lb_search_orb.xml │ │ ├── lb_section_header.xml │ │ ├── lb_shadow.xml │ │ ├── lb_speech_orb.xml │ │ ├── lb_title_view.xml │ │ ├── lb_vertical_grid.xml │ │ ├── lb_vertical_grid_fragment.xml │ │ ├── lb_video_surface.xml │ │ └── video_surface_fragment.xml │ ├── raw/ │ │ ├── lb_voice_failure.ogg │ │ ├── lb_voice_no_input.ogg │ │ ├── lb_voice_open.ogg │ │ └── lb_voice_success.ogg │ ├── transition-v19/ │ │ ├── lb_browse_headers_in.xml │ │ └── lb_browse_headers_out.xml │ ├── transition-v21/ │ │ ├── lb_browse_enter_transition.xml │ │ ├── lb_browse_entrance_transition.xml │ │ ├── lb_browse_headers_in.xml │ │ ├── lb_browse_headers_out.xml │ │ ├── lb_browse_return_transition.xml │ │ ├── lb_details_enter_transition.xml │ │ ├── lb_details_return_transition.xml │ │ ├── lb_enter_transition.xml │ │ ├── lb_guidedstep_activity_enter.xml │ │ ├── lb_guidedstep_activity_enter_bottom.xml │ │ ├── lb_return_transition.xml │ │ ├── lb_shared_element_enter_transition.xml │ │ ├── lb_shared_element_return_transition.xml │ │ ├── lb_title_in.xml │ │ ├── lb_title_out.xml │ │ ├── lb_vertical_grid_enter_transition.xml │ │ ├── lb_vertical_grid_entrance_transition.xml │ │ └── lb_vertical_grid_return_transition.xml │ ├── values/ │ │ └── values.xml │ ├── values-af/ │ │ └── values-af.xml │ ├── values-am/ │ │ └── values-am.xml │ ├── values-ar/ │ │ └── values-ar.xml │ ├── values-as/ │ │ └── values-as.xml │ ├── values-az/ │ │ └── values-az.xml │ ├── values-b+sr+Latn/ │ │ └── values-b+sr+Latn.xml │ ├── values-be/ │ │ └── values-be.xml │ ├── values-bg/ │ │ └── values-bg.xml │ ├── values-bn/ │ │ └── values-bn.xml │ ├── values-bs/ │ │ └── values-bs.xml │ ├── values-ca/ │ │ └── values-ca.xml │ ├── values-cs/ │ │ └── values-cs.xml │ ├── values-da/ │ │ └── values-da.xml │ ├── values-de/ │ │ └── values-de.xml │ ├── values-el/ │ │ └── values-el.xml │ ├── values-en-rAU/ │ │ └── values-en-rAU.xml │ ├── values-en-rCA/ │ │ └── values-en-rCA.xml │ ├── values-en-rGB/ │ │ └── values-en-rGB.xml │ ├── values-en-rIN/ │ │ └── values-en-rIN.xml │ ├── values-en-rXC/ │ │ └── values-en-rXC.xml │ ├── values-es/ │ │ └── values-es.xml │ ├── values-es-rUS/ │ │ └── values-es-rUS.xml │ ├── values-et/ │ │ └── values-et.xml │ ├── values-eu/ │ │ └── values-eu.xml │ ├── values-fa/ │ │ └── values-fa.xml │ ├── values-fi/ │ │ └── values-fi.xml │ ├── values-fr/ │ │ └── values-fr.xml │ ├── values-fr-rCA/ │ │ └── values-fr-rCA.xml │ ├── values-gl/ │ │ └── values-gl.xml │ ├── values-gu/ │ │ └── values-gu.xml │ ├── values-hi/ │ │ └── values-hi.xml │ ├── values-hr/ │ │ └── values-hr.xml │ ├── values-hu/ │ │ └── values-hu.xml │ ├── values-hy/ │ │ └── values-hy.xml │ ├── values-in/ │ │ └── values-in.xml │ ├── values-is/ │ │ └── values-is.xml │ ├── values-it/ │ │ └── values-it.xml │ ├── values-iw/ │ │ └── values-iw.xml │ ├── values-ja/ │ │ └── values-ja.xml │ ├── values-ka/ │ │ └── values-ka.xml │ ├── values-kk/ │ │ └── values-kk.xml │ ├── values-km/ │ │ └── values-km.xml │ ├── values-kn/ │ │ └── values-kn.xml │ ├── values-ko/ │ │ └── values-ko.xml │ ├── values-ky/ │ │ └── values-ky.xml │ ├── values-ldrtl-v17/ │ │ └── values-ldrtl-v17.xml │ ├── values-lo/ │ │ └── values-lo.xml │ ├── values-lt/ │ │ └── values-lt.xml │ ├── values-lv/ │ │ └── values-lv.xml │ ├── values-mk/ │ │ └── values-mk.xml │ ├── values-ml/ │ │ └── values-ml.xml │ ├── values-mn/ │ │ └── values-mn.xml │ ├── values-mr/ │ │ └── values-mr.xml │ ├── values-ms/ │ │ └── values-ms.xml │ ├── values-my/ │ │ └── values-my.xml │ ├── values-nb/ │ │ └── values-nb.xml │ ├── values-ne/ │ │ └── values-ne.xml │ ├── values-nl/ │ │ └── values-nl.xml │ ├── values-or/ │ │ └── values-or.xml │ ├── values-pa/ │ │ └── values-pa.xml │ ├── values-pl/ │ │ └── values-pl.xml │ ├── values-pt/ │ │ └── values-pt.xml │ ├── values-pt-rBR/ │ │ └── values-pt-rBR.xml │ ├── values-pt-rPT/ │ │ └── values-pt-rPT.xml │ ├── values-ro/ │ │ └── values-ro.xml │ ├── values-ru/ │ │ └── values-ru.xml │ ├── values-si/ │ │ └── values-si.xml │ ├── values-sk/ │ │ └── values-sk.xml │ ├── values-sl/ │ │ └── values-sl.xml │ ├── values-sq/ │ │ └── values-sq.xml │ ├── values-sr/ │ │ └── values-sr.xml │ ├── values-sv/ │ │ └── values-sv.xml │ ├── values-sw/ │ │ └── values-sw.xml │ ├── values-ta/ │ │ └── values-ta.xml │ ├── values-te/ │ │ └── values-te.xml │ ├── values-th/ │ │ └── values-th.xml │ ├── values-tl/ │ │ └── values-tl.xml │ ├── values-tr/ │ │ └── values-tr.xml │ ├── values-uk/ │ │ └── values-uk.xml │ ├── values-ur/ │ │ └── values-ur.xml │ ├── values-uz/ │ │ └── values-uz.xml │ ├── values-v18/ │ │ └── values-v18.xml │ ├── values-v19/ │ │ └── values-v19.xml │ ├── values-v21/ │ │ └── values-v21.xml │ ├── values-v22/ │ │ └── values-v22.xml │ ├── values-vi/ │ │ └── values-vi.xml │ ├── values-zh-rCN/ │ │ └── values-zh-rCN.xml │ ├── values-zh-rHK/ │ │ └── values-zh-rHK.xml │ ├── values-zh-rTW/ │ │ └── values-zh-rTW.xml │ └── values-zu/ │ └── values-zu.xml ├── leanbackassistant/ │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── liskovsoft/ │ │ │ └── leanbackassistant/ │ │ │ ├── channels/ │ │ │ │ ├── ChannelsProvider.java │ │ │ │ ├── UpdateChannelsJobService.java │ │ │ │ ├── UpdateChannelsReceiver.java │ │ │ │ ├── UpdateChannelsTask.java │ │ │ │ └── UpdateChannelsWorker.java │ │ │ ├── media/ │ │ │ │ ├── Clip.java │ │ │ │ ├── ClipService.java │ │ │ │ ├── ClipServiceCached.java │ │ │ │ ├── Playlist.java │ │ │ │ └── scheduler/ │ │ │ │ └── ClipData.java │ │ │ ├── recommendations/ │ │ │ │ ├── RecommendationBuilder.java │ │ │ │ └── RecommendationsProvider.java │ │ │ ├── search/ │ │ │ │ ├── MockDatabase.java │ │ │ │ ├── SearchableActivity.java │ │ │ │ └── VideoContentProvider.java │ │ │ └── utils/ │ │ │ └── AppUtil.java │ │ └── res/ │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ ├── values-es/ │ │ │ └── strings.xml │ │ ├── values-ru/ │ │ │ └── strings.xml │ │ ├── values-tr/ │ │ │ └── strings.xml │ │ ├── values-uk/ │ │ │ └── strings.xml │ │ └── xml/ │ │ └── searchable.xml │ ├── stbeta/ │ │ ├── AndroidManifest.xml │ │ └── res/ │ │ ├── values/ │ │ │ └── strings.xml │ │ └── xml/ │ │ └── searchable.xml │ ├── stfdroid/ │ │ ├── AndroidManifest.xml │ │ └── res/ │ │ ├── values/ │ │ │ └── strings.xml │ │ └── xml/ │ │ └── searchable.xml │ └── ststable/ │ ├── AndroidManifest.xml │ └── res/ │ ├── values/ │ │ └── strings.xml │ └── xml/ │ └── searchable.xml ├── settings.gradle ├── slidableactivity/ │ ├── .gitignore │ ├── LICENSE.md │ ├── README.md │ ├── build.gradle │ ├── gradle.properties │ └── src/ │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── r0adkll/ │ │ │ └── slidr/ │ │ │ ├── ColorPanelSlideListener.java │ │ │ ├── ConfigPanelSlideListener.java │ │ │ ├── FragmentPanelSlideListener.java │ │ │ ├── Slidr.java │ │ │ ├── model/ │ │ │ │ ├── SlidrConfig.java │ │ │ │ ├── SlidrInterface.java │ │ │ │ ├── SlidrListener.java │ │ │ │ ├── SlidrListenerAdapter.java │ │ │ │ └── SlidrPosition.java │ │ │ ├── util/ │ │ │ │ └── ViewDragHelper.java │ │ │ └── widget/ │ │ │ ├── ScrimRenderer.java │ │ │ └── SliderPanel.java │ │ └── res/ │ │ └── values/ │ │ └── ids.xml │ └── test/ │ └── java/ │ └── com/ │ └── r0adkll/ │ └── slidr/ │ └── widget/ │ ├── ScrimRendererTest.java │ └── SliderPanelTest.java └── smarttubetv/ ├── build.gradle ├── google-services.json ├── multidex-keep.pro ├── proguard-rules.pro └── src/ ├── main/ │ ├── AndroidManifest.xml │ ├── assets/ │ │ └── common.properties │ ├── java/ │ │ └── com/ │ │ └── liskovsoft/ │ │ └── smartyoutubetv2/ │ │ └── tv/ │ │ ├── adapter/ │ │ │ ├── DeferredVideoGroupObjectAdapter.java │ │ │ ├── HeaderVideoGroupObjectAdapter.java │ │ │ ├── VideoGroupObjectAdapter.java │ │ │ └── vineyard/ │ │ │ ├── PaginationAdapter.java │ │ │ └── TagAdapter.java │ │ ├── launchers/ │ │ │ ├── ChannelsLauncherActivity.java │ │ │ ├── GamesLauncherActivity.java │ │ │ ├── HistoryLauncherActivity.java │ │ │ ├── HomeLauncherActivity.java │ │ │ ├── MusicLauncherActivity.java │ │ │ ├── NewsLauncherActivity.java │ │ │ ├── PlaylistsLauncherActivity.java │ │ │ ├── SearchLauncherActivity.java │ │ │ └── SubscriptionsLauncherActivity.java │ │ ├── presenter/ │ │ │ ├── ChannelCardPresenter.java │ │ │ ├── ChannelHeaderPresenter.java │ │ │ ├── CustomListRowPresenter.java │ │ │ ├── CustomVerticalGridPresenter.java │ │ │ ├── DetailsDescriptionPresenter.java │ │ │ ├── GridItemPresenter.java │ │ │ ├── IconHeaderItemPresenter.java │ │ │ ├── SearchFieldPresenter.java │ │ │ ├── SettingsCardPresenter.java │ │ │ ├── ShortsCardPresenter.java │ │ │ ├── TinyCardPresenter.java │ │ │ ├── VideoCardPresenter.java │ │ │ ├── base/ │ │ │ │ ├── LongClickPresenter.java │ │ │ │ └── OnItemLongPressedListener.java │ │ │ └── vineyard/ │ │ │ ├── IconItemPresenter.java │ │ │ ├── LoadingPresenter.java │ │ │ └── TagPresenter.java │ │ ├── ui/ │ │ │ ├── adddevice/ │ │ │ │ ├── AddDeviceActivity.java │ │ │ │ └── AddDeviceFragment.java │ │ │ ├── browse/ │ │ │ │ ├── BrowseActivity.java │ │ │ │ ├── BrowseFragment.java │ │ │ │ ├── BrowseSectionFragmentFactory.java │ │ │ │ ├── CategoryFragmentFactory.java.old │ │ │ │ ├── SectionHeaderItem.java │ │ │ │ ├── dialog/ │ │ │ │ │ └── ErrorDialogFragment.java │ │ │ │ ├── interfaces/ │ │ │ │ │ ├── Section.java │ │ │ │ │ ├── SettingsSection.java │ │ │ │ │ └── VideoSection.java │ │ │ │ ├── settings/ │ │ │ │ │ └── SettingsGridFragment.java │ │ │ │ └── video/ │ │ │ │ ├── GridFragmentHelper.java │ │ │ │ ├── MultiVideoGridFragment.java │ │ │ │ ├── MultipleRowsFragment.java │ │ │ │ ├── ShortsGridFragment.java │ │ │ │ ├── VideoGridFragment.java │ │ │ │ └── VideoRowsFragment.java │ │ │ ├── channel/ │ │ │ │ ├── ChannelActivity.java │ │ │ │ └── ChannelFragment.java │ │ │ ├── channeluploads/ │ │ │ │ ├── ChannelUploadsActivity.java │ │ │ │ └── ChannelUploadsFragment.java │ │ │ ├── common/ │ │ │ │ ├── DisplayUtils.java │ │ │ │ ├── LeanbackActivity.java │ │ │ │ ├── UriBackgroundManager.java │ │ │ │ └── keyhandler/ │ │ │ │ ├── DoubleBackManager.java │ │ │ │ ├── DoubleBackManager2.java │ │ │ │ └── LongClickManager.java │ │ │ ├── dialogs/ │ │ │ │ ├── AppDialogActivity.java │ │ │ │ ├── AppDialogFragment.java │ │ │ │ ├── AppPreferenceManager.java │ │ │ │ └── other/ │ │ │ │ ├── ChatPreference.java │ │ │ │ ├── ChatPreferenceDialogFragment.java │ │ │ │ ├── CommentsPreference.java │ │ │ │ ├── CommentsPreferenceDialogFragment.java │ │ │ │ ├── RadioListPreferenceDialogFragment.java │ │ │ │ ├── StringListPreference.java │ │ │ │ └── StringListPreferenceDialogFragment.java │ │ │ ├── main/ │ │ │ │ ├── MainApplication.java │ │ │ │ └── SplashActivity.java │ │ │ ├── mod/ │ │ │ │ ├── clickable/ │ │ │ │ │ ├── FindAddress.java │ │ │ │ │ ├── LinkifyCompat.java │ │ │ │ │ ├── PatternsCompat.java │ │ │ │ │ └── TextViewLinkHandler.java │ │ │ │ ├── fragments/ │ │ │ │ │ ├── ErrorSupportFragment.java │ │ │ │ │ ├── GridFragment.java │ │ │ │ │ └── MultiGridFragment.java │ │ │ │ └── leanback/ │ │ │ │ ├── headers/ │ │ │ │ │ └── ExtendedHeadersSupportFragment.java │ │ │ │ ├── misc/ │ │ │ │ │ ├── CustomBrowseSupportFragment.java │ │ │ │ │ ├── ProgressBarManager.java │ │ │ │ │ └── SeekBar.java │ │ │ │ ├── playerglue/ │ │ │ │ │ ├── MathUtil.java │ │ │ │ │ ├── framedrops/ │ │ │ │ │ │ ├── PlaybackBaseControlGlue.java │ │ │ │ │ │ └── PlaybackTransportControlGlue.java │ │ │ │ │ ├── seekpreview/ │ │ │ │ │ │ └── ThumbsBar.java │ │ │ │ │ ├── tooltips/ │ │ │ │ │ │ ├── ControlButtonPresenterSelector.java │ │ │ │ │ │ ├── TooltipCompatHandler.java │ │ │ │ │ │ └── TooltipPopup.java │ │ │ │ │ └── tweaks/ │ │ │ │ │ ├── ControlBar.java │ │ │ │ │ ├── ControlBarPresenter.java │ │ │ │ │ ├── MaxControlsVideoPlayerGlue.java │ │ │ │ │ ├── PlaybackControlsPresenter.java │ │ │ │ │ ├── PlaybackTransportRowPresenter.java │ │ │ │ │ └── PlaybackTransportRowView.java │ │ │ │ ├── preference/ │ │ │ │ │ ├── LeanbackListPreferenceDialogFragment.java │ │ │ │ │ ├── LeanbackPreferenceDialogFragment.java │ │ │ │ │ └── LeanbackPreferenceFragmentTransitionHelperApi21.java │ │ │ │ ├── search/ │ │ │ │ │ ├── NoScrollRowsSupportFragment.java │ │ │ │ │ └── SearchSupportFragment.java │ │ │ │ ├── transition/ │ │ │ │ │ ├── FadeAndShortSlide.java │ │ │ │ │ └── TranslationAnimationCreator.java │ │ │ │ └── widget/ │ │ │ │ └── OnActionLongClickedListener.java │ │ │ ├── playback/ │ │ │ │ ├── PlaybackActivity.java │ │ │ │ ├── PlaybackFragment.java │ │ │ │ ├── actions/ │ │ │ │ │ ├── AFRAction.java │ │ │ │ │ ├── ActionHelpers.java │ │ │ │ │ ├── ChannelAction.java │ │ │ │ │ ├── ChatAction.java │ │ │ │ │ ├── ClosedCaptioningAction.java │ │ │ │ │ ├── ContentBlockAction.java │ │ │ │ │ ├── FlipAction.java │ │ │ │ │ ├── HighQualityAction.java │ │ │ │ │ ├── PaddingAction.java │ │ │ │ │ ├── PipAction.java │ │ │ │ │ ├── PlaybackModeAction.java │ │ │ │ │ ├── PlaybackQueueAction.java │ │ │ │ │ ├── PlaylistAddAction.java │ │ │ │ │ ├── RotateAction.java │ │ │ │ │ ├── ScreenDimmingAction.java │ │ │ │ │ ├── SearchAction.java │ │ │ │ │ ├── SeekIntervalAction.java │ │ │ │ │ ├── ShareAction.java │ │ │ │ │ ├── SoundOffAction.java │ │ │ │ │ ├── SubscribeAction.java │ │ │ │ │ ├── ThumbsDownAction.java │ │ │ │ │ ├── ThumbsUpAction.java │ │ │ │ │ ├── TwoStateAction.java │ │ │ │ │ ├── VideoInfoAction.java │ │ │ │ │ ├── VideoSpeedAction.java │ │ │ │ │ ├── VideoStatsAction.java │ │ │ │ │ └── VideoZoomAction.java │ │ │ │ ├── mod/ │ │ │ │ │ ├── EventsOverridePlaybackFragment.java │ │ │ │ │ ├── SeekModePlaybackFragment.java │ │ │ │ │ └── surface/ │ │ │ │ │ ├── SurfacePlaybackFragment.java │ │ │ │ │ ├── SurfacePlaybackFragmentGlueHost.java │ │ │ │ │ ├── SurfaceViewWrapper.java │ │ │ │ │ ├── SurfaceWrapper.java │ │ │ │ │ ├── TextureViewSurfaceHolder.java │ │ │ │ │ └── TextureViewWrapper.java │ │ │ │ ├── other/ │ │ │ │ │ ├── BackboneQueueNavigator.java │ │ │ │ │ └── VideoPlayerGlue.java │ │ │ │ └── previewtimebar/ │ │ │ │ ├── GlideThumbnailTransformation.java │ │ │ │ ├── StoryboardManager.java │ │ │ │ └── StoryboardSeekDataProvider.java │ │ │ ├── search/ │ │ │ │ └── tags/ │ │ │ │ ├── SearchTagsActivity.java │ │ │ │ ├── SearchTagsFragment.java │ │ │ │ └── vineyard/ │ │ │ │ └── SearchTagsFragmentBase.java │ │ │ ├── signin/ │ │ │ │ ├── SignInActivity.java │ │ │ │ └── SignInFragment.java │ │ │ ├── webbrowser/ │ │ │ │ ├── WebBrowserActivity.java │ │ │ │ └── WebBrowserFragment.java │ │ │ └── widgets/ │ │ │ ├── browse/ │ │ │ │ └── NavigateTitleView.java │ │ │ ├── chat/ │ │ │ │ ├── ChatItemAuthor.java │ │ │ │ ├── ChatItemMessage.java │ │ │ │ └── LiveChatView.java │ │ │ ├── complexcardview/ │ │ │ │ ├── ComplexImageCardView.java │ │ │ │ └── ComplexImageView.java │ │ │ ├── embedplayer/ │ │ │ │ └── EmbedPlayerView.java │ │ │ ├── focus/ │ │ │ │ └── FocusFixBrowseFrameLayout.java │ │ │ ├── layout/ │ │ │ │ ├── TouchHorizontalGridView.java │ │ │ │ └── TouchVerticalGridView.java │ │ │ ├── marqueetextview/ │ │ │ │ ├── HeaderMarqueeTextView.java │ │ │ │ ├── MarqueeTextView.java │ │ │ │ └── TitleMarqueeTextView.java │ │ │ ├── marqueetextviewcompat/ │ │ │ │ ├── HeaderMarqueeTextViewCompat.java │ │ │ │ ├── MarqueeTextViewCompat.java │ │ │ │ └── TitleMarqueeTextViewCompat.java │ │ │ ├── search/ │ │ │ │ ├── LongClickSearchOrbView.java │ │ │ │ └── SearchSettingsOrbView.java │ │ │ ├── speedmarquee/ │ │ │ │ └── SpeedMarquee.java │ │ │ ├── styled/ │ │ │ │ └── CardProgressBar.java │ │ │ ├── test/ │ │ │ │ └── TestVerticalGridView.java │ │ │ ├── time/ │ │ │ │ ├── DateTimeView.java │ │ │ │ └── EndingTimeView.java │ │ │ └── vineyard/ │ │ │ ├── IconCardView.java │ │ │ ├── LoadingCardView.java │ │ │ ├── TagCardView.java │ │ │ └── videoview/ │ │ │ ├── LoopingVideoView.java │ │ │ ├── PreviewCardView.java │ │ │ └── VideoCardView.java │ │ └── util/ │ │ ├── GlideCachingModule.java │ │ ├── LongPressHandler.java │ │ ├── ViewUtil.java │ │ └── vineyard/ │ │ ├── NetworkUtil.java │ │ └── ToastFactory.java │ └── res/ │ ├── anim/ │ │ └── scroll_animation.xml │ ├── drawable/ │ │ ├── lb_search_orb.xml │ │ ├── player_background_controls.xml │ │ ├── player_background_controls_new.xml │ │ ├── player_background_suggestions.xml │ │ ├── progress_bar_grey.xml │ │ ├── progress_bar_red.xml │ │ ├── progress_bar_semi_red.xml │ │ ├── rounded_search_bg.xml │ │ ├── search_bar_cursor.xml │ │ ├── text_bg.xml │ │ ├── tooltip_frame_dark_mod.xml │ │ ├── tooltip_frame_light_mod.xml │ │ └── transparent_dialog_item_bg.xml │ ├── drawable-xhdpi/ │ │ └── default_background_gradient.xml │ ├── layout/ │ │ ├── abc_tooltip.xml │ │ ├── activity_playback.xml │ │ ├── channel_card.xml │ │ ├── channel_card_old.xml │ │ ├── channel_header.xml │ │ ├── chat_preference_fragment.xml │ │ ├── dialog_list_preference_item_multi.xml │ │ ├── fragment_app_settings.xml │ │ ├── fragment_channel.xml │ │ ├── fragment_channel_uploads.xml │ │ ├── fragment_grid.xml │ │ ├── fragment_main.xml │ │ ├── fragment_multi_grid.xml │ │ ├── fragment_playback.xml │ │ ├── fragment_search_tags.xml │ │ ├── fragment_signin.xml │ │ ├── fragment_webbrowser.xml │ │ ├── guidedstep_second_guidance.xml │ │ ├── icon_header_item.xml │ │ ├── lb_browse_title.xml │ │ ├── lb_control_bar.xml │ │ ├── lb_image_card_view.xml │ │ ├── lb_image_card_view_themed_content.xml │ │ ├── lb_image_card_view_themed_title.xml │ │ ├── lb_playback_fragment.xml │ │ ├── lb_playback_transport_controls_row.xml │ │ ├── lb_search_bar.xml │ │ ├── lb_title_view.xml │ │ ├── lb_title_view_logo.xml │ │ ├── lb_vertical_grid.xml │ │ ├── lb_vertical_grid1.xml │ │ ├── lb_vertical_grid2.xml │ │ ├── lb_video_card_view.xml │ │ ├── lb_video_surface.xml │ │ ├── lb_video_texture.xml │ │ ├── leanback_list_preference_fragment.xml │ │ ├── leanback_preference_fragment.xml │ │ ├── search_field.xml │ │ ├── settings_card.xml │ │ ├── text_badge_image_view.xml │ │ ├── text_badge_image_view_red.xml │ │ ├── view_loading_card.xml │ │ ├── view_options_item.xml │ │ ├── view_tag_card.xml │ │ ├── webbrowser.xml │ │ └── widget_preview_card.xml │ ├── layout-ldrtl/ │ │ └── lb_title_view.xml │ ├── raw/ │ │ └── keep.xml │ ├── values/ │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── marqueetextviewcompat.xml │ │ ├── speedmarquee.xml │ │ ├── strings.xml │ │ ├── styles.xml │ │ └── themes_old.xml │ └── xml/ │ ├── settings.xml │ └── whisperplay.xml ├── stbeta/ │ └── res/ │ └── values/ │ └── strings.xml ├── stfdroid/ │ └── res/ │ └── values/ │ └── strings.xml └── ststable/ └── res/ └── values/ └── strings.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # yuliskov patreon: smarttube open_collective: # smarttubeapp ko_fi: # smarttube tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # smarttube issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/ISSUE_TEMPLATE/1-bug_report.yml ================================================ name: Bug Report description: Create a bug report to help us improve title: "[BUG]: " labels: [bug] body: - type: markdown attributes: value: | Thank you for helping to make SmartTube better by reporting a bug. Please fill in as much information as possible about your bug. - type: checkboxes id: checklist attributes: label: "Checklist" options: - label: "I made sure that there are *no existing issues* - [open](https://github.com/yuliskov/SmartTube/issues) or [closed](https://github.com/yuliskov/SmartTube/issues?q=is%3Aissue+is%3Aclosed) - which I could contribute my information to." required: true - label: "I have read the [FAQ](https://github.com/yuliskov/SmartTube#faq) and my problem isn't listed." required: true - label: "I have taken the time to fill in all the required details. I understand that the bug report will be dismissed otherwise." required: true - label: "This issue contains only one bug." required: true - type: input id: app-version attributes: label: Affected version description: "In which SmartTube version did you encounter the bug?" placeholder: "x.xx.x - Can be seen in the app from the 'About' section in Settings" validations: required: true - type: dropdown id: device-type attributes: label: Device Type description: Is it Smart TV/Box or Phone options: - "Smart TV/Box" - "Phone/Tablet" validations: required: true - type: input id: device-os-info attributes: label: Affected Android description: | With what operating system (+ version) did you encounter the bug? placeholder: "Example: Android TV 10" validations: required: true - type: textarea id: steps-to-reproduce attributes: label: Steps to reproduce the bug description: | What did you do for the bug to show up? If you can't cause the bug to show up again reliably (and hence don't have a proper set of steps to give us), please still try to give as many details as possible on how you think you encountered the bug. placeholder: | 1. Go to '...' 2. Press on '....' validations: required: true - type: textarea id: actual-behavior attributes: label: Actual behavior description: | Tell us what happens with the steps given above. - type: textarea id: additional-information attributes: label: Additional information description: | Any other information you'd like to include, for instance that * Device (NVIDIA Shield, Xiaomi Mi Box, etc) * screenshot * your cat disabled your network connection * ... ================================================ FILE: .github/ISSUE_TEMPLATE/2-feature-request.yml ================================================ name: Feature Request description: Create a feature request for SmartTube title: '[Feature Request]: ' labels: ['enhancement'] body: - type: markdown attributes: value: Thanks for taking the time to file a feature request! Please fill out this form as completely as possible. - type: textarea attributes: label: Describe the feature you'd like to request description: A clear and concise description of what you want and what your use case is. validations: required: true - type: textarea attributes: label: Describe the solution you'd like description: A clear and concise description of what you want to happen. validations: required: true - type: textarea attributes: label: Describe alternatives you've considered description: A clear and concise description of any alternative solutions or features you've considered. validations: required: true - type: textarea id: additional-information attributes: label: Additional information description: | Any other information you'd like to include, for instance * Relevant issues * Mockup images * The feature would make your cat happy * ... ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false contact_links: - name: Telegram group (international) url: http://t.me/SmartTubeEN about: "The international group is in **English only**. But don't worry if your English is not perfect, we have a friendly international community." - name: Telegram group (RU/UA) url: http://t.me/SmartTubeUA about: "Group for Russian and Ukrainian speakers." ================================================ FILE: .github/workflows/CI.yml ================================================ name: Build Debug APK on: push: branches: [ "master" ] workflow_dispatch: jobs: build: runs-on: ubuntu-latest env: HAS_SIGNING_KEY: ${{ secrets.SIGNING_KEY != '' }} HAS_VT_KEY: ${{ secrets.VIRUS_TOTAL_API_KEY != '' }} steps: - name: Checkout Code uses: actions/checkout@v6 with: submodules: recursive - name: Set up JDK 17 uses: actions/setup-java@v5 with: java-version: '17' distribution: 'temurin' cache: 'gradle' - name: Update Version Name id: get_version run: | # Keep the original code to make the nightly and original builds interchangeable sed -i "s/versionName \"\(.*\)\"/versionName \"\1-nightly-${{ github.run_number }}\"/" smarttubetv/build.gradle echo "VERSION_NAME=$(grep "versionName" smarttubetv/build.gradle | head -n 1 | awk -F'"' '{print $2}')" >> $GITHUB_OUTPUT - name: Configure Build Signing if: ${{ env.HAS_SIGNING_KEY == 'true' }} run: | echo "storePassword=${{ secrets.KEY_STORE_PASSWORD }}" > keystore.properties echo "keyAlias=${{ secrets.ALIAS }}" >> keystore.properties echo "keyPassword=${{ secrets.KEY_PASSWORD }}" >> keystore.properties echo "storeFile=${{ github.workspace }}/key.jks" >> keystore.properties echo "${{ secrets.SIGNING_KEY }}" | base64 --decode > ${{ github.workspace }}/key.jks - name: Grant Gradle permissions run: chmod +x gradlew - name: Lint with Gradle run: ./gradlew lintStbetaRelease - name: Upload Lint Report uses: actions/upload-artifact@v7 if: always() with: name: lint-report path: '**/build/reports/lint-results-*.html' if-no-files-found: warn - name: Build with Gradle run: ./gradlew clean assembleStbetaRelease - name: VirusTotal Scan if: ${{ env.HAS_VT_KEY == 'true' }} id: vt uses: crazy-max/ghaction-virustotal@v5 with: vt_api_key: ${{ secrets.VIRUS_TOTAL_API_KEY }} files: | ./smarttubetv/build/outputs/apk/stbeta/release/*.apk request_rate: 4 - name: VirusTotal Summary if: steps.vt.outcome == 'success' run: | echo "Waiting 150s for VirusTotal engines to report..." sleep 150 echo "### Security Scan Results" >> $GITHUB_STEP_SUMMARY echo "| Artifact Name | VirusTotal Status | Detailed Report |" >> $GITHUB_STEP_SUMMARY echo "| :--- | :--- | :--- |" >> $GITHUB_STEP_SUMMARY for apk in ./smarttubetv/build/outputs/apk/stbeta/release/*.apk; do filename=$(basename "$apk") sha256=$(sha256sum "$apk" | awk '{print $1}') # Construct the dynamic badge URL using the hash badge_url="https://badges.cssnr.com/vt/id/$sha256?start=green&end=red&n=8" vt_link="https://www.virustotal.com/gui/file/$sha256" echo "| $filename | []($vt_link) | [View Report]($vt_link) |" >> $GITHUB_STEP_SUMMARY done - name: Upload ARM64 APK uses: actions/upload-artifact@v7 with: name: SmartTube_${{ steps.get_version.outputs.VERSION_NAME }}_arm64 path: ./smarttubetv/build/outputs/apk/stbeta/release/*_arm64-v8a.apk if-no-files-found: error - name: Upload ARMv7 APK uses: actions/upload-artifact@v7 with: name: SmartTube_${{ steps.get_version.outputs.VERSION_NAME }}_armeabi-v7a path: ./smarttubetv/build/outputs/apk/stbeta/release/*_armeabi-v7a.apk if-no-files-found: error - name: Upload Universal APK uses: actions/upload-artifact@v7 with: name: SmartTube_${{ steps.get_version.outputs.VERSION_NAME }}_universal path: ./smarttubetv/build/outputs/apk/stbeta/release/*_universal.apk if-no-files-found: error - name: Upload x86 APK uses: actions/upload-artifact@v7 with: name: SmartTube_${{ steps.get_version.outputs.VERSION_NAME }}_x86 path: ./smarttubetv/build/outputs/apk/stbeta/release/*_x86.apk if-no-files-found: error ================================================ FILE: .github/workflows/cleanup.yml ================================================ name: Cleanup old workflow runs on: schedule: - cron: '0 3 * * *' workflow_dispatch: jobs: cleanup: runs-on: ubuntu-latest if: github.event.repository.fork == false permissions: actions: write steps: - uses: actions/github-script@v8 with: script: | const KEEP = 0; const workflowNames = ["VirusTotal Scan", "Cleanup old workflow runs"]; // Get workflow ID const workflows = await github.rest.actions.listRepoWorkflows({ owner: context.repo.owner, repo: context.repo.repo }); for (const name of workflowNames) { const wf = workflows.data.workflows.find(w => w.name === name); if (!wf) { core.info(`Workflow "${name}" not found, skipping`); continue; } // List runs const runs = await github.rest.actions.listWorkflowRuns({ owner: context.repo.owner, repo: context.repo.repo, workflow_id: wf.id, per_page: 100 }); const toDelete = runs.data.workflow_runs.slice(KEEP); core.info(`Deleting ${toDelete.length} old runs`); for (const run of toDelete) { try { await github.rest.actions.deleteWorkflowRun({ owner: context.repo.owner, repo: context.repo.repo, run_id: run.id }); } catch (e) { core.info(`Skip run ${run.id}: ${e.message}`); } } } ================================================ FILE: .github/workflows/stale.yml ================================================ name: "Close stale issues" on: workflow_dispatch: branches: - master jobs: stale: runs-on: ubuntu-latest permissions: contents: read actions: write issues: write steps: - uses: actions/stale@v10 with: repo-token: ${{ secrets.GITHUB_TOKEN }} operations-per-run: 3000 # This may result in rate limiting, could we reduce and run in batches? days-before-stale: 420 # 1st Jan 2025, as of 25th Feb 2026 days-before-close: 0 ignore-updates: true stale-issue-message: '' close-issue-message: | **Due to a high volume of stale issues, all issues older than January 1st 2025 are being closed automatically.** Please feel free to resubmit your issue if you believe it has not been appropriately dealt with, and tag this issue in the "Additional Information" section. ================================================ FILE: .github/workflows/virustotal_scan.yml ================================================ name: VirusTotal Scan on: release: types: [published] workflow_dispatch: inputs: release_tag: description: 'Release tag to scan' required: true jobs: virustotal_scan: permissions: contents: write runs-on: ubuntu-latest env: HAS_VT_KEY: ${{ secrets.VIRUS_TOTAL_API_KEY != '' }} steps: - name: Set tag variable run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then echo "TAG_NAME=${{ github.event.inputs.release_tag }}" >> $GITHUB_ENV else echo "TAG_NAME=${{ github.event.release.tag_name }}" >> $GITHUB_ENV fi - name: Set report marker variable run: | echo -e "MARKER=\t\t\t" >> $GITHUB_ENV - name: Checkout code uses: actions/checkout@v6 - name: Download Release Assets env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p release_assets gh release download "$TAG_NAME" --dir release_assets --pattern "*.apk" - name: VirusTotal Scan if: ${{ env.HAS_VT_KEY == 'true' }} id: vt uses: crazy-max/ghaction-virustotal@v5 with: vt_api_key: ${{ secrets.VIRUS_TOTAL_API_KEY }} files: | release_assets/*.apk request_rate: 4 - name: Generate Custom Badge Report if: steps.vt.outcome == 'success' run: | echo "Waiting 150s for VirusTotal engines to report..." sleep 150 echo -e "$MARKER\n## 🛡️ VirusTotal Analysis" > vt_report.txt echo "" >> vt_report.txt echo "| Build Variant | VirusTotal Status | Detailed Report |" >> vt_report.txt echo "| :--- | :--- | :--- |" >> vt_report.txt for file in release_assets/*.apk; do [ -e "$file" ] || continue filename=$(basename "$file") sha256=$(sha256sum "$file" | awk '{print $1}') vt_link="https://www.virustotal.com/gui/file/$sha256/detection" badge_url="https://badges.cssnr.com/vt/id/$sha256?start=green&end=red&n=8" echo "Purging badge cache for $filename..." curl -s -X POST $badge_url asset_link="https://github.com/${{ github.repository }}/releases/download/${{ env.TAG_NAME }}/$filename" echo "| [$filename]($asset_link) | [)]($vt_link) | [View Report]($vt_link) |" >> vt_report.txt done - name: Update Release Notes env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | gh release view "$TAG_NAME" --json body -q .body > current_notes.txt || echo "" > current_notes.txt # If the header exists, delete from that line to the end if grep -q "$MARKER" current_notes.txt; then echo "Previous report found. Cleaning up..." sed -i "/$MARKER/,\$d" current_notes.txt fi { cat current_notes.txt cat vt_report.txt } > final_notes.txt gh release edit "$TAG_NAME" --notes-file final_notes.txt ================================================ FILE: .gitignore ================================================ # built application files *.apk *.ap_ # files for the dex VM *.dex # Java class files *.class # generated files bin/ gen/ # Local configuration file (sdk path, etc) local.properties # Windows thumbnail db Thumbs.db # OSX files .DS_Store # Eclipse project files .classpath .project # Android Studio .idea *.iml #.idea/workspace.xml - remove # and delete .idea if it better suit your needs. .gradle .google build/ # my other stuff /smartyoutubetv2/release /smarttube/release /smarttubetv/release # gradle 6 fix /smarttube/output /smarttubetv/output /smarttubemobile/release # google-services.json *.hprof log.txt android.keystore android-upd2.keystore build.log notes.txt out.txt out1.txt deps.txt logcat.txt /releases /files /misc /other /TODO.txt tmp/ *_bak* *_tmp *.bak* *.tmp *.7z .DS_Store /captures .externalNativeBuild fabric.properties *BAK.java hs_err_pid*.log ================================================ FILE: .gitmodules ================================================ [submodule "SharedModules"] path = SharedModules url = https://github.com/yuliskov/SharedModules [submodule "MediaServiceCore"] path = MediaServiceCore url = https://github.com/yuliskov/MediaServiceCore ================================================ FILE: .reuse/dep5 ================================================ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: SmartTube Source: https://github.com/yuliskov/SmartTube Files: * Copyright: 2020-present yuliskov License: MIT License: MIT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: . The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. . THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020-present yuliskov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================
# Important announcement about the app My development environment was infected by unknown malicious software, as a result of which a few builds may have been affected. Once the issue was detected, I secured everything with a full disk wipe, restored a clean setup, and now all builds are scanned with VirusTotal. The F-Droid version will also be verified before release. Public keys may have been compromised, which is why I am sharing this issue. You can download the new version and the new public key below, and instructions for restoring backups are provided. No extra actions are required since the app uses **one-time connection codes**. These codes have very limited permissions (for example, they cannot change your password). Still, you can revoke them if you want full peace of mind. # How to revoke access: 1. Open [myaccount.google.com/security](https://myaccount.google.com/security) 2. Find **“Your connections to third-party apps & services”** 3. Tap **“See all connection”** and locate **YouTube TV** or **Google Drive** 4. Select the app → **“Remove access”** Please keep built-in security features enabled to stay protected. # SmartTube  SmartTube is a free and open-source advanced media player for Android TVs and TV boxes. It allows you to play content from various public sources. ### ✅ Features - No ads - SponsorBlock integration - Adjustable playback speed - 8K resolution support - 60fps playback - HDR compatibility - View live chat - Customizable buttons - Does not require Google Services - Helpful international community ### ❌ Limitations - Not supported on phones and tablets - Comment functionality is unstable - Voice search and casting performance may be inferior to official apps, depending on your device Give it a try! **Do you have any question?** Ctrl+F or ⌘F this readme first! [**Installation**](#installation) | [**Official Site**](https://smarttubeapp.github.io) | [**Donation**](#donation) | [**FAQ**](#faq) | [Support / Chat](#support) | [Build](#build) | [Translate the app](https://jtbrinkmann.de/tools/android-strings.xml-translator.html) | [Changelog](https://t.me/s/SmartTubeNewsEN) | [Liability](#liability) ## Device support > [!IMPORTANT] > Starting in October 2025 new Amazon FireTV devices no longer run Android under the hood. SmartTube will **not** be compatibile with the Fire Stick 4k Select and newer devices which run Amazon's own VegaOS.  * **Supported:** all Android TVs and TV boxes (incl. All FireTV devices released before Oct. 2025, NVIDIA Shield & Chromecast with Google TV), even older ones with Android 4.3 (Kitkat). * **Not supported:** Smartphones, non-Android platforms like Samsung Tizen, LG webOS, Apple TV, etc. ## Installation > [video of the installation](images/new/zPV0imF.mp4) (note: download url changed to `kutt.to/stn_beta` or `kutt.to/stn_stable`) **Do not** download SmartTube from any **app store**, APK websites or blogs; these were uploaded by other people and may contain malware or ads. SmartTube is not officially published on any app store. Sadly, the Google PlayStore does not allow ad-free Youtube apps using unofficial APIs. There is a **beta release** (recommended) and a **stable release**. Beta gets new features and bugfixes faster than the stable release. You can use either of the following methods to install the app: - (**Easiest**) Install [Downloader by AFTVnews](https://www.aftvnews.com/downloader/) on your Android TV, open it and enter `kutt.to/stn_beta` or `kutt.to/stn_stable`, then read, understand and confirm the security prompts. (You can also enter [**79015**](https://aftv.news/79015) (for beta) or [**28544**](https://aftv.news/28544) (for stable), but this requires an extra step to install the AFTVnews Downloader browser addon if you haven't already.) - Install a file transfer app on your Android TV, download the APK on your phone or computer and transfer it to your TV (e.g. [_Send Files to TV_](https://sendfilestotv.app/) from the Google Play Store / Amazon AppStore) - Download the APK onto a USB stick, put the USB stick into your TV and use a file manager app from the Google Play Store / Amazon AppStore (e.g. [_FX File Explorer_](https://play.google.com/store/apps/details?id=nextapp.fx) or [_X-plore_](https://play.google.com/store/apps/details?id=com.lonelycatgames.Xplore)). Android's preinstalled file manager does not work! Do **not** get the ad-infested _FileCommander_. - If you are an advanced user, you can install it using ADB. [guide](https://fossbytes.com/side-load-apps-android-tv/#h-how-to-sideload-apps-on-your-android-tv-using-adb) | [alternative guide](https://www.aftvnews.com/sideload/) **Troubleshooting:** See device specific notes below. If installation fails, either your **disk space is full** or the APK file didn't download correctly; clear up space and try downloading again. If the app installed, but crashes when opening, make sure to install it to internal memory, not to an SD card / external storage. **The app has a built-in updater** with changelog. You can also find all releases and the **changelog** on the [Telegram channel @SmartTubeNewsEN](https://t.me/s/SmartTubeNewsEN) (readable without account) or on [Github](https://github.com/yuliskov/SmartTube/releases/). > latest [**beta download**](https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_beta.apk) > > latest [stable download](https://github.com/yuliskov/SmartTube/releases/download/latest/smarttube_stable.apk) ### Installation (Chromecast with Google TV) On **Chromecast with Google TV**, installation of apps is blocked by default, so an extra step is required: > **4.1. Enable Developer Options** > > On your Chromecast, open the side menu and go to _Settings > System > About_. Scroll down to the _Android TV OS build_ section and click that repeatedly. A toast message will appear, explaining that you are a few steps away from being a developer. Continue clicking until you trigger it. > > > **4.2. Turn on the "unknown sources" setting** > > Go back to the main _Settings_ page and select _Apps > Security & Restrictions > Unknown sources_. Turn on the toggle for \[_Downloader by AFTVnews_ or\] whichever file browser you decided to use [...]. > > [[source & picture guide](https://www.androidpolice.com/2021/02/07/how-to-sideload-any-apk-on-the-chromecast-with-android-tv/#install-the-apk)] After this, you can follow the [general installation guide](#installation) above. ### Installation (Xiaomi devices with Chinese firmware) Xiaomi's **Chinese firmware** might block the installation **of the beta version**. The international firmware is not affected. Solutions: 1. Use SmartTube's **stable version** instead (**recommended**) 2. Use the international firmware for your device 3. (if your device is from 2020 or before) You can do a factory reset and then install SmartTube beta before doing any system updates. You can then safely update your system, SmartTube should continue working. ### Updating The app has a built-in updater. You only need to follow the installation procedure **once**. A few seconds after launching SmartTube, it will notify you if there is any update and also show a changelog. You can disable automatic update checks or manually update in the settings under "about". If the installation fails, either your **disk space is full** or the update didn't download correctly; clear up space and try updating again (_Settings > About > Check for updates_). ## Compatibility SmartTube requires Android 4.3 or above. It does not work on non-Android devices (incl. LG or Samsung TVs). On unsupported TVs, you can use a TV stick or TV box. Though this app technically runs on smartphones and tablets, it is not optimized for such and offers no official support! It has been successfully tested on TVs, TV boxes and TV sticks that are based on Android, including: - Android TVs & Google TVs (e.g. Philips, Sony) - Chromecast with Google TV & TVs with _Chromecast built-in_ - Amazon FireTV stick (all generations) - NVIDIA Shield - TV boxes running Android (many cheap chinese no-name boxes) - Xiaomi Mi Box ## Features ### Adblocking SmartTube does not show any ad banners, preroll ads or ad intermissions. It not just tries to prevent them, it is literally programmed to be completely **unable** to display any ads, so YouTube cannot slip anything in. This also means you cannot allow ads or whitelist channels. Some YouTube channels include sponsored messages in their videos, these can also be skipped, see [SponsorBlock](#SponsorBlock) below. ### SponsorBlock SmartTube includes a SponsorBlock integration. From the [SponsorBlock website](https://sponsor.ajay.app/): > SponsorBlock is an open-source crowdsourced browser extension and open API for **skipping sponsor segments** in YouTube videos. [...] the extension automatically skips sponsors **it knows about** using a privacy preserving query system. It also supports skipping **other categories**, such as intros, outros and reminders to subscribe [and non-music parts in music videos]. You can select which categories you want to skip in the settings. Unlike the browser addon, in SmartTube you cannot submit new segments (TVs and TV remotes aren't great devices for such precise operations). Note that SponsorBlock is a free and voluntary project based on user submissions, so don't expect it to 100% work every time. Sometimes, sponsor segments are not yet submitted to the database, sometimes the SponsorBlock servers are offline/overloaded. ### Casting To cast videos from your phone (or other devices), you must link that device to your TV. Unlike the original YouTube app, SmartTube does not automatically show up when you are in the same wifi network. How to link your smartphone and TV: 1. open SmartTube and go to settings 2. go to "Remote control" (2nd option) 3. open your YouTube app on your phone, go to settings > General > watch on TV 4. click on _connect using TV-code_ and enter the code from your TV [**Screenshot guide**](https://t.me/SmartTubeEN/8514) Due to technical limitations, you need to open the app on the TV before casting; SmartTube cannot automatically wake up the TV. ### Picture-in-Picture (PiP) SmartTube supports playing videos in PiP mode. This needs to be enabled under _Settings > General > Background playback > Picture in picture_. The video will go into PiP mode when you press home while playing a video, and also when you press _back_ if enabled in _Settings > General > Background playback (activation)_. ### Adjust Speed You can adjust the playback speed pressing the speed-indicator icon (gauge) in the top row of the player. This is remembered across videos. Some speeds may case frame drops, this is a known issue. ### Voice Search To enable global voice search, an additional app must be installed alongside SmartTube. This _bridge app_ can intercept the System's attempts to open the original YouTube app and open SmartTube instead. For this to work, you must uninstall the original YouTube app. We know this sucks, but you can always reinstall it if you change your mind. The _bridge app_ will not show up in your launcher and you cannot launch it directly; it is only used internally by the system's voice search. On some devices, you need to explicitly say "Youtube" when searching (e.g. say "youtube cute cats" instead of just "cute cats"). **On Amazon Fire TV**: 1. Uninstall the original YouTube app (no root required) 2. Download and install the Amazon Bridge SmartTube app: https://kutt.to/stn_bridge_amazon (e.g. via _Downloader by AFTVnews_) **On Google Chromecast with Google TV**: 1. Uninstall the original YouTube app (no root required) 2. Download and install the ATV Bridge SmartTube app: https://kutt.to/stn_bridge_atv (e.g. via _Downloader by AFTVnews_) **On all other Android devices**, sadly root is required to enable this: 1. Root your device (search for a guide for your specific device) 2. Uninstall the official YouTube app using root (`adb shell pm uninstall com.google.android.youtube.tv`) 3. Download and install the ATV Bridge SmartTube app: https://kutt.to/stn_bridge_atv (e.g. via _Downloader by AFTVnews_) ## Donation If you want to support my developments you are welcome to buy me a cup of coffee :) - [**Patreon (Visa, Mastercard, PayPal)**](https://www.patreon.com/smarttube) - **PayPal**: firsthash@gmail.com - **BTC**: 1JAT5VVWarVBkpVbNDn8UA8HXNdrukuBSx - **LTC**: ltc1qgc24eq9jl9cq78qnd5jpqhemkajg9vudwyd8pw - **ETH**: 0xe455E21a085ae195a097cd4F456051A9916A5064 - **ETC**: 0x209eCd33Fa61fA92167595eB3Aea92EE1905c815 - **TRX**: TJNPY794aSGZf3WGHTna2VCWm2G5Yua7E8 - **USDT (TRC20)**: TJNPY794aSGZf3WGHTna2VCWm2G5Yua7E8 - **USDT (BEP20)**: 0x64B28da787BE6ac5889D276A5638d4f077840eC5 - **USDT (ERC20)**: 0xe455e21a085ae195a097cd4f456051a9916a5064 - **TON**: UQAc9zgnnzwS8yb5wxAu5CB0RddmjPBjWI-n46oQ7XfCQrgI - **XMR**: 48QsMjqfkeW54vkgKyRnjodtYxdmLk6HXfTWPSZoaFPEDpoHDwFUciGCe1QC9VAeGrgGw4PKNAksX9RW7myFqYJQDN5cHGT ## Support **Please check the [FAQ](#faq) first!** Also at least have a short look at the recent chat history. You can report in our Telegram group or via [issue tracker on Github](https://github.com/yuliskov/SmartTube/issues) (account required). - **Telegram group (international)**: [@SmartTubeEN](http://t.me/SmartTubeEN) - **Discord group (international)**: [SmartTube Official](https://discord.gg/Wt8HDDej5z) - **Telegram group (RU/UA)**: [@SmartTubeUA](http://t.me/SmartTubeUA) - **Email**: firsthash@gmail.com The international group is in **English only**. But don't worry if your English is not perfect, we have a friendly international community. ## Team SmartTube is developed single-handedly; there is no larger team or company behind this. This is an open source, hobby project. Several others have helped with translations, some of which can be seen on [Github](https://github.com/yuliskov/SmartTube/graphs/contributors), some have sent their translations directly to Yurii. There are also helpful people in the support chat. ## Build **NOTE: OpenJDK 14 or older (!) is required. Newer JDK could cause app crash!** To build and install debug version, run these commands: ``` git clone https://github.com/yuliskov/SmartTube.git cd SmartTube git submodule update --init adb connect* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at *
* http://www.apache.org/licenses/LICENSE-2.0 *
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.commons;
import android.widget.ImageView;
import androidx.annotation.Nullable;
/**
* Callback for implementing images loading in message list
*/
public interface ImageLoader {
void loadImage(ImageView imageView, @Nullable String url, @Nullable Object payload);
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/commons/InputTrackingRecyclerViewAdapter.java
================================================
package com.stfalcon.chatkit.commons;
import android.view.KeyEvent;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by vektor on 31/05/16.
*/
public abstract class InputTrackingRecyclerViewAdapter
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.commons;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Build.VERSION;
import android.util.AttributeSet;
import android.util.TypedValue;
import androidx.annotation.AttrRes;
import androidx.annotation.ColorRes;
import androidx.annotation.DimenRes;
import androidx.annotation.DrawableRes;
import androidx.core.content.ContextCompat;
import com.stfalcon.chatkit.R;
/**
* Base class for chat component styles
*/
public abstract class Style {
protected Context context;
protected Resources resources;
protected AttributeSet attrs;
protected Style(Context context, AttributeSet attrs) {
this.context = context;
this.resources = context.getResources();
this.attrs = attrs;
}
protected final int getSystemAccentColor() {
return getSystemColor(VERSION.SDK_INT >= 21 ? android.R.attr.colorAccent : R.attr.colorAccent);
}
protected final int getSystemPrimaryColor() {
return getSystemColor(VERSION.SDK_INT >= 21 ? android.R.attr.colorPrimary : R.attr.colorPrimary);
}
protected final int getSystemPrimaryDarkColor() {
return getSystemColor(VERSION.SDK_INT >= 21 ? android.R.attr.colorPrimaryDark : R.attr.colorPrimaryDark);
}
protected final int getSystemPrimaryTextColor() {
return getSystemColor(android.R.attr.textColorPrimary);
}
protected final int getSystemHintColor() {
return getSystemColor(android.R.attr.textColorHint);
}
protected final int getSystemColor(@AttrRes int attr) {
TypedValue typedValue = new TypedValue();
TypedArray a = context.obtainStyledAttributes(typedValue.data, new int[]{attr});
// MOD: Invisible link fix on old devices (provide the default color)
int color = a.getColor(0, getColor(R.color.dark_red));
a.recycle();
return color;
}
protected final int getDimension(@DimenRes int dimen) {
return resources.getDimensionPixelSize(dimen);
}
protected final int getColor(@ColorRes int color) {
return ContextCompat.getColor(context, color);
}
protected final Drawable getDrawable(@DrawableRes int drawable) {
return ContextCompat.getDrawable(context, drawable);
}
protected final Drawable getVectorDrawable(@DrawableRes int drawable) {
return ContextCompat.getDrawable(context, drawable);
}
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/commons/ViewHolder.java
================================================
/*******************************************************************************
* Copyright 2016 stfalcon.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.commons;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView;
/**
* Base ViewHolder
*/
public abstract class ViewHolder extends RecyclerView.ViewHolder {
public abstract void onBind(DATA data);
public ViewHolder(View itemView) {
super(itemView);
}
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/commons/models/IDialog.java
================================================
/*******************************************************************************
* Copyright 2016 stfalcon.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.commons.models;
import java.util.List;
/**
* For implementing by real dialog model
*/
public interface IDialog
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.commons.models;
import java.util.Date;
/**
* For implementing by real message model
*/
public interface IMessage {
/**
* Returns message identifier
*
* @return the message id
*/
String getId();
/**
* Returns message text
*
* @return the message text
*/
CharSequence getText();
/**
* Returns message author. See the {@link IUser} for more details
*
* @return the message author
*/
IUser getUser();
/**
* Returns message creation date
*
* @return the message creation date
*/
Date getCreatedAt();
static boolean checkMessage(IMessage message) {
return message != null && message.getId() != null && message.getUser() != null && message.getUser().getId() != null
&& message.getText() != null;
}
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/commons/models/IUser.java
================================================
/*******************************************************************************
* Copyright 2016 stfalcon.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.commons.models;
/**
* For implementing by real user model
*/
public interface IUser {
/**
* Returns the user's id
*
* @return the user's id
*/
String getId();
/**
* Returns the user's name
*
* @return the user's name
*/
String getName();
/**
* Returns the user's avatar image url
*
* @return the user's avatar image url
*/
String getAvatar();
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/commons/models/MessageContentType.java
================================================
/*******************************************************************************
* Copyright 2016 stfalcon.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.commons.models;
import androidx.annotation.Nullable;
import com.stfalcon.chatkit.messages.MessageHolders;
/*
* Created by troy379 on 28.03.17.
*/
/**
* Interface used to mark messages as custom content types. For its representation see {@link MessageHolders}
*/
public interface MessageContentType extends IMessage {
/**
* Default media type for image message.
*/
interface Image extends IMessage {
@Nullable
String getImageUrl();
}
// other default types will be here
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/commons/widgets/FocusFixRelativeLayout.java
================================================
package com.stfalcon.chatkit.commons.widgets;
import android.content.Context;
import android.util.AttributeSet;
import android.widget.RelativeLayout;
/**
* https://stackoverflow.com/questions/34277425/recyclerview-items-lose-focus
*/
public class FocusFixRelativeLayout extends RelativeLayout {
public FocusFixRelativeLayout(Context context) {
super(context);
}
public FocusFixRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FocusFixRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@SuppressWarnings("NewApi")
public FocusFixRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void clearFocus() {
if (getParent() != null) {
super.clearFocus();
}
}
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/commons/widgets/WrapWidthTextView.java
================================================
package com.stfalcon.chatkit.commons.widgets;
import android.content.Context;
import android.text.Layout;
import android.util.AttributeSet;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatTextView;
/**
* Alter multiline TextView behavior to wrap content exactly.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.dialogs;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.util.AttributeSet;
import com.stfalcon.chatkit.R;
import com.stfalcon.chatkit.commons.Style;
/**
* Style for DialogList customization by xml attributes
*/
@SuppressWarnings("WeakerAccess")
class DialogListStyle extends Style {
private int dialogTitleTextColor;
private int dialogTitleTextSize;
private int dialogTitleTextStyle;
private int dialogUnreadTitleTextColor;
private int dialogUnreadTitleTextStyle;
private int dialogMessageTextColor;
private int dialogMessageTextSize;
private int dialogMessageTextStyle;
private int dialogUnreadMessageTextColor;
private int dialogUnreadMessageTextStyle;
private int dialogDateColor;
private int dialogDateSize;
private int dialogDateStyle;
private int dialogUnreadDateColor;
private int dialogUnreadDateStyle;
private boolean dialogUnreadBubbleEnabled;
private int dialogUnreadBubbleTextColor;
private int dialogUnreadBubbleTextSize;
private int dialogUnreadBubbleTextStyle;
private int dialogUnreadBubbleBackgroundColor;
private int dialogAvatarWidth;
private int dialogAvatarHeight;
private boolean dialogMessageAvatarEnabled;
private int dialogMessageAvatarWidth;
private int dialogMessageAvatarHeight;
private boolean dialogDividerEnabled;
private int dialogDividerColor;
private int dialogDividerLeftPadding;
private int dialogDividerRightPadding;
private int dialogItemBackground;
private int dialogUnreadItemBackground;
static DialogListStyle parse(Context context, AttributeSet attrs) {
DialogListStyle style = new DialogListStyle(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DialogsList);
//Item background
style.dialogItemBackground = typedArray.getColor(R.styleable.DialogsList_dialogItemBackground,
style.getColor(R.color.transparent));
style.dialogUnreadItemBackground = typedArray.getColor(R.styleable.DialogsList_dialogUnreadItemBackground,
style.getColor(R.color.transparent));
//Title text
style.dialogTitleTextColor = typedArray.getColor(R.styleable.DialogsList_dialogTitleTextColor,
style.getColor(R.color.dialog_title_text));
style.dialogTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogTitleTextSize,
context.getResources().getDimensionPixelSize(R.dimen.dialog_title_text_size));
style.dialogTitleTextStyle = typedArray.getInt(R.styleable.DialogsList_dialogTitleTextStyle, Typeface.NORMAL);
//Title unread text
style.dialogUnreadTitleTextColor = typedArray.getColor(R.styleable.DialogsList_dialogUnreadTitleTextColor,
style.getColor(R.color.dialog_title_text));
style.dialogUnreadTitleTextStyle = typedArray.getInt(R.styleable.DialogsList_dialogUnreadTitleTextStyle, Typeface.NORMAL);
//Message text
style.dialogMessageTextColor = typedArray.getColor(R.styleable.DialogsList_dialogMessageTextColor,
style.getColor(R.color.dialog_message_text));
style.dialogMessageTextSize = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogMessageTextSize,
context.getResources().getDimensionPixelSize(R.dimen.dialog_message_text_size));
style.dialogMessageTextStyle = typedArray.getInt(R.styleable.DialogsList_dialogMessageTextStyle, Typeface.NORMAL);
//Message unread text
style.dialogUnreadMessageTextColor = typedArray.getColor(R.styleable.DialogsList_dialogUnreadMessageTextColor,
style.getColor(R.color.dialog_message_text));
style.dialogUnreadMessageTextStyle = typedArray.getInt(R.styleable.DialogsList_dialogUnreadMessageTextStyle, Typeface.NORMAL);
//Date text
style.dialogDateColor = typedArray.getColor(R.styleable.DialogsList_dialogDateColor,
style.getColor(R.color.dialog_date_text));
style.dialogDateSize = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogDateSize,
context.getResources().getDimensionPixelSize(R.dimen.dialog_date_text_size));
style.dialogDateStyle = typedArray.getInt(R.styleable.DialogsList_dialogDateStyle, Typeface.NORMAL);
//Date unread text
style.dialogUnreadDateColor = typedArray.getColor(R.styleable.DialogsList_dialogUnreadDateColor,
style.getColor(R.color.dialog_date_text));
style.dialogUnreadDateStyle = typedArray.getInt(R.styleable.DialogsList_dialogUnreadDateStyle, Typeface.NORMAL);
//Unread bubble
style.dialogUnreadBubbleEnabled = typedArray.getBoolean(R.styleable.DialogsList_dialogUnreadBubbleEnabled, true);
style.dialogUnreadBubbleBackgroundColor = typedArray.getColor(R.styleable.DialogsList_dialogUnreadBubbleBackgroundColor,
style.getColor(R.color.dialog_unread_bubble));
//Unread bubble text
style.dialogUnreadBubbleTextColor = typedArray.getColor(R.styleable.DialogsList_dialogUnreadBubbleTextColor,
style.getColor(R.color.dialog_unread_text));
style.dialogUnreadBubbleTextSize = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogUnreadBubbleTextSize,
context.getResources().getDimensionPixelSize(R.dimen.dialog_unread_bubble_text_size));
style.dialogUnreadBubbleTextStyle = typedArray.getInt(R.styleable.DialogsList_dialogUnreadBubbleTextStyle, Typeface.NORMAL);
//Avatar
style.dialogAvatarWidth = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogAvatarWidth,
context.getResources().getDimensionPixelSize(R.dimen.dialog_avatar_width));
style.dialogAvatarHeight = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogAvatarHeight,
context.getResources().getDimensionPixelSize(R.dimen.dialog_avatar_height));
//Last message avatar
style.dialogMessageAvatarEnabled = typedArray.getBoolean(R.styleable.DialogsList_dialogMessageAvatarEnabled, true);
style.dialogMessageAvatarWidth = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogMessageAvatarWidth,
context.getResources().getDimensionPixelSize(R.dimen.dialog_last_message_avatar_width));
style.dialogMessageAvatarHeight = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogMessageAvatarHeight,
context.getResources().getDimensionPixelSize(R.dimen.dialog_last_message_avatar_height));
//Divider
style.dialogDividerEnabled = typedArray.getBoolean(R.styleable.DialogsList_dialogDividerEnabled, true);
style.dialogDividerColor = typedArray.getColor(R.styleable.DialogsList_dialogDividerColor, style.getColor(R.color.dialog_divider));
style.dialogDividerLeftPadding = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogDividerLeftPadding,
context.getResources().getDimensionPixelSize(R.dimen.dialog_divider_margin_left));
style.dialogDividerRightPadding = typedArray.getDimensionPixelSize(R.styleable.DialogsList_dialogDividerRightPadding,
context.getResources().getDimensionPixelSize(R.dimen.dialog_divider_margin_right));
typedArray.recycle();
return style;
}
private DialogListStyle(Context context, AttributeSet attrs) {
super(context, attrs);
}
protected int getDialogTitleTextColor() {
return dialogTitleTextColor;
}
protected int getDialogTitleTextSize() {
return dialogTitleTextSize;
}
protected int getDialogTitleTextStyle() {
return dialogTitleTextStyle;
}
protected int getDialogUnreadTitleTextColor() {
return dialogUnreadTitleTextColor;
}
protected int getDialogUnreadTitleTextStyle() {
return dialogUnreadTitleTextStyle;
}
protected int getDialogMessageTextColor() {
return dialogMessageTextColor;
}
protected int getDialogMessageTextSize() {
return dialogMessageTextSize;
}
protected int getDialogMessageTextStyle() {
return dialogMessageTextStyle;
}
protected int getDialogUnreadMessageTextColor() {
return dialogUnreadMessageTextColor;
}
protected int getDialogUnreadMessageTextStyle() {
return dialogUnreadMessageTextStyle;
}
protected int getDialogDateColor() {
return dialogDateColor;
}
protected int getDialogDateSize() {
return dialogDateSize;
}
protected int getDialogDateStyle() {
return dialogDateStyle;
}
protected int getDialogUnreadDateColor() {
return dialogUnreadDateColor;
}
protected int getDialogUnreadDateStyle() {
return dialogUnreadDateStyle;
}
protected boolean isDialogUnreadBubbleEnabled() {
return dialogUnreadBubbleEnabled;
}
protected int getDialogUnreadBubbleTextColor() {
return dialogUnreadBubbleTextColor;
}
protected int getDialogUnreadBubbleTextSize() {
return dialogUnreadBubbleTextSize;
}
protected int getDialogUnreadBubbleTextStyle() {
return dialogUnreadBubbleTextStyle;
}
protected int getDialogUnreadBubbleBackgroundColor() {
return dialogUnreadBubbleBackgroundColor;
}
protected int getDialogAvatarWidth() {
return dialogAvatarWidth;
}
protected int getDialogAvatarHeight() {
return dialogAvatarHeight;
}
protected boolean isDialogDividerEnabled() {
return dialogDividerEnabled;
}
protected int getDialogDividerColor() {
return dialogDividerColor;
}
protected int getDialogDividerLeftPadding() {
return dialogDividerLeftPadding;
}
protected int getDialogDividerRightPadding() {
return dialogDividerRightPadding;
}
protected int getDialogItemBackground() {
return dialogItemBackground;
}
protected int getDialogUnreadItemBackground() {
return dialogUnreadItemBackground;
}
protected boolean isDialogMessageAvatarEnabled() {
return dialogMessageAvatarEnabled;
}
protected int getDialogMessageAvatarWidth() {
return dialogMessageAvatarWidth;
}
protected int getDialogMessageAvatarHeight() {
return dialogMessageAvatarHeight;
}
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/dialogs/DialogsList.java
================================================
/*******************************************************************************
* Copyright 2016 stfalcon.com
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*******************************************************************************/
package com.stfalcon.chatkit.dialogs;
import android.content.Context;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.recyclerview.widget.SimpleItemAnimator;
import com.stfalcon.chatkit.commons.models.IDialog;
/**
* Component for displaying list of dialogs
*/
public class DialogsList extends RecyclerView {
private DialogListStyle dialogStyle;
public DialogsList(Context context) {
super(context);
}
public DialogsList(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
parseStyle(context, attrs);
}
public DialogsList(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
parseStyle(context, attrs);
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
LinearLayoutManager layout = new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
SimpleItemAnimator animator = new DefaultItemAnimator();
setLayoutManager(layout);
setItemAnimator(animator);
}
/**
* Don't use this method for setting your adapter, otherwise exception will by thrown.
* Call {@link #setAdapter(DialogsListAdapter)} instead.
*/
@Override
public void setAdapter(Adapter adapter) {
throw new IllegalArgumentException("You can't set adapter to DialogsList. Use #setAdapter(DialogsListAdapter) instead.");
}
/**
* Sets adapter for DialogsList
*
* @param adapter Adapter. Must extend DialogsListAdapter
* @param
* Original discussion 1
* Original discussion 2
*/
public class WrapWidthTextView extends AppCompatTextView {
public WrapWidthTextView(@NonNull Context context) {
super(context);
}
public WrapWidthTextView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public WrapWidthTextView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Layout layout = getLayout();
if (layout != null) {
int width = (int) Math.ceil(getMaxLineWidth(layout))
+ getCompoundPaddingLeft() + getCompoundPaddingRight();
int height = getMeasuredHeight();
setMeasuredDimension(width, height);
}
}
private float getMaxLineWidth(Layout layout) {
float max_width = 0.0f;
int lines = layout.getLineCount();
for (int i = 0; i < lines; i++) {
if (layout.getLineWidth(i) > max_width) {
max_width = layout.getLineWidth(i);
}
}
return max_width;
}
}
================================================
FILE: chatkit/src/main/java/com/stfalcon/chatkit/dialogs/DialogListStyle.java
================================================
/*******************************************************************************
* Copyright 2016 stfalcon.com
*