├── .github
├── ISSUE_TEMPLATE
│ ├── bug-report.yaml
│ ├── config.yml
│ ├── feature-requests.yaml
│ └── function-enhancement.yaml
└── workflows
│ └── release.yml
├── .gitignore
├── .gitmodules
├── EasyBangumi.png
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle.kts
├── libs
│ └── rhino-1.7.15.jar
├── lint.xml
├── proguard-rules.pro
├── schemas
│ ├── com.heyanle.easybangumi4.base.db.AppDatabase
│ │ ├── 1.json
│ │ ├── 2.json
│ │ ├── 3.json
│ │ ├── 4.json
│ │ ├── 5.json
│ │ └── 6.json
│ ├── com.heyanle.easybangumi4.base.db.AppLocalDatabase
│ │ └── 1.json
│ ├── com.heyanle.easybangumi4.base.db.CacheDatabase
│ │ └── 1.json
│ ├── com.heyanle.easybangumi4.cartoon.db.AppDatabase
│ │ ├── 1.json
│ │ ├── 2.json
│ │ ├── 3.json
│ │ ├── 4.json
│ │ ├── 5.json
│ │ └── 6.json
│ ├── com.heyanle.easybangumi4.cartoon.db.CacheDatabase
│ │ └── 1.json
│ ├── com.heyanle.easybangumi4.cartoon.old.repository.db.AppDatabase
│ │ └── 9.json
│ ├── com.heyanle.easybangumi4.cartoon.old.repository.db.CacheDatabase
│ │ └── 3.json
│ ├── com.heyanle.easybangumi4.cartoon.repository.db.AppDatabase
│ │ ├── 7.json
│ │ ├── 8.json
│ │ └── 9.json
│ ├── com.heyanle.easybangumi4.cartoon.repository.db.CacheDatabase
│ │ ├── 1.json
│ │ ├── 2.json
│ │ └── 3.json
│ ├── com.heyanle.easybangumi4.cartoon.repository.db.CartoonDatabase
│ │ ├── 1.json
│ │ └── 2.json
│ └── com.heyanle.easybangumi4.db.AppDatabase
│ │ ├── 1.json
│ │ ├── 2.json
│ │ └── 3.json
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── heyanle
│ │ └── myapplication
│ │ ├── ExampleInstrumentedTest.kt
│ │ └── easybangumi4
│ │ └── MigrationTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── extension_test.js
│ │ ├── loading_ryo.gif
│ │ ├── need_known.txt
│ │ └── update_log.txt
│ ├── java
│ │ └── com
│ │ │ └── heyanle
│ │ │ └── easybangumi4
│ │ │ ├── AnnoConst.kt
│ │ │ ├── App.kt
│ │ │ ├── AppModule.kt
│ │ │ ├── C.kt
│ │ │ ├── LauncherBus.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── Migrate.kt
│ │ │ ├── Router.kt
│ │ │ ├── Scheduler.kt
│ │ │ ├── TestMain.kt
│ │ │ ├── base
│ │ │ ├── DataResult.kt
│ │ │ ├── hekv
│ │ │ │ └── HeKV.kt
│ │ │ ├── json
│ │ │ │ ├── JsonFileHelper.kt
│ │ │ │ └── JsonFileProvider.kt
│ │ │ └── preferences
│ │ │ │ ├── Preference.kt
│ │ │ │ ├── PreferenceStore.kt
│ │ │ │ ├── android
│ │ │ │ ├── AndroidPreference.kt
│ │ │ │ └── AndroidPreferenceStore.kt
│ │ │ │ ├── hekv
│ │ │ │ ├── HeKVPreference.kt
│ │ │ │ └── HeKVPreferenceStore.kt
│ │ │ │ └── mmkv
│ │ │ │ ├── MMKVPreference.kt
│ │ │ │ └── MMKVPreferenceStore.kt
│ │ │ ├── bus
│ │ │ └── DownloadingBus.kt
│ │ │ ├── cartoon
│ │ │ ├── CartoonModule.kt
│ │ │ ├── CartoonUpdateController.kt
│ │ │ ├── entity
│ │ │ │ ├── CartoonDownloadInfo.kt
│ │ │ │ ├── CartoonInfo.kt
│ │ │ │ ├── CartoonLocalItem.kt
│ │ │ │ ├── CartoonStoryItem.kt
│ │ │ │ ├── CartoonTag.kt
│ │ │ │ ├── PlayLineWrapper.kt
│ │ │ │ └── SearchHistory.kt
│ │ │ ├── old
│ │ │ │ ├── entity
│ │ │ │ │ ├── CartoonHistory.kt
│ │ │ │ │ ├── CartoonInfoOld73.kt
│ │ │ │ │ ├── CartoonInfoV1.kt
│ │ │ │ │ ├── CartoonStar.kt
│ │ │ │ │ ├── CartoonTagOld.kt
│ │ │ │ │ └── SearchHistoryOld.kt
│ │ │ │ └── repository
│ │ │ │ │ ├── CartoonNetworkDataSource.kt
│ │ │ │ │ ├── CartoonRepository.kt
│ │ │ │ │ └── db
│ │ │ │ │ ├── AppDatabase.kt
│ │ │ │ │ ├── CacheDatabase.kt
│ │ │ │ │ └── dao
│ │ │ │ │ ├── CartoonHistoryDao.kt
│ │ │ │ │ ├── CartoonInfoDao.kt
│ │ │ │ │ ├── CartoonStarDao.kt
│ │ │ │ │ ├── CartoonTagDao.kt
│ │ │ │ │ └── SearchHistoryDao.kt
│ │ │ ├── repository
│ │ │ │ ├── CartoonNetworkDataSource.kt
│ │ │ │ ├── CartoonRepository.kt
│ │ │ │ └── db
│ │ │ │ │ ├── CartoonDatabase.kt
│ │ │ │ │ └── dao
│ │ │ │ │ ├── CartoonInfoDao.kt
│ │ │ │ │ ├── CartoonTagDao.kt
│ │ │ │ │ ├── OtherDao.kt
│ │ │ │ │ └── SearchHistoryDao.kt
│ │ │ ├── star
│ │ │ │ ├── CartoonInfoSortFilterConst.kt
│ │ │ │ ├── CartoonStarController.kt
│ │ │ │ └── CartoonTagsController.kt
│ │ │ └── story
│ │ │ │ ├── CartoonStoryController.kt
│ │ │ │ ├── CartoonStoryControllerImpl.kt
│ │ │ │ ├── download
│ │ │ │ ├── CartoonDownloadBus.kt
│ │ │ │ ├── CartoonDownloadPreference.kt
│ │ │ │ ├── action
│ │ │ │ │ ├── AriaAction.kt
│ │ │ │ │ ├── BaseAction.kt
│ │ │ │ │ ├── CopyAndNfoAction.kt
│ │ │ │ │ ├── ParseAction.kt
│ │ │ │ │ ├── TranscodeAction.kt
│ │ │ │ │ └── TransformerAction.kt
│ │ │ │ ├── req
│ │ │ │ │ ├── CartoonDownloadReqController.kt
│ │ │ │ │ └── CartoonDownloadReqFactory.kt
│ │ │ │ ├── runtime
│ │ │ │ │ ├── CartoonDownloadDispatcher.kt
│ │ │ │ │ └── CartoonDownloadRuntime.kt
│ │ │ │ ├── service
│ │ │ │ │ └── DownloadingService.kt
│ │ │ │ └── utils
│ │ │ │ │ └── M3U8Utils.kt
│ │ │ │ └── local
│ │ │ │ ├── CartoonLocalController.kt
│ │ │ │ ├── LocalCartoonPreference.kt
│ │ │ │ ├── LocalItemFactory.kt
│ │ │ │ └── source
│ │ │ │ ├── LocalSource.kt
│ │ │ │ └── LocalSourceComponent.kt
│ │ │ ├── case
│ │ │ ├── CartoonInfoCase.kt
│ │ │ ├── CartoonTagCase.kt
│ │ │ ├── CaseModule.kt
│ │ │ ├── ExtensionCase.kt
│ │ │ └── SourceStateCase.kt
│ │ │ ├── crash
│ │ │ ├── NativeHandler.kt
│ │ │ └── SourceCrashController.kt
│ │ │ ├── dlna
│ │ │ ├── DLNAUtils.kt
│ │ │ ├── DlnaModule.kt
│ │ │ └── EasyDlna.kt
│ │ │ ├── exo
│ │ │ ├── CartoonMediaSourceFactory.kt
│ │ │ ├── ClippingConfigMediaSourceFactory.kt
│ │ │ ├── EasyExoPlayer.kt
│ │ │ ├── HeaderDataSourceFactory.kt
│ │ │ ├── MediaCacheDB.kt
│ │ │ ├── MediaModule.kt
│ │ │ ├── recorded
│ │ │ │ ├── RecordedController.kt
│ │ │ │ └── task
│ │ │ │ │ ├── AbsRecordedTask.kt
│ │ │ │ │ ├── GifRecordedTask.kt
│ │ │ │ │ ├── Mp4RecordedTask.kt
│ │ │ │ │ └── RecordedTask.kt
│ │ │ └── thumbnail
│ │ │ │ ├── OutputThumbnailHelper.kt
│ │ │ │ └── ThumbnailBuffer.kt
│ │ │ ├── plugin
│ │ │ ├── extension
│ │ │ │ ├── ExtensionController.kt
│ │ │ │ ├── ExtensionIconFactoryImpl.kt
│ │ │ │ ├── ExtensionInfo.kt
│ │ │ │ ├── ExtensionModule.kt
│ │ │ │ ├── loader
│ │ │ │ │ ├── AbsExtensionLoader.kt
│ │ │ │ │ ├── AppExtensionLoader.kt
│ │ │ │ │ ├── ExtensionLoader.kt
│ │ │ │ │ ├── ExtensionLoaderFactory.kt
│ │ │ │ │ └── FileExtensionLoader.kt
│ │ │ │ ├── provider
│ │ │ │ │ ├── AbsFolderExtensionProvider.kt
│ │ │ │ │ ├── ExtensionProvider.kt
│ │ │ │ │ ├── FileApkExtensionProvider.kt
│ │ │ │ │ ├── InstalledAppExtensionProvider.kt
│ │ │ │ │ └── JsExtensionProvider.kt
│ │ │ │ ├── push
│ │ │ │ │ ├── ExtensionPushController.kt
│ │ │ │ │ ├── ExtensionPushTask.kt
│ │ │ │ │ ├── PushFromCode.kt
│ │ │ │ │ ├── PushFromFileUrl.kt
│ │ │ │ │ └── PushFromRepo.kt
│ │ │ │ ├── service
│ │ │ │ │ └── NativeLoadService.kt
│ │ │ │ └── utils
│ │ │ │ │ └── log.kt
│ │ │ ├── js
│ │ │ │ ├── JSDebugPreference.kt
│ │ │ │ ├── JSIconFactory.kt
│ │ │ │ ├── JsTestProvider.kt
│ │ │ │ ├── component
│ │ │ │ │ ├── JSBaseComponent.kt
│ │ │ │ │ ├── JSDetailedComponent.kt
│ │ │ │ │ ├── JSPageComponent.kt
│ │ │ │ │ ├── JSPlayComponent.kt
│ │ │ │ │ ├── JSPreferenceComponent.kt
│ │ │ │ │ └── JSSearchComponent.kt
│ │ │ │ ├── entity
│ │ │ │ │ └── MainTab.kt
│ │ │ │ ├── extension
│ │ │ │ │ ├── JSExtensionCryLoader.kt
│ │ │ │ │ ├── JSExtensionInnerLoader.kt
│ │ │ │ │ └── JSExtensionLoader.kt
│ │ │ │ ├── runtime
│ │ │ │ │ ├── JSRuntime.kt
│ │ │ │ │ ├── JSRuntimeProvider.kt
│ │ │ │ │ └── JSScope.kt
│ │ │ │ ├── source
│ │ │ │ │ ├── AsyncIconSource.kt
│ │ │ │ │ ├── JSComponentBundle.kt
│ │ │ │ │ └── JsSource.kt
│ │ │ │ └── utils
│ │ │ │ │ ├── JSLogUtils.java
│ │ │ │ │ ├── JSSourceUtils.java
│ │ │ │ │ └── JsExt.kt
│ │ │ └── source
│ │ │ │ ├── SourceController.kt
│ │ │ │ ├── SourceException.kt
│ │ │ │ ├── SourceInfo.kt
│ │ │ │ ├── SourceModule.kt
│ │ │ │ ├── SourcePreferences.kt
│ │ │ │ ├── SourcesHost.kt
│ │ │ │ ├── bundle
│ │ │ │ ├── ComponentBundle.kt
│ │ │ │ ├── ComponentProxy.kt
│ │ │ │ ├── SimpleComponentBundle.kt
│ │ │ │ └── SourceBundle.kt
│ │ │ │ ├── debug
│ │ │ │ ├── DebugSource.kt
│ │ │ │ └── DebugSrouceComponent.kt
│ │ │ │ └── utils
│ │ │ │ ├── CaptchaHelperImpl.kt
│ │ │ │ ├── LightweightGettingWebViewClient.kt
│ │ │ │ ├── NativeHelperImpl.kt
│ │ │ │ ├── PreferenceHelperImpl.kt
│ │ │ │ ├── StringHelperImpl.kt
│ │ │ │ └── network
│ │ │ │ ├── NetworkHelperImpl.kt
│ │ │ │ ├── OkhttpHelperImpl.kt
│ │ │ │ ├── WebViewHelperImpl.kt
│ │ │ │ ├── WebViewHelperV2Impl.kt
│ │ │ │ └── interceptor
│ │ │ │ ├── CloudflareInterceptor.kt
│ │ │ │ ├── CloudflareUserInterceptor.kt
│ │ │ │ ├── CookieInterceptor.kt
│ │ │ │ ├── UserAgentInterceptor.kt
│ │ │ │ └── WebViewInterceptor.kt
│ │ │ ├── provider
│ │ │ └── MediaContentProvider.kt
│ │ │ ├── setting
│ │ │ ├── SettingMMKVPreferences.kt
│ │ │ ├── SettingModule.kt
│ │ │ └── SettingPreferences.kt
│ │ │ ├── splash
│ │ │ ├── SplashActivity.kt
│ │ │ ├── SplashCompose.kt
│ │ │ ├── SplashGuildController.kt
│ │ │ └── step
│ │ │ │ ├── BaseStep.kt
│ │ │ │ ├── LocalStep.kt
│ │ │ │ ├── PermissionStep.kt
│ │ │ │ └── ThemeStep.kt
│ │ │ ├── storage
│ │ │ ├── BackupController.kt
│ │ │ ├── RestoreController.kt
│ │ │ ├── StorageMigrate.kt
│ │ │ ├── StorageModule.kt
│ │ │ ├── StoragePreference.kt
│ │ │ └── entity
│ │ │ │ └── CartoonStorage.kt
│ │ │ ├── theme
│ │ │ ├── EasyTheme.kt
│ │ │ ├── EasyThemeController.kt
│ │ │ ├── Theme.kt
│ │ │ └── colors
│ │ │ │ ├── DefaultColor.kt
│ │ │ │ ├── GreenappleColor.kt
│ │ │ │ ├── MidnightduskColor.kt
│ │ │ │ ├── StrawberryColor.kt
│ │ │ │ ├── TakoColor.kt
│ │ │ │ └── TealturqoiseColor.kt
│ │ │ ├── ui
│ │ │ ├── WebViewUser.kt
│ │ │ ├── about
│ │ │ │ └── About.kt
│ │ │ ├── cartoon_play
│ │ │ │ ├── CartoonComponent.kt
│ │ │ │ ├── CartoonDownloadComponent.kt
│ │ │ │ ├── CartoonPlay.kt
│ │ │ │ ├── VideoComponent.kt
│ │ │ │ ├── cartoon_recorded
│ │ │ │ │ ├── CartoonRecorded.kt
│ │ │ │ │ ├── CartoonRecordedModel.kt
│ │ │ │ │ ├── clip_video
│ │ │ │ │ │ ├── ClipVideoModel.kt
│ │ │ │ │ │ └── ClipVideoSeek.kt
│ │ │ │ │ └── task
│ │ │ │ │ │ ├── CartoonRecordedTaskDialog.kt
│ │ │ │ │ │ └── CartoonRecordedTaskModel.kt
│ │ │ │ └── view_model
│ │ │ │ │ ├── CartoonDownloadReqModel.kt
│ │ │ │ │ ├── CartoonPlayViewModel.kt
│ │ │ │ │ ├── CartoonPlayingViewModel.kt
│ │ │ │ │ ├── CartoonStoryViewModel.kt
│ │ │ │ │ └── DetailedViewModel.kt
│ │ │ ├── common
│ │ │ │ ├── Action.kt
│ │ │ │ ├── BackgroundBasedBox.kt
│ │ │ │ ├── Cartoon.kt
│ │ │ │ ├── CartoonCard.kt
│ │ │ │ ├── CombineClickIconButton.kt
│ │ │ │ ├── EasyDialog.kt
│ │ │ │ ├── EasyDragGesture.kt
│ │ │ │ ├── ExtensionCompose.kt
│ │ │ │ ├── FastScrollToTopFab.kt
│ │ │ │ ├── GradientImage.kt
│ │ │ │ ├── LazyPaging.kt
│ │ │ │ ├── MD3PullRefreshIndicator.kt
│ │ │ │ ├── MoeDialogHost.kt
│ │ │ │ ├── MoeSnackBar.kt
│ │ │ │ ├── OkImage.kt
│ │ │ │ ├── Preference.kt
│ │ │ │ ├── SelectionTopAppBar.kt
│ │ │ │ ├── SourceContainer.kt
│ │ │ │ ├── TabPage.kt
│ │ │ │ ├── Tabs.kt
│ │ │ │ ├── ToggleButton.kt
│ │ │ │ ├── TopBarScreen.kt
│ │ │ │ ├── WhitePage.kt
│ │ │ │ ├── cover_star
│ │ │ │ │ ├── CoverStar.kt
│ │ │ │ │ └── CoverStarViewModel.kt
│ │ │ │ ├── page
│ │ │ │ │ ├── CartoonPageUI.kt
│ │ │ │ │ ├── list
│ │ │ │ │ │ ├── SourceGroupListViewModel.kt
│ │ │ │ │ │ ├── SourceListPage.kt
│ │ │ │ │ │ └── SourceListViewModel.kt
│ │ │ │ │ ├── listgroup
│ │ │ │ │ │ ├── SourceListGroupViewModel.kt
│ │ │ │ │ │ └── SourceListPageGroup.kt
│ │ │ │ │ └── paging
│ │ │ │ │ │ └── ListPagePagingSource.kt
│ │ │ │ └── proc
│ │ │ │ │ ├── FilterWith.kt
│ │ │ │ │ ├── Proc.kt
│ │ │ │ │ └── SortBy.kt
│ │ │ ├── dlna
│ │ │ │ ├── Dlna.kt
│ │ │ │ └── DlnaPlayingViewModel.kt
│ │ │ ├── extension_push
│ │ │ │ ├── ExtensionPush.kt
│ │ │ │ └── ExtensionPushViewModel.kt
│ │ │ ├── main
│ │ │ │ ├── Main.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ ├── history
│ │ │ │ │ ├── History.kt
│ │ │ │ │ └── HistoryViewModel.kt
│ │ │ │ ├── home
│ │ │ │ │ ├── Home.kt
│ │ │ │ │ └── HomeViewModel.kt
│ │ │ │ ├── more
│ │ │ │ │ └── More.kt
│ │ │ │ └── star
│ │ │ │ │ ├── Star.kt
│ │ │ │ │ └── StarViewModel.kt
│ │ │ ├── search_migrate
│ │ │ │ ├── PagingSearchSource.kt
│ │ │ │ ├── migrate
│ │ │ │ │ ├── Migrate.kt
│ │ │ │ │ ├── MigrateGather.kt
│ │ │ │ │ ├── MigrateItemViewModel.kt
│ │ │ │ │ └── MigrateViewModel.kt
│ │ │ │ └── search
│ │ │ │ │ ├── Search.kt
│ │ │ │ │ ├── SearchViewModel.kt
│ │ │ │ │ ├── gather
│ │ │ │ │ ├── GatherSearch.kt
│ │ │ │ │ └── GatherSearchViewModel.kt
│ │ │ │ │ └── normal
│ │ │ │ │ ├── NormalSearch.kt
│ │ │ │ │ └── NormalSearchViewModel.kt
│ │ │ ├── setting
│ │ │ │ ├── AppearanceSetting.kt
│ │ │ │ ├── DevelopersSetting.kt
│ │ │ │ ├── DownloadSetting.kt
│ │ │ │ ├── ExtensionSetting.kt
│ │ │ │ ├── InnerJsExtensionSetting.kt
│ │ │ │ ├── PlayerSetting.kt
│ │ │ │ └── Setting.kt
│ │ │ ├── source_config
│ │ │ │ └── SourceConfig.kt
│ │ │ ├── source_manage
│ │ │ │ ├── SourceManager.kt
│ │ │ │ ├── extension
│ │ │ │ │ ├── Extension.kt
│ │ │ │ │ └── ExtensionViewModel.kt
│ │ │ │ └── source
│ │ │ │ │ ├── Source.kt
│ │ │ │ │ └── SourceViewModel.kt
│ │ │ ├── storage
│ │ │ │ ├── Storage.kt
│ │ │ │ └── StorageViewModel.kt
│ │ │ ├── story
│ │ │ │ ├── Story.kt
│ │ │ │ ├── download
│ │ │ │ │ ├── Download.kt
│ │ │ │ │ └── DownloadViewModel.kt
│ │ │ │ └── local
│ │ │ │ │ ├── Local.kt
│ │ │ │ │ └── LocalViewModel.kt
│ │ │ └── tags
│ │ │ │ ├── CartoonTagManage.kt
│ │ │ │ └── CartoonTagViewModel.kt
│ │ │ └── utils
│ │ │ ├── AESHelper.kt
│ │ │ ├── AnimatedGifEncoder.java
│ │ │ ├── AppCenterManager.kt
│ │ │ ├── CDAction.kt
│ │ │ ├── CollectionUtils.kt
│ │ │ ├── ComposeUtils.kt
│ │ │ ├── ContentUriUtils.java
│ │ │ ├── CoroutineProvider.kt
│ │ │ ├── CoroutineUtils.kt
│ │ │ ├── DateExtensions.kt
│ │ │ ├── Dimens.kt
│ │ │ ├── Extens.kt
│ │ │ ├── FileUtils.kt
│ │ │ ├── GsonExtensions.kt
│ │ │ ├── ImmutableExt.kt
│ │ │ ├── IntentHelper.kt
│ │ │ ├── KtorUtil.kt
│ │ │ ├── Md5Extension.kt
│ │ │ ├── MediaAndroidUtils.kt
│ │ │ ├── MediaUtils.kt
│ │ │ ├── MemoryHelper.kt
│ │ │ ├── MoshiArrayListJsonAdapter.kt
│ │ │ ├── OkhttpHelper.kt
│ │ │ ├── OverlayHelper.kt
│ │ │ ├── PackageHelper.kt
│ │ │ ├── PathHelper.kt
│ │ │ ├── PostThread.kt
│ │ │ ├── SizeHelper.kt
│ │ │ ├── StatusData.kt
│ │ │ ├── String.kt
│ │ │ ├── TODO.kt
│ │ │ ├── Text.kt
│ │ │ ├── ThreadUtils.kt
│ │ │ ├── TimeLogUtils.kt
│ │ │ ├── Toast.kt
│ │ │ ├── UUIDHelper.kt
│ │ │ ├── UniFileUtils.kt
│ │ │ ├── UriHelper.kt
│ │ │ ├── ViewModelOwnerMap.kt
│ │ │ ├── WebViewManager.kt
│ │ │ ├── WebViewUtils.kt
│ │ │ ├── ZipUtils.kt
│ │ │ └── exo_ssl
│ │ │ ├── CropUtil.java
│ │ │ └── TrustAllHostnameVerifier.kt
│ └── res
│ │ ├── drawable
│ │ ├── ayala.png
│ │ ├── baseline_arrow_back_24.xml
│ │ ├── baseline_close_24.xml
│ │ ├── baseline_download_24.xml
│ │ ├── baseline_keyboard_arrow_left_24.xml
│ │ ├── baseline_replay_24.xml
│ │ ├── empty_bocchi.png
│ │ ├── error_ikuyo.png
│ │ ├── github.xml
│ │ ├── ic_baseline_brightness_6_24.xml
│ │ ├── ic_baseline_fast_forward_24.xml
│ │ ├── ic_baseline_fullscreen_24.xml
│ │ ├── ic_baseline_lock_24.xml
│ │ ├── ic_baseline_lock_open_24.xml
│ │ ├── ic_baseline_pause_24.xml
│ │ ├── ic_baseline_play_arrow_24.xml
│ │ ├── ic_baseline_volume_down_24.xml
│ │ ├── ic_baseline_volume_off_24.xml
│ │ ├── ic_baseline_volume_up_24.xml
│ │ ├── ic_webview_24dp.xml
│ │ ├── placeholder.png
│ │ ├── play_vb_bg.xml
│ │ ├── player_bottom_bg.xml
│ │ ├── player_controller_bt_bg.xml
│ │ ├── player_up_bg.xml
│ │ ├── qq.xml
│ │ └── telegram.xml
│ │ ├── mipmap-xxhdpi
│ │ ├── app_logo.png
│ │ └── logo_new.png
│ │ ├── values
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ ├── data_extraction_rules.xml
│ │ ├── file_path.xml
│ │ └── network_security_config.xml
│ └── test
│ └── java
│ └── com
│ └── heyanle
│ └── myapplication
│ └── ExampleUnitTest.kt
├── app_logo.png
├── build.gradle.kts
├── buildSrc
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── heyanle
│ └── buildsrc
│ ├── Android.kt
│ ├── Base64Util.java
│ └── RoomSchemaArgProvider.kt
├── easy-crasher
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── heyanle
│ │ └── easy_crasher
│ │ ├── CrashActivity.kt
│ │ └── CrashHandler.kt
│ └── res
│ └── layout
│ └── activity_crash.xml
├── easy-i18n
├── .gitignore
├── build.gradle.kts
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── heyanle
│ │ └── easy_i18n
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── res
│ │ └── values
│ │ └── strings.xml
│ └── test
│ └── java
│ └── com
│ └── heyanle
│ └── easy_i18n
│ └── ExampleUnitTest.kt
├── git_email.sh
├── gradle.properties
├── gradle
├── androidx.versions.toml
├── build.versions.toml
├── compose.versions.toml
├── extension.versions.toml
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── inject
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── com
│ └── heyanle
│ └── inject
│ ├── api
│ ├── Factory.kt
│ ├── InjectModule.kt
│ ├── InjectScope.kt
│ ├── InjectionException.kt
│ ├── Registry.kt
│ └── TypeInfo.kt
│ └── core
│ ├── DefaultInjectScope.kt
│ └── InjectMain.kt
├── jitpack.yml
├── lib_upnp
├── .gitignore
├── build.gradle.kts
└── src
│ └── main
│ └── java
│ └── org
│ └── cybergarage
│ ├── http
│ ├── Date.java
│ ├── HTML.java
│ ├── HTTP.java
│ ├── HTTPHeader.java
│ ├── HTTPPacket.java
│ ├── HTTPRequest.java
│ ├── HTTPRequestListener.java
│ ├── HTTPResponse.java
│ ├── HTTPServer.java
│ ├── HTTPServerList.java
│ ├── HTTPServerThread.java
│ ├── HTTPSocket.java
│ ├── HTTPStatus.java
│ ├── Parameter.java
│ └── ParameterList.java
│ ├── net
│ └── HostInterface.java
│ ├── soap
│ ├── SOAP.java
│ ├── SOAPRequest.java
│ └── SOAPResponse.java
│ ├── upnp
│ ├── Action.java
│ ├── ActionList.java
│ ├── AllowedValue.java
│ ├── AllowedValueList.java
│ ├── AllowedValueRange.java
│ ├── Argument.java
│ ├── ArgumentList.java
│ ├── ControlPoint.java
│ ├── Device.java
│ ├── DeviceList.java
│ ├── Icon.java
│ ├── IconList.java
│ ├── RootDescription.java
│ ├── Service.java
│ ├── ServiceList.java
│ ├── ServiceStateTable.java
│ ├── StateVariable.java
│ ├── UPnP.java
│ ├── UPnPStatus.java
│ ├── control
│ │ ├── ActionListener.java
│ │ ├── ActionRequest.java
│ │ ├── ActionResponse.java
│ │ ├── Control.java
│ │ ├── ControlRequest.java
│ │ ├── ControlResponse.java
│ │ ├── QueryListener.java
│ │ ├── QueryRequest.java
│ │ ├── QueryResponse.java
│ │ └── RenewSubscriber.java
│ ├── device
│ │ ├── Advertiser.java
│ │ ├── Description.java
│ │ ├── DeviceChangeListener.java
│ │ ├── Disposer.java
│ │ ├── InvalidDescriptionException.java
│ │ ├── MAN.java
│ │ ├── NT.java
│ │ ├── NTS.java
│ │ ├── NotifyListener.java
│ │ ├── PresentationListener.java
│ │ ├── ST.java
│ │ ├── SearchListener.java
│ │ ├── SearchResponseListener.java
│ │ └── USN.java
│ ├── event
│ │ ├── EventListener.java
│ │ ├── NotifyRequest.java
│ │ ├── Property.java
│ │ ├── PropertyList.java
│ │ ├── Subscriber.java
│ │ ├── SubscriberList.java
│ │ ├── Subscription.java
│ │ ├── SubscriptionRequest.java
│ │ └── SubscriptionResponse.java
│ ├── ssdp
│ │ ├── HTTPMUSocket.java
│ │ ├── HTTPUSocket.java
│ │ ├── SSDP.java
│ │ ├── SSDPNotifyRequest.java
│ │ ├── SSDPNotifySocket.java
│ │ ├── SSDPNotifySocketList.java
│ │ ├── SSDPPacket.java
│ │ ├── SSDPRequest.java
│ │ ├── SSDPResponse.java
│ │ ├── SSDPSearchRequest.java
│ │ ├── SSDPSearchResponse.java
│ │ ├── SSDPSearchResponseSocket.java
│ │ ├── SSDPSearchResponseSocketList.java
│ │ ├── SSDPSearchSocket.java
│ │ └── SSDPSearchSocketList.java
│ ├── std
│ │ └── av
│ │ │ ├── controller
│ │ │ ├── MediaController.java
│ │ │ └── server
│ │ │ │ ├── BrowseAction.java
│ │ │ │ └── BrowseResult.java
│ │ │ ├── player
│ │ │ ├── MediaPlayer.java
│ │ │ └── action
│ │ │ │ ├── BrowseAction.java
│ │ │ │ ├── BrowseResult.java
│ │ │ │ └── BrowseResultNode.java
│ │ │ ├── renderer
│ │ │ ├── AVTransport.java
│ │ │ ├── AVTransportInfo.java
│ │ │ ├── AVTransportInfoList.java
│ │ │ ├── ConnectionInfo.java
│ │ │ ├── ConnectionInfoList.java
│ │ │ ├── ConnectionManager.java
│ │ │ ├── MediaRenderer.java
│ │ │ └── RenderingControl.java
│ │ │ └── server
│ │ │ ├── ConnectionInfo.java
│ │ │ ├── ConnectionInfoList.java
│ │ │ ├── ConnectionManager.java
│ │ │ ├── ContentDirectory.java
│ │ │ ├── DC.java
│ │ │ ├── Directory.java
│ │ │ ├── DirectoryList.java
│ │ │ ├── MediaServer.java
│ │ │ ├── UPnP.java
│ │ │ ├── action
│ │ │ ├── BrowseAction.java
│ │ │ └── SearchAction.java
│ │ │ ├── directory
│ │ │ ├── file
│ │ │ │ └── FileDirectory.java
│ │ │ ├── gateway
│ │ │ │ └── GatewayDirectory.java
│ │ │ └── mythtv
│ │ │ │ └── MythRecordedInfo.java
│ │ │ └── object
│ │ │ ├── ContentNode.java
│ │ │ ├── ContentNodeList.java
│ │ │ ├── ContentProperty.java
│ │ │ ├── ContentPropertyList.java
│ │ │ ├── DIDLLite.java
│ │ │ ├── DIDLLiteNode.java
│ │ │ ├── Format.java
│ │ │ ├── FormatList.java
│ │ │ ├── FormatObject.java
│ │ │ ├── SearchCap.java
│ │ │ ├── SearchCapList.java
│ │ │ ├── SearchCriteria.java
│ │ │ ├── SearchCriteriaList.java
│ │ │ ├── SortCap.java
│ │ │ ├── SortCapList.java
│ │ │ ├── SortCriterionList.java
│ │ │ ├── container
│ │ │ ├── ContainerNode.java
│ │ │ └── RootNode.java
│ │ │ ├── format
│ │ │ ├── DefaultFormat.java
│ │ │ ├── GIFFormat.java
│ │ │ ├── Header.java
│ │ │ ├── ID3Format.java
│ │ │ ├── ID3Frame.java
│ │ │ ├── ID3FrameList.java
│ │ │ ├── ImageIOFormat.java
│ │ │ ├── JPEGFormat.java
│ │ │ ├── MPEGFormat.java
│ │ │ └── PNGFormat.java
│ │ │ ├── item
│ │ │ ├── ItemNode.java
│ │ │ ├── ItemNodeList.java
│ │ │ ├── ResourceNode.java
│ │ │ ├── ResourceNodeList.java
│ │ │ └── file
│ │ │ │ ├── FileItemNode.java
│ │ │ │ └── FileItemNodeList.java
│ │ │ ├── search
│ │ │ ├── IdSearchCap.java
│ │ │ └── TitleSearchCap.java
│ │ │ └── sort
│ │ │ ├── DCDateSortCap.java
│ │ │ ├── DCTitleSortCap.java
│ │ │ └── UPnPClassSortCap.java
│ └── xml
│ │ ├── ActionData.java
│ │ ├── ArgumentData.java
│ │ ├── DeviceData.java
│ │ ├── NodeData.java
│ │ ├── ServiceData.java
│ │ └── StateVariableData.java
│ ├── util
│ ├── Debug.java
│ ├── FileUtil.java
│ ├── ListenerList.java
│ ├── Mutex.java
│ ├── StringUtil.java
│ ├── ThreadCore.java
│ └── TimerUtil.java
│ └── xml
│ ├── Attribute.java
│ ├── AttributeList.java
│ ├── Node.java
│ ├── NodeList.java
│ ├── Parser.java
│ ├── ParserException.java
│ ├── XML.java
│ └── parser
│ └── JaxpParser.java
├── screenshots
├── 1.jpg
├── 2.jpg
├── 3.jpg
├── 4.jpg
├── 5.jpg
├── 6.jpg
└── 7.jpg
├── settings.gradle.kts
├── thanks_wx.png
└── thanks_zfb.jpg
/.github/ISSUE_TEMPLATE/bug-report.yaml:
--------------------------------------------------------------------------------
1 | name: Bug上报
2 | description: 提交一个非预期行为(Bug)
3 | title: "[BUG]: "
4 | type: Bug
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | 在开始之前请给问卷**起个标题**
10 | - type: input
11 | id: phone
12 | attributes:
13 | label: 手机型号
14 | description: 您使用手机型号
15 | placeholder: 例如 小米10 Ultra
16 | validations:
17 | required: false
18 | - type: input
19 | id: os
20 | attributes:
21 | label: 系统
22 | description: 开发者需要知道您手机的系统及版本,请尽量填写安卓版本号,如果不知道也可以填写诸如Hyper OS 2.0这样的外壳版本号。
23 | placeholder: 例如 Android 15
24 | validations:
25 | required: true
26 | - type: input
27 | id: version
28 | attributes:
29 | label: 纯纯看番版本号
30 | description: 在更多->关于里可以查看当前版本
31 | placeholder: 例如 5.4.2
32 | validations:
33 | required: true
34 | - type: textarea
35 | id: what-happened
36 | attributes:
37 | label: 发生了什么?
38 | description: 开发者需要了解问题发生的背景,确定问题是如何发生的。
39 | placeholder: 请简单说说你做了什么,本来期望发生什么,结果却是什么
40 | validations:
41 | required: true
42 | - type: textarea
43 | id: stack-trace
44 | attributes:
45 | label: 错误栈报告
46 | description: 如果问题导致了纯纯看番崩溃,请在下方粘贴崩溃页面的内容,文本和截图都可以。
47 | placeholder: Stack Trace
48 | validations:
49 | required: false
50 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: true
2 | contact_links:
3 | - name: 纯纯看番社区
4 | url: https://github.com/orgs/easybangumiorg/discussions
5 | about: 可以在这里看看是否有人遇到了相同的问题
6 | - name: QQ频道
7 | url: https://pd.qq.com/s/4q8rd0285
8 | about: 从QQ频道发起讨论或是加入群
9 | - name: Telegram
10 | url: https://t.me/easy_bangumi
11 | about: 从Tg群聊发起讨论
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-requests.yaml:
--------------------------------------------------------------------------------
1 | name: 新功能
2 | description: 向开发者提出新功能
3 | type: Feature
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | 在开始之前请给问卷**起个标题**
9 | - type: dropdown
10 | id: feature-range
11 | attributes:
12 | label: 功能范围
13 | multiple: true
14 | options:
15 | - 番剧管理
16 | - 插件能力
17 | - 播放器
18 | - 界面
19 | - 信息集成
20 | - 其他
21 | validations:
22 | required: true
23 | - type: textarea
24 | id: feature-requests
25 | attributes:
26 | label: 功能描述
27 | description: 请简单说说您期望这个功能是什么样的,应该怎么运作。
28 | validations:
29 | required: true
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/function-enhancement.yaml:
--------------------------------------------------------------------------------
1 | name: 改进建议
2 | description: 向开发者提出对原有功能的改进建议
3 | type: Enhance
4 | body:
5 | - type: markdown
6 | attributes:
7 | value: |
8 | 在开始之前请给问卷**起个标题**
9 | - type: dropdown
10 | id: feature-range
11 | attributes:
12 | label: 改进范围
13 | multiple: true
14 | options:
15 | - 番剧管理
16 | - 插件能力
17 | - 播放器
18 | - 界面
19 | - 信息集成
20 | - 其他
21 | validations:
22 | required: true
23 | - type: textarea
24 | id: feature-requests
25 | attributes:
26 | label: 功能描述
27 | description: 请简单说说您期望这个功能是什么样的,应该怎么运作。
28 | validations:
29 | required: true
30 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .idea
4 | /local.properties
5 | /.idea/caches
6 | /.idea/libraries
7 | /.idea/modules.xml
8 | /.idea/workspace.xml
9 | /.idea/navEditor.xml
10 | /.idea/assetWizardSettings.xml
11 | .DS_Store
12 | /build
13 | /captures
14 | /publishing
15 | .externalNativeBuild
16 | .cxx
17 | local.properties
18 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "EasyPlayer2"]
2 | path = EasyPlayer2
3 | url = git@github.com:easybangumiorg/EasyPlayer2.git
4 | [submodule "EasyMediaTransformer"]
5 | path = EasyMediaTransformer
6 | url = git@github.com:easybangumiorg/EasyMediaTransformer.git
7 |
--------------------------------------------------------------------------------
/EasyBangumi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/EasyBangumi.png
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /release
3 | google-services.json
--------------------------------------------------------------------------------
/app/libs/rhino-1.7.15.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/libs/rhino-1.7.15.jar
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/heyanle/myapplication/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.myapplication
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.*
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("com.heyanle.myapplication", appContext.packageName)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/assets/loading_ryo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/src/main/assets/loading_ryo.gif
--------------------------------------------------------------------------------
/app/src/main/assets/need_known.txt:
--------------------------------------------------------------------------------
1 | ICAgICAgICAxLiDnuq/nuq/nnIvnlarmmK/kuLrkuoblrabkuaAgSml0cGFjayBjb21wb3NlIOWSjOmfs+inhumikeebuOWFs+aKgOacr+i/m+ihjOW8gOWPkeeahOS4gOS4qumhueebru+8jOWumOaWueS4jeaPkOS+m+aJk+WMheWSjOS4i+i9ve+8jOWFtua6kOS7o+eggeS7heS+m+S6pOa1geWtpuS5oOOAguWboOWFtuS7luS6uuengeiHquaJk+WMheWPkeihjOWQjumAoOaIkOeahOS4gOWIh+WQjuaenOacrOaWueamguS4jei0n+i0o+OAggogICAgICAgIDIuIOe6r+e6r+eci+eVquaJk+WMheWQjuS4jeaPkOS+m+S7u+S9leinhumikeWGheWuue+8jOmcgOimgeeUqOaIt+iHquW3seaJi+WKqOa3u+WKoOOAgueUqOaIt+iHquihjOWvvOWFpeeahOWGheWuueWSjOacrOi9r+S7tuaXoOWFs+OAggogICAgICAgIDMuIOe6r+e6r+eci+eVqua6kOeggeWujOWFqOWFjei0ue+8jOWcqCBHaXRodWIg5byA5rqQ44CC55So5oi35Y+v6Ieq6KGM5LiL6L295omT5YyF44CC5aaC5p6c5L2g5piv5pS26LS56LSt5Lmw55qE5pys6L2v5Lu277yM5YiZ5pys5pa55qaC5LiN6LSf6LSj44CC
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/base/preferences/Preference.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.base.preferences
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.flow.Flow
5 | import kotlinx.coroutines.flow.StateFlow
6 |
7 | /**
8 | * Created by HeYanLe on 2023/7/29 16:44.
9 | * https://github.com/heyanLE
10 | */
11 | interface Preference {
12 |
13 | fun key(): String
14 |
15 | fun get(): T
16 |
17 | fun set(value: T)
18 |
19 | fun defaultValue(): T
20 |
21 | fun isSet(): Boolean
22 |
23 | fun delete()
24 |
25 | fun flow(): Flow
26 |
27 | fun stateIn(scope: CoroutineScope): StateFlow
28 |
29 | }
30 |
31 | inline fun Preference.getAndSet(crossinline block: (T) -> R) = set(block(get()))
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/base/preferences/PreferenceStore.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.base.preferences
2 |
3 | /**
4 | * Created by HeYanLe on 2023/7/29 16:48.
5 | * https://github.com/heyanLE
6 | */
7 | interface PreferenceStore {
8 |
9 | fun getString(key: String, default: String = ""): Preference
10 |
11 | fun getInt(key: String, default: Int = 0): Preference
12 |
13 | fun getLong(key: String, default: Long = 0L): Preference
14 |
15 | fun getFloat(key: String, default:Float = 0f): Preference
16 |
17 | fun getBoolean(key: String, default: Boolean = false): Preference
18 |
19 | fun getStringSet(key: String, defaultValue: Set = emptySet()): Preference>
20 |
21 |
22 | fun getObject(
23 | key: String,
24 | defaultValue: T,
25 | serializer: (T) -> String,
26 | deserializer: (String) -> T,
27 | ): Preference
28 |
29 | fun keySet(): Set
30 |
31 | }
32 |
33 | inline fun > PreferenceStore.getEnum(
34 | key: String,
35 | defaultValue: T,
36 | ): Preference {
37 | return getObject(
38 | key = key,
39 | defaultValue = defaultValue,
40 | serializer = { it.name },
41 | deserializer = {
42 | try {
43 | enumValueOf(it)
44 | } catch (e: IllegalArgumentException) {
45 | defaultValue
46 | }
47 | },
48 | )
49 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/bus/DownloadingBus.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.bus
2 |
3 | import androidx.compose.runtime.MutableState
4 | import androidx.compose.runtime.mutableFloatStateOf
5 | import androidx.compose.runtime.mutableStateOf
6 | import java.util.concurrent.ConcurrentHashMap
7 |
8 | /**
9 | * 下载进度分发
10 | * Created by heyanlin on 2023/11/16.
11 | */
12 | class DownloadingBus {
13 |
14 | // 下载场景
15 | enum class DownloadScene {
16 | CARTOON, EXTENSION
17 | }
18 |
19 | class DownloadingInfo(
20 | val status: MutableState = mutableStateOf(""),
21 | val subStatus: MutableState = mutableStateOf(""),
22 | val process: MutableState = mutableFloatStateOf(-1f)
23 | )
24 | private val map = ConcurrentHashMap()
25 |
26 | fun getInfo(scene: DownloadScene, key: String): DownloadingInfo {
27 | return map.getOrPut("${scene}-${key}") {
28 | DownloadingInfo()
29 | }
30 | }
31 |
32 | fun remove(scene: DownloadScene, key: String) {
33 | map.remove("${scene}-${key}")
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/entity/CartoonStoryItem.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.entity
2 |
3 | import com.heyanle.easybangumi4.cartoon.story.download.runtime.CartoonDownloadRuntime
4 |
5 |
6 | /**
7 | * Created by heyanle on 2024/7/12.
8 | * https://github.com/heyanLE
9 | */
10 | data class CartoonStoryItem (
11 | val cartoonLocalItem: CartoonLocalItem,
12 | val downloadInfoList: List,
13 | ) {
14 |
15 | // 不能提交下载任务的集数,包括
16 | // 1. 本地已经下载的集数
17 | // 2. runtime 为空的集数(下载未完成时重启 app),当用户点击重试时,必会触发 Runtime 刷新
18 | val cantReqEpisode: Set by lazy {
19 | cartoonLocalItem.episodes.map { it.episode }.toSet()
20 | }
21 |
22 |
23 |
24 | // 对于下载错误的信息,这里无法稳定,需要每次重新获取
25 | val errorDownloadEpisode: Set
26 | get() = downloadInfoList.filter { it.runtime?.state == CartoonDownloadRuntime.State.ERROR }.map { it.req.toEpisode }.toSet()
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/entity/SearchHistory.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.entity
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity
7 | data class SearchHistory(
8 | @PrimaryKey(autoGenerate = true)
9 | val id: Int = 0,
10 | val timestamp: Long,
11 | val content: String
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/old/entity/CartoonHistory.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.old.entity
2 |
3 | import androidx.room.Entity
4 | import com.heyanle.easybangumi4.utils.getMatchReg
5 | import java.net.URLEncoder
6 |
7 | /**
8 | * Created by HeYanLe on 2023/3/7 14:55.
9 | * https://github.com/heyanLE
10 | */
11 | @Entity(primaryKeys = ["id", "source", "url"])
12 | data class CartoonHistory(
13 |
14 | val id: String,
15 | val url: String,
16 | val source: String,
17 |
18 | val name: String,
19 | val cover: String,
20 | val intro: String,
21 |
22 | val lastLineId: String,
23 | val lastLinesIndex: Int,
24 | val lastLineTitle: String,
25 |
26 | val lastEpisodeId: String,
27 | val lastEpisodeOrder: Int,
28 | val lastEpisodeIndex: Int,
29 | val lastEpisodeTitle: String,
30 |
31 | val lastProcessTime: Long,
32 | val createTime: Long,
33 | ){
34 | fun matches(query: String): Boolean{
35 | var matched = false
36 | for(match in query.split(',')){
37 | val regex = match.getMatchReg()
38 | if(name.matches(regex)){
39 | matched = true
40 | break
41 | }
42 | }
43 | return matched
44 |
45 | }
46 |
47 | fun toIdentify(): String {
48 | return "${id},${source},${URLEncoder.encode(url, "utf-8")}"
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/old/entity/CartoonTagOld.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.old.entity
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | /**
7 | * Created by HeYanLe on 2023/8/6 16:12.
8 | * https://github.com/heyanLE
9 | */
10 | @Entity(tableName = "CartoonTag")
11 | data class CartoonTagOld(
12 | @PrimaryKey(autoGenerate = true)
13 | val id: Int = 0,
14 | val label: String,
15 | val order: Int,
16 | ){
17 |
18 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/old/entity/SearchHistoryOld.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.old.entity
2 |
3 | import androidx.room.Entity
4 | import androidx.room.PrimaryKey
5 |
6 | @Entity(tableName = "SearchHistory")
7 | data class SearchHistoryOld(
8 | @PrimaryKey(autoGenerate = true)
9 | val id: Int = 0,
10 | val timestamp: Long,
11 | val content: String
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/old/repository/CartoonNetworkDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.old.repository
2 |
3 | import com.heyanle.easybangumi4.base.DataResult
4 | import com.heyanle.easybangumi4.base.toDataResult
5 | import com.heyanle.easybangumi4.case.SourceStateCase
6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon
7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary
8 | import com.heyanle.easybangumi4.source_api.entity.PlayLine
9 |
10 | /**
11 | * Created by HeYanLe on 2023/8/13 16:35.
12 | * https://github.com/heyanLE
13 | */
14 | class CartoonNetworkDataSource(
15 | private val sourceStateCase: SourceStateCase,
16 | ) {
17 |
18 | suspend fun awaitCartoonWithPlayLines(
19 | id: String,
20 | source: String,
21 | url: String
22 | ): DataResult>> {
23 | val result = sourceStateCase.awaitBundle().detailed(source)
24 | ?.getAll(CartoonSummary(id, source))
25 | ?: return DataResult.error("没有番剧源")
26 | return result.toDataResult()
27 | }
28 |
29 | suspend fun awaitPlayLines(id: String, source: String, url: String): DataResult> {
30 | val result = sourceStateCase.awaitBundle().detailed(source)
31 | ?.getPlayLine(CartoonSummary(id, source))
32 | ?: return DataResult.error("没有番剧源")
33 | return result.toDataResult()
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/old/repository/db/CacheDatabase.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.old.repository.db
2 |
3 | import android.content.Context
4 | import androidx.room.Database
5 | import androidx.room.Room
6 | import androidx.room.RoomDatabase
7 | import com.heyanle.easybangumi4.Migrate
8 | import com.heyanle.easybangumi4.cartoon.old.repository.db.dao.CartoonInfoDao
9 | import com.heyanle.easybangumi4.cartoon.old.entity.CartoonInfoOld
10 |
11 | /**
12 | * 用于缓存的数据库,可以管理大小和单独删除
13 | * Created by HeYanLe on 2023/8/13 17:24.
14 | * https://github.com/heyanLE
15 | */
16 | @Database(
17 | entities = [
18 | CartoonInfoOld::class
19 | ],
20 | autoMigrations = [],
21 | version = 3,
22 | exportSchema = true
23 | )
24 | abstract class CacheDatabase : RoomDatabase() {
25 |
26 | abstract fun cartoonInfoDao(): CartoonInfoDao
27 | val cartoonInfo: CartoonInfoDao by lazy {cartoonInfoDao() }
28 | companion object {
29 | fun build(context: Context): CacheDatabase {
30 | return Room.databaseBuilder(
31 | context,
32 | CacheDatabase::class.java, "easy_cache"
33 | ).apply {
34 | fallbackToDestructiveMigration()
35 | Migrate.CacheDB.getDBMigration().forEach {
36 | addMigrations(it)
37 | }
38 | }.build()
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/old/repository/db/dao/CartoonTagDao.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.old.repository.db.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Delete
5 | import androidx.room.Insert
6 | import androidx.room.Query
7 | import androidx.room.Transaction
8 | import androidx.room.Update
9 | import com.heyanle.easybangumi4.cartoon.old.entity.CartoonTagOld
10 | import kotlinx.coroutines.flow.Flow
11 |
12 | /**
13 | * Created by HeYanLe on 2023/8/6 16:14.
14 | * https://github.com/heyanLE
15 | */
16 | @Dao
17 | interface CartoonTagDao {
18 |
19 | @Insert
20 | suspend fun insert(cartoonTag: CartoonTagOld)
21 |
22 | @Update
23 | suspend fun update(cartoonTag: CartoonTagOld)
24 |
25 | @Delete
26 | suspend fun delete(cartoonTag: CartoonTagOld)
27 |
28 | @Query("SELECT * FROM CartoonTag WHERE id=(:id)")
29 | suspend fun findById(id: Int): CartoonTagOld?
30 |
31 | @Query("SELECT * FROM CartoonTag")
32 | fun flowAll(): Flow>
33 |
34 | @Query("SELECT * FROM CartoonTag")
35 | suspend fun getAll(): List
36 |
37 | @Query("DELETE FROM CartoonTag WHERE 1=1")
38 | suspend fun clear()
39 |
40 | @Transaction
41 | suspend fun updateAll(list: List) {
42 | list.forEach {
43 | val old = findById(it.id)
44 | if (old == null) {
45 | insert(it)
46 | } else {
47 | update(it)
48 | }
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/repository/CartoonNetworkDataSource.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.repository
2 |
3 | import com.heyanle.easybangumi4.base.DataResult
4 | import com.heyanle.easybangumi4.base.toDataResult
5 | import com.heyanle.easybangumi4.case.SourceStateCase
6 | import com.heyanle.easybangumi4.source_api.entity.Cartoon
7 | import com.heyanle.easybangumi4.source_api.entity.CartoonSummary
8 | import com.heyanle.easybangumi4.source_api.entity.PlayLine
9 |
10 | /**
11 | * Created by heyanle on 2023/12/17.
12 | * https://github.com/heyanLE
13 | */
14 | class CartoonNetworkDataSource(
15 | private val sourceStateCase: SourceStateCase,
16 | ) {
17 |
18 | suspend fun awaitCartoonWithPlayLines(
19 | id: String,
20 | source: String,
21 | ): DataResult>> {
22 | val result = sourceStateCase.awaitBundle().detailed(source)
23 | ?.getAll(CartoonSummary(id, source))
24 | ?: return DataResult.error("没有番剧源")
25 | return result.toDataResult()
26 | }
27 |
28 | suspend fun awaitPlayLines(id: String, source: String, url: String): DataResult> {
29 | val result = sourceStateCase.awaitBundle().detailed(source)
30 | ?.getPlayLine(CartoonSummary(id, source))
31 | ?: return DataResult.error("没有番剧源")
32 | return result.toDataResult()
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/repository/db/dao/OtherDao.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.repository.db.dao
2 |
3 | import androidx.room.Dao
4 | import androidx.room.Query
5 | import com.heyanle.easybangumi4.cartoon.old.entity.CartoonInfoV1
6 |
7 | /**
8 | * 其他的 Dao,一般是用于版本迁移
9 | * Created by heyanle on 2024/1/28.
10 | * https://github.com/heyanLE
11 | */
12 | @Dao
13 | interface OtherDao {
14 |
15 |
16 | @Query("SELECT * FROM CartoonInfo")
17 | suspend fun getAllCartoonInfoV1(): List
18 |
19 |
20 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/story/CartoonStoryController.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.story
2 |
3 | import com.heyanle.easybangumi4.base.DataResult
4 | import com.heyanle.easybangumi4.cartoon.entity.CartoonDownloadInfo
5 | import com.heyanle.easybangumi4.cartoon.entity.CartoonDownloadReq
6 | import com.heyanle.easybangumi4.cartoon.entity.CartoonLocalEpisode
7 | import com.heyanle.easybangumi4.cartoon.entity.CartoonLocalMsg
8 | import com.heyanle.easybangumi4.cartoon.entity.CartoonStoryItem
9 | import kotlinx.coroutines.flow.StateFlow
10 |
11 | /**
12 | * 番剧下载 和 本地番源一起的耦合控制器
13 | * Created by heyanle on 2024/7/15.
14 | * https://github.com/heyanLE
15 | */
16 | interface CartoonStoryController {
17 |
18 | // 番剧下载信息,包括持久化的请求和运行时的 Runtime
19 | val downloadInfoList: StateFlow>>
20 |
21 | // 本地剧集信息包含与其关联的番剧下载信息
22 | val storyItemList: StateFlow>>
23 |
24 |
25 | // 下载任务新建和删除
26 | fun newDownloadReq(reqList: Collection)
27 |
28 | fun removeDownloadReq(reqList: Collection)
29 |
30 | // 尝试重新下载,只有下载错误或者重启后点击才能恢复
31 | // @param closeQuickMode 关闭快速下载模式
32 | fun tryResumeDownloadReq(info: CartoonDownloadInfo, closeQuickMode: Boolean)
33 |
34 |
35 |
36 | // 本地剧集新建和删除,这里会同时删除关联的下载信息
37 |
38 | fun refreshLocal()
39 | suspend fun newStory(localMsg: CartoonLocalMsg): String?
40 |
41 | fun removeStory(cartoonStoryItem: Collection)
42 |
43 | fun removeEpisodeItem(episode: Collection)
44 |
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/story/download/CartoonDownloadBus.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.story.download
2 |
3 | import com.heyanle.easybangumi4.bus.DownloadingBus
4 |
5 | /**
6 | * Created by HeYanLe on 2023/9/17 15:39.
7 | * https://github.com/heyanLE
8 | */
9 | class CartoonDownloadBus (
10 | private val downloadBus: DownloadingBus,
11 | ){
12 |
13 |
14 | fun getInfo(key: String): DownloadingBus.DownloadingInfo {
15 | return downloadBus.getInfo(DownloadingBus.DownloadScene.CARTOON, key)
16 | }
17 |
18 | fun remove(key: String) {
19 | downloadBus.remove(DownloadingBus.DownloadScene.CARTOON, key = key)
20 | }
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/story/download/CartoonDownloadPreference.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.story.download
2 |
3 | import android.os.Build
4 | import com.heyanle.easybangumi4.base.preferences.android.AndroidPreferenceStore
5 | import com.heyanle.easybangumi4.base.preferences.getEnum
6 |
7 | /**
8 | * Created by heyanle on 2024/7/7.
9 | * https://github.com/heyanLE
10 | */
11 | class CartoonDownloadPreference(
12 | private val androidPreferenceStore: AndroidPreferenceStore
13 | ) {
14 |
15 | // 最大下载数量,仅快速转码模式可用
16 | val downloadMaxCountPref = androidPreferenceStore.getLong("download_max_count", 3L)
17 |
18 |
19 | // 最大编解码数量,仅完整编码模式可用
20 | val transformMaxCountPref = androidPreferenceStore.getLong("transform_max_count", 1L)
21 |
22 |
23 | // 下载编码方式,仅完整编码模式可用
24 | enum class DownloadEncode {
25 | H264, H265
26 | }
27 | val downloadEncodeSelection = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
28 | listOf(
29 | DownloadEncode.H264 to "H.264",
30 | DownloadEncode.H265 to "H.265",
31 | )
32 | } else {
33 | listOf(
34 | DownloadEncode.H264 to "H.264",
35 | )
36 | }
37 |
38 | val downloadEncode = androidPreferenceStore.getEnum("download_encode", if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
39 | DownloadEncode.H265
40 | } else {
41 | DownloadEncode.H264
42 | })
43 |
44 | // 是否让手机相册忽略本地文件夹
45 | var localNoMedia = androidPreferenceStore.getBoolean("local_path_no_media", true)
46 |
47 |
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/story/download/action/BaseAction.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.story.download.action
2 |
3 | import com.heyanle.easybangumi4.cartoon.entity.CartoonDownloadReq
4 | import com.heyanle.easybangumi4.cartoon.story.download.runtime.CartoonDownloadRuntime
5 |
6 | /**
7 | * Created by heyanle on 2024/8/2.
8 | * https://github.com/heyanLE
9 | */
10 | interface BaseAction {
11 |
12 | // 对于同步任务,Dispatcher 会在工作线程池执行 push,直接在里面执行即可
13 | // 对于异步任务,Dispatcher 会在调度线程池执行 push,push 需要同步返回
14 | fun isAsyncAction(): Boolean = true
15 | suspend fun canResume(
16 | cartoonDownloadReq: CartoonDownloadReq
17 | ): Boolean
18 | suspend fun toggle(cartoonDownloadRuntime: CartoonDownloadRuntime): Boolean
19 |
20 | // 当整个任务完成时调用,可以用于清理中间产物
21 | fun onTaskCompletely(cartoonDownloadRuntime: CartoonDownloadRuntime) {}
22 | fun push(cartoonDownloadRuntime: CartoonDownloadRuntime)
23 | fun onCancel(cartoonDownloadRuntime: CartoonDownloadRuntime)
24 |
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/cartoon/story/local/source/LocalSource.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.cartoon.story.local.source
2 |
3 | import android.graphics.drawable.Drawable
4 | import androidx.core.content.res.ResourcesCompat
5 | import com.heyanle.easybangumi4.APP
6 | import com.heyanle.easybangumi4.R
7 | import com.heyanle.easybangumi4.utils.stringRes
8 | import com.heyanle.extension_api.ExtensionIconSource
9 | import kotlin.reflect.KClass
10 |
11 | /**
12 | * Created by heyanle on 2024/7/7.
13 | * https://github.com/heyanLE
14 | */
15 | object LocalSource: ExtensionIconSource {
16 |
17 | const val LOCAL_SOURCE_KEY = "easybangumi_local"
18 |
19 | override fun getIconResourcesId(): Int? = null
20 | override fun getIconFactory(): () -> Drawable? {
21 | return {
22 | ResourcesCompat.getDrawable(APP.resources, R.mipmap.logo_new, null)
23 | }
24 | }
25 |
26 | override val describe: String?
27 | get() = stringRes(com.heyanle.easy_i18n.R.string.local_source_desc)
28 | override val key: String
29 | get() = LOCAL_SOURCE_KEY
30 | override val label: String
31 | get() = stringRes(com.heyanle.easy_i18n.R.string.local_source)
32 | override val version: String
33 | get() = "1.0.0"
34 | override val versionCode: Int
35 | get() = 0
36 |
37 | override fun register(): List> {
38 | return listOf(
39 | LocalSourceComponent::class
40 | )
41 | }
42 |
43 |
44 |
45 |
46 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/case/CartoonInfoCase.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.case
2 |
3 | import com.heyanle.easybangumi4.base.DataResult
4 | import com.heyanle.easybangumi4.cartoon.entity.CartoonInfo
5 | import com.heyanle.easybangumi4.cartoon.repository.CartoonRepository
6 | import com.heyanle.easybangumi4.cartoon.repository.db.dao.CartoonInfoDao
7 | import com.heyanle.easybangumi4.source_api.entity.PlayLine
8 | import kotlinx.coroutines.flow.Flow
9 |
10 | /**
11 | * Created by heyanlin on 2023/10/2.
12 | */
13 | class CartoonInfoCase(
14 | private val cartoonRepository: CartoonRepository,
15 | private val cartoonInfoDao: CartoonInfoDao,
16 | ) {
17 |
18 | suspend fun awaitCartoonInfoWithPlayLines(
19 | id: String,
20 | source: String,
21 | time: Long = System.currentTimeMillis(),
22 | ): DataResult {
23 | return cartoonRepository.awaitCartoonInfoWIthPlayLines(id, source, time)
24 | }
25 |
26 | suspend fun flowCartoonStar(): Flow> {
27 | return cartoonInfoDao.flowAllStar()
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/case/CartoonTagCase.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.case
2 |
3 | import com.heyanle.easybangumi4.cartoon.star.CartoonStarController
4 |
5 | /**
6 | * Created by heyanlin on 2024/11/20.
7 | */
8 | class CartoonTagCase(
9 | private val cartoonStarController: CartoonStarController,
10 | ) {
11 |
12 |
13 |
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/case/CaseModule.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.case
2 |
3 | import android.app.Application
4 | import com.heyanle.inject.api.InjectModule
5 | import com.heyanle.inject.api.InjectScope
6 | import com.heyanle.inject.api.addSingletonFactory
7 | import com.heyanle.inject.api.get
8 |
9 | /**
10 | * Created by heyanlin on 2023/10/30.
11 | */
12 | class CaseModule(
13 | private val application: Application
14 | ) : InjectModule {
15 |
16 | override fun InjectScope.registerInjectables() {
17 | addSingletonFactory {
18 | CartoonInfoCase(get(), get())
19 | }
20 | addSingletonFactory {
21 | ExtensionCase(get())
22 | }
23 | addSingletonFactory {
24 | SourceStateCase(get())
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/case/ExtensionCase.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.case
2 |
3 |
4 | import com.heyanle.easybangumi4.plugin.extension.ExtensionController
5 | import com.heyanle.easybangumi4.plugin.extension.ExtensionInfo
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.StateFlow
8 | import kotlinx.coroutines.flow.filter
9 | import kotlinx.coroutines.flow.first
10 | import kotlinx.coroutines.flow.map
11 |
12 | /**
13 | * Created by heyanlin on 2023/10/25.
14 | */
15 | class ExtensionCase(
16 | private val extensionController: ExtensionController
17 | ) {
18 |
19 | fun flowExtensionState(): StateFlow {
20 | return extensionController.state
21 | }
22 |
23 | fun flowExtension(): Flow> {
24 | return extensionController.state
25 | .filter {
26 | !it.loading
27 | }
28 | .map {
29 | it.extensionInfoMap.values
30 | }
31 | }
32 |
33 | suspend fun awaitExtension(): Collection {
34 | return flowExtension().first()
35 | }
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/case/SourceStateCase.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.case
2 |
3 | import com.heyanle.easybangumi4.plugin.source.SourceController
4 | import com.heyanle.easybangumi4.plugin.source.bundle.SourceBundle
5 | import kotlinx.coroutines.flow.Flow
6 | import kotlinx.coroutines.flow.StateFlow
7 | import kotlinx.coroutines.flow.filterIsInstance
8 | import kotlinx.coroutines.flow.first
9 |
10 | /**
11 | * Created by heyanlin on 2023/10/2.
12 | */
13 | class SourceStateCase(
14 | private val sourceController: SourceController,
15 | ) {
16 |
17 | // 等到下一个番剧源就绪状态
18 | suspend fun awaitBundle(): SourceBundle {
19 | return sourceController.sourceBundle.filterIsInstance().first()
20 | }
21 |
22 | fun flowBundle(): Flow {
23 | return sourceController.sourceBundle.filterIsInstance()
24 | }
25 |
26 | fun flowState(): StateFlow {
27 | return sourceController.sourceInfo
28 | }
29 |
30 | fun stateFlowBundle(): StateFlow {
31 | return sourceController.sourceBundle
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/crash/NativeHandler.kt:
--------------------------------------------------------------------------------
1 | //package com.heyanle.easybangumi4.crash
2 | //
3 | //import android.content.Context
4 | //import com.pika.lib_signal.CallOnCatchSignal
5 | //
6 | ///**
7 | // * Created by heyanle on 2024/5/31.
8 | // * https://github.com/heyanLE
9 | // */
10 | //class NativeHandler: CallOnCatchSignal {
11 | //
12 | // override fun checkIsAnr(): Boolean {
13 | // return false
14 | // }
15 | //
16 | // override fun handleAnr(context: Context, logcat: String) {
17 | //
18 | // }
19 | //
20 | // override fun handleCrash(context: Context, signal: Int, logcat: String) {
21 | // SourceCrashController.onNativeCrash(signal, logcat)
22 | // }
23 | //}
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/dlna/DLNAUtils.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.dlna
2 |
3 | import org.cybergarage.upnp.Device
4 |
5 | /**
6 | * Created by heyanlin on 2024/2/6 14:35.
7 | */
8 | object DLNAUtils {
9 |
10 | const val Renderer = "urn:schemas-upnp-org:device:MediaRenderer:1"
11 |
12 | const val AVTransport1 = "urn:schemas-upnp-org:service:AVTransport:1"
13 | const val RenderingControl = "urn:schemas-upnp-org:service:RenderingControl:1"
14 |
15 |
16 | const val ActionSetAVTransportURI = "SetAVTransportURI"
17 | const val ActionPlay = "Play"
18 | const val ActionPause = "Pause"
19 | const val ActionStop = "Stop"
20 | const val ActionGetVolume = "GetVolume"
21 | const val ActionGetVolumeRange = "GetVolumeDBRange"
22 | const val ActionSetVolume = "SetVolume"
23 | const val ActionGetMute = "GetMute"
24 | const val ActionSetMute = "SetMute"
25 | const val ActionGetPosition = "GetPositionInfo"
26 | const val ActionSeek = "Seek"
27 | const val ActionGetMediaInfo = "GetMediaInfo"
28 |
29 |
30 | fun isMediaRenderDevice(device: Device?): Boolean {
31 | device ?: return false
32 | return device.deviceType == Renderer
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/dlna/DlnaModule.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.dlna
2 |
3 | import android.app.Application
4 | import com.heyanle.inject.api.InjectModule
5 | import com.heyanle.inject.api.InjectScope
6 | import com.heyanle.inject.api.addSingletonFactory
7 | import com.heyanle.inject.api.get
8 | import org.cybergarage.upnp.ControlPoint
9 |
10 | /**
11 | * Created by heyanlin on 2024/2/6 15:27.
12 | */
13 | class DlnaModule(
14 | private val application: Application
15 | ) : InjectModule {
16 |
17 | override fun InjectScope.registerInjectables() {
18 | addSingletonFactory {
19 | ControlPoint()
20 | }
21 | addSingletonFactory {
22 | EasyDlna(get())
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/exo/ClippingConfigMediaSourceFactory.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.exo
2 |
3 | import androidx.media3.common.MediaItem
4 | import androidx.media3.common.MediaItem.ClippingConfiguration
5 | import androidx.media3.common.util.UnstableApi
6 | import androidx.media3.exoplayer.drm.DrmSessionManagerProvider
7 | import androidx.media3.exoplayer.source.ClippingMediaSource
8 | import androidx.media3.exoplayer.source.MediaSource
9 | import androidx.media3.exoplayer.upstream.LoadErrorHandlingPolicy
10 |
11 | /**
12 | * Created by heyanle on 2024/6/23.
13 | * https://github.com/heyanLE
14 | */
15 | class ClippingConfigMediaSourceFactory(
16 | private val inner: MediaSource.Factory,
17 | private val clippingConfiguration: ClippingConfiguration
18 | ): MediaSource.Factory by inner {
19 |
20 | @UnstableApi
21 | override fun createMediaSource(mediaItem: MediaItem): MediaSource {
22 | return ClippingMediaSource(
23 | inner.createMediaSource(mediaItem),
24 | clippingConfiguration.startPositionUs,
25 | clippingConfiguration.endPositionUs,
26 | clippingConfiguration.startsAtKeyFrame,
27 | clippingConfiguration.relativeToLiveWindow,
28 | clippingConfiguration.relativeToDefaultPosition,
29 | )
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/exo/recorded/RecordedController.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.exo.recorded
2 |
3 | /**
4 | * Created by heyanle on 2024/6/23.
5 | * https://github.com/heyanLE
6 | */
7 | class RecordedController {
8 |
9 |
10 |
11 |
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/exo/recorded/task/RecordedTask.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.exo.recorded.task
2 |
3 | import kotlinx.coroutines.flow.StateFlow
4 | import java.lang.Exception
5 |
6 | /**
7 | * Created by heyanlin on 2024/6/24.
8 | */
9 | interface RecordedTask {
10 |
11 | data class TaskState(
12 | // 0 -> idle 1 -> doing 2 -> done 3 -> error
13 | val status: Int = 0,
14 | val statusLabel: String = "",
15 | val process: Int = -1,
16 | val errorException: Exception? = null,
17 | val error: String = ""
18 | )
19 |
20 | val state: StateFlow
21 |
22 | fun start()
23 |
24 | fun stop()
25 |
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/extension/ExtensionIconFactoryImpl.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.extension
2 |
3 | import android.graphics.drawable.Drawable
4 | import androidx.core.content.res.ResourcesCompat
5 | import com.heyanle.easybangumi4.case.ExtensionCase
6 | import com.heyanle.extension_api.ExtensionIconSource
7 | import com.heyanle.extension_api.IconFactory
8 | import java.lang.ref.SoftReference
9 |
10 | /**
11 | * Created by heyanlin on 2023/11/1.
12 | */
13 | class ExtensionIconFactoryImpl(
14 | private val extensionCase: ExtensionCase
15 | ) : IconFactory {
16 |
17 | private val iconMap = mutableMapOf>()
18 |
19 | override fun getIcon(source: ExtensionIconSource): Drawable? {
20 | val old = iconMap.get(source.key)?.get()
21 | if (old != null) {
22 | return old
23 | }
24 | val state = extensionCase.flowExtensionState().value
25 | val extensionInfo =
26 | state.extensionInfoMap.values.filterIsInstance()
27 | .find {
28 | it.sources.find { it.key == source.key } != null
29 | } ?: return null
30 | return source.getIconResourcesId()?.let { resId ->
31 | extensionInfo.resources?.let {
32 | runCatching {
33 | ResourcesCompat.getDrawable(it, resId, null)
34 | }.getOrNull()
35 | }
36 | }?.apply {
37 | iconMap[source.key] = SoftReference(this)
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/extension/loader/ExtensionLoader.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.extension.loader
2 |
3 | import com.heyanle.easybangumi4.plugin.extension.ExtensionInfo
4 |
5 |
6 | /**
7 | import com.heyanle.easy_extension.Extension
8 | * Created by heyanlin on 2023/10/24.
9 | */
10 | interface ExtensionLoader {
11 |
12 | /**
13 | * 拓展的唯一标识
14 | */
15 | val key: String
16 |
17 | /**
18 | * 加载拓展
19 | */
20 | fun load(): ExtensionInfo?
21 |
22 | fun canLoad(): Boolean
23 |
24 |
25 |
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/extension/provider/ExtensionProvider.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.extension.provider
2 |
3 | import com.heyanle.easybangumi4.plugin.extension.ExtensionInfo
4 | import kotlinx.coroutines.flow.StateFlow
5 |
6 | /**
7 | * Created by heyanle on 2024/7/29.
8 | * https://github.com/heyanLE
9 | */
10 | interface ExtensionProvider {
11 |
12 | data class ExtensionProviderState (
13 | val loading: Boolean,
14 | val extensionMap: Map = emptyMap(),
15 | )
16 |
17 | val flow: StateFlow
18 |
19 | fun init()
20 |
21 | fun release()
22 |
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/extension/provider/FileApkExtensionProvider.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.extension.provider
2 |
3 | import android.content.Context
4 | import com.heyanle.easybangumi4.plugin.extension.loader.ExtensionLoader
5 | import com.heyanle.easybangumi4.plugin.extension.loader.ExtensionLoaderFactory
6 | import kotlinx.coroutines.CoroutineDispatcher
7 | import java.io.File
8 |
9 | /**
10 | * Created by heyanle on 2024/7/29.
11 | * https://github.com/heyanLE
12 | */
13 | class FileApkExtensionProvider(
14 | private val context: Context,
15 | private val apkFileExtensionFolder: String,
16 | dispatcher: CoroutineDispatcher,
17 | cacheFolder: String,
18 | ) : AbsFolderExtensionProvider(apkFileExtensionFolder, cacheFolder, dispatcher) {
19 |
20 | companion object {
21 | const val TAG = "FileApkExtensionProvider"
22 |
23 | // 扩展名
24 | const val EXTENSION_SUFFIX = ".easybangumi.apk"
25 | }
26 |
27 | fun getSuffix(): String {
28 | return EXTENSION_SUFFIX
29 | }
30 |
31 | override fun checkName(displayName: String): Boolean {
32 | return displayName.endsWith(getSuffix())
33 | }
34 |
35 | override fun getNameWhenLoad(displayName: String, time: Long, atomicLong: Long): String {
36 | return "${time}-${atomicLong}${getSuffix()}"
37 | }
38 |
39 | override fun loadExtensionLoader(fileList: List): List {
40 | return ExtensionLoaderFactory.getFileApkExtensionLoaders(context, fileList.map { it.absolutePath })
41 | }
42 |
43 |
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/extension/push/ExtensionPushTask.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.extension.push
2 |
3 | import kotlinx.coroutines.CoroutineScope
4 | import kotlinx.coroutines.Job
5 |
6 | /**
7 | * Created by heyanlin on 2024/10/29.
8 | */
9 | interface ExtensionPushTask {
10 |
11 | companion object {
12 | const val EXTENSION_PUSH_TASK_IDENTIFY_FROM_FILE_URL = "from_file_url"
13 | const val EXTENSION_PUSH_TASK_IDENTIFY_FROM_CODE = "from_code"
14 | const val EXTENSION_PUSH_TASK_IDENTIFY_FROM_FILE_REPO = "from_file_repo"
15 | }
16 |
17 | data class Param(
18 | val identify: String,
19 | val str1: String = "",
20 | val str2: String = "",
21 | )
22 |
23 | fun identify(): String
24 |
25 | suspend fun invoke(
26 | scope: CoroutineScope,
27 | param: Param,
28 | container: ExtensionPushController.ExtensionPushTaskContainer,
29 | )
30 |
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/extension/service/NativeLoadService.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.extension.service
2 |
3 | /**
4 | * Created by heyanle on 2024/6/2.
5 | * https://github.com/heyanLE
6 | */
7 | class NativeLoadService(
8 | private val classLoader: ClassLoader
9 | ) {
10 |
11 | fun load(path: String): Boolean{
12 | try {
13 | val clazz = classLoader.loadClass("com.heyanle.extension_core.NativeLoadService") ?: return false
14 | val method = clazz.getDeclaredMethod("load", String::class.java) ?: return false
15 | method.invoke(null, path)
16 | return true
17 | }catch (e: Exception){
18 | e.printStackTrace()
19 | return false
20 | }
21 | }
22 |
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/extension/utils/log.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.extension.utils
2 |
3 | import android.util.Log
4 |
5 | /**
6 | * 打印info log
7 | */
8 | fun T.logi(tag: String = ""): T = apply {
9 | if (toString().length > 4000) {
10 | for (i in toString().indices step 4000) {
11 | if (i + 4000 < toString().length) {
12 | Log.i(tag, toString().substring(i, i + 4000))
13 | } else {
14 | Log.i(tag, toString().substring(i, toString().length))
15 | }
16 | }
17 | } else {
18 | Log.i(tag, toString())
19 | }
20 | }
21 |
22 | /**
23 | * 打印error log
24 | */
25 | fun T.loge(tag: String = ""): T = apply {
26 | if (toString().length > 4000) {
27 | for (i in toString().indices step 4000) {
28 | if (i + 4000 < toString().length) {
29 | Log.e(tag, toString().substring(i, i + 4000))
30 | } else {
31 | Log.e(tag, toString().substring(i, toString().length))
32 | }
33 | }
34 | } else {
35 | Log.e(tag, toString())
36 | }
37 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/JSIconFactory.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js
2 |
3 | /**
4 | * Created by heyanle on 2024/7/29.
5 | * https://github.com/heyanLE
6 | */
7 | class JSIconFactory {
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/component/JSBaseComponent.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js.component
2 |
3 | import com.heyanle.easybangumi4.source_api.component.Component
4 |
5 | interface JSBaseComponent: Component {
6 | suspend fun init() {}
7 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/entity/MainTab.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js.entity
2 |
3 | import java.util.ArrayList
4 | import java.util.Objects
5 |
6 | class MainTab (
7 | val label: String,
8 | val type: Int,
9 |
10 | // js 维护用以透传数据
11 | val ext: Object?,
12 | ) {
13 | companion object {
14 | const val MAIN_TAB_GROUP = 0
15 | const val MAIN_TAB_WITH_COVER = 1
16 | const val MAIN_TAB_WITHOUT_COVER = 2
17 | }
18 |
19 | constructor(label: String, type: Int): this(label, type, null)
20 | }
21 |
22 | class NonLabelMainTab(
23 | val type: Int,
24 |
25 | // js 维护用以透传数据
26 | val ext: Object?,
27 | ): ArrayList() {
28 | constructor(type: Int): this(type, null)
29 | }
30 |
31 | class SubTab (
32 | val label: String,
33 | val isCover: Boolean,
34 |
35 | // js 维护用以透传数据
36 | val ext: String?,
37 | ){
38 | constructor(label: String, isCover: Boolean): this(label, isCover, null)
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/runtime/JSRuntimeProvider.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js.runtime
2 |
3 | /**
4 | * Created by heyanle on 2024/7/30.
5 | * https://github.com/heyanLE
6 | */
7 | class JSRuntimeProvider(
8 | private val maxRuntimeCount: Int = 1
9 | ) {
10 |
11 | private val runtimes: MutableList = mutableListOf()
12 | private var currentPoint = 0
13 |
14 | fun getRuntime(): JSRuntime {
15 | synchronized(runtimes){
16 | currentPoint ++
17 | currentPoint %= maxRuntimeCount
18 | var runtime = runtimes.getOrNull(currentPoint)
19 | if(runtime != null){
20 | return runtime
21 | }
22 | runtime = JSRuntime()
23 | runtime.init()
24 | runtimes.add(runtime)
25 | return runtime
26 | }
27 | }
28 |
29 |
30 |
31 | fun release() {
32 | synchronized(runtimes){
33 | runtimes.forEach {
34 | it.release()
35 | }
36 | runtimes.clear()
37 | }
38 | }
39 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/source/AsyncIconSource.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js.source
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.drawable.Drawable
5 | import android.net.Uri
6 | import androidx.annotation.DrawableRes
7 | import com.heyanle.easybangumi4.source_api.IconSource
8 | import com.heyanle.easybangumi4.source_api.Source
9 | import okhttp3.HttpUrl
10 | import java.io.File
11 | import java.nio.ByteBuffer
12 |
13 | /**
14 | * Created by heyanle on 2024/7/29.
15 | * https://github.com/heyanLE
16 | */
17 | interface AsyncIconSource: Source {
18 |
19 |
20 | /**
21 | * Set the data to load.
22 | *
23 | * The default supported data types are:
24 | * - [String] (mapped to a [Uri])
25 | * - [Uri] ("android.resource", "content", "file", "http", and "https" schemes only)
26 | * - [HttpUrl]
27 | * - [File]
28 | * - [DrawableRes]
29 | * - [Drawable]
30 | * - [Bitmap]
31 | * - [ByteArray]
32 | * - [ByteBuffer]
33 | */
34 | fun getAsyncIconData(): Any
35 | }
36 |
37 |
38 | fun IconSource.getIconWithAsyncOrDrawable(): Any? {
39 | return if(this is AsyncIconSource){
40 | this.getAsyncIconData()
41 | }else{
42 | this.getIconFactory().invoke()
43 | }
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/utils/JSLogUtils.java:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js.utils;
2 |
3 |
4 | import android.util.Log;
5 |
6 | /**
7 | * Created by HeYanLe on 2024/11/3 21:42.
8 | * https://github.com/heyanLE
9 | */
10 |
11 | public class JSLogUtils {
12 |
13 | public static void i(String tag, String msg){
14 | Log.i(tag, msg);
15 | }
16 |
17 | public static void e(String tag, String msg){
18 | Log.e(tag, msg);
19 | }
20 |
21 | public static void d(String tag, String msg){
22 | Log.d(tag, msg);
23 | }
24 |
25 | public static void w(String tag, String msg){
26 | Log.w(tag, msg);
27 | }
28 |
29 | public static void v(String tag, String msg){
30 | Log.v(tag, msg);
31 | }
32 |
33 | public static void wtf(String tag, String msg){
34 | Log.wtf(tag, msg);
35 | }
36 |
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/utils/JSSourceUtils.java:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js.utils;
2 |
3 |
4 | import com.heyanle.easybangumi4.source_api.utils.core.SourceUtils;
5 |
6 | import kotlin.text.Regex;
7 |
8 | /**
9 | * Created by HeYanLe on 2024/11/3 20:54.
10 | * https://github.com/heyanLE
11 | */
12 |
13 | public class JSSourceUtils {
14 |
15 |
16 | public static String urlParser(String rootURL, String source) {
17 | return SourceUtils.INSTANCE.urlParser(rootURL, source);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/js/utils/JsExt.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.js.utils
2 |
3 | import com.heyanle.easybangumi4.utils.logi
4 | import org.mozilla.javascript.Context
5 | import org.mozilla.javascript.Function
6 | import org.mozilla.javascript.NativeJavaObject
7 |
8 | /**
9 | * Created by heyanle on 2024/7/30.
10 | * https://github.com/heyanLE
11 | */
12 |
13 | typealias JSContext = Context
14 | typealias JSFunction = Function
15 |
16 | fun Any?.jsUnwrap(): Any? {
17 | return if(this is NativeJavaObject){
18 | this.unwrap().apply {
19 | this.logi("jsUnwrap")
20 | }
21 | }else{
22 | this
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/SourceException.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source
2 |
3 | /**
4 | * Created by HeYanLe on 2023/10/29 18:11.
5 | * https://github.com/heyanLE
6 | */
7 | class SourceException(
8 | val msg: String,
9 | ): Exception(msg) {
10 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/SourceInfo.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source
2 |
3 | import com.heyanle.easybangumi4.plugin.source.bundle.ComponentBundle
4 | import com.heyanle.easybangumi4.plugin.source.bundle.SimpleComponentBundle
5 | import com.heyanle.easybangumi4.source_api.Source
6 |
7 | /**
8 | * Created by heyanlin on 2023/10/27.
9 | */
10 |
11 | sealed class SourceInfo {
12 | abstract val source: Source
13 |
14 | // // 数据迁移中
15 | // class Migrating(
16 | // override val source: Source,
17 | // val componentBundle: ComponentBundle,
18 | // ): SourceInfo()
19 |
20 | // 加载成功
21 | class Loaded(
22 | override val source: Source,
23 | val componentBundle: ComponentBundle,
24 | ): SourceInfo()
25 |
26 | // 加载失败
27 | class Error(
28 | override val source: Source,
29 | val msg: String,
30 | val exception: Exception? = null,
31 | ): SourceInfo()
32 | }
33 |
34 | class ConfigSource(
35 | val sourceInfo: SourceInfo,
36 | val config: SourceConfig,
37 | ){
38 | val source: Source
39 | get() = sourceInfo.source
40 | }
41 |
42 | data class SourceConfig(
43 | val key: String,
44 | val order: Int,
45 | val enable: Boolean,
46 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/SourcePreferences.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source
2 |
3 | import com.heyanle.easybangumi4.base.json.JsonFileProvider
4 | import com.heyanle.easybangumi4.base.preferences.Preference
5 | import com.heyanle.easybangumi4.base.preferences.PreferenceStore
6 | import com.heyanle.easybangumi4.source_api.Source
7 | import com.heyanle.easybangumi4.utils.jsonTo
8 | import com.heyanle.easybangumi4.utils.toJson
9 |
10 | /**
11 | * Created by HeYanLe on 2023/7/29 21:34.
12 | * https://github.com/heyanLE
13 | */
14 | class SourcePreferences(
15 | private val preferenceStore: PreferenceStore,
16 | private val jsonFileProvider: JsonFileProvider,
17 | ) {
18 |
19 | val configs = jsonFileProvider.sourceConfig
20 |
21 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/SourcesHost.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.CompositionLocalProvider
5 | import androidx.compose.runtime.collectAsState
6 | import androidx.compose.runtime.staticCompositionLocalOf
7 | import com.heyanle.easybangumi4.case.SourceStateCase
8 | import com.heyanle.easybangumi4.plugin.source.bundle.SourceBundle
9 | import com.heyanle.inject.core.Inject
10 |
11 | /**
12 | * Created by HeYanLe on 2023/2/22 20:41.
13 | * https://github.com/heyanLE
14 | */
15 |
16 | val LocalSourceBundleController = staticCompositionLocalOf {
17 | error("SourceBundle Not Provide")
18 | }
19 |
20 | @Composable
21 | fun SourcesHost(content: @Composable () -> Unit) {
22 | val sourceStateCase: SourceStateCase by Inject.injectLazy()
23 | val state = sourceStateCase.flowBundle().collectAsState(
24 | initial = SourceBundle(
25 | emptyList()
26 | )
27 | )
28 |
29 | CompositionLocalProvider(
30 | LocalSourceBundleController provides state.value
31 | ) {
32 | content()
33 | }
34 |
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/bundle/ComponentProxy.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source.bundle
2 |
3 | import com.heyanle.easy_crasher.CrashHandler
4 | import com.heyanle.easybangumi4.base.preferences.mmkv.MMKVPreference
5 | import com.heyanle.easybangumi4.crash.SourceCrashController
6 | import com.heyanle.easybangumi4.plugin.source.SourcePreferences
7 | import com.heyanle.easybangumi4.source_api.component.Component
8 | import com.tencent.mmkv.MMKV
9 | import java.lang.reflect.InvocationHandler
10 | import java.lang.reflect.Method
11 |
12 | /**
13 | * hook 源的所有方法,遇到不能闭环的在下次启动时进入安全模式
14 | * Created by heyanle on 2024/5/31.
15 | * https://github.com/heyanLE
16 | */
17 | class ComponentProxy(
18 | private val component: Component,
19 | ): InvocationHandler {
20 |
21 | override fun invoke(proxy: Any?, method: Method?, args: Array?): Any? {
22 | method ?: return null
23 | SourceCrashController.onComponentStart()
24 | val result = try {
25 | method.invoke(component, *args.orEmpty())
26 | }catch (e: Throwable){
27 | throw e
28 | }
29 | SourceCrashController.onComponentEnd()
30 | return result
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/debug/DebugSource.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source.debug
2 |
3 | import android.graphics.drawable.Drawable
4 | import androidx.core.content.res.ResourcesCompat
5 | import com.heyanle.easybangumi4.APP
6 | import com.heyanle.easybangumi4.R
7 | import com.heyanle.easybangumi4.cartoon.story.local.source.LocalSourceComponent
8 | import com.heyanle.extension_api.ExtensionIconSource
9 | import kotlin.reflect.KClass
10 |
11 |
12 | /**
13 | * Created by HeYanLe on 2024/11/3 19:52.
14 | * https://github.com/heyanLE
15 | */
16 |
17 | object DebugSource: ExtensionIconSource {
18 |
19 | const val DEBUG_SOURCE_KEY = "easybangumi_debug"
20 |
21 | override fun getIconResourcesId(): Int? = null
22 | override fun getIconFactory(): () -> Drawable? {
23 | return {
24 | ResourcesCompat.getDrawable(APP.resources, R.mipmap.logo_new, null)
25 | }
26 | }
27 |
28 | override val describe: String?
29 | get() = "调试番剧"
30 | override val key: String
31 | get() = DEBUG_SOURCE_KEY
32 | override val label: String
33 | get() = "调试番源"
34 | override val version: String
35 | get() = "1.0.0"
36 | override val versionCode: Int
37 | get() = 0
38 |
39 | override fun register(): List> {
40 | return listOf(
41 | DebugSourceComponent::class
42 | )
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/utils/PreferenceHelperImpl.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source.utils
2 |
3 | import com.heyanle.easybangumi4.base.hekv.HeKV
4 | import com.heyanle.easybangumi4.source_api.utils.api.PreferenceHelper
5 |
6 | /**
7 | * Created by HeYanLe on 2023/10/29 16:31.
8 | * https://github.com/heyanLE
9 | */
10 | class PreferenceHelperImpl(
11 | private val heKV: HeKV
12 | ): PreferenceHelper {
13 |
14 | override fun map(): Map {
15 | return heKV.map()
16 | }
17 | override fun get(key: String, def: String): String {
18 | return heKV.get(key, def)
19 | }
20 |
21 | override fun put(key: String, value: String) {
22 | heKV.put(key, value)
23 | }
24 |
25 |
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/utils/StringHelperImpl.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source.utils
2 |
3 | import android.widget.Toast
4 | import com.heyanle.easybangumi4.APP
5 | import com.heyanle.easybangumi4.source_api.utils.api.StringHelper
6 | import com.heyanle.easybangumi4.ui.common.moeDialogAlert
7 | import com.heyanle.easybangumi4.ui.common.moeSnackBar
8 |
9 | /**
10 | * Created by HeYanLe on 2023/10/29 16:29.
11 | * https://github.com/heyanLE
12 | */
13 | object StringHelperImpl: StringHelper {
14 |
15 | override fun moeDialog(text: String, title: String?) {
16 | text.moeDialogAlert(title)
17 | }
18 |
19 | override fun moeSnackBar(string: String) {
20 | string.moeSnackBar()
21 | }
22 |
23 | override fun toast(string: String) {
24 | Toast.makeText(APP, string, Toast.LENGTH_SHORT).show()
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/plugin/source/utils/network/interceptor/UserAgentInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.plugin.source.utils.network.interceptor
2 |
3 | import com.heyanle.easybangumi4.source_api.utils.api.NetworkHelper
4 | import okhttp3.Interceptor
5 | import okhttp3.Response
6 |
7 | /**
8 | * Created by heyanlin on 2024/6/4.
9 | */
10 | class UserAgentInterceptor(
11 | private val networkHelper: NetworkHelper
12 | ) : Interceptor {
13 |
14 | override fun intercept(chain: Interceptor.Chain): Response {
15 | val originalRequest = chain.request()
16 |
17 | return if (originalRequest.header("User-Agent").isNullOrEmpty()) {
18 | val newRequest = originalRequest
19 | .newBuilder()
20 | .removeHeader("User-Agent")
21 | .addHeader("User-Agent", networkHelper.randomUA)
22 | .build()
23 | chain.proceed(newRequest)
24 | } else {
25 | chain.proceed(originalRequest)
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/setting/SettingMMKVPreferences.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.setting
2 |
3 | import com.heyanle.easybangumi4.base.preferences.mmkv.MMKVPreferenceStore
4 |
5 | /**
6 | * 一些配置需要很早时候,因此不能存 sp,需要存 mmkv
7 | * Created by HeYanLe on 2023/7/30 19:31.
8 | * https://github.com/heyanLE
9 | */
10 | class SettingMMKVPreferences(
11 | private val preferenceStore: MMKVPreferenceStore
12 | ) {
13 |
14 | // WebView 兼容模式
15 | val webViewCompatible = preferenceStore.getBoolean("web_view_compatible", false)
16 |
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/splash/SplashGuildController.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.splash
2 |
3 | import com.heyanle.easybangumi4.base.preferences.android.AndroidPreferenceStore
4 | import com.heyanle.easybangumi4.cartoon.story.local.CartoonLocalController
5 | import com.heyanle.easybangumi4.splash.step.LocalStep
6 | import com.heyanle.easybangumi4.splash.step.PermissionStep
7 | import com.heyanle.easybangumi4.splash.step.ThemeStep
8 | import com.heyanle.okkv2.core.okkv
9 |
10 | /**
11 | * Created by heyanlin on 2024/7/4.
12 | */
13 | class SplashGuildController(
14 | private val androidPreferenceStore: AndroidPreferenceStore,
15 | private val localController: CartoonLocalController,
16 | ) {
17 |
18 |
19 | private val stepList = listOf(
20 | ThemeStep(),
21 | LocalStep(),
22 | PermissionStep(),
23 | )
24 | val realStep = stepList.flatMap {
25 | val version by okkv("splash_step_${it.name}_version", def = -1)
26 | if(it.version > version){
27 | listOf(it)
28 | } else {
29 | emptyList()
30 | }
31 | }
32 |
33 | fun end() {
34 | stepList.forEach {
35 | val version = okkv("splash_step_${it.name}_version", it.version)
36 | version.set(it.version)
37 | }
38 | }
39 |
40 |
41 |
42 |
43 |
44 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/storage/StorageModule.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.storage
2 |
3 | import android.app.Application
4 | import com.heyanle.inject.api.InjectModule
5 | import com.heyanle.inject.api.InjectScope
6 | import com.heyanle.inject.api.addSingletonFactory
7 | import com.heyanle.inject.api.get
8 |
9 | /**
10 | * Created by heyanlin on 2024/5/7.
11 | */
12 | class StorageModule(
13 | private val application: Application
14 | ) : InjectModule {
15 |
16 |
17 | override fun InjectScope.registerInjectables() {
18 |
19 | addSingletonFactory {
20 | StoragePreference(get())
21 | }
22 |
23 | addSingletonFactory {
24 | BackupController(get(), get(), get(), get())
25 | }
26 |
27 | addSingletonFactory {
28 | RestoreController(get(), get(), get(), get(), get())
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/common/BackgroundBasedBox.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.common
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.ui.Modifier
5 | import androidx.compose.ui.layout.Layout
6 | import androidx.compose.ui.unit.Constraints
7 |
8 | /**
9 | * Created by LoliBall on 2023/3/1 13:41.
10 | * https://github.com/WhichWho
11 | */
12 | @Composable
13 | fun BackgroundBasedBox(
14 | modifier: Modifier = Modifier,
15 | background: @Composable () -> Unit,
16 | foreground: @Composable () -> Unit,
17 | ) {
18 | Layout(
19 | modifier = modifier,
20 | content = {
21 | background()
22 | foreground()
23 | }
24 | ) { measurables, constraints ->
25 | val back = measurables[0].measure(constraints)
26 | val backConstraints = Constraints(maxWidth = back.width, maxHeight = back.height)
27 | val fore = measurables[1].measure(backConstraints)
28 | layout(back.width, back.height) {
29 | back.place(0, 0)
30 | fore.place(0, 0)
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/common/ExtensionCompose.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.common
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.fillMaxSize
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.runtime.collectAsState
8 | import androidx.compose.runtime.getValue
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import com.heyanle.easybangumi4.plugin.extension.ExtensionInfo
12 | import com.heyanle.easybangumi4.case.ExtensionCase
13 | import com.heyanle.inject.core.Inject
14 |
15 | /**
16 | * Created by HeYanLe on 2023/2/22 19:29.
17 | * https://github.com/heyanLE
18 | */
19 |
20 | @Composable
21 | fun ExtensionContainer(
22 | modifier: Modifier = Modifier,
23 | loadingContainerColor: Color = Color.Transparent,
24 | content: @Composable (List) -> Unit,
25 | ) {
26 |
27 | val extension: ExtensionCase by Inject.injectLazy()
28 | val state by extension.flowExtensionState().collectAsState()
29 | Box(
30 | modifier = Modifier
31 | .fillMaxSize()
32 | .then(modifier)
33 | ) {
34 | if (state.loading) {
35 | LoadingPage(
36 | modifier = Modifier
37 | .fillMaxSize()
38 | .background(loadingContainerColor)
39 | )
40 | } else {
41 | content(state.extensionInfoMap.values.toList())
42 | }
43 |
44 | }
45 |
46 |
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/common/Tabs.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.common
2 |
3 | import androidx.compose.foundation.layout.padding
4 | import androidx.compose.foundation.shape.RoundedCornerShape
5 | import androidx.compose.material3.TabPosition
6 | import androidx.compose.material3.TabRowDefaults
7 | import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.draw.clip
11 | import androidx.compose.ui.unit.dp
12 |
13 | /**
14 | * Created by HeYanLe on 2023/7/30 11:03.
15 | * https://github.com/heyanLE
16 | */
17 | @Composable
18 | fun TabIndicator(currentTabPosition: TabPosition) {
19 | TabRowDefaults.Indicator(
20 | Modifier
21 | .tabIndicatorOffset(currentTabPosition)
22 | .padding(horizontal = 8.dp)
23 | .clip(RoundedCornerShape(topStart = 3.dp, topEnd = 3.dp)),
24 | )
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/common/TopBarScreen.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.common
2 |
3 | import androidx.compose.runtime.Composable
4 |
5 | /**
6 | * Created by HeYanLe on 2023/2/21 23:27.
7 | * https://github.com/heyanLE
8 | */
9 | class HomePage(
10 | val tabLabel: @Composable (() -> Unit),
11 | val topAppBar: @Composable (()->Unit),
12 | val content: @Composable (() -> Unit),
13 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/common/proc/FilterWith.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.common.proc
2 |
3 | import kotlinx.coroutines.flow.Flow
4 | import kotlinx.coroutines.flow.MutableStateFlow
5 |
6 | /**
7 | * 筛选
8 | * Created by heyanlin on 2023/11/3.
9 | */
10 | class FilterWith(
11 | val id: String,
12 | val label: String,
13 | val filter: (T) -> Boolean,
14 | )
15 |
16 | class FilterState (
17 | val list: List>,
18 | val statusMap: Map,
19 | ){
20 |
21 | companion object {
22 | const val STATUS_OFF = 0
23 | const val STATUS_ON = 1
24 | const val STATUS_EXCLUDE = 2
25 | }
26 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/common/proc/SortBy.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.common.proc
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.MutableState
5 | import androidx.compose.runtime.remember
6 | import kotlinx.coroutines.flow.Flow
7 | import kotlinx.coroutines.flow.MutableStateFlow
8 | import kotlinx.coroutines.flow.StateFlow
9 |
10 |
11 | /**
12 | * 排序项目
13 | * Created by heyanlin on 2023/11/3.
14 | */
15 | class SortBy(
16 | val id: String,
17 | val label: String,
18 | val comparator: Comparator,
19 | )
20 |
21 | data class SortState(
22 | val sortList: List>,
23 | val current: String,
24 | val isReverse: Boolean,
25 | ){
26 | companion object {
27 | const val STATUS_OFF = 0
28 | const val STATUS_ON = 1
29 | const val STATUS_REVERSE = 2
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/main/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.main
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.mutableStateOf
6 | import androidx.compose.runtime.setValue
7 | import androidx.lifecycle.ViewModel
8 |
9 | /**
10 | * Created by HeYanLe on 2023/3/20 16:22.
11 | * https://github.com/heyanLE
12 | */
13 | class MainViewModel: ViewModel() {
14 |
15 |
16 | var customBottomBar by mutableStateOf<(@Composable ()->Unit)?>(null)
17 |
18 |
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/ui/setting/InnerJsExtensionSetting.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.ui.setting
2 |
3 | import androidx.compose.foundation.layout.ColumnScope
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.rememberCoroutineScope
6 | import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
7 | import com.heyanle.easybangumi4.setting.SettingPreferences
8 | import com.heyanle.inject.core.Inject
9 |
10 |
11 | /**
12 | * Created by HeYanLe on 2024/11/3 17:04.
13 | * https://github.com/heyanLE
14 | */
15 |
16 | @Composable
17 | fun ColumnScope.InnerJsExtensionSetting(
18 | nestedScrollConnection: NestedScrollConnection? = null
19 | ) {
20 |
21 | val scope = rememberCoroutineScope()
22 |
23 | val settingPreferences: SettingPreferences by Inject.injectLazy()
24 |
25 |
26 |
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/CDAction.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | /**
4 | * Created by HeYanLe on 2023/3/10 20:03.
5 | * https://github.com/heyanLE
6 | */
7 | class CDAction(
8 | private val cd: Int = 300,
9 | ) {
10 |
11 | private var lastTime = -1L
12 |
13 | fun action(block: ()->Unit){
14 | val now = System.currentTimeMillis()
15 | if(lastTime < 0 || now - lastTime >= cd){
16 | lastTime = now
17 | block()
18 | }
19 | }
20 |
21 |
22 |
23 |
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/CollectionUtils.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import java.util.concurrent.ConcurrentHashMap
4 |
5 | fun List.insertSeparators(
6 | generator: (T?, T?) -> R?,
7 | ): List {
8 | if (isEmpty()) return emptyList()
9 | val newList = mutableListOf()
10 | for (i in -1..lastIndex) {
11 | val before = getOrNull(i)
12 | before?.let { newList.add(it) }
13 | val after = getOrNull(i + 1)
14 | val separator = generator.invoke(before, after)
15 | separator?.let { newList.add(it) }
16 | }
17 | return newList
18 | }
19 |
20 | /**
21 | * Returns a new map containing only the key entries of [transform] that are not null.
22 | */
23 | inline fun Map.mapNotNullKeys(transform: (Map.Entry) -> R?): ConcurrentHashMap {
24 | val mutableMap = ConcurrentHashMap()
25 | forEach { element -> transform(element)?.let { mutableMap[it] = element.value } }
26 | return mutableMap
27 | }
28 |
29 | fun HashSet.addOrRemove(value: E, shouldAdd: Boolean) {
30 | if (shouldAdd) {
31 | add(value)
32 | } else {
33 | remove(value)
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/CoroutineProvider.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import kotlinx.coroutines.asCoroutineDispatcher
4 | import java.util.concurrent.Executors
5 | import java.util.concurrent.LinkedBlockingQueue
6 | import java.util.concurrent.ThreadPoolExecutor
7 | import java.util.concurrent.TimeUnit
8 |
9 | object CoroutineProvider {
10 |
11 | val SINGLE = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
12 |
13 | val newSingleExecutor
14 | get() = ThreadPoolExecutor(
15 | 0, 1,
16 | 10L, TimeUnit.SECONDS,
17 | LinkedBlockingQueue()
18 | )
19 |
20 | // 未使用的线程将在 10 秒后被终止
21 | val newSingleDispatcher
22 | get() = ThreadPoolExecutor(
23 | 0, 1,
24 | 10L, TimeUnit.SECONDS,
25 | LinkedBlockingQueue()
26 | ).asCoroutineDispatcher()
27 |
28 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/CoroutineUtils.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import kotlin.coroutines.Continuation
4 | import kotlin.coroutines.resume
5 |
6 | /**
7 | * Created by heyanlin on 2024/6/24.
8 | */
9 |
10 | fun Continuation.safeResume(t: T) {
11 | kotlin.runCatching {
12 | this.resume(t)
13 | }.onFailure {
14 | it.printStackTrace()
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/Dimens.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import android.content.Context
4 | import com.heyanle.easybangumi4.APP
5 |
6 | /**
7 | * Created by HeYanLe on 2023/1/12 22:25.
8 | * https://github.com/heyanLE
9 | */
10 | fun dip2px(context: Context, dpValue: Float): Int {
11 | val scale: Float = context.resources.displayMetrics.density
12 | return (dpValue * scale + 0.5f).toInt()
13 | }
14 |
15 | fun px2dip(context: Context, pxValue: Float): Float {
16 | val scale: Float = context.resources.displayMetrics.density
17 | return (pxValue / scale)
18 | }
19 |
20 | fun Float.dip2px(): Int {
21 | return dip2px(APP, this)
22 | }
23 |
24 | fun Int.dip2px(): Int {
25 | return dip2px(APP, this.toFloat())
26 | }
27 |
28 | fun Float.px2dip(): Float {
29 | return px2dip(APP, this)
30 | }
31 |
32 | fun Int.px2dip(): Float {
33 | return px2dip(APP, this.toFloat())
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/ImmutableExt.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import com.google.common.collect.ImmutableList
4 | import com.google.common.collect.ImmutableMap
5 | import com.google.common.collect.ImmutableSet
6 |
7 | /**
8 | * Created by heyanlin on 2024/2/19 15:26.
9 | */
10 |
11 | fun Collection.toImmutableList(): ImmutableList {
12 | return ImmutableList.copyOf(this)
13 | }
14 |
15 | fun Map.toImmutableMap(): ImmutableMap {
16 | return ImmutableMap.copyOf(this)
17 | }
18 |
19 | fun Collection.toImmutableSet(): ImmutableSet {
20 | return ImmutableSet.copyOf(this)
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/KtorUtil.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.call.body
5 | import io.ktor.client.request.prepareGet
6 | import io.ktor.utils.io.ByteReadChannel
7 | import io.ktor.utils.io.core.isEmpty
8 | import io.ktor.utils.io.core.readBytes
9 | import java.io.File
10 |
11 | /**
12 | * Created by heyanlin on 2023/11/17.
13 | */
14 | object KtorUtil {
15 |
16 | val client = HttpClient()
17 |
18 |
19 | }
20 |
21 | suspend fun String.downloadTo(path: String) {
22 | val targetFileTemp = File("${path}.temp")
23 | val targetFile = File(path)
24 | targetFile.parentFile?.mkdirs()
25 |
26 | if(targetFileTemp.exists()){
27 | targetFileTemp.delete()
28 | }
29 | KtorUtil.client.prepareGet (this).execute { httpResponse ->
30 | val channel: ByteReadChannel = httpResponse.body()
31 | while (!channel.isClosedForRead) {
32 | val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
33 | while (!packet.isEmpty) {
34 | val bytes = packet.readBytes()
35 | if(!targetFileTemp.exists()){
36 | targetFileTemp.createNewFile()
37 | }
38 | targetFileTemp.appendBytes(bytes)
39 | }
40 | }
41 | }
42 | if (targetFileTemp.exists() && targetFileTemp.length() > 0){
43 | targetFile.delete()
44 | targetFileTemp.renameTo(targetFile)
45 | }
46 |
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/Md5Extension.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import java.io.File
4 | import java.io.FileInputStream
5 | import java.security.MessageDigest
6 | import java.security.DigestInputStream
7 |
8 | /**
9 | * Created by heyanlin on 2023/11/13.
10 | */
11 | fun File.getFileMD5(): String {
12 | val digest = MessageDigest.getInstance("MD5")
13 | return kotlin.runCatching {
14 | FileInputStream(this).use {
15 | DigestInputStream(it, digest).use {
16 | while (it.read() != -1) { }
17 | val md5 = digest.digest()
18 | bytesToHex(md5)
19 | }
20 | }
21 | }.getOrElse {
22 | it.printStackTrace()
23 | ""
24 | }
25 | }
26 |
27 | fun String.getMD5(): String {
28 | val digest = MessageDigest.getInstance("MD5")
29 | return kotlin.runCatching {
30 | val md5 = digest.digest(this.toByteArray())
31 | bytesToHex(md5)
32 | }.getOrElse {
33 | it.printStackTrace()
34 | ""
35 | }
36 | }
37 |
38 | fun bytesToHex(bytes: ByteArray): String {
39 | val hexArray = "0123456789ABCDEF".toCharArray()
40 | val hexChars = CharArray(bytes.size * 2)
41 | for (j in bytes.indices) {
42 | val v = bytes[j].toInt() and 0xFF
43 | hexChars[j * 2] = hexArray[v.ushr(4)]
44 | hexChars[j * 2 + 1] = hexArray[v and 0x0F]
45 | }
46 | return String(hexChars)
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/MemoryHelper.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import android.app.ActivityManager
4 | import android.content.Context
5 | import android.content.Context.ACTIVITY_SERVICE
6 |
7 | /**
8 | * Created by heyanlin on 2023/12/25.
9 | */
10 | fun Context.getMemoryInfo(): ActivityManager.MemoryInfo {
11 | val activityManager = getSystemService(ACTIVITY_SERVICE) as ActivityManager
12 | return ActivityManager.MemoryInfo().also {
13 | activityManager.getMemoryInfo(it)
14 | }
15 | }
16 |
17 | class EasyMemoryInfo(
18 | context: Context
19 | ) : ActivityManager.MemoryInfo() {
20 |
21 | private val activityManager = context.getSystemService(ACTIVITY_SERVICE) as ActivityManager
22 | fun update() {
23 | activityManager.getMemoryInfo(this)
24 | }
25 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/OkhttpHelper.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import com.heyanle.easybangumi4.APP
4 | import com.heyanle.easybangumi4.BuildConfig
5 | import com.heyanle.easybangumi4.base.DataResult
6 | import okhttp3.Cache
7 | import okhttp3.OkHttpClient
8 | import okhttp3.Request
9 | import okhttp3.logging.HttpLoggingInterceptor
10 | import java.io.File
11 | import java.io.IOException
12 |
13 | /**
14 | * Created by HeYanLe on 2023/4/3 23:03.
15 | * https://github.com/heyanLE
16 | */
17 | object OkhttpHelper {
18 |
19 | private val cacheDir: File by lazy {
20 | File(APP.cacheDir, "network_cache")
21 | }
22 | private val cacheSize = 5L * 1024 * 1024 // 5 MiB
23 |
24 | private val baseClientBuilder: OkHttpClient.Builder
25 | get() {
26 | val builder = OkHttpClient.Builder()
27 |
28 | if (BuildConfig.DEBUG) {
29 | val httpLoggingInterceptor = HttpLoggingInterceptor().apply {
30 | level = HttpLoggingInterceptor.Level.HEADERS
31 | }
32 | builder.addNetworkInterceptor(httpLoggingInterceptor)
33 | }
34 | return builder
35 | }
36 |
37 | val client by lazy { baseClientBuilder.cache(Cache(cacheDir, cacheSize)).build() }
38 |
39 |
40 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/PathHelper.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import android.content.Context
4 | import android.content.Context.MODE_PRIVATE
5 | import android.content.Context.MODE_WORLD_WRITEABLE
6 | import java.io.File
7 |
8 | /**
9 | * Created by HeYanLe on 2023/8/5 16:57.
10 | * https://github.com/heyanLE
11 | */
12 | fun Context.getFilePath(): String {
13 | return getExternalFilesDir(null)?.absolutePath ?: cacheDir.absolutePath
14 | }
15 |
16 | fun Context.getInnerFilePath(): String {
17 | return File(getDir("files", MODE_PRIVATE)?.absolutePath ?: cacheDir.absolutePath).absolutePath
18 | }
19 |
20 |
21 | fun Context.getInnerFilePath(type: String): String {
22 | return File(getDir("files", MODE_PRIVATE)?.absolutePath ?: cacheDir.absolutePath, type).absolutePath
23 | }
24 |
25 |
26 | fun Context.getFilePath(type: String): String {
27 | return getExternalFilesDir(type)?.absolutePath ?: File(cacheDir, type).absolutePath
28 | }
29 |
30 | fun Context.getCachePath(): String {
31 | return externalCacheDir?.absolutePath ?: cacheDir.absolutePath
32 | }
33 |
34 | fun Context.getCachePath(type: String): String {
35 | return externalCacheDir?.let { File(it, type) }?.absolutePath ?: File(
36 | cacheDir,
37 | type
38 | ).absolutePath
39 | }
40 |
41 |
42 | fun Context.getInnerCachePath(type: String) : String {
43 | return File(
44 | cacheDir,
45 | type
46 | ).absolutePath
47 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/SizeHelper.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.collectAsState
6 | import androidx.compose.runtime.getValue
7 | import androidx.compose.runtime.remember
8 | import com.heyanle.easybangumi4.LocalWindowSizeController
9 | import com.heyanle.easybangumi4.setting.SettingPreferences
10 | import com.heyanle.inject.core.Inject
11 |
12 | /**
13 | * Created by HeYanLe on 2023/6/4 16:42.
14 | * https://github.com/heyanLE
15 | */
16 | @Composable
17 | fun isCurPadeMode(): Boolean {
18 | val windowSize = LocalWindowSizeController.current
19 | val settingPreferences: SettingPreferences by Inject.injectLazy()
20 | val padMode by settingPreferences.padMode.flow()
21 | .collectAsState(settingPreferences.padMode.get())
22 | val isPad = remember(padMode, windowSize) {
23 | when (padMode) {
24 | SettingPreferences.PadMode.AUTO -> windowSize.widthSizeClass == WindowWidthSizeClass.Expanded
25 | SettingPreferences.PadMode.ENABLE -> true
26 | else -> false
27 | }
28 | }
29 | padMode.loge("SizeHelper")
30 | return isPad
31 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/TODO.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import com.heyanle.easybangumi4.ui.common.moeSnackBar
4 |
5 | /**
6 | * Created by HeYanLe on 2023/3/1 15:59.
7 | * https://github.com/heyanLE
8 | */
9 | fun TODO(msg: String = "") {
10 | "在做了,在做了 $msg".moeSnackBar()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/Text.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | /**
4 | * 屏蔽带有某些关键字的弹幕
5 | *
6 | * @return 若屏蔽此字符串,则返回true,否则false
7 | */
8 | fun String.shield(): Boolean {
9 | return false
10 | }
11 |
12 | /**
13 | * 格式化合成字符串,生成类似"1 - 2 - 3"这样的字符串,空白或者null不会加上多余分隔符
14 | */
15 | fun formatMergedStr(delimiter: String, vararg strs: String?) = StringBuilder().apply {
16 | for (str in strs)
17 | if (!str.isNullOrBlank())
18 | append(str).append(delimiter)
19 | }.removeSuffix(delimiter).toString()
20 |
21 | /**
22 | * 如果此CharSequence包含指定的其他多个字符序列中的任意一个作为子字符串,则返回true 。
23 | */
24 | fun CharSequence.containStrs(vararg strs: CharSequence) = strs.find { contains(it) } != null
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/ThreadUtils.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import android.os.Looper
4 |
5 | /**
6 | * Created by heyanle on 2024/7/27.
7 | * https://github.com/heyanLE
8 | */
9 | fun isMainThread(): Boolean {
10 | return Looper.getMainLooper() == Looper.myLooper()
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/TimeLogUtils.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | /**
4 | * Created by heyanlin on 2023/11/1.
5 | */
6 | object TimeLogUtils {
7 |
8 | const val TAG = "TimeLogUtils"
9 |
10 | private var lastTime: Long = System.currentTimeMillis()
11 | fun i(msg: String){
12 | val time = System.currentTimeMillis()
13 | "${time} ${msg} ${time - lastTime}".logi(TAG)
14 | lastTime = time
15 | }
16 |
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/Toast.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import android.widget.Toast
4 | import com.heyanle.easybangumi4.APP
5 |
6 | /**
7 | * Created by HeYanLe on 2022/12/23 17:50.
8 | * https://github.com/heyanLE
9 | */
10 | fun T.toast(len: Int = Toast.LENGTH_SHORT): T = apply {
11 | Toast.makeText(APP, toString(), len).show()
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/UUIDHelper.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import com.heyanle.okkv2.core.okkv
4 | import java.util.UUID
5 |
6 | /**
7 | * Created by heyanlin on 2024/2/13 21:18.
8 | */
9 | object UUIDHelper {
10 |
11 |
12 | fun getUUID(): String {
13 | var uuid by okkv("easy_bangumi_uuid")
14 | val uu = uuid
15 | return if (uu == null) {
16 | val u = UUID.randomUUID().toString()
17 | uuid = u
18 | u
19 | } else {
20 | uu
21 | }
22 | }
23 |
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/UniFileUtils.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import com.hippo.unifile.UniFile
4 |
5 | /**
6 | * Created by heyanle on 2024/7/9.
7 | * https://github.com/heyanLE
8 | */
9 | fun UniFile.deleteRecursively() {
10 | if (!exists()) {
11 | return
12 | }
13 | if(isDirectory){
14 | listFiles()?.forEach {
15 | it.deleteRecursively()
16 | }
17 | }
18 | delete()
19 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/UriHelper.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import android.content.Context
4 | import android.net.Uri
5 | import android.provider.OpenableColumns
6 |
7 | /**
8 | * Created by heyanlin on 2024/5/21.
9 | */
10 | object UriHelper {
11 |
12 | fun getFileNameFromUri(context: Context, uri: Uri): String? {
13 | var fileName: String? = null
14 | context.contentResolver.query(uri, null, null, null, null)?.use { cursor ->
15 | val nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)
16 | cursor.moveToFirst()
17 | fileName = cursor.getString(nameIndex)
18 | }
19 | return fileName
20 | }
21 |
22 |
23 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/ViewModelOwnerMap.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils
2 |
3 | import androidx.lifecycle.ViewModelStore
4 | import androidx.lifecycle.ViewModelStoreOwner
5 |
6 | /**
7 | * Created by HeYanLe on 2023/3/25 15:42.
8 | * https://github.com/heyanLE
9 | */
10 | class ViewModelOwnerMap {
11 |
12 | private val viewModelOwnerStore = hashMapOf()
13 |
14 | fun getViewModelStoreOwner(key: T) = object: ViewModelStoreOwner {
15 |
16 | override val viewModelStore: ViewModelStore
17 | get() {
18 | var viewModelStore = viewModelOwnerStore[key]
19 | if (viewModelStore == null) {
20 | viewModelStore = ViewModelStore()
21 | viewModelOwnerStore[key] = viewModelStore
22 | }
23 | return viewModelStore
24 | }
25 | }
26 |
27 | fun clear(){
28 | viewModelOwnerStore.iterator().forEach {
29 | it.value.clear()
30 | }
31 | viewModelOwnerStore.clear()
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/heyanle/easybangumi4/utils/exo_ssl/TrustAllHostnameVerifier.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easybangumi4.utils.exo_ssl
2 |
3 | import javax.net.ssl.HostnameVerifier
4 | import javax.net.ssl.SSLSession
5 |
6 | class TrustAllHostnameVerifier : HostnameVerifier {
7 | override fun verify(hostname: String?, session: SSLSession?): Boolean {
8 | return true
9 | }
10 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ayala.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/src/main/res/drawable/ayala.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_arrow_back_24.xml:
--------------------------------------------------------------------------------
1 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_close_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_download_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_keyboard_arrow_left_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_replay_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/empty_bocchi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/src/main/res/drawable/empty_bocchi.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/error_ikuyo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/src/main/res/drawable/error_ikuyo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/github.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_brightness_6_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_fast_forward_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_fullscreen_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_lock_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_lock_open_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_pause_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_play_arrow_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_volume_down_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_volume_off_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_volume_up_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_webview_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/src/main/res/drawable/placeholder.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/play_vb_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/player_bottom_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/player_controller_bt_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/player_up_bg.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/qq.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/telegram.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/src/main/res/mipmap-xxhdpi/app_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/logo_new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app/src/main/res/mipmap-xxhdpi/logo_new.png
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/file_path.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
12 |
15 |
16 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/test/java/com/heyanle/myapplication/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.myapplication
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
--------------------------------------------------------------------------------
/app_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/app_logo.png
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(build.plugins.android.application) apply false
4 | alias(build.plugins.android.library) apply false
5 | alias(build.plugins.kotlin.android) apply false
6 | alias(build.plugins.kotlin.jvm) apply false
7 |
8 | id("com.google.gms.google-services") version "4.4.2" apply false
9 | id("com.google.firebase.crashlytics") version "3.0.2" apply false
10 | }
11 |
12 |
13 | tasks.create("clean") {
14 | delete {
15 | rootProject.layout.buildDirectory
16 | }
17 | }
18 |
19 | subprojects {
20 | // 定义检查依赖变化的时间间隔,!!配置为0实时刷新
21 | configurations.all {
22 | // check for updates every build
23 | resolutionStrategy.cacheChangingModulesFor(0, java.util.concurrent.TimeUnit.SECONDS)
24 | }
25 | }
--------------------------------------------------------------------------------
/buildSrc/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /src/main/java/com/heyanle/buildsrc/PrivateValue.java
--------------------------------------------------------------------------------
/buildSrc/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | `kotlin-dsl`
3 | }
4 |
5 | repositories {
6 | mavenCentral()
7 | google()
8 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/heyanle/buildsrc/Android.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.buildsrc
2 |
3 | /**
4 | * Created by HeYanLe on 2023/1/3 17:17.
5 | * https://github.com/heyanLE
6 | */
7 | object Android {
8 | const val minSdk = 21
9 | const val targetSdk = 34
10 | const val compileSdk = 34
11 |
12 | const val versionCode = 99
13 | const val versionName = "5.4.7"
14 |
15 |
16 | }
--------------------------------------------------------------------------------
/buildSrc/src/main/java/com/heyanle/buildsrc/RoomSchemaArgProvider.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.buildsrc
2 |
3 | import org.gradle.api.tasks.InputDirectory
4 | import org.gradle.api.tasks.PathSensitive
5 | import org.gradle.api.tasks.PathSensitivity
6 | import org.gradle.process.CommandLineArgumentProvider
7 | import java.io.File
8 |
9 | class RoomSchemaArgProvider(
10 | @get:InputDirectory
11 | @get:PathSensitive(PathSensitivity.RELATIVE)
12 | val schemaDir: File
13 | ) : CommandLineArgumentProvider {
14 |
15 | override fun asArguments(): Iterable {
16 | // Note: If you're using KSP, change the line below to return
17 | // listOf("room.schemaLocation=${schemaDir.path}").
18 | return listOf("room.schemaLocation=${schemaDir.path}")
19 | }
20 | }
--------------------------------------------------------------------------------
/easy-crasher/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/easy-crasher/build.gradle.kts:
--------------------------------------------------------------------------------
1 |
2 | import com.heyanle.buildsrc.Android
3 |
4 | plugins {
5 | alias(build.plugins.android.library)
6 | alias(build.plugins.kotlin.android)
7 | // id("com.android.library")
8 | // id("org.jetbrains.kotlin.android")
9 | }
10 |
11 | android {
12 | namespace = "com.heyanle.easy_crasher"
13 | compileSdk = Android.compileSdk
14 |
15 | defaultConfig {
16 | minSdk = Android.minSdk
17 | lint.targetSdk = Android.compileSdk
18 |
19 | consumerProguardFiles("consumer-rules.pro")
20 | }
21 |
22 | buildTypes {
23 | release {
24 | isMinifyEnabled = false
25 | proguardFiles(
26 | getDefaultProguardFile("proguard-android-optimize.txt"),
27 | "proguard-rules.pro"
28 | )
29 | }
30 | }
31 | compileOptions {
32 | sourceCompatibility = JavaVersion.VERSION_1_8
33 | targetCompatibility = JavaVersion.VERSION_1_8
34 | }
35 | kotlinOptions {
36 | jvmTarget = "1.8"
37 | }
38 | }
39 |
40 | dependencies {
41 | implementation(androidx.core.ktx)
42 | implementation(androidx.appcompat)
43 | }
--------------------------------------------------------------------------------
/easy-crasher/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/easy-crasher/consumer-rules.pro
--------------------------------------------------------------------------------
/easy-crasher/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -keep org.bouncycastle.jsse.BCSSLParameters
24 | -keep org.bouncycastle.jsse.BCSSLSocket
25 | -keep org.bouncycastle.jsse.provider.BouncyCastleJsseProvider
26 | -keep org.conscrypt.Conscrypt
27 | -keep org.conscrypt.Conscrypt
28 | -keep org.conscrypt.ConscryptHostnameVerifier
29 | -keep org.openjsse.javax.net.ssl.SSLParameters
30 | -keep org.openjsse.javax.net.ssl.SSLSocket
31 | -keep org.openjsse.net.ssl.OpenJSSE
--------------------------------------------------------------------------------
/easy-crasher/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/easy-crasher/src/main/java/com/heyanle/easy_crasher/CrashActivity.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easy_crasher
2 |
3 | import android.app.Activity
4 | import android.os.Bundle
5 | import android.widget.LinearLayout
6 | import android.widget.TextView
7 | import org.w3c.dom.Text
8 |
9 | /**
10 | * Created by HeYanLe on 2022/9/4 15:02.
11 | * https://github.com/heyanLE
12 | */
13 | class CrashActivity : Activity() {
14 |
15 | companion object {
16 | const val KEY_ERROR_MSG = "ERROR_MSG"
17 | }
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | super.onCreate(savedInstanceState)
21 | setContentView(R.layout.activity_crash)
22 | initView()
23 | }
24 |
25 | private fun initView() {
26 | val btTitle = findViewById(R.id.tv_title)
27 | val title =
28 | "手机型号:${android.os.Build.MODEL}\n安卓版本:${android.os.Build.VERSION.RELEASE}"
29 | btTitle.text = title
30 |
31 | val tvErrMsg = findViewById(R.id.tv_msg)
32 |
33 |
34 | tvErrMsg.text = intent?.getStringExtra(KEY_ERROR_MSG)
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/easy-crasher/src/main/java/com/heyanle/easy_crasher/CrashHandler.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easy_crasher
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import java.io.PrintWriter
6 | import java.io.StringWriter
7 | import java.lang.Thread.getDefaultUncaughtExceptionHandler
8 |
9 | /**
10 | * Created by HeYanLe on 2022/9/4 15:10.
11 | * https://github.com/heyanLE
12 | */
13 | class CrashHandler(
14 | private val context: Context
15 | ) : Thread.UncaughtExceptionHandler {
16 |
17 | override fun uncaughtException(t: Thread, e: Throwable) {
18 | runCatching {
19 | val stringWriter = StringWriter()
20 | val printWriter = PrintWriter(stringWriter)
21 | e.printStackTrace(printWriter)
22 | var th: Throwable? = e.cause
23 | while (th != null) {
24 | th.printStackTrace(printWriter)
25 | th = th.cause
26 | }
27 | e.printStackTrace()
28 | val intent = Intent(context, CrashActivity::class.java)
29 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
30 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
31 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
32 | intent.putExtra(CrashActivity.KEY_ERROR_MSG, stringWriter.toString())
33 | context.startActivity(intent)
34 | }.onFailure {
35 | it.printStackTrace()
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/easy-crasher/src/main/res/layout/activity_crash.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
20 |
21 |
22 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/easy-i18n/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/easy-i18n/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | // id("com.android.library")
3 | // id("org.jetbrains.kotlin.android")
4 | alias(build.plugins.android.library)
5 | alias(build.plugins.kotlin.android)
6 | }
7 |
8 | android {
9 | namespace = "com.heyanle.easy_i18n"
10 | compileSdk = com.heyanle.buildsrc.Android.compileSdk
11 |
12 | defaultConfig {
13 | minSdk = com.heyanle.buildsrc.Android.minSdk
14 | lint.targetSdk = com.heyanle.buildsrc.Android.targetSdk
15 | }
16 |
17 | buildTypes {
18 | release {
19 | isMinifyEnabled = false
20 | proguardFiles(
21 | getDefaultProguardFile("proguard-android-optimize.txt"),
22 | "proguard-rules.pro"
23 | )
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility = JavaVersion.VERSION_11
28 | targetCompatibility = JavaVersion.VERSION_11
29 | }
30 | kotlinOptions {
31 | jvmTarget = "11"
32 | }
33 | }
34 |
35 | dependencies {
36 | }
--------------------------------------------------------------------------------
/easy-i18n/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/easy-i18n/consumer-rules.pro
--------------------------------------------------------------------------------
/easy-i18n/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/easy-i18n/src/androidTest/java/com/heyanle/easy_i18n/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easy_i18n
2 |
3 |
4 | /**
5 | * Instrumented test, which will execute on an Android device.
6 | *
7 | * See [testing documentation](http://d.android.com/tools/testing).
8 | */
9 | //@RunWith(AndroidJUnit4::class)
10 | //class ExampleInstrumentedTest {
11 | // @Test
12 | // fun useAppContext() {
13 | // // Context of the app under test.
14 | // val appContext = InstrumentationRegistry.getInstrumentation().targetContext
15 | // assertEquals("com.heyanle.easy_i18n.test", appContext.packageName)
16 | // }
17 | //}
--------------------------------------------------------------------------------
/easy-i18n/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/easy-i18n/src/test/java/com/heyanle/easy_i18n/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.easy_i18n
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/git_email.sh:
--------------------------------------------------------------------------------
1 | git filter-branch -f --env-filter '
2 | OLD_EMAIL="1371735400@qq.com"
3 | CORRECT_NAME="heyanle"
4 | CORRECT_EMAIL="eke_le@163.com"
5 | if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
6 | then
7 | export GIT_COMMITTER_NAME="$CORRECT_NAME"
8 | export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
9 | fi
10 | if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
11 | then
12 | export GIT_AUTHOR_NAME="$CORRECT_NAME"
13 | export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
14 | fi
15 | ' --tag-name-filter cat -- --branches --tags
16 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx4000m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 |
25 | #android.disableAutomaticComponentCreation=true
26 | android.nonFinalResIds=false
--------------------------------------------------------------------------------
/gradle/build.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | android_gradle_plugin = "8.2.2"
3 | compose_compiler = "1.5.9"
4 | kotlin = "1.9.22"
5 | ksp = "1.9.22-1.0.17"
6 |
7 | [plugins]
8 | android_application = {id = "com.android.application", version.ref = "android_gradle_plugin"}
9 | android_library = {id = "com.android.library", version.ref = "android_gradle_plugin"}
10 | kotlin_android = {id = "org.jetbrains.kotlin.android", version.ref = "kotlin"}
11 | kotlin_jvm = {id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin"}
12 | ksp = {id = "com.google.devtools.ksp", version.ref = "ksp"}
13 | #maven_publish = {id = "maven-publish"}
14 | #signing = {id = "signing"}
15 |
16 |
--------------------------------------------------------------------------------
/gradle/extension.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | # 对应 library 版本 5
3 | version_code = "11"
4 | extensions = "1.11-SNAPSHOT"
5 | [libraries]
6 | source_api = {module = "io.github.easybangumiorg:source-api", version.ref = "extensions"}
7 | source_utils = {module = "io.github.easybangumiorg:source-utils", version.ref = "extensions"}
8 | extension_api = {module = "io.github.easybangumiorg:extension-api", version.ref = "extensions"}
9 | [plugins]
10 |
11 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/easybangumiorg/EasyBangumi/8f12a1b6964c711ad3f53898348c81f9f462892b/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Feb 08 10:48:12 CST 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/inject/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/inject/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | alias(build.plugins.kotlin.jvm)
4 | }
5 | kotlin {
6 | jvmToolchain(17)
7 |
8 | }
9 | java {
10 | sourceCompatibility = JavaVersion.VERSION_17
11 | targetCompatibility = JavaVersion.VERSION_17
12 | }
--------------------------------------------------------------------------------
/inject/src/main/java/com/heyanle/inject/api/Factory.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.inject.api
2 |
3 | import java.lang.reflect.Type
4 |
5 | /**
6 | * Created by HeYanLe on 2023/7/29 19:32.
7 | * https://github.com/heyanLE
8 | */
9 | interface InjectFactory {
10 | fun getInstance(forType: Type): R
11 |
12 | // 返回当前容器中缓存的实例,如果没有则返回 null
13 | // 对于 factory 模式,直接返回 null
14 | fun getCurrentInstanceOrNull(forType: Type): R?
15 |
16 | fun getKeyedInstance(forType: Type, key: K): R
17 |
18 |
19 | }
20 |
21 | @Suppress("NOTHING_TO_INLINE")
22 | inline fun InjectFactory.get(forType: TypeReference): R = getInstance(forType.type)
23 |
24 | @Suppress("NOTHING_TO_INLINE")
25 | inline fun InjectFactory.get(forType: TypeReference, key: Any): R = getKeyedInstance(forType.type, key)
26 |
27 | inline fun InjectFactory.get(key: Any): R = getKeyedInstance(fullType().type, key)
28 |
29 | inline fun InjectFactory.getOrNull(): R? = runCatching {
30 | get()
31 | }.getOrNull()
32 |
33 | inline fun InjectFactory.get(): R = getInstance(fullType().type)
--------------------------------------------------------------------------------
/inject/src/main/java/com/heyanle/inject/api/InjectModule.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.inject.api
2 |
3 | /**
4 | * Created by HeYanLe on 2023/7/29 20:11.
5 | * https://github.com/heyanLE
6 | */
7 | abstract class InjectScopedMain(val scope: InjectScope) : InjectModule {
8 | init {
9 | scope.registerInjectables()
10 | }
11 | }
12 |
13 | interface InjectModule {
14 | fun registerWith(intoScope: InjectScope) {
15 | intoScope.registerInjectables()
16 | }
17 |
18 | fun InjectScope.registerInjectables()
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/inject/src/main/java/com/heyanle/inject/api/InjectionException.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.inject.api
2 |
3 | class InjectionException(msg: String) : RuntimeException(msg)
--------------------------------------------------------------------------------
/inject/src/main/java/com/heyanle/inject/core/InjectMain.kt:
--------------------------------------------------------------------------------
1 | package com.heyanle.inject.core
2 |
3 | import com.heyanle.inject.api.InjectScope
4 | import com.heyanle.inject.api.fullType
5 | import com.heyanle.inject.api.get
6 |
7 | /**
8 | * 顶层默认 scope
9 | * Created by HeYanLe on 2023/7/29 20:00.
10 | * https://github.com/heyanLE
11 | */
12 | val Inject: InjectScope = DefaultInjectScope()
13 |
14 | inline fun injectLazy(): Lazy {
15 | return lazy { Inject.get(fullType()) }
16 | }
17 |
18 | inline fun injectValue(): Lazy {
19 | return lazyOf(Inject.get(fullType()))
20 | }
21 |
22 | inline fun injectLazy(key: Any): Lazy {
23 | return lazy { Inject.get(fullType(), key) }
24 | }
25 |
26 | inline fun injectValue(key: Any): Lazy {
27 | return lazyOf(Inject.get(fullType(), key))
28 | }
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | jdk:
2 | - openjdk14
--------------------------------------------------------------------------------
/lib_upnp/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/lib_upnp/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | id("java-library")
3 | alias(build.plugins.kotlin.jvm)
4 | }
5 | kotlin {
6 | jvmToolchain(17)
7 |
8 | }
9 | java {
10 | sourceCompatibility = JavaVersion.VERSION_17
11 | targetCompatibility = JavaVersion.VERSION_17
12 | }
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/http/HTML.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberHTTP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002-2003
6 | *
7 | * File: HTML.java
8 | *
9 | * Revision;
10 | *
11 | * 01/05/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.http;
17 |
18 | public class HTML
19 | {
20 | public static final String CONTENT_TYPE = "text/html; charset=\"utf-8\"";
21 | }
22 |
23 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/http/HTTPRequestListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberHTTP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: HTTPRequestListener.java
8 | *
9 | * Revision;
10 | *
11 | * 12/13/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.http;
17 |
18 | public interface HTTPRequestListener
19 | {
20 | public void httpRequestRecieved(HTTPRequest httpReq);
21 | }
22 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/http/HTTPServerThread.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberHTTP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002-2003
6 | *
7 | * File: HTTPServerThread.java
8 | *
9 | * Revision;
10 | *
11 | * 10/10/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.http;
17 |
18 | import java.net.Socket;
19 |
20 | public class HTTPServerThread extends Thread
21 | {
22 | private HTTPServer httpServer;
23 | private Socket sock;
24 |
25 | ////////////////////////////////////////////////
26 | // Constructor
27 | ////////////////////////////////////////////////
28 |
29 | public HTTPServerThread(HTTPServer httpServer, Socket sock)
30 | {
31 | super("Cyber.HTTPServerThread");
32 | this.httpServer = httpServer;
33 | this.sock = sock;
34 | }
35 |
36 | ////////////////////////////////////////////////
37 | // run
38 | ////////////////////////////////////////////////
39 |
40 | public void run()
41 | {
42 | HTTPSocket httpSock = new HTTPSocket(sock);
43 | if (httpSock.open() == false)
44 | return;
45 | HTTPRequest httpReq = new HTTPRequest();
46 | httpReq.setSocket(httpSock);
47 | while (httpReq.read() == true) {
48 | httpServer.performRequestListener(httpReq);
49 | if (httpReq.isKeepAlive() == false)
50 | break;
51 | }
52 | httpSock.close();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/http/Parameter.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberHTTP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002-2004
6 | *
7 | * File: Parameter.java
8 | *
9 | * Revision;
10 | *
11 | * 02/01/04
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.http;
17 |
18 | public class Parameter
19 | {
20 | private String name = new String();
21 | private String value = new String();
22 |
23 | public Parameter()
24 | {
25 | }
26 |
27 | public Parameter(String name, String value)
28 | {
29 | setName(name);
30 | setValue(value);
31 | }
32 |
33 | ////////////////////////////////////////////////
34 | // name
35 | ////////////////////////////////////////////////
36 |
37 | public void setName(String name)
38 | {
39 | this.name = name;
40 | }
41 |
42 | public String getName()
43 | {
44 | return name;
45 | }
46 |
47 | ////////////////////////////////////////////////
48 | // value
49 | ////////////////////////////////////////////////
50 |
51 | public void setValue(String value)
52 | {
53 | this.value = value;
54 | }
55 |
56 | public String getValue()
57 | {
58 | return value;
59 | }
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/http/ParameterList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberHTTP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002-2004
6 | *
7 | * File: ParameterList.java
8 | *
9 | * Revision;
10 | *
11 | * 02/01/04
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.http;
17 |
18 | import java.util.Vector;
19 |
20 | public class ParameterList extends Vector
21 | {
22 | public ParameterList()
23 | {
24 | }
25 |
26 | public Parameter at(int n)
27 | {
28 | return (Parameter)get(n);
29 | }
30 |
31 | public Parameter getParameter(int n)
32 | {
33 | return (Parameter)get(n);
34 | }
35 |
36 | public Parameter getParameter(String name)
37 | {
38 | if (name == null)
39 | return null;
40 |
41 | int nLists = size();
42 | for (int n=0; n
6 | * Copyright (c) 2005
7 | *
8 | */
9 | public interface RootDescription {
10 |
11 | public final String ROOT_ELEMENT = "root";
12 | public final String ROOT_ELEMENT_NAMESPACE = "urn:schemas-upnp-org:device-1-0";
13 |
14 |
15 | public final String SPECVERSION_ELEMENT = "specVersion";
16 | public final String MAJOR_ELEMENT = "major";
17 | public final String MINOR_ELEMENT = "minor";
18 | public final String SERVICE_LIST_ELEMENT = "serviceList";
19 | }
20 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/ServiceList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: ServiceList.java
8 | *
9 | * Revision;
10 | *
11 | * 12/04/02
12 | * - first revision.
13 | * 06/18/03
14 | * - Added caching a ArrayIndexOfBound exception.
15 | *
16 | ******************************************************************/
17 |
18 | package org.cybergarage.upnp;
19 |
20 | import java.util.Vector;
21 |
22 | public class ServiceList extends Vector
23 | {
24 | ////////////////////////////////////////////////
25 | // Constants
26 | ////////////////////////////////////////////////
27 |
28 | public final static String ELEM_NAME = "serviceList";
29 |
30 | ////////////////////////////////////////////////
31 | // Constructor
32 | ////////////////////////////////////////////////
33 |
34 | public ServiceList()
35 | {
36 | }
37 |
38 | ////////////////////////////////////////////////
39 | // Methods
40 | ////////////////////////////////////////////////
41 |
42 | public Service getService(int n)
43 | {
44 | Object obj = null;
45 | try {
46 | obj = get(n);
47 | }
48 | catch (Exception e) {};
49 | return (Service)obj;
50 | }
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/ServiceStateTable.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: ServiceStateTable.java
8 | *
9 | * Revision:
10 | *
11 | * 12/06/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp;
17 |
18 | import java.util.Vector;
19 |
20 | public class ServiceStateTable extends Vector
21 | {
22 | ////////////////////////////////////////////////
23 | // Constants
24 | ////////////////////////////////////////////////
25 |
26 | public final static String ELEM_NAME = "serviceStateTable";
27 |
28 | ////////////////////////////////////////////////
29 | // Constructor
30 | ////////////////////////////////////////////////
31 |
32 | public ServiceStateTable()
33 | {
34 | }
35 |
36 | ////////////////////////////////////////////////
37 | // Methods
38 | ////////////////////////////////////////////////
39 |
40 | public StateVariable getStateVariable(int n)
41 | {
42 | return (StateVariable)get(n);
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/control/ActionListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: ActionListener.java
8 | *
9 | * Revision;
10 | *
11 | * 01/16/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.control;
17 |
18 | import org.cybergarage.upnp.*;
19 |
20 | public interface ActionListener
21 | {
22 | public boolean actionControlReceived(Action action);
23 | }
24 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/control/Control.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: Control.java
8 | *
9 | * Revision;
10 | *
11 | * 01/20/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.control;
17 |
18 | public class Control
19 | {
20 | public final static String NS = "u";
21 | public final static String QUERY_SOAPACTION = "urn:schemas-upnp-org:control-1-0#QueryStateVariable";
22 | public final static String XMLNS = "urn:schemas-upnp-org:control-1-0";
23 | public final static String QUERY_STATE_VARIABLE = "QueryStateVariable";
24 | public final static String QUERY_STATE_VARIABLE_RESPONSE = "QueryStateVariableResponse";
25 | public final static String VAR_NAME = "varName";
26 | public final static String RETURN = "return";
27 | }
28 |
29 |
30 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/control/QueryListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002-2003
6 | *
7 | * File: QueryListener.java
8 | *
9 | * Revision;
10 | *
11 | * 01/30/03
12 | * - first revision.
13 | * 01/04/04
14 | * - Changed the interface.
15 | *
16 | ******************************************************************/
17 |
18 | package org.cybergarage.upnp.control;
19 |
20 | import org.cybergarage.upnp.*;
21 |
22 | public interface QueryListener
23 | {
24 | public boolean queryControlReceived(StateVariable stateVar);
25 | }
26 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/Description.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: MAN.java
8 | *
9 | * Revision;
10 | *
11 | * 12/30/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | public class Description
19 | {
20 | public final static String LOADING_EXCEPTION = "Couldn't load a specified description file ";
21 | public final static String NOROOT_EXCEPTION = "Couldn't find a root node";
22 | public final static String NOROOTDEVICE_EXCEPTION = "Couldn't find a root device node";
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/DeviceChangeListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberLink for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002-2004
6 | *
7 | * File: DeviceChangeListener.java
8 | *
9 | * Revision;
10 | *
11 | * 09/12/04
12 | * - Oliver Newell
13 | * - Added this class to allow ControlPoint applications to
14 | * be notified when the ControlPoint base class adds/removes
15 | * a UPnP device
16 | *
17 | ******************************************************************/
18 |
19 | package org.cybergarage.upnp.device;
20 |
21 | import org.cybergarage.upnp.Device;
22 |
23 | public interface DeviceChangeListener
24 | {
25 | public void deviceAdded( Device dev );
26 | public void deviceRemoved( Device dev );
27 | }
28 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/InvalidDescriptionException.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: InvalidDescriptionException.java
8 | *
9 | * Revision;
10 | *
11 | * 12/26/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | import java.io.*;
19 |
20 | public class InvalidDescriptionException extends Exception
21 | {
22 | public InvalidDescriptionException()
23 | {
24 | super();
25 | }
26 |
27 | public InvalidDescriptionException(String s)
28 | {
29 | super(s);
30 | }
31 |
32 | public InvalidDescriptionException(String s, File file)
33 | {
34 | super(s + " (" + file.toString() + ")");
35 | }
36 |
37 | public InvalidDescriptionException(Exception e)
38 | {
39 | super(e.getMessage());
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/MAN.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: MAN.java
8 | *
9 | * Revision;
10 | *
11 | * 12/30/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | public class MAN
19 | {
20 | public final static String DISCOVER = "ssdp:discover";
21 |
22 | public final static boolean isDiscover(String value)
23 | {
24 | if (value == null)
25 | return false;
26 | if (value.equals(MAN.DISCOVER) == true)
27 | return true;
28 | return value.equals("\"" + MAN.DISCOVER + "\"");
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/NT.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: NT.java
8 | *
9 | * Revision;
10 | *
11 | * 12/09/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | public class NT
19 | {
20 | public final static String ROOTDEVICE = "upnp:rootdevice";
21 | public final static String EVENT = "upnp:event";
22 |
23 | public final static boolean isRootDevice(String ntValue)
24 | {
25 | if (ntValue == null)
26 | return false;
27 | return ntValue.startsWith(ROOTDEVICE);
28 | }
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/NTS.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: NTS.java
8 | *
9 | * Revision;
10 | *
11 | * 12/09/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | public class NTS
19 | {
20 | public final static String ALIVE = "ssdp:alive";
21 | public final static String BYEBYE = "ssdp:byebye";
22 | public final static String PROPCHANGE = "upnp:propchange";
23 |
24 | public final static boolean isAlive(String ntsValue)
25 | {
26 | if (ntsValue == null)
27 | return false;
28 | return ntsValue.startsWith(NTS.ALIVE);
29 | }
30 |
31 | public final static boolean isByeBye(String ntsValue)
32 | {
33 | if (ntsValue == null)
34 | return false;
35 | return ntsValue.startsWith(NTS.BYEBYE);
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/NotifyListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: DeviceNotifyListener.java
8 | *
9 | * Revision;
10 | *
11 | * 11/18/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | import org.cybergarage.upnp.ssdp.*;
19 |
20 | public interface NotifyListener
21 | {
22 | public void deviceNotifyReceived(SSDPPacket ssdpPacket);
23 | }
24 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/PresentationListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: DeviceNotifyListener.java
8 | *
9 | * Revision;
10 | *
11 | * 11/18/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | import org.cybergarage.http.HTTPRequest;
19 |
20 | public interface PresentationListener
21 | {
22 | public void httpRequestRecieved(HTTPRequest httpReq);
23 | }
24 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/SearchListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: SearchListener.java
8 | *
9 | * Revision;
10 | *
11 | * 11/18/02b
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | import org.cybergarage.upnp.ssdp.*;
19 |
20 | public interface SearchListener
21 | {
22 | public void deviceSearchReceived(SSDPPacket ssdpPacket);
23 | }
24 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/SearchResponseListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: SearchResponseListener.java
8 | *
9 | * Revision;
10 | *
11 | * 11/18/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | import org.cybergarage.upnp.ssdp.*;
19 |
20 | public interface SearchResponseListener
21 | {
22 | public void deviceSearchResponseReceived(SSDPPacket ssdpPacket);
23 | }
24 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/device/USN.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: USN.java
8 | *
9 | * Revision;
10 | *
11 | * 12/09/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.device;
17 |
18 | public class USN
19 | {
20 | public final static String ROOTDEVICE = "upnp:rootdevice";
21 |
22 | public final static boolean isRootDevice(String usnValue)
23 | {
24 | if (usnValue == null)
25 | return false;
26 | return usnValue.endsWith(ROOTDEVICE);
27 | }
28 |
29 | public final static String getUDN(String usnValue)
30 | {
31 | if (usnValue == null)
32 | return "";
33 | int idx = usnValue.indexOf("::");
34 | if (idx < 0)
35 | return usnValue.trim();
36 | String udnValue = new String(usnValue.getBytes(), 0, idx);
37 | return udnValue.trim();
38 | }
39 | }
40 |
41 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/event/EventListener.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002-2003
6 | *
7 | * File: EventListener.java
8 | *
9 | * Revision;
10 | *
11 | * 11/18/02
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.event;
17 |
18 | public interface EventListener
19 | {
20 | public void eventNotifyReceived(String uuid, long seq, String varName, String value);
21 | }
22 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/event/PropertyList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: PropertyList.java
8 | *
9 | * Revision;
10 | *
11 | * 09/08/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.event;
17 |
18 | import java.util.*;
19 |
20 | public class PropertyList extends Vector
21 | {
22 | ////////////////////////////////////////////////
23 | // Constants
24 | ////////////////////////////////////////////////
25 |
26 | public final static String ELEM_NAME = "PropertyList";
27 |
28 | ////////////////////////////////////////////////
29 | // Constructor
30 | ////////////////////////////////////////////////
31 |
32 | public PropertyList()
33 | {
34 | }
35 |
36 | ////////////////////////////////////////////////
37 | // Methods
38 | ////////////////////////////////////////////////
39 |
40 | public Property getProperty(int n)
41 | {
42 | return (Property)get(n);
43 | }
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/event/SubscriberList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: SubscriberList.java
8 | *
9 | * Revision;
10 | *
11 | * 01/31/03
12 | * - first revision.
13 | * 06/18/03
14 | * - Fixed to catch ArrayIndexOutOfBounds.
15 | *
16 | ******************************************************************/
17 |
18 | package org.cybergarage.upnp.event;
19 |
20 | import java.util.*;
21 |
22 | public class SubscriberList extends Vector
23 | {
24 | ////////////////////////////////////////////////
25 | // Constructor
26 | ////////////////////////////////////////////////
27 |
28 | public SubscriberList()
29 | {
30 | }
31 |
32 | ////////////////////////////////////////////////
33 | // Methods
34 | ////////////////////////////////////////////////
35 |
36 | public Subscriber getSubscriber(int n)
37 | {
38 | Object obj = null;
39 | try {
40 | obj = get(n);
41 | }
42 | catch (Exception e) {}
43 | return (Subscriber)obj;
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/ssdp/SSDPNotifyRequest.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: SSDPMSearchRequest.java
8 | *
9 | * Revision;
10 | *
11 | * 01/14/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.ssdp;
17 |
18 | import org.cybergarage.http.HTTP;
19 |
20 | public class SSDPNotifyRequest extends SSDPRequest
21 | {
22 | ////////////////////////////////////////////////
23 | // Constructor
24 | ////////////////////////////////////////////////
25 |
26 | public SSDPNotifyRequest()
27 | {
28 | setMethod(HTTP.NOTIFY);
29 | setURI("*");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/ssdp/SSDPSearchResponse.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * CyberUPnP for Java
4 | *
5 | * Copyright (C) Satoshi Konno 2002
6 | *
7 | * File: SSDPSearchResponse.java
8 | *
9 | * Revision;
10 | *
11 | * 01/14/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.ssdp;
17 |
18 | import org.cybergarage.http.*;
19 |
20 | import org.cybergarage.upnp.*;
21 |
22 | public class SSDPSearchResponse extends SSDPResponse
23 | {
24 | ////////////////////////////////////////////////
25 | // Constructor
26 | ////////////////////////////////////////////////
27 |
28 | public SSDPSearchResponse()
29 | {
30 | setStatusCode(HTTPStatus.OK);
31 | setCacheControl(Device.DEFAULT_LEASE_TIME);
32 | setHeader(HTTP.SERVER, UPnP.getServerName());
33 | setHeader(HTTP.EXT, "");
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/controller/server/BrowseResult.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaPlayer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2005
6 | *
7 | * File : BrowseAction.java
8 | *
9 | * 09/26/05
10 | * - first revision.
11 | *
12 | ******************************************************************/
13 |
14 | package org.cybergarage.upnp.std.av.controller.server;
15 |
16 | import org.cybergarage.xml.*;
17 |
18 | public class BrowseResult
19 | {
20 | ////////////////////////////////////////////////
21 | // Member
22 | ////////////////////////////////////////////////
23 |
24 | private Node resultNode;
25 |
26 | ////////////////////////////////////////////////
27 | // Constrictor
28 | ////////////////////////////////////////////////
29 |
30 | public BrowseResult(Node node)
31 | {
32 | setResultNode(node);
33 | }
34 |
35 | ////////////////////////////////////////////////
36 | // Request
37 | ////////////////////////////////////////////////
38 |
39 | public void setResultNode(Node node)
40 | {
41 | resultNode = node;
42 | }
43 |
44 | public Node getResultNode()
45 | {
46 | return resultNode;
47 | }
48 |
49 | ////////////////////////////////////////////////
50 | // ContentNode
51 | ////////////////////////////////////////////////
52 |
53 | public int getNContentNodes()
54 | {
55 | return resultNode.getNNodes();
56 | }
57 |
58 | public Node getContentNode(int n)
59 | {
60 | return resultNode.getNode(n);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/player/action/BrowseResult.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaPlayer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2005
6 | *
7 | * File : BrowseAction.java
8 | *
9 | * 09/26/05
10 | * - first revision.
11 | *
12 | ******************************************************************/
13 |
14 | package org.cybergarage.upnp.std.av.player.action;
15 |
16 | import org.cybergarage.xml.*;
17 |
18 | public class BrowseResult
19 | {
20 | ////////////////////////////////////////////////
21 | // Member
22 | ////////////////////////////////////////////////
23 |
24 | private Node resultNode;
25 |
26 | ////////////////////////////////////////////////
27 | // Constrictor
28 | ////////////////////////////////////////////////
29 |
30 | public BrowseResult(Node node)
31 | {
32 | setResultNode(node);
33 | }
34 |
35 | ////////////////////////////////////////////////
36 | // Request
37 | ////////////////////////////////////////////////
38 |
39 | public void setResultNode(Node node)
40 | {
41 | resultNode = node;
42 | }
43 |
44 | public Node getResultNode()
45 | {
46 | return resultNode;
47 | }
48 |
49 | ////////////////////////////////////////////////
50 | // ContentNode
51 | ////////////////////////////////////////////////
52 |
53 | public int getNContentNodes()
54 | {
55 | return resultNode.getNNodes();
56 | }
57 |
58 | public Node getContentNode(int n)
59 | {
60 | return resultNode.getNode(n);
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/player/action/BrowseResultNode.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaPlayer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2005
6 | *
7 | * File : BrowseAction.java
8 | *
9 | * 09/26/05
10 | * - first revision.
11 | *
12 | ******************************************************************/
13 |
14 | package org.cybergarage.upnp.std.av.player.action;
15 |
16 | import java.io.*;
17 |
18 | import org.cybergarage.upnp.std.av.server.object.item.*;
19 |
20 | public class BrowseResultNode extends ItemNode
21 | {
22 | ////////////////////////////////////////////////
23 | // Constroctor
24 | ////////////////////////////////////////////////
25 |
26 | public BrowseResultNode()
27 | {
28 | }
29 |
30 | ////////////////////////////////////////////////
31 | // Abstract methods
32 | ////////////////////////////////////////////////
33 |
34 | public long getContentLength()
35 | {
36 | return 0;
37 | }
38 |
39 | public InputStream getContentInputStream()
40 | {
41 | return null;
42 | }
43 |
44 | public String getMimeType()
45 | {
46 | return "*/*";
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/renderer/AVTransportInfoList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003-2004
6 | *
7 | * File: ConnectionInfoList.java
8 | *
9 | * Revision;
10 | *
11 | * 02/22/08
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.renderer;
17 |
18 | import java.util.*;
19 |
20 | public class AVTransportInfoList extends Vector
21 | {
22 | ////////////////////////////////////////////////
23 | // Constrictor
24 | ////////////////////////////////////////////////
25 |
26 | public AVTransportInfoList()
27 | {
28 | }
29 |
30 | ////////////////////////////////////////////////
31 | // getConnectionInfo
32 | ////////////////////////////////////////////////
33 |
34 | public AVTransportInfo getAVTransportInfo(int n)
35 | {
36 | return (AVTransportInfo)get(n);
37 | }
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/renderer/ConnectionInfoList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003-2004
6 | *
7 | * File: ConnectionInfoList.java
8 | *
9 | * Revision;
10 | *
11 | * 02/22/08
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.renderer;
17 |
18 | import java.util.*;
19 |
20 | public class ConnectionInfoList extends Vector
21 | {
22 | ////////////////////////////////////////////////
23 | // Constrictor
24 | ////////////////////////////////////////////////
25 |
26 | public ConnectionInfoList()
27 | {
28 | }
29 |
30 | ////////////////////////////////////////////////
31 | // getConnectionInfo
32 | ////////////////////////////////////////////////
33 |
34 | public ConnectionInfo getConnectionInfo(int n)
35 | {
36 | return (ConnectionInfo)get(n);
37 | }
38 |
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/server/ConnectionInfoList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003-2004
6 | *
7 | * File: ConnectionInfoList.java
8 | *
9 | * Revision;
10 | *
11 | * 06/19/04
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.server;
17 |
18 | import java.util.*;
19 |
20 | public class ConnectionInfoList extends Vector
21 | {
22 | ////////////////////////////////////////////////
23 | // Constrictor
24 | ////////////////////////////////////////////////
25 |
26 | public ConnectionInfoList()
27 | {
28 | }
29 |
30 | ////////////////////////////////////////////////
31 | // getConnectionInfo
32 | ////////////////////////////////////////////////
33 |
34 | public ConnectionInfo getConnectionInfo(int n)
35 | {
36 | return (ConnectionInfo)get(n);
37 | }
38 |
39 |
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/server/DC.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003
6 | *
7 | * File : UPnP
8 | *
9 | * Revision:
10 | *
11 | * 11/13/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.server;
17 |
18 | public class DC
19 | {
20 | ////////////////////////////////////////////////
21 | // Constants
22 | ////////////////////////////////////////////////
23 |
24 | public final static String TITLE = "dc:title";
25 | public final static String DATE = "dc:date";
26 | public final static String CREATOR = "dc:creator";
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/server/UPnP.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003
6 | *
7 | * File : UPnP
8 | *
9 | * Revision:
10 | *
11 | * 11/13/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.server;
17 |
18 | public class UPnP
19 | {
20 | ////////////////////////////////////////////////
21 | // Constants
22 | ////////////////////////////////////////////////
23 |
24 | public final static String CLASS = "upnp:class";
25 | public final static String WRITE_STATUS = "upnp:writeStatus";
26 | public final static String STORAGE_MEDIUM = "upnp:storageMedium";
27 | public final static String STORAGE_USED = "upnp:storageUsed";
28 | public final static String ALBUMART_URI = "upnp:albumArtURI";
29 |
30 | public final static String OBJECT_ITEM_IMAGEITEM_PHOTO = "object.item.imageItem.photo";
31 | public final static String OBJECT_ITEM_VIDEOITEM_MOVIE = "object.item.videoItem.movie";
32 |
33 | }
34 |
35 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/server/object/ContentNodeList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003
6 | *
7 | * File: ContentNodeList.java
8 | *
9 | * Revision;
10 | *
11 | * 10/30/03
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.server.object;
17 |
18 | import java.util.*;
19 |
20 | public class ContentNodeList extends Vector
21 | {
22 | public ContentNodeList()
23 | {
24 | }
25 |
26 | public ContentNode getContentNode(int n)
27 | {
28 | return (ContentNode)get(n);
29 | }
30 |
31 | public ContentNode getContentNode(String name)
32 | {
33 | if (name == null)
34 | return null;
35 |
36 | int nLists = size();
37 | for (int n=0; n
15 | * - Changed DIDLLiteNode is a subclass of Node instead of ContentNode
16 | * because the node has the parentID attributes.
17 | *
18 | ******************************************************************/
19 |
20 | package org.cybergarage.upnp.std.av.server.object;
21 |
22 | import org.cybergarage.xml.Node;
23 |
24 | public class DIDLLiteNode extends Node // Thanks for Brent Hills (10/28/04)
25 | {
26 | ////////////////////////////////////////////////
27 | // Constroctor
28 | ////////////////////////////////////////////////
29 |
30 | public DIDLLiteNode()
31 | {
32 | setName(DIDLLite.NAME);
33 | setAttribute(DIDLLite.XMLNS, DIDLLite.XMLNS_URL);
34 | setAttribute(DIDLLite.XMLNS_DC, DIDLLite.XMLNS_DC_URL);
35 | setAttribute(DIDLLite.XMLNS_UPNP, DIDLLite.XMLNS_UPNP_URL);
36 | }
37 |
38 | ////////////////////////////////////////////////
39 | // Child node
40 | ////////////////////////////////////////////////
41 |
42 | public void addContentNode(ContentNode node)
43 | {
44 | addNode(node);
45 | }
46 |
47 | public boolean removeContentNode(ContentNode node)
48 | {
49 | return removeNode(node);
50 | }
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/server/object/Format.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003-2004
6 | *
7 | * File: Format.java
8 | *
9 | * Revision;
10 | *
11 | * 01/12/04
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.server.object;
17 |
18 | import java.io.*;
19 |
20 | public interface Format
21 | {
22 | public abstract FormatObject createObject(File file);
23 | public abstract boolean equals(File file);
24 | public abstract String getMimeType();
25 | public abstract String getMediaClass();
26 | }
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/lib_upnp/src/main/java/org/cybergarage/upnp/std/av/server/object/FormatList.java:
--------------------------------------------------------------------------------
1 | /******************************************************************
2 | *
3 | * MediaServer for CyberLink
4 | *
5 | * Copyright (C) Satoshi Konno 2003-2004
6 | *
7 | * File: FormatList
8 | *
9 | * Revision;
10 | *
11 | * 01/12/04
12 | * - first revision.
13 | *
14 | ******************************************************************/
15 |
16 | package org.cybergarage.upnp.std.av.server.object;
17 |
18 | import java.io.*;
19 | import java.util.*;
20 |
21 | public class FormatList extends Vector
22 | {
23 | public FormatList()
24 | {
25 | }
26 |
27 | public Format getFormat(int n)
28 | {
29 | return (Format)get(n);
30 | }
31 |
32 | public Format getFormat(String type)
33 | {
34 | if (type == null)
35 | return null;
36 |
37 | int nLists = size();
38 | for (int n=0; n