├── .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 | 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