Full Code of lyswhut/lx-music-mobile for AI

master 05c322a3ff05 cached
706 files
2.4 MB
657.0k tokens
1736 symbols
1 requests
Download .txt
Showing preview only (2,609K chars total). Download the full file or copy to clipboard to get everything.
Repository: lyswhut/lx-music-mobile
Branch: master
Commit: 05c322a3ff05
Files: 706
Total size: 2.4 MB

Directory structure:
gitextract_rqtblcgl/

├── .bundle/
│   └── config
├── .editorconfig
├── .eslintrc.cjs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug.yml
│   │   └── feature.yml
│   ├── actions/
│   │   ├── setup/
│   │   │   └── action.yml
│   │   └── upload-artifact/
│   │       └── action.yml
│   └── workflows/
│       ├── beta-pack.yml
│       ├── build-test.yml
│       ├── publish-version-info.yml
│       └── release.yml
├── .gitignore
├── .ncurc.js
├── .nvmrc
├── .vscode/
│   ├── i18n-ally-custom-framework.yml
│   ├── javascript.code-snippets
│   └── settings.json
├── CHANGELOG.md
├── FAQ.md
├── Gemfile
├── LICENSE
├── README.md
├── android/
│   ├── app/
│   │   ├── build.gradle
│   │   ├── debug.keystore
│   │   ├── proguard-rules.pro
│   │   └── src/
│   │       ├── debug/
│   │       │   └── AndroidManifest.xml
│   │       ├── main/
│   │       │   ├── AndroidManifest.xml
│   │       │   ├── assets/
│   │       │   │   └── script/
│   │       │   │       └── user-api-preload.js
│   │       │   ├── java/
│   │       │   │   └── cn/
│   │       │   │       └── toside/
│   │       │   │           └── music/
│   │       │   │               └── mobile/
│   │       │   │                   ├── MainActivity.java
│   │       │   │                   ├── MainApplication.java
│   │       │   │                   ├── cache/
│   │       │   │                   │   ├── CacheClearAsyncTask.java
│   │       │   │                   │   ├── CacheModule.java
│   │       │   │                   │   ├── CachePackage.java
│   │       │   │                   │   └── Utils.java
│   │       │   │                   ├── crypto/
│   │       │   │                   │   ├── AES.java
│   │       │   │                   │   ├── CryptoModule.java
│   │       │   │                   │   ├── CryptoPackage.java
│   │       │   │                   │   └── RSA.java
│   │       │   │                   ├── lyric/
│   │       │   │                   │   ├── Lyric.java
│   │       │   │                   │   ├── LyricEvent.java
│   │       │   │                   │   ├── LyricModule.java
│   │       │   │                   │   ├── LyricPackage.java
│   │       │   │                   │   ├── LyricPlayer.java
│   │       │   │                   │   ├── LyricSwitchView.java
│   │       │   │                   │   ├── LyricTextView.java
│   │       │   │                   │   ├── LyricView.java
│   │       │   │                   │   └── Utils.java
│   │       │   │                   ├── userApi/
│   │       │   │                   │   ├── Console.java
│   │       │   │                   │   ├── HandlerWhat.java
│   │       │   │                   │   ├── JavaScriptThread.java
│   │       │   │                   │   ├── JsHandler.java
│   │       │   │                   │   ├── QuickJS.java
│   │       │   │                   │   ├── UserApiModule.java
│   │       │   │                   │   ├── UserApiPackage.java
│   │       │   │                   │   └── UtilsEvent.java
│   │       │   │                   └── utils/
│   │       │   │                       ├── AsyncTask.java
│   │       │   │                       ├── BatteryOptimizationUtil.java
│   │       │   │                       ├── NotificationPermissionUtil.java
│   │       │   │                       ├── Utils.java
│   │       │   │                       ├── UtilsEvent.java
│   │       │   │                       ├── UtilsModule.java
│   │       │   │                       └── UtilsPackage.java
│   │       │   └── res/
│   │       │       ├── drawable/
│   │       │       │   ├── ic_launcher_foreground.xml
│   │       │       │   ├── rn_edit_text_material.xml
│   │       │       │   └── rounded_corner.xml
│   │       │       ├── mipmap-anydpi-v26/
│   │       │       │   ├── ic_launcher.xml
│   │       │       │   └── ic_launcher_round.xml
│   │       │       ├── values/
│   │       │       │   ├── ic_launcher_background.xml
│   │       │       │   ├── strings.xml
│   │       │       │   └── styles.xml
│   │       │       └── xml/
│   │       │           ├── file_paths.xml
│   │       │           └── network_security_config.xml
│   │       └── release/
│   │           └── java/
│   │               └── cn/
│   │                   └── toside/
│   │                       └── music/
│   │                           └── ReactNativeFlipper.java
│   ├── build.gradle
│   ├── gradle/
│   │   └── wrapper/
│   │       ├── gradle-wrapper.jar
│   │       └── gradle-wrapper.properties
│   ├── gradle.properties
│   ├── gradlew
│   ├── gradlew.bat
│   └── settings.gradle
├── app.json
├── babel.config.js
├── dependencies-patch.js
├── index.js
├── ios/
│   ├── .xcode.env
│   ├── LxMusicMobile/
│   │   ├── AppDelegate.h
│   │   ├── AppDelegate.mm
│   │   ├── Images.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   └── Contents.json
│   │   ├── Info.plist
│   │   ├── LaunchScreen.storyboard
│   │   └── main.m
│   ├── LxMusicMobile.xcodeproj/
│   │   ├── project.pbxproj
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── LxMusicMobile.xcscheme
│   ├── LxMusicMobileTests/
│   │   ├── Info.plist
│   │   └── LxMusicMobileTests.m
│   └── Podfile
├── metro.config.js
├── package.json
├── publish/
│   ├── changeLog.md
│   ├── index.js
│   ├── utils/
│   │   ├── index.js
│   │   ├── parseChangelog.js
│   │   └── updateChangeLog.js
│   └── version.json
├── shim.js
├── src/
│   ├── app.ts
│   ├── components/
│   │   ├── DesktopLyricEnable.tsx
│   │   ├── MetadataEditModal/
│   │   │   ├── InputItem.tsx
│   │   │   ├── MetadataForm.tsx
│   │   │   ├── ParseName.tsx
│   │   │   ├── PicItem.tsx
│   │   │   ├── TextAreaItem.tsx
│   │   │   └── index.tsx
│   │   ├── MusicAddModal/
│   │   │   ├── CreateUserList.tsx
│   │   │   ├── List.tsx
│   │   │   ├── ListItem.tsx
│   │   │   ├── MusicAddModal.tsx
│   │   │   ├── Title.tsx
│   │   │   └── index.tsx
│   │   ├── MusicMultiAddModal/
│   │   │   ├── List.tsx
│   │   │   ├── ListItem.tsx
│   │   │   ├── MusicMultiAddModal.tsx
│   │   │   ├── Title.tsx
│   │   │   └── index.tsx
│   │   ├── OnlineList/
│   │   │   ├── List.tsx
│   │   │   ├── ListItem.tsx
│   │   │   ├── ListMenu.tsx
│   │   │   ├── MultipleModeBar.tsx
│   │   │   ├── index.tsx
│   │   │   └── listAction.ts
│   │   ├── PageContent.tsx
│   │   ├── SearchTipList/
│   │   │   ├── List.tsx
│   │   │   └── index.tsx
│   │   ├── SizeView.tsx
│   │   ├── SourceSelector.tsx
│   │   ├── TimeoutExitEditModal.tsx
│   │   ├── common/
│   │   │   ├── Badge.tsx
│   │   │   ├── Button.tsx
│   │   │   ├── ButtonPrimary.tsx
│   │   │   ├── CheckBox/
│   │   │   │   ├── Checkbox.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── ChoosePath/
│   │   │   │   ├── List.tsx
│   │   │   │   ├── components/
│   │   │   │   │   ├── Footer.tsx
│   │   │   │   │   ├── Header.tsx
│   │   │   │   │   ├── ListItem.tsx
│   │   │   │   │   ├── Main.tsx
│   │   │   │   │   ├── NewFolderModal.tsx
│   │   │   │   │   └── OpenStorageModal.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── ConfirmAlert.tsx
│   │   │   ├── Dialog.tsx
│   │   │   ├── DorpDownMenu.tsx
│   │   │   ├── DorpDownPanel/
│   │   │   │   ├── Panel.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── DrawerLayoutFixed.tsx
│   │   │   ├── FileSelect.tsx
│   │   │   ├── Icon.tsx
│   │   │   ├── Image.tsx
│   │   │   ├── ImageBackground.tsx
│   │   │   ├── Input.tsx
│   │   │   ├── Loading.tsx
│   │   │   ├── LoadingMask.tsx
│   │   │   ├── Menu.tsx
│   │   │   ├── Modal.tsx
│   │   │   ├── Popup.tsx
│   │   │   ├── ScaledImage.tsx
│   │   │   ├── Slider.tsx
│   │   │   ├── StatusBar.tsx
│   │   │   └── Text.tsx
│   │   └── player/
│   │       ├── PlayerBar/
│   │       │   ├── components/
│   │       │   │   ├── ControlBtn.tsx
│   │       │   │   ├── Pic.tsx
│   │       │   │   ├── PlayInfo.tsx
│   │       │   │   ├── Status.tsx
│   │       │   │   └── Title.tsx
│   │       │   └── index.tsx
│   │       ├── Progress.tsx
│   │       └── ProgressBar.tsx
│   ├── config/
│   │   ├── constant.ts
│   │   ├── defaultSetting.ts
│   │   ├── globalData.ts
│   │   ├── index.js
│   │   ├── migrate.ts
│   │   ├── migrateSetting.ts
│   │   └── setting.ts
│   ├── core/
│   │   ├── apiSource.ts
│   │   ├── common.ts
│   │   ├── desktopLyric.ts
│   │   ├── dislikeList.ts
│   │   ├── hotSearch.ts
│   │   ├── init/
│   │   │   ├── common.ts
│   │   │   ├── dataInit.ts
│   │   │   ├── deeplink/
│   │   │   │   ├── fileAction.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── musicAction.js
│   │   │   │   ├── playSonglist.ts
│   │   │   │   ├── playerAction.ts
│   │   │   │   ├── songlistAction.js
│   │   │   │   └── utils.js
│   │   │   ├── i18n.ts
│   │   │   ├── index.ts
│   │   │   ├── player/
│   │   │   │   ├── index.ts
│   │   │   │   ├── lyric.ts
│   │   │   │   ├── playInfo.ts
│   │   │   │   ├── playProgress.ts
│   │   │   │   ├── playStatus.ts
│   │   │   │   ├── player.ts
│   │   │   │   ├── playerEvent.ts
│   │   │   │   ├── preloadNextMusic.ts
│   │   │   │   └── watchList.ts
│   │   │   ├── sync.ts
│   │   │   ├── theme.ts
│   │   │   └── userApi/
│   │   │       ├── index.ts
│   │   │       └── request.js
│   │   ├── leaderboard.ts
│   │   ├── list.ts
│   │   ├── lyric.ts
│   │   ├── music/
│   │   │   ├── download.ts
│   │   │   ├── index.ts
│   │   │   ├── local.ts
│   │   │   ├── online.ts
│   │   │   └── utils.ts
│   │   ├── player/
│   │   │   ├── playInfo.ts
│   │   │   ├── playStatus.ts
│   │   │   ├── playedList.ts
│   │   │   ├── player.ts
│   │   │   ├── progress.ts
│   │   │   ├── tempPlayList.ts
│   │   │   ├── timeoutExit.ts
│   │   │   └── utils.ts
│   │   ├── search/
│   │   │   ├── music.ts
│   │   │   ├── search.ts
│   │   │   └── songlist.ts
│   │   ├── songlist.ts
│   │   ├── sync.ts
│   │   ├── syncSourceList.ts
│   │   ├── theme.ts
│   │   ├── userApi.ts
│   │   └── version.ts
│   ├── event/
│   │   ├── Event.ts
│   │   ├── appEvent.ts
│   │   ├── dislikeEvent.ts
│   │   ├── listEvent.ts
│   │   └── stateEvent.ts
│   ├── lang/
│   │   ├── Readme.md
│   │   ├── en-us.json
│   │   ├── i18n.ts
│   │   ├── index.ts
│   │   ├── zh-cn.json
│   │   └── zh-tw.json
│   ├── navigation/
│   │   ├── components/
│   │   │   ├── ModalContent.tsx
│   │   │   ├── PactModal.tsx
│   │   │   ├── SyncModeModal.tsx
│   │   │   ├── Toast.js
│   │   │   └── VersionModal.tsx
│   │   ├── event.ts
│   │   ├── hooks.ts
│   │   ├── index.ts
│   │   ├── navigation.ts
│   │   ├── regLaunchedEvent.ts
│   │   ├── registerScreens.tsx
│   │   ├── screenNames.ts
│   │   └── utils.ts
│   ├── plugins/
│   │   ├── lyric.ts
│   │   ├── player/
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   ├── playList.ts
│   │   │   ├── service.ts
│   │   │   └── utils.ts
│   │   ├── storage.ts
│   │   └── sync/
│   │       ├── client/
│   │       │   ├── auth.ts
│   │       │   ├── client.ts
│   │       │   ├── index.ts
│   │       │   ├── modules/
│   │       │   │   ├── dislike/
│   │       │   │   │   ├── handler.ts
│   │       │   │   │   ├── index.ts
│   │       │   │   │   └── localEvent.ts
│   │       │   │   ├── index.ts
│   │       │   │   └── list/
│   │       │   │       ├── handler.ts
│   │       │   │       ├── index.ts
│   │       │   │       └── localEvent.ts
│   │       │   ├── sync/
│   │       │   │   ├── handler.ts
│   │       │   │   └── index.ts
│   │       │   └── utils.ts
│   │       ├── constants.ts
│   │       ├── data.ts
│   │       ├── dislikeEvent.ts
│   │       ├── index.ts
│   │       ├── listEvent.ts
│   │       ├── log.ts
│   │       └── utils.ts
│   ├── resources/
│   │   └── fonts/
│   │       └── selection.json
│   ├── screens/
│   │   ├── Comment/
│   │   │   ├── CommentHot.tsx
│   │   │   ├── CommentNew.tsx
│   │   │   ├── components/
│   │   │   │   ├── CommentFloor.tsx
│   │   │   │   ├── CommentImage.tsx
│   │   │   │   ├── CommentText.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   └── List.tsx
│   │   │   ├── index.tsx
│   │   │   └── utils.ts
│   │   ├── Home/
│   │   │   ├── Horizontal/
│   │   │   │   ├── Aside.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── Main.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── Vertical/
│   │   │   │   ├── Content.tsx
│   │   │   │   ├── DrawerNav.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── Main.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── Views/
│   │   │   │   ├── Download/
│   │   │   │   │   └── index.js
│   │   │   │   ├── Leaderboard/
│   │   │   │   │   ├── BoardsList/
│   │   │   │   │   │   ├── List.tsx
│   │   │   │   │   │   ├── ListItem.tsx
│   │   │   │   │   │   ├── ListMenu.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── Horizontal/
│   │   │   │   │   │   ├── LeftBar.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── MusicList.tsx
│   │   │   │   │   ├── Vertical/
│   │   │   │   │   │   ├── HeaderBar/
│   │   │   │   │   │   │   ├── ActiveListName.tsx
│   │   │   │   │   │   │   ├── SourceSelector.tsx
│   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── listAction.ts
│   │   │   │   ├── Mylist/
│   │   │   │   │   ├── MusicList/
│   │   │   │   │   │   ├── ActiveList.tsx
│   │   │   │   │   │   ├── List.tsx
│   │   │   │   │   │   ├── ListItem.tsx
│   │   │   │   │   │   ├── ListMenu.tsx
│   │   │   │   │   │   ├── ListMusicSearch.tsx
│   │   │   │   │   │   ├── ListSearchBar.tsx
│   │   │   │   │   │   ├── MultipleModeBar.tsx
│   │   │   │   │   │   ├── MusicPositionModal.tsx
│   │   │   │   │   │   ├── MusicToggleModal.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── listAction.ts
│   │   │   │   │   ├── MyList/
│   │   │   │   │   │   ├── DuplicateMusic.tsx
│   │   │   │   │   │   ├── List.tsx
│   │   │   │   │   │   ├── ListImportExport.tsx
│   │   │   │   │   │   ├── ListMenu.tsx
│   │   │   │   │   │   ├── ListMusicSort.tsx
│   │   │   │   │   │   ├── ListNameEdit.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   ├── listAction.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── Search/
│   │   │   │   │   ├── BlankView/
│   │   │   │   │   │   ├── HistorySearch.tsx
│   │   │   │   │   │   ├── HotSearch.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── HeaderBar/
│   │   │   │   │   │   ├── SearchInput.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── List.tsx
│   │   │   │   │   ├── MusicList.tsx
│   │   │   │   │   ├── SearchTypeSelector.tsx
│   │   │   │   │   ├── SonglistList.tsx
│   │   │   │   │   ├── TipList.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── Setting/
│   │   │   │   │   ├── Horizontal/
│   │   │   │   │   │   ├── NavList.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── Main.tsx
│   │   │   │   │   ├── Vertical/
│   │   │   │   │   │   ├── Header.tsx
│   │   │   │   │   │   ├── Main.tsx
│   │   │   │   │   │   ├── NavList.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── Button.tsx
│   │   │   │   │   │   ├── CheckBoxItem.tsx
│   │   │   │   │   │   ├── InputItem.tsx
│   │   │   │   │   │   ├── Section.tsx
│   │   │   │   │   │   ├── Slider.tsx
│   │   │   │   │   │   └── SubTitle.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── settings/
│   │   │   │   │       ├── About.tsx
│   │   │   │   │       ├── Backup/
│   │   │   │   │       │   ├── All.js
│   │   │   │   │       │   ├── ListImportExport.tsx
│   │   │   │   │       │   ├── Part.tsx
│   │   │   │   │       │   ├── actions.ts
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Basic/
│   │   │   │   │       │   ├── DrawerLayoutPosition.tsx
│   │   │   │   │       │   ├── FontSize.tsx
│   │   │   │   │       │   ├── IsAllowProgressBarSeek.tsx
│   │   │   │   │       │   ├── IsAlwaysKeepStatusbarHeight.tsx
│   │   │   │   │       │   ├── IsAutoHidePlayBar.tsx
│   │   │   │   │       │   ├── IsHomePageScroll.tsx
│   │   │   │   │       │   ├── IsShowBackBtn.tsx
│   │   │   │   │       │   ├── IsShowExitBtn.tsx
│   │   │   │   │       │   ├── IsStartupAutoPlay.tsx
│   │   │   │   │       │   ├── IsStartupPushPlayDetailScreen.tsx
│   │   │   │   │       │   ├── IsUseSystemFileSelector.tsx
│   │   │   │   │       │   ├── Language.tsx
│   │   │   │   │       │   ├── ShareType.tsx
│   │   │   │   │       │   ├── Source.tsx
│   │   │   │   │       │   ├── SourceName.tsx
│   │   │   │   │       │   ├── UserApiEditModal/
│   │   │   │   │       │   │   ├── ImportBtn.tsx
│   │   │   │   │       │   │   ├── List.tsx
│   │   │   │   │       │   │   ├── ScriptImportExport.tsx
│   │   │   │   │       │   │   ├── ScriptImportOnline.tsx
│   │   │   │   │       │   │   ├── action.ts
│   │   │   │   │       │   │   └── index.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── List/
│   │   │   │   │       │   ├── AddMusicLocationType.tsx
│   │   │   │   │       │   ├── IsClickPlayList.tsx
│   │   │   │   │       │   ├── IsShowAlbumName.tsx
│   │   │   │   │       │   ├── IsShowInterval.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── LyricDesktop/
│   │   │   │   │       │   ├── IsLockLyric.tsx
│   │   │   │   │       │   ├── IsShowLyric.tsx
│   │   │   │   │       │   ├── IsShowToggleAnima.tsx
│   │   │   │   │       │   ├── IsSingleLine.tsx
│   │   │   │   │       │   ├── MaxLineNum.tsx
│   │   │   │   │       │   ├── TextOpacity.tsx
│   │   │   │   │       │   ├── TextPositionX.tsx
│   │   │   │   │       │   ├── TextPositionY.tsx
│   │   │   │   │       │   ├── TextSize.tsx
│   │   │   │   │       │   ├── Theme.tsx
│   │   │   │   │       │   ├── ViewWidth.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Other/
│   │   │   │   │       │   ├── DislikeEditModal.tsx
│   │   │   │   │       │   ├── DislikeList.tsx
│   │   │   │   │       │   ├── Log.tsx
│   │   │   │   │       │   ├── MetaCache.tsx
│   │   │   │   │       │   ├── ResourceCache.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Player/
│   │   │   │   │       │   ├── IsAutoCleanPlayedList.tsx
│   │   │   │   │       │   ├── IsEnableAudioOffload.tsx
│   │   │   │   │       │   ├── IsHandleAudioFocus.tsx
│   │   │   │   │       │   ├── IsS2T.tsx
│   │   │   │   │       │   ├── IsSavePlayTime.tsx
│   │   │   │   │       │   ├── IsShowBluetoothLyric.tsx
│   │   │   │   │       │   ├── IsShowLyricRoma.tsx
│   │   │   │   │       │   ├── IsShowLyricTranslation.tsx
│   │   │   │   │       │   ├── IsShowNotificationImage.tsx
│   │   │   │   │       │   ├── MaxCache.tsx
│   │   │   │   │       │   ├── PlayHighQuality.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Search/
│   │   │   │   │       │   ├── IsShowHistorySearch.tsx
│   │   │   │   │       │   ├── IsShowHotSearch.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Sync/
│   │   │   │   │       │   ├── History.tsx
│   │   │   │   │       │   ├── IsEnable.tsx
│   │   │   │   │       │   ├── index.tsx
│   │   │   │   │       │   └── isEnable.tsx.bak
│   │   │   │   │       ├── Theme/
│   │   │   │   │       │   ├── IsAutoTheme.tsx
│   │   │   │   │       │   ├── IsDynamicBg.tsx
│   │   │   │   │       │   ├── IsFontShadow.tsx
│   │   │   │   │       │   ├── IsHideBgDark.tsx
│   │   │   │   │       │   ├── Theme.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       └── Version.tsx
│   │   │   │   └── SongList/
│   │   │   │       ├── Content.tsx
│   │   │   │       ├── HeaderBar/
│   │   │   │       │   ├── OpenList/
│   │   │   │       │   │   ├── Modal.tsx
│   │   │   │       │   │   └── index.tsx
│   │   │   │       │   ├── SortTab.tsx
│   │   │   │       │   ├── SourceSelector.tsx
│   │   │   │       │   ├── Tag/
│   │   │   │       │   │   ├── CurrentTagBtn.tsx
│   │   │   │       │   │   └── index.tsx
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── List.tsx
│   │   │   │       ├── TagList/
│   │   │   │       │   ├── List.tsx
│   │   │   │       │   ├── TagGroup.tsx
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── components/
│   │   │   │       │   └── Songlist/
│   │   │   │       │       ├── List.tsx
│   │   │   │       │       ├── ListItem.tsx
│   │   │   │       │       └── index.tsx
│   │   │   │       └── index.tsx
│   │   │   └── index.tsx
│   │   ├── PlayDetail/
│   │   │   ├── Horizontal/
│   │   │   │   ├── Lyric.tsx
│   │   │   │   ├── MoreBtn/
│   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   ├── MusicAddBtn.tsx
│   │   │   │   │   ├── PlayModeBtn.tsx
│   │   │   │   │   ├── TimeoutExitBtn.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── Pic.tsx
│   │   │   │   ├── Player/
│   │   │   │   │   ├── ControlBtn.tsx
│   │   │   │   │   ├── PlayInfo.tsx
│   │   │   │   │   ├── Status.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── components/
│   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   ├── CommentBtn.tsx
│   │   │   │   │   ├── DesktopLyricBtn.tsx
│   │   │   │   │   └── Header.tsx
│   │   │   │   ├── constant.ts
│   │   │   │   └── index.tsx
│   │   │   ├── Vertical/
│   │   │   │   ├── Lyric.tsx
│   │   │   │   ├── Pic.tsx
│   │   │   │   ├── Player/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── ControlBtn.tsx
│   │   │   │   │   │   ├── MoreBtn/
│   │   │   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   │   │   ├── CommentBtn.tsx
│   │   │   │   │   │   │   ├── DesktopLyricBtn.tsx
│   │   │   │   │   │   │   ├── MusicAddBtn.tsx
│   │   │   │   │   │   │   ├── PlayModeBtn.tsx
│   │   │   │   │   │   │   ├── TimeoutExitBtn.tsx
│   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   ├── PlayInfo.tsx
│   │   │   │   │   │   └── Status.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── components/
│   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   ├── Header.tsx
│   │   │   │   │   └── TimeoutExitBtn.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── components/
│   │   │   │   ├── PlayLine.tsx
│   │   │   │   └── SettingPopup/
│   │   │   │       ├── index.tsx
│   │   │   │       └── settings/
│   │   │   │           ├── SettingLrcAlign.tsx
│   │   │   │           ├── SettingLrcFontSize.tsx
│   │   │   │           ├── SettingLyricProgress.tsx
│   │   │   │           ├── SettingPlaybackRate.tsx
│   │   │   │           ├── SettingVolume.tsx
│   │   │   │           └── style.ts
│   │   │   └── index.tsx
│   │   ├── SonglistDetail/
│   │   │   ├── ActionBar.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── MusicList.tsx
│   │   │   ├── index.tsx
│   │   │   ├── listAction.ts
│   │   │   └── state.ts
│   │   └── index.ts
│   ├── store/
│   │   ├── Provider/
│   │   │   ├── Provider.tsx
│   │   │   ├── ThemeProvider.tsx
│   │   │   └── index.ts
│   │   ├── common/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── dislikeList/
│   │   │   ├── action.ts
│   │   │   ├── event.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   └── state.ts
│   │   ├── hotSearch/
│   │   │   ├── action.ts
│   │   │   └── state.ts
│   │   ├── index.ts
│   │   ├── leaderboard/
│   │   │   ├── action.ts
│   │   │   └── state.ts
│   │   ├── list/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── player/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── search/
│   │   │   ├── action.ts
│   │   │   ├── music/
│   │   │   │   ├── action.ts
│   │   │   │   └── state.ts
│   │   │   ├── songlist/
│   │   │   │   ├── action.ts
│   │   │   │   └── state.ts
│   │   │   └── state.ts
│   │   ├── setting/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── songlist/
│   │   │   ├── action.ts
│   │   │   └── state.ts
│   │   ├── sync/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── theme/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── userApi/
│   │   │   ├── action.ts
│   │   │   ├── event.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   └── state.ts
│   │   └── version/
│   │       ├── action.ts
│   │       ├── hook.ts
│   │       └── state.ts
│   ├── theme/
│   │   ├── Colors.js
│   │   ├── Typography.js
│   │   ├── index.js
│   │   └── themes/
│   │       ├── colorUtils.js
│   │       ├── createThemes.js
│   │       ├── index.ts
│   │       ├── themes.ts
│   │       └── utils.js
│   ├── types/
│   │   ├── app.d.ts
│   │   ├── app_setting.d.ts
│   │   ├── common.d.ts
│   │   ├── config_files.d.ts
│   │   ├── dislike_list.d.ts
│   │   ├── dislike_list_sync.d.ts
│   │   ├── download_list.d.ts
│   │   ├── list.d.ts
│   │   ├── list_sync.d.ts
│   │   ├── music.d.ts
│   │   ├── player.d.ts
│   │   ├── sync.d.ts
│   │   ├── sync_common.d.ts
│   │   ├── theme.d.ts
│   │   ├── user_api.d.ts
│   │   └── utils.d.ts
│   └── utils/
│       ├── bootLog.ts
│       ├── common.ts
│       ├── data.ts
│       ├── dislikeManage.ts
│       ├── errorHandle.ts
│       ├── fs.ts
│       ├── hooks/
│       │   ├── index.js
│       │   ├── useAnimateColor.ts
│       │   ├── useAnimateNumber.ts
│       │   ├── useAssertApiSupport.js
│       │   ├── useBackHandler.ts
│       │   ├── useDeviceOrientation.js
│       │   ├── useDrag.ts
│       │   ├── useHorizontalMode.ts
│       │   ├── useKeyboard.js
│       │   ├── useLayout.tsx
│       │   ├── usePlayTime.js
│       │   ├── useUnmounted.tsx
│       │   └── useWindowSize.ts
│       ├── index.ts
│       ├── listManage.ts
│       ├── localMediaMetadata.ts
│       ├── log.ts
│       ├── lrcTools.ts
│       ├── message.ts
│       ├── music.ts
│       ├── musicSdk/
│       │   ├── api-source-info.ts
│       │   ├── api-source.js
│       │   ├── bd/
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   └── songList.js
│       │   ├── index.js
│       │   ├── kg/
│       │   │   ├── album.js
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── pic.js
│       │   │   ├── singer.js
│       │   │   ├── songList.js
│       │   │   ├── temp/
│       │   │   │   ├── musicSearch-new.js
│       │   │   │   └── songList-new.js
│       │   │   ├── tipSearch.js
│       │   │   └── util.js
│       │   ├── kw/
│       │   │   ├── album.js
│       │   │   ├── comment.js
│       │   │   ├── decodeLyric.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicSearch.js
│       │   │   ├── pic.js
│       │   │   ├── songList.js
│       │   │   ├── tipSearch.js
│       │   │   └── util.js
│       │   ├── mg/
│       │   │   ├── album.js
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── pic.js
│       │   │   ├── songId.js
│       │   │   ├── songList.js
│       │   │   ├── temp/
│       │   │   │   └── leaderboard-old.js
│       │   │   ├── tipSearch.js
│       │   │   └── utils/
│       │   │       ├── index.js
│       │   │       └── mrc.js
│       │   ├── options.js
│       │   ├── tx/
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── songList.js
│       │   │   └── tipSearch.js
│       │   ├── utils.js
│       │   ├── wy/
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicDetail.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── songList.js
│       │   │   ├── tipSearch.js
│       │   │   └── utils/
│       │   │       ├── crypto.js
│       │   │       └── index.js
│       │   └── xm.js
│       ├── nativeModules/
│       │   ├── cache.ts
│       │   ├── crypto.ts
│       │   ├── cryptoTest.ts
│       │   ├── lyricDesktop.ts
│       │   ├── userApi.ts
│       │   └── utils.ts
│       ├── pixelRatio.ts
│       ├── request.js
│       ├── scroll.ts
│       ├── simplify-chinese-main/
│       │   ├── .gitignore
│       │   ├── LICENSE.md
│       │   ├── README.md
│       │   ├── chinese.js
│       │   ├── index.d.ts
│       │   ├── index.js
│       │   └── package.json
│       ├── tools.ts
│       ├── version.js
│       └── windowSizeTools.ts
├── test.js
└── tsconfig.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .bundle/config
================================================
BUNDLE_PATH: "vendor/bundle"
BUNDLE_FORCE_RUBY_PLATFORM: 1


================================================
FILE: .editorconfig
================================================
root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true


================================================
FILE: .eslintrc.cjs
================================================
const baseRule = {
  'no-new': 'off',
  camelcase: 'off',
  'no-return-assign': 'off',
  'space-before-function-paren': ['error', 'never'],
  'no-var': 'error',
  'no-fallthrough': 'off',
  eqeqeq: 'off',
  'require-atomic-updates': ['error', { allowProperties: true }],
  'no-multiple-empty-lines': [1, { max: 2 }],
  'comma-dangle': [2, 'always-multiline'],
  'standard/no-callback-literal': 'off',
  'prefer-const': 'off',
  'no-labels': 'off',
  'node/no-callback-literal': 'off',
  'multiline-ternary': 'off',
  'react/display-name': 'off',
  'react/prop-types': 'off',
}

module.exports = {
  root: true,
  extends: [
    'standard',
    'plugin:react/recommended',
    'plugin:react-hooks/recommended',
    'plugin:react/jsx-runtime',
  ],
  plugins: [
    'react',
  ],
  rules: baseRule,
  parser: '@babel/eslint-parser',
  overrides: [
    {
      files: ['*.ts', '*.tsx'],
      extends: ['standard-with-typescript'],
      rules: {
        ...baseRule,
        '@typescript-eslint/strict-boolean-expressions': 'off',
        '@typescript-eslint/explicit-function-return-type': 'off',
        '@typescript-eslint/space-before-function-paren': 'off',
        '@typescript-eslint/no-non-null-assertion': 'off',
        '@typescript-eslint/restrict-template-expressions': [
          1,
          {
            allowBoolean: true,
          },
        ],
        '@typescript-eslint/no-misused-promises': [
          'error',
          {
            checksVoidReturn: {
              arguments: false,
              attributes: false,
            },
          },
        ],
        '@typescript-eslint/naming-convention': 'off',
        '@typescript-eslint/return-await': 'off',
        '@typescript-eslint/comma-dangle': 'off',
        '@typescript-eslint/no-dynamic-delete': 'off',
        '@typescript-eslint/ban-ts-comment': 'off',
        '@typescript-eslint/ban-types': 'off',
      },
      parserOptions: {
        project: './tsconfig.json',
      },
    },
  ],
  settings: {
    react: {
      version: 'detect', // React version. "detect" automatically picks the version you have installed.
      // You can also use `16.0`, `16.3`, etc, if you want to override the detected value.
      // It will default to "latest" and warn if missing, and to "detect" in the future
    },
  },
  ignorePatterns: [
    'node_modules',
    '*.min.js',
    'test.js',
    '*Test.ts',
  ],
}


================================================
FILE: .github/ISSUE_TEMPLATE/bug.yml
================================================
name: 🐞 报告错误
description: 报告一个错误(Bug),请先查看常见问题及搜索 Issue 列表中有无你要提的问题。
title: "[Bug]: "
body:
- type: checkboxes
  id: check-answer
  attributes:
    label: 解决方案检查
    description: 请确保你已完成以下所有操作
    options:
      - label: 我已阅读 [常见问题](https://lyswhut.github.io/lx-music-doc/mobile/faq),但没有找到解决方案。
        required: true
      - label: 我已搜索 [Issue 列表](https://github.com/lyswhut/lx-music-mobile/issues?q=is%3Aissue+),但没有发现类似的问题。
        required: true
- type: textarea
  id: expected-behavior
  attributes:
    label: 预期行为
    description: 对期望发生的事情的清晰简明描述。
  validations:
    required: true
- type: textarea
  id: actual-behavior
  attributes:
    label: 实际行为
    description: 对实际发生的事情的清晰简明描述。
  validations:
    required: true
- type: input
  id: version
  attributes:
    label: LX Music 版本
    description: 你使用什么版本的 LX Music?
    placeholder: 例如 1.6.0 arm64-v8a
  validations:
    required: true
- type: input
  id: last-known-working-version
  attributes:
    label: 最后正常的版本
    description: 如果有,请在此处填写最后正常的版本。
    placeholder: 1.5.0 arm64-v8a
- type: input
  id: operating-system-version
  attributes:
    label: 操作系统版本
    description: |
      你使用什么版本的操作系统?
      在 Android 上,通常单击「系统设置 > 关于」。
      各类厂商定制 Android 系统的位置可能有所不同。
    placeholder: 例如 Android 15
  validations:
    required: true
- type: textarea
  id: additional-information
  attributes:
    label: 附加信息
    description: 如果你的问题需要进一步解释,或者你所遇到的问题不容易重现,请在此处添加更多信息。(直接把图片/视频拖到编辑框即可添加图片/视频)


================================================
FILE: .github/ISSUE_TEMPLATE/feature.yml
================================================
name: ✨ 功能请求
description: 为这个项目提出一个想法,请先查看常见问题及搜索 Issue 列表中有无你要提的问题。
title: "[Feature]: "
body:
- type: checkboxes
  id: check-answer
  attributes:
    label: 解决方案检查
    description: 请确保你已完成以下所有操作。
    options:
      - label: 我已阅读 [常见问题](https://lyswhut.github.io/lx-music-doc/mobile/faq),但没有找到解决方案。
        required: true
      - label: 我已搜索 [Issue 列表](https://github.com/lyswhut/lx-music-mobile/issues?q=is%3Aissue+),但没有发现类似的问题。
        required: true
- type: textarea
  id: problem-description
  attributes:
    label: 问题描述
    description: 请添加清晰简洁的描述,说明你希望通过此功能请求解决的问题。
  validations:
    required: true
- type: textarea
  id: proposed-solution
  attributes:
    label: 描述你想要的解决方案
    description: 简洁明了地描述你要发生的事情。
  validations:
    required: true
- type: textarea
  id: alternatives-considered
  attributes:
    label: 描述你考虑过的替代方案
    description: 对你考虑过的所有替代解决方案或功能的简洁明了的描述。
  validations:
    required: false
- type: textarea
  id: additional-information
  attributes:
    label: 附加信息
    description: 如果你的问题需要进一步解释,或者想要表达其他内容,请在此处添加更多信息。(直接把图片/视频拖到编辑框即可添加图片/视频)


================================================
FILE: .github/actions/setup/action.yml
================================================
name: Setup
description: Setup Env

runs:
  using: composite
  steps:
    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version-file: .nvmrc

    - name: Setup Java Env
      uses: actions/setup-java@v4
      with:
        distribution: 'microsoft'
        java-version: '17'
        cache: gradle

    - name: Get npm cache directory
      id: npm-cache-dir
      shell: bash
      run: echo "cachedir=$(npm config get cache)" >> ${GITHUB_OUTPUT}

    - name: Cache node modules
      id: cache-npm
      uses: actions/cache@v4
      with:
        # npm cache files are stored in `~/.npm` on Linux/macOS
        path: ${{ steps.npm-cache-dir.outputs.cachedir }}
        key: ${{ runner.os }}-npm-cache-${{ hashFiles('package-lock.json') }}
        restore-keys: |
          ${{ runner.os }}-npm-cache-

    - name: Install dependencies
      shell: bash
      run: npm ci


================================================
FILE: .github/actions/upload-artifact/action.yml
================================================
name: Setup
description: Setup Env

runs:
  using: composite
  steps:
    - name: Upload Artifact arm64-v8a
      if: env.PACKAGE_TYPE != 'Android_5'
      uses: actions/upload-artifact@v4
      with:
        name: app-v${{ env.PACKAGE_VERSION }}-arm64-v8a-release
        path: android/app/build/outputs/apk/release/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-arm64-v8a.apk

    - name: Upload Artifact armeabi-v7a
      if: env.PACKAGE_TYPE == null
      uses: actions/upload-artifact@v4
      with:
        name: app-v${{ env.PACKAGE_VERSION }}-armeabi-v7a-release
        path: android/app/build/outputs/apk/release/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-armeabi-v7a.apk

    - name: Upload Artifact universal
      if: env.PACKAGE_TYPE != 'Android_SL'
      uses: actions/upload-artifact@v4
      with:
        name: app-v${{ env.PACKAGE_VERSION }}-universal-release
        path: android/app/build/outputs/apk/release/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-universal.apk

    - name: Upload Artifact x86_64
      if: env.PACKAGE_TYPE == null
      uses: actions/upload-artifact@v4
      with:
        name: app-v${{ env.PACKAGE_VERSION }}-x86_64-release
        path: android/app/build/outputs/apk/release/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-x86_64.apk

    - name: Upload Artifact x86
      if: env.PACKAGE_TYPE == null
      uses: actions/upload-artifact@v4
      with:
        name: app-v${{ env.PACKAGE_VERSION }}-x86-release
        path: android/app/build/outputs/apk/release/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-x86.apk


================================================
FILE: .github/workflows/beta-pack.yml
================================================
name: Build Beta

on:
  push:
    branches:
      - beta

jobs:
  Android:
    name: Android
    runs-on: ubuntu-latest
    steps:
      - name: Check out git repository
        uses: actions/checkout@v4

      - name: Setup Env
        uses: ./.github/actions/setup

      - name: Build Packages
        env:
          DISABLE_SVG: 1
        shell: bash
        run: |
          cd android
          echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }}
          ./gradlew assembleRelease -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}'
          rm -f app/${{ secrets.KEYSTORE_STORE_FILE }}

      # Push tag to GitHub if package.json version's tag is not tagged
      - name: Get package version
        run: |
          PACKAGE_VERSION=$(node -p "require('./package.json').version")
          echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV

      - name: Generate file MD5
        run: |
          cd android/app/build/outputs/apk/release
          md5sum *.apk

      - name: Upload Artifact
        uses: ./.github/actions/upload-artifact
        env:
          PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}


================================================
FILE: .github/workflows/build-test.yml
================================================
name: Run build test

on:
  pull_request:
    branches:
      - dev

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - name: Check out git repository
        uses: actions/checkout@v4

      - name: Install Node.js
        uses: actions/setup-node@v4
        with:
          node-version: 20

      - name: Install Dependencies
        run: npm ci

      - name: Eslint check
        run: npm run lint

      - name: Test Build
        run: npm run build-test


================================================
FILE: .github/workflows/publish-version-info.yml
================================================
name: Publish NPM Version Info

on:
  workflow_dispatch:
  release:
    types: [published]

jobs:
  dispatch:
    runs-on: ubuntu-latest
    steps:
      - name: Repository Dispatch
        uses: peter-evans/repository-dispatch@v2
        with:
          token: ${{ secrets.PAT }}
          repository: lyswhut/lx-music-mobile-version-info
          event-type: npm-release


================================================
FILE: .github/workflows/release.yml
================================================
name: Build

on:
  push:
    branches:
      - master

jobs:
  Android:
    name: Android
    runs-on: ubuntu-latest
    steps:
      - name: Check out git repository
        uses: actions/checkout@v4

      - name: Setup Env
        uses: ./.github/actions/setup

      - name: Build Packages
        env:
          DISABLE_SVG: 1
        shell: bash
        run: |
          cd android
          echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }}
          ./gradlew assembleRelease -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}'
          rm -f app/${{ secrets.KEYSTORE_STORE_FILE }}

      # Push tag to GitHub if package.json version's tag is not tagged
      - name: Get package version
        run: |
          PACKAGE_VERSION=$(node -p "require('./package.json').version")
          echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV

      - name: Create git tag
        uses: pkgdeps/git-tag-action@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          github_repo: ${{ github.repository }}
          version: ${{ env.PACKAGE_VERSION }}
          git_commit_sha: ${{ github.sha }}
          git_tag_prefix: "v"

      - name: Generate file MD5
        run: |
          cd android/app/build/outputs/apk/release
          md5sum *.apk

      - name: Upload Artifact
        uses: ./.github/actions/upload-artifact
        env:
          PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}

  # Android_SL:
  #   name: Android_SL
  #   runs-on: ubuntu-latest
  #   steps:
  #     - name: Check out git repository
  #       uses: actions/checkout@v4
  #       with:
  #         ref: statusbar_lyric

  #     - name: Setup Env
  #       uses: ./.github/actions/setup

  #     - name: Build Packages
  #       shell: bash
  #       run: |
  #         cd android
  #         echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }}
  #         ./gradlew assembleRelease -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}'
  #         rm -f app/${{ secrets.KEYSTORE_STORE_FILE }}

  #     # Push tag to GitHub if package.json version's tag is not tagged
  #     - name: Get package version
  #       run: |
  #         node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV
  #         echo "COMMIT_SHA=$(git show -s --format=%H)" >> $GITHUB_ENV

  #     - name: Create git tag
  #       uses: pkgdeps/git-tag-action@v3
  #       with:
  #         github_token: ${{ secrets.GITHUB_TOKEN }}
  #         github_repo: ${{ github.repository }}
  #         version: ${{ env.PACKAGE_VERSION }}
  #         git_commit_sha: ${{ env.COMMIT_SHA }}
  #         git_tag_prefix: "v"

  #     - name: Generate file MD5
  #       run: |
  #         echo "current commit sha: ${{ env.COMMIT_SHA }}"
  #         cd android/app/build/outputs/apk/release
  #         md5sum *.apk

  #     - name: Upload Artifact
  #       uses: ./.github/actions/upload-artifact
  #       env:
  #         PACKAGE_TYPE: 'Android_SL'
  #         PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}

  # Android_5:
  #   name: Android_5
  #   runs-on: ubuntu-latest
  #   steps:
  #     - name: Check out git repository
  #       uses: actions/checkout@v4
  #       with:
  #         ref: android_5

  #     - name: Setup Env
  #       uses: ./.github/actions/setup

  #     - name: Build Packages
  #       shell: bash
  #       run: |
  #         cd android
  #         echo ${{ secrets.KEYSTORE_STORE_FILE_BASE64 }} | base64 --decode > app/${{ secrets.KEYSTORE_STORE_FILE }}
  #         ./gradlew assembleRelease -PMYAPP_UPLOAD_STORE_FILE='${{ secrets.KEYSTORE_STORE_FILE }}' -PMYAPP_UPLOAD_KEY_ALIAS='${{ secrets.KEYSTORE_KEY_ALIAS }}' -PMYAPP_UPLOAD_STORE_PASSWORD='${{ secrets.KEYSTORE_PASSWORD }}' -PMYAPP_UPLOAD_KEY_PASSWORD='${{ secrets.KEYSTORE_KEY_PASSWORD }}'
  #         rm -f app/${{ secrets.KEYSTORE_STORE_FILE }}

  #     # Push tag to GitHub if package.json version's tag is not tagged
  #     - name: Get package version
  #       run: |
  #         node -p -e '`PACKAGE_VERSION=${require("./package.json").version}`' >> $GITHUB_ENV
  #         echo "COMMIT_SHA=$(git show -s --format=%H)" >> $GITHUB_ENV

  #     - name: Create git tag
  #       uses: pkgdeps/git-tag-action@v3
  #       with:
  #         github_token: ${{ secrets.GITHUB_TOKEN }}
  #         github_repo: ${{ github.repository }}
  #         version: ${{ env.PACKAGE_VERSION }}
  #         git_commit_sha: ${{ env.COMMIT_SHA }}
  #         git_tag_prefix: "v"

  #     - name: Generate file MD5
  #       run: |
  #         echo "current commit sha: ${{ env.COMMIT_SHA }}"
  #         cd android/app/build/outputs/apk/release
  #         md5sum *.apk

  #     - name: Upload Artifact
  #       uses: ./.github/actions/upload-artifact
  #       env:
  #         PACKAGE_TYPE: 'Android_5'
  #         PACKAGE_VERSION: ${{ env.PACKAGE_VERSION }}

  Release:
    name: Release
    runs-on: ubuntu-latest
    # needs: [Android, Android_SL]
    needs: [Android]
    steps:
      - name: Check out git repository
        uses: actions/checkout@v4

      - name: Download Artifacts
        uses: actions/download-artifact@v4
        with:
          path: ./artifacts
          merge-multiple: true

      - name: Generate file MD5
        run: |
          echo -e '\n### File MD5\n```' >> ./publish/changeLog.md
          cd artifacts
          md5sum *.apk >> ../publish/changeLog.md
          echo -e '```\n' >> ../publish/changeLog.md
          echo -e '\n[软件安装包说明](https://lyswhut.github.io/lx-music-doc/download#%E8%BD%AF%E4%BB%B6%E5%AE%89%E8%A3%85%E5%8C%85%E8%AF%B4%E6%98%8E)\n' >> ../publish/changeLog.md

      - name: Get package version
        run: |
          PACKAGE_VERSION=$(node -p "require('./package.json').version")
          echo "PACKAGE_VERSION=$PACKAGE_VERSION" >> $GITHUB_ENV

      - name: Release
        uses: softprops/action-gh-release@v2
        with:
          body_path: ./publish/changeLog.md
          prerelease: false
          draft: false
          tag_name: v${{ env.PACKAGE_VERSION }}
          files: |
            artifacts/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-arm64-v8a.apk
            artifacts/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-armeabi-v7a.apk
            artifacts/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-x86_64.apk
            artifacts/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-x86.apk
            artifacts/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-universal.apk

          # artifacts/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-sl-arm64-v8a.apk
          # artifacts/lx-music-mobile-v${{ env.PACKAGE_VERSION }}-android_5-universal.apk
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .gitignore
================================================
# OSX
#
.DS_Store

# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
ios/.xcode.env.local

# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
keystore.properties
*.iml
*.hprof
.cxx/

# node.js
#
node_modules/
npm-debug.log
yarn-error.log

# BUCK
buck-out/
\.buckd/
*.keystore
!debug.keystore

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/

**/fastlane/report.xml
**/fastlane/Preview.html
**/fastlane/screenshots
**/fastlane/test_output

# Bundle artifact
*.jsbundle

# Ruby / CocoaPods
/ios/Pods/
/vendor/bundle/

# testing
/coverage


================================================
FILE: .ncurc.js
================================================
module.exports = {
  upgrade: true,
  reject: [
    '@types/react',
    '@types/react-native',
    'message2call',
    'react',
    'react-native',
    'react-native-pager-view',
    'react-native-navigation',
    'eslint-plugin-react-hooks',
    '@react-native/metro-config',
    '@react-native/babel-preset',
    '@react-native/typescript-config',
    '@react-native-community/slider',
    '@react-native-async-storage/async-storage'
  ],

  // target: 'newest',
  // filter: [
  //   'react-native-navigation',
  // ],

  // target: 'patch',
  // filter: [
  //   '@types/react',
  //   '@types/react-native',
  //   'react',
  //   'react-native',
  //   '@react-native/metro-config',
  //   '@react-native/babel-preset',
  //   '@react-native/typescript-config',
  //   '@react-native-community/slider',
  //   'eslint-plugin-react-hooks',
  //   '@react-native-async-storage/async-storage'
  // ],
}


================================================
FILE: .nvmrc
================================================
v18


================================================
FILE: .vscode/i18n-ally-custom-framework.yml
================================================
# .vscode/i18n-ally-custom-framework.yml

# An array of strings which contain Language Ids defined by VS Code
# You can check avaliable language ids here: https://code.visualstudio.com/docs/languages/overview#_language-id
languageIds:
  - javascript
  - javascriptreact
  - typescript
  - typescriptreact

# An array of RegExes to find the key usage. **The key should be captured in the first match group**.
# You should unescape RegEx strings in order to fit in the YAML file
# To help with this, you can use https://www.freeformatter.com/json-escape.html
usageMatchRegex:
  # The following example shows how to detect `t("your.i18n.keys")`
  # the `{key}` will be placed by a proper keypath matching regex,
  # you can ignore it and use your own matching rules as well
  - "[^\\w\\d]t\\(['\"`]({key})['\"`]"


# An array of strings containing refactor templates.
# The "$1" will be replaced by the keypath specified.
# Optional: uncomment the following two lines to use

# refactorTemplates:
#  - i18n.get("$1")


# If set to true, only enables this custom framework (will disable all built-in frameworks)
monopoly: true


================================================
FILE: .vscode/javascript.code-snippets
================================================
{
	// Place your LxMusicMobile 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
	// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
	// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
	// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
	// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
	// Placeholders with the same ids are connected.
	// Example:
	// "Print to console": {
	// 	"scope": "javascript,typescript",
	// 	"prefix": "log",
	// 	"body": [
	// 		"console.log('$1');",
	// 		"$2"
	// 	],
	// 	"description": "Log output to console"
	// }
	"Import translation": {
		"scope": "javascript,typescript,typescriptreact",
		"prefix": "imtl",
		"body": [
			"import { useTranslation } from '@/plugins/i18n'",
			"$1const { t } = useTranslation()"
		],
		"description": "Translation Language"
	},
	"Import store setting": {
		"scope": "javascript,typescript,typescriptreact",
		"prefix": "imss",
		"body": [
			"import settingState from '@/store/setting/state'"
		],
		"description": "Import store setting"
	},
	"Import store player": {
		"scope": "javascript,typescript,typescriptreact",
		"prefix": "imsp",
		"body": [
			"import playerState from '@/store/player/state'"
		],
		"description": "Import store player"
	},
	"Import store list": {
		"scope": "javascript,typescript,typescriptreact",
		"prefix": "imsl",
		"body": [
			"import listState from '@/store/list/state'"
		],
		"description": "Import store list"
	},
	"Import toast": {
		"scope": "javascript,typescript,typescriptreact",
		"prefix": "imts",
		"body": [
			"import { toast } from '@/utils/tools'",
			"$1toast(t(''), 'long')"
		],
		"description": "Import toast"
	},
	"Use getter theme": {
		"scope": "javascript,typescript,typescriptreact",
		"prefix": "ugt",
		"body": [
			"const theme = useGetter('common', 'theme')"
		],
		"description": "Use getter theme"
	},
}


================================================
FILE: .vscode/settings.json
================================================
{
  "i18n-ally.localesPaths": [
    "src/lang"
  ],
  // "i18n-ally.fullReloadOnChanged": true,
  "i18n-ally.keystyle": "nested",
  "i18n-ally.displayLanguage": "zh-cn",
  "i18n-ally.sourceLanguage": "zh-cn",
  "i18n-ally.translate.engines": [
    "google-cn",
    "google"
  ],
  "i18n-ally.sortKeys": true,
  "typescript.tsdk": "packages/shared/eslint/node_modules/typescript/lib",
}


================================================
FILE: CHANGELOG.md
================================================
# lx-music-mobile change log

All notable changes to this project will be documented in this file.

Project versioning adheres to [Semantic Versioning](http://semver.org/).
Commit convention is based on [Conventional Commits](http://conventionalcommits.org).
Change log format is based on [Keep a Changelog](http://keepachangelog.com/).

## [1.8.1](https://github.com/lyswhut/lx-music-mobile/compare/v1.8.0...v1.8.1) - 2026-02-16

我们很高兴地宣布新项目 Any Listen 的桌面版已发布,目前已支持列表跟随本地文件自动更新、加载并播放WebDAV上的歌曲等功能,更多功能仍在积极开发中,桌面版与Web版将同步更新。
对于有播放本地音乐或播放服务器上音乐需求的人可以试试,若遇到任何问题可以发 issue 反馈。

### 优化

- 启动APP时将最后播放的歌曲信息初始化到播放器(显示到系统通知栏,可用媒体键控制播放)
- 优化图片组件性能
- 优化版本检查提示,使用 toast 显示未知版本信息(#946)

### 修复

- 修复在某些Android设备上字体显示异常的问题(#926, #718)

## [1.8.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.7.1...v1.8.0) - 2025-11-29

我们很高兴地宣布新项目 Any Listen 的桌面版已发布,目前已支持列表跟随本地文件自动更新、加载并播放WebDAV上的歌曲等功能,更多功能仍在积极开发中,桌面版与Web版将同步更新。
对于有播放本地音乐或播放服务器上音乐需求的人可以试试,若遇到任何问题可以发 issue 反馈。

### 新增

- 新增「设置 → 基本设置 → 允许通过底栏进度条调整播放进度」设置(#778)
   *默认为原来的启用状态,若你觉得在进入播放详情页时会误触调整进度,则可以将其关闭*
- 新增 Any Listen 歌词标签数据读取与播放
- 编辑本地歌曲的标签信息时,添加 Any Listen 歌词标签数据生成(用于支持已下载歌曲的歌词在桌面版逐字播放)

### 优化

- 设置-备份与恢复 导入列表数据时,增加二次确认提示(#809)

### 修复

- 修复切歌时,偶现歌词不播放的问题
- 修复TX源搜索失败 (@Folltoshe)
- 修复MG源歌单加载失败(#913)
- 修复MG源评论加载失败(#914)

### 其他

- 更新 Media3 到 v1.8.0

## [1.7.1](https://github.com/lyswhut/lx-music-mobile/compare/v1.7.0...v1.7.1) - 2025-05-01

### 修复

- 修复 tx 歌单搜索名字、描述出现乱码的问题
- 修复解析某些本地歌词文件时出现乱码的问题(#694)
- 修复 Android 5.1 下添加本地歌曲时报错的问题(#730)
- 修复 kw 歌单详情出现打开失败的问题
- 修复 kg 热门评论无法获取的问题
- 修复 kg 歌单打开失败的问题(thanks @Folltoshe)

### 优化

- 优化软件文案编排(#701, #703, @3gf8jv4dv)

### 变更

- 我的列表-歌曲菜单中的 歌曲换源 功能从之前的类似软连接的形式改成替换歌曲的形式,也就是说,现在该功能相当于快速在线搜索歌曲,确认换源后将自动将原来的歌曲删除再将选择的歌曲插入被删除歌曲的位置。

### 其他

- 更新项目文档(@3gf8jv4dv)

## [1.7.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.6.0...v1.7.0) - 2025-01-27

落雪祝大家新年快乐!

### 关于之前提到的新项目

新项目我取名叫 Any Listen,希望它能像它的名字一样让我们能到处任意听歌。
经过一年多的开发,因各种原因,实际进度比预期的慢,但还是赶在年前发布了第一个web服务预览版,第一个版本仅支持播放服务器上的歌曲,扩展功能暂时未能开放,但已趋于完成,一两个月内可以搞定。
目前的版本仅是“能用”的状态,因时间关系,部分UI未能重新设计,但后面会继续完善。
该项目目前的目标用户是拥有自己服务器且上面存储有歌曲的人使用。

项目刚发布,文档未能完善,遇到使用问题或有任何建议欢迎提 issue 交流,
项目地址: https://github.com/any-listen/any-listen

---

*为了防止歌曲缓存被第三方软件当做垃圾意外清理,歌曲缓存不再存储到缓存目录。若想清理缓存,需去「设置 → 其他 → 资源缓存管理」清理。*

*更新到该版本后首次播放歌曲时,会将之前的歌曲缓存迁移到新位置,需要等待的时间取决于你已缓存资源的大小。*

*感谢 @3gf8jv4dv 对 LX 系列项目翻译、文档等文案的大幅修订优化。*

### 新增

- 新增蓝牙歌词支持,可以通过「设置 → 播放设置 → 显示蓝牙歌词」启用(#615)
- 新增繁体中文语言(#659, @3gf8jv4dv)
- 将 LX Music 设置为「音乐应用」类,允许将 LX Music 设置为系统默认音乐播放器
- 支持在程序外使用 LX Music 打开常见音乐文件及 `.js`、`.json`、`.lxmc` 等文件

### 优化

- 防止歌曲缓存被第三方软件当做垃圾意外清理
- 优化正常播放结束时的下一首歌曲播放衔接度,在歌曲即将结束播放时将预获取下一首歌曲的播放链接,减少自动切歌时的等待时间
- 优化歌曲换源机制,提升换源正确率
- 首次使用的提示窗口可以通过点击背景或者返回键关闭(#577)
- 上移 Toast 位置避免遮挡播放模式图标(#603, @sibojia)
- 优化简体中文文案编排,大幅修订英语文案编排(#658, #660 等, @3gf8jv4dv)
- 优化应用图标质量,在高版本系统上使用矢量图标

### 修复

- 修复导出文件到范围存储类型的目录时,扩展名丢失的问题
- 修复切换列表播放歌曲时可能会出现播放的歌曲不对应的问题
- 修复内置列表名称硬编码和语言切换显示的问题(#662)
- 修复某些情况下进播放详情页时,详情页不显示或应用界面无响应的问题
- 修复低版本 Android 在某些情况下对 Emoji 字符编码的处理问题

### 变更

- 歌曲缓存不再存储到缓存目录
- 不再长期缓存换源歌曲信息

### 其他

- 更新 Readme 文档,优化文案编排(#651, Thanks @3gf8jv4dv)
- 更新 Issue 模板(#652, @3gf8jv4dv)
- 更新项目文档(@3gf8jv4dv)
- 更新 React Native 到 v0.73.11

## [1.6.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.5.0...v1.6.0) - 2024-08-24

### 新增

- 新增 我的列表-歌曲右击菜单-歌曲换源 功能,换源后下次再播放该列表的该歌曲时将优先尝试播放所选源的歌曲,该功能允许你手动指定来源以解决自动换源失败或者换源不准确的问题
- 新增 Scheme URL 调用支持,调用传参格式与PC端一致,详情看文档说明: https://lyswhut.github.io/lx-music-doc/mobile/scheme-url

## [1.5.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.4.2...v1.5.0) - 2024-08-03

我们发布了关于 LX Music 项目发展调整与新项目计划的说明,
详情看: https://github.com/lyswhut/lx-music-desktop/issues/1912

### 新增

- 新增重复歌曲列表,可以方便移除我的列表中的重复歌曲,此列表会列出目标列表里歌曲名相同的歌曲,可在“我的列表”里的列表名菜单中使用(注:该功能与PC端的区别是可以点击歌曲名多选删除)
- 新增打开当前歌曲详情页菜单,可以在歌曲菜单中使用

### 修复

- 修复潜在桌面歌词导致的崩溃问题

### 其他

- 更新 React native 到 v0.73.9
- 更新 exoplayer 到 v1.4.0

## [1.4.2](https://github.com/lyswhut/lx-music-mobile/compare/v1.4.1...v1.4.2) - 2024-06-01

我们发布了关于 LX Music 项目发展调整与新项目计划的说明,
详情看: https://github.com/lyswhut/lx-music-desktop/issues/1912

### 修复

- 修复数据存储管理在移除数据时可能出现移除失败的问题

## [1.4.1](https://github.com/lyswhut/lx-music-mobile/compare/v1.4.0...v1.4.1) - 2024-06-01

我们发布了关于 LX Music 项目发展调整与新项目计划的说明,
详情看: https://github.com/lyswhut/lx-music-desktop/issues/1912

### 修复

- 修复播放详情页歌词滚动问题(#518)

## [1.4.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.3.0...v1.4.0) - 2024-06-01

我们发布了关于 LX Music 项目发展调整与新项目计划的说明,
详情看: https://github.com/lyswhut/lx-music-desktop/issues/1912

### 新增

- 新增 设置-基本设置-启动后打开播放详情界面 设置,默认关闭(#502 @mingcc7)

### 修复

- 修复重复的数据初始化调用
- 修复导入歌单时可能会导致歌单数据存储异常的问题(#500)

### 变更

- 设置-播放设置-优先播放320k音质选项改为“优先播放的音质”,允许选择更高优先播放的音质,如果歌曲及音源支持的话(#487)

### 其他

- 更新 React native 到 v0.73.8

## [1.3.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.2.0...v1.3.0) - 2024-04-14

### 新增

- 新增棕色主题“泥牛入海”
- 新增设置-基本设置-总是保留状态栏高度设置,如果在你的设备上出现软件可交互内容与状态栏内容显示重叠的情况,可以启用该设置以始终为系统状态栏保留空间
- 新增在线自定义源导入功能,允许通过http/https链接导入自定义源

### 优化

- 不再丢弃kg源逐行歌词(@helloplhm-qwq)
- 支持kw源排行榜显示大小(revert @Folltoshe #1460)
- 优化本地歌曲换源匹配机制

### 修复

- 修复mg歌词在某些情况下获取失败的问题
- 修复mg歌单搜索(@helloplhm-qwq)
- 修复kg最新评论无法获取的问题(@helloplhm-qwq)

### 其他

- 更新 React native 到 v0.73.6

## [1.2.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.1.1...v1.2.0) - 2024-02-01

提前祝大家新年快乐!

### 新增

- 新增自定义源(实验性功能),调用方式与PC端一致,但需要注意的是,移动端自定义源的环境与PC端不同,某些环境API不可用,详情看自定义说明文档
- 新增长按收藏列表名自动跳转列表顶部的功能
- 新增实验性的添加本地歌曲到我的收藏支持,与PC端类似,在我的收藏的列表菜单中选择歌曲目录,将添加所选目录下的所有歌曲,目前支持mp3/flac/ogg/wav等格式
- 新增歌曲标签编辑功能,允许编辑本地源且文件存在的歌曲标签信息
- 新增动态背景,启用后将使用当前播放歌曲封面做APP背景,默认关闭,可到设置-主题设置启用
- 新增APP全局字体阴影,默认关闭,可到设置-主题设置启用
- 新增启用竖屏首页横向滚动设置,默认开启(原来的行为),如果你不想要竖屏的首页左右滑动则可以关闭此设置(#397)
- 新增“使用系统文件选择器”设置,默认启用,启用该选项后,导入备份文件、自定义源等操作将不需要申请存储权限,但可能在某些系统上不可用
- 播放详情页新增桌面歌词显示/隐藏切换按钮,长按可切换歌词锁定状态
- 我的列表菜单列表新增“新建列表”菜单
- 我的列表菜单列表新增“排序歌曲”菜单,可以排序所选列表内的歌曲,排序功能与PC一致
- 添加 墨·状态栏特别版(版本号包含`sl`)的 release 构建

### 优化

- 添加是否忽略电池优化检查,用于提醒用户添加白名单,确保APP后台播放稳定性
- 在设置界面返回时,不再直接返回桌面,将回到进入设置界面前的界面,在非设置界面返回时才会返回桌面
- 更新播放栏进度条样式,进度条允许拖动调整进度
- 优化播放详情页歌曲封面、控制按钮对各尺寸屏幕的适配,修改横屏下的控制栏按钮布局
- 优化横竖屏界面的展示判断,现在趋于方屏的屏幕按竖屏的方式显示,横屏下的播放栏添加上一曲切歌按钮
- 添加对wy源某些歌曲有问题的歌词进行修复(#370)
- 文件选择器允许选择外置存储设备上的路径,添加SD卡、USB存储等外置存储设备的读写支持
- 图片显示改用第三方的图片组件,支持gif类型的图片显示,尝试解决某些设备上图片过多导致的应用崩溃问题
- 歌曲评论内容过长时自动折叠,需手动展开
- 改进本地音乐在线信息的匹配机制
- 移除播放服务唤醒锁,解决APP在空闲时仍然处于唤醒状态的问题
- 添加创建同名列表时的二次确认

### 修复

- 修复主题背景覆盖不全的问题
- 修复清理缓存后查看日志时会导致APP崩溃的问题
- 修复临时列表变更会意外触发同步的问题

### 变更

- 在更低版本的安卓上启用跟随系统亮暗主题功能(#317)
- 由于歌曲评论的图片太大占用较多资源,评论图片不再直接加载,需要点击图片区域后再加载
- 导入文件(歌单备份、自定义源文件等)默认不再需要设备存储权限,但如果这导致在你的设备上无法选择文件,则可以关闭基本设置的“使用系统文件选择器”设置,回退到原来的文件选择方式

### 其他

- 移除所有内置源,由于收到腾讯投诉要求停止提供软件内置的连接到他们平台的在线播放及下载服务,所以从即日(2023年10月18日)起LX本身不再提供上述服务
- 更新许可协议的排版,使其看起来更加清晰明了,更新数据来源原理说明
- 更新 React native 到 v0.73.3
- 核心播放器从 ExoPlayer 迁移到 media3 v1.2.1

## [1.1.1](https://github.com/lyswhut/lx-music-mobile/compare/v1.1.0...v1.1.1) - 2023-09-28

落雪提前祝大家中秋快乐~🥮😘!

### 优化

- 通过歌曲菜单添加不喜欢歌曲时需要二次确认防止手抖
- 减慢歌词详情页歌词滚动速度
- 更改应用窗口大小获取方式,尝试解决在某些设备上的背景、弹出菜单显示问题
- 优化同步功能错误消息提示,因同步服务版本不匹配导致的连接失败现在将区分提示

### 修复

- 修复横屏状态下的歌词滚动位置计算问题
- 修复切歌时歌词激活行的重置问题
- 修复更新翻译歌词、罗马音歌词设置后需重启应用才生效的问题,现在更新设置后会立即生效

### 其他

- 更新 React native 到 v0.72.5

## [1.1.0](https://github.com/lyswhut/lx-music-mobile/compare/v1.0.6...v1.1.0) - 2023-09-09

目前本项目的原始发布地址只有 **GitHub** 及 **蓝奏网盘** (在设置-关于有说明),其他渠道均为第三方转载发布,可信度请自行鉴别。

本项目无微信公众号之类的官方账号,也未在小米、华为、vivo等应用商店发布应用,商店内的“LX Music”、“洛雪音乐”相关的应用全部属于假冒应用,谨防被骗。

本软件完全无广告且无引流(如需要加群、关注公众号之类才能使用或者升级)的行为,若你使用过程中遇到广告或者引流的信息,则表明你当前运行的软件是第三方修改版。

若在升级新版本时提示签名不一致,则表明你手机上的旧版本或者将要安装的新版本中有一方是第三方修改版。

若在升级新版本时提示无法降级安装,则表明你使用的是universal(通用)版安装包(安装包大小20M+),尝试使用arm64-v8a版安装包或者到GitHub下载其他版本安装包。

该版本针对一加、OPPO、Pixel无法播放歌曲(提示音频加载出错,5 秒后切换下一首)或者无法完整播放歌曲的问题做了处理,但如果你使用该版本后问题依然存在,临时的解决方案是去设置-播放设置关闭“音频卸载”选项后完全重启应用

### 不兼容性变更

该版本修改了同步协议逻辑,同步功能至少需要PC端v2.4.0或移动端v1.1.0或同步服务v2.0.0版本才能连接使用

### 新增

- 新增列表设置-是否显示歌曲专辑名,默认关闭
- 新增列表设置-是否显示歌曲时长,默认开启
- 新增是否允许通过歌词调整播放进度功能,默认关闭,可到播放详情页右上角设置开启
- 新增“不喜欢歌曲”功能,可以在我的列表或者在线列表内歌曲的右击菜单使用,还可以去“设置-其他”手动编辑不喜欢规则,注:“上一曲”、“下一曲”功能将跳过符合“不喜欢歌曲”规则的歌曲,但你仍可以手动播放这些歌曲
- 新增同步功能对“不喜欢歌曲”列表的同步
- 新增设置-播放设置-是否启用音频卸载,该设置之前默认是启用的,现在添加开关允许将其关闭,若出现播放器问题可尝试将其关闭
- 新增设置-播放设置-自动清空已播放列表选项,默认关闭

### 优化

- 优化歌单列表歌单封面大小计算方式
- 调整竖屏下的排行榜布局
- 调整歌曲列表信息布局
- 调整横屏下的歌曲列表为两列
- 调整桌面歌词主题配色,增强歌词字体阴影(#276)
- 优化数据传输逻辑,列表同步指令使用队列机制,保证列表同步操作的顺序
- 暂停播放时播放详情歌词页不要自动滚动歌词回播放位置
- 播放详情页歌词添加延迟滚动及着色动画
- 优化息屏下的逻辑处理,尽量减少电量消耗

### 修复

- 修复wy歌单分类切换无效的问题
- 修复因插入数字类型的ID导致其意外在末尾追加 .0 导致列表数据异常的问题,同时也可能导致同步数据丢失的问题(此问题会影响PC端,要完全修复这个问题还需要同时将PC端、同步服务更新到最新版本)
- 修复在线列表、我的列表内的歌曲批量操作后,没有自动取消选择的问题
- 修复tx热门评论昵称被错误切割的问题 (By: @helloplhm-qwq, @Folltoshe)
- 修复wy源热搜词失效的问题(@Folltoshe)
- 修复mg歌单搜索歌单播放数量显示问题
- 修复搜索提示功能失效的问题(@Folltoshe)
- 修复潜在导致列表数据不同步的问题
- 修复kg无评论时的加载处理问题
- 修复顺序播放时播放完列表的最后一首歌播放按钮状态没有更新的问题(#300)

### 变更

- 随机模式下,通过点击与播放列表相同的列表切歌时,将不再清空已播放列表,即已播放的歌曲不再重新参与随机,若想恢复之前的行为可以去设置-播放设置启用清空已播放列表选项

### 其他

- 更新 React native 到 v0.72.4

## [1.0.6](https://github.com/lyswhut/lx-music-mobile/compare/v1.0.5...v1.0.6) - 2023-05-01

### 修复

- 修复wy歌单分类切换无效的问题

## [1.0.5](https://github.com/lyswhut/lx-music-mobile/compare/v1.0.4...v1.0.5) - 2023-05-01

### 优化

- 增加kg歌单歌曲flac24bit显示(@helloplhm-qwq)
- 增加tx源热门评论图片显示(@Folltoshe)
- 支持wy热门评论翻页
- 微调排行榜列表宽度及字体大小

### 修复

- 修复wy我喜欢列表使用token的方式导入,现在移动端可以使用token的方式导入我喜欢列表的音乐了,这意味着从PC端同步过来的歌单也可以在移动端上更新
- 修复在线列表的多选问题
- 修复mg搜索不显示时长的问题(@Folltoshe)
- 修复mg评论加载失败的问题(@Folltoshe)
- 修复在Android 5.1下报错的问题
- 修复对存在错误时间标签的歌词的解析
- 修复聚合搜索时未显示源名称的问题
- 修复更改音源的列表歌曲颜色的实时更新问题

### 其他

- 更新kg、tx、wy等平台排行榜列表
- 更新react native到v0.71.7

## [1.0.4](https://github.com/lyswhut/lx-music-mobile/compare/v1.0.3...v1.0.4) - 2023-04-01

### 新增

- 隐藏黑色主题背景设置,默认关闭,可以去设置-主题设置更改

### 优化

- 添加歌单分类、排行榜激活指示器
- 调整设置界面竖屏下的UI布局

### 修复

- 修复歌单排序列表滚动重置问题
- 修复搜索提示列表的显示时机问题
- 就放tx源歌词获取失败的问题
- 修复将播放速率调整为0.6后,再次打开设置面板将会导致app崩溃的问题
- 修复播放详情页设置面板当前音量显示格式问题

### 其他

- 升级 React Native 到 v0.71.5

## [1.0.3](https://github.com/lyswhut/lx-music-mobile/compare/v1.0.2...v1.0.3) - 2023-03-26

### 修复

- 修复歌单详情页内歌曲最多只加载30首的问题

## [1.0.2](https://github.com/lyswhut/lx-music-mobile/compare/v1.0.1...v1.0.2) - 2023-03-26

### 优化

- 竖屏下的首页允许滑动切换页面(恢复v0.x.x的切页操作)
- 优化更新语言、主题设置时的流畅度

### 其他

- 启用新架构

## [1.0.1](https://github.com/lyswhut/lx-music-mobile/compare/v1.0.0...v1.0.1) - 2023-03-26

### 修复

- 修复在线列表翻页问题

## [1.0.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.15.5...v1.0.0) - 2023-03-26

从v1.0.0起,我们发布了一个独立版的[数据同步服务](https://github.com/lyswhut/lx-music-sync-server#readme),如果你有服务器,可以将其部署到服务器上作为私人多端同步服务使用,详情看该项目说明

由于该版本涉及旧版数据迁移,建议更新前先到设置-备份与恢复备份歌单

### 不兼容性变更说明

- 同步功能,该功能不支持与PC端v2.2.0之前的版本使用

### 新增

- 新增聚合搜索,注:由于这个方式需要对各个源的结果进行排序,所以需要以“歌曲名 歌手”的顺序输入(例如:突然的自我 伍佰),否则排序后的结果可能不是你想要的
- 新增歌单搜索功能
- 新增热门搜索显示,默认关闭,需要到设置-搜索设置开启
- 新增搜索历史记录,默认关闭,需要到设置-搜索设置开启
- 启动软件时自动回到上次的界面,例如上次退出软件时在我的收藏,下次启动软件时会自动进入我的收藏
- 新增PC端所拥有的内置皮肤
- 新增界面字体大小设置
- 新增播放器音量大小设置,可以去播放详情页-播放器设置-音量大小更改
- 新增播放器播放速率设置,可以去播放详情页-播放器设置-播放速率更改
- 新增播放详情页歌词对齐方式设置,可以去播放详情页-播放器设置-歌词对齐方式更改
- 新增是否在左侧导航栏显示返回桌面按钮设置,默认关闭,可以去设置-基本设置-是否显示返回桌面按钮开启
- 新增是否在左侧导航栏显示退出应用按钮设置,默认关闭,可以去设置-基本设置-是否显示退出应用按钮开启
- 支持wy源flac hires歌曲类型的显示
- 添加kg源评论图片展示(@helloplhm-qwq)
- 支持kg源搜索列表、排行榜flac hires歌曲类型的显示(@helloplhm-qwq, @Folltoshe)

### 优化(界面/交互/功能)

- 调整了首页的界面布局
- 优化大屏幕下的字体大小及界面布局显示
- 支持wy源flac hires歌曲类型的显示
- 优化列表数据导入导出的性能,现在进行这些操作应该可以一下子完成且不会再冻结UI了
- 支持kg源搜索列表flac hires歌曲类型的显示(@helloplhm-qwq)

### 优化(程序)

- 优化程序启动性能,优化与程序交互的流畅度
- 重构整个程序,重新梳理了程序逻辑,使其更容易扩展及维护,将大部分代码从JavaScript迁移到TypeScript
- 重写配置管理、列表管理功能,使其与PC端同步,更容易复用PC端的代码

### 修复

- 修复使用酷狗码无法打开某些类型的歌单的问题
- 修复tx源某些歌单无法打开的问题

### 变更

- 原来播放详情页的歌词字体大小设置改为播放器设置

### 其他

- 升级React Native到v0.71.4

## [0.15.5](https://github.com/lyswhut/lx-music-mobile/compare/v0.15.4...v0.15.5) - 2023-01-02

### 修复

- 修复导入PC端v2列表文件歌曲信息转换丢失的问题
- 修复上面问题导致的tx源评论加载失败的问题

## [0.15.4](https://github.com/lyswhut/lx-music-mobile/compare/v0.15.3...v0.15.4) - 2022-12-10

### 修复

- 修复播放详情页歌词翻译、罗马音歌词匹配问题

## [0.15.3](https://github.com/lyswhut/lx-music-mobile/compare/v0.15.2...v0.15.3) - 2022-12-10

### 修复

- 修复鸿蒙系统下的崩溃问题

## [0.15.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.15.1...v0.15.2) - 2022-12-10

### 修复

- 修复潜在的歌词解析导致应用崩溃问题

## [0.15.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.15.0...v0.15.1) - 2022-12-10

### 修复

- 修复某些歌曲的桌面歌词翻译或罗马音没有显示的问题
- 修复kg某些歌单链接无法打开的问题

## [0.15.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.14.3...v0.15.0) - 2022-11-20

### 新增

- 支持导入PC端v2版本的列表数据
- 添加kg源罗马音歌词的支持
- 支持打开波点音乐歌单(需在酷我源打开)

### 修复

- 支持单行多时间标签歌词解析,修复某些歌词会出现时间标签的问题
- 修复某些类型的kg歌单无法导入的问题
- 修复异常歌单、歌曲数据导致的崩溃问题(#157)

### 其他

- 升级react-native到 v0.68.5

## [0.14.3](https://github.com/lyswhut/lx-music-mobile/compare/v0.14.2...v0.14.3) - 2022-09-03

### 修复

- 修复因音源的域名到期导致的音源失效的问题

## [0.14.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.14.1...v0.14.2) - 2022-08-18

### 优化

- 为tx、kw源添加 Flac 24bit 音质显示,注:由于之前没有记录此音质,所以之前收藏的歌曲信息中不包含它

### 修复

- 修复排行榜在旋转屏幕后,选中的榜单被重置回第一个的问题
- 修复企鹅音乐搜索失效的问题

## [0.14.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.14.0...v0.14.1) - 2022-07-09

### 优化

- 添加“弹出键盘时自动隐藏播放栏”设置,默认启用(原来的行为),若在某些设备上播放栏无法显示时则可以关闭此设置
- 优化切歌时桌面歌词的切换动画显示
- 暂停播放时自动隐藏桌面歌词
- 在我的列表-列表名左侧添加了一个图标,以表示此处可以点击切换列表

### 修复

- 修复tx源搜索失效的问题

## [0.14.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.13.0...v0.14.0) - 2022-06-17

### 新增

- 新增设置-桌面歌词-单行歌词设置,默认关闭,启用后只显示一行歌词,超出窗口宽度自动滚动到末尾
- 新增设置-桌面歌词-显示歌词切换动画,默认启用,如果你觉得切换动画影响视觉可以将其关闭
- 新增设置-基本设置-启动后自动播放音乐,默认关闭

### 优化

- 支持mg源的歌词翻译(之前添加的歌曲需要去设置清空缓存才会刷新歌词)
- 添加歌曲列表更新操作的二次确认
- 添加导入文件错误时的指引提示

### 修复

- 修复桌面歌词转繁体设置不立即生效的问题
- 修复搜索、歌单、排行榜列表可能在切换新内容后出现上次列表内容的残留问题(#118)
- 修复在某些系统上播放音乐会导致应用崩溃的问题(#129)
- 修复停止播放后的播放器状态清理问题

### 文档

移动版文档已迁移到:<https://lyswhut.github.io/lx-music-doc/mobile>

## [0.13.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.12.0...v0.13.0) - 2022-05-22

从这个版本起,你可以将桌面歌词拖动到状态栏上,然后将歌词字体调小后配合新增的歌词窗口宽度、行数设置,模拟出类似状态栏歌词的效果。

如果你的设备装有Xposed框架,可以使用状态栏版(详情看GitHub置顶issue),它通过调用第三方Xposed模块【墨•状态栏歌词】的API支持来状态栏歌词(感谢@ftevxk)。
但考虑到要依赖第三方应用,并且是Xposed模块,预计用的人会比较少,所以暂不考虑将此特性包含在正式版中。

### 新增

- 新增设置-播放设置-显示歌词罗马音,默认关闭,注:目前只有网易源能获取到罗马音歌词(得益于 Binaryify/NeteaseCloudMusicApi/pull/1523),如果你知道其他源的歌词罗马音获取方式,欢迎PR或开issue交流!
- 新增黑、白桌面歌词主题
- 桌面歌词新增窗口宽度百分比、最大歌词行数调整设置,允许将歌词拖动到刘海屏状态栏上。提示:有了这组功能你就可以模拟状态栏歌词了
- 新增设置-播放设置-将播放的歌词转繁体功能(#114)

### 优化

- 允许桌面歌词拖动到状态栏上(感谢@ftevxk)
- 允许选择更新日志弹窗里的文本内容
- 桌面歌词的最大字体大小允许调整到500(#107)

### 修复

- 修复潜在的桌面歌词导致应用崩溃问题

### 文档

- 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
- 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空

### 其他

- 升级React native到v0.68.2

## [0.12.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.11.1...v0.12.0) - 2022-04-16

### 新增

- 为搜索、歌单、排行榜的歌曲菜单添加分享“分享歌曲”按钮
- 新增设置-基本设置-分享设置,它用于控制歌曲菜单的分享行为,默认使用系统分享
- 新增是否在通知栏显示歌曲图片设置,默认开启(原来的行为)
- 新增黑色皮肤“黑灯瞎火”
- 新增设置-基本设置-主题颜色-跟随系统亮、暗模式切换主题设置,注:此设置需要android 10或ios 13及以上的版本才支持

### 优化

- 现在即使切歌模式处于单曲循环、顺序播放、禁用时,手动切歌将会按照列表循环的规则处理(#69)
- 添加定时退出计时结束后的提示

### 修复

- 修复wy源搜索某些歌曲时第一页之后的歌曲无法加载的问题
- 每次启动时过滤无效的歌曲
- 修复换源失败时的处理问题
- 修复非循环模式下播放结束后的状态显示问题及无法重新播放的问题(#104)
- 修复定时退出可能导致崩溃的问题
- 修复播放详情页歌词界面在把应用切到后台再切回来会导致屏幕常亮失效的问题

### 变更

- 歌曲菜单的“复制歌曲名”改为“分享歌曲”,点击后可以选择第三方应用分享歌曲详情页链接
- 已存在目录列表的歌曲再次添加时将不会变成移除

### 其他

- 升级react-native到 v0.68.1

## [0.11.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.11.0...v0.11.1) - 2022-03-20

### 修复

- 修复播放栏在某些设备不显示的问题

## [0.11.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.10.3...v0.11.0) - 2022-03-19

### 新增

- 新增“点击列表里的歌曲时自动切换到当前列表播放”设置,此功能仅对歌单、排行榜有效,默认关闭
- 添加试听接口,这是测试接口、临时接口都不可用时最后的选择...

### 优化

- 过滤tx源某些不支持播放的歌曲,解决播放此类内容会导致意外的问题
- 备份与恢复兼容单个列表文件的导入
- 添加通知权限的检查提醒,点击“不再提示”后,将会在设置-清空缓存后才会恢复提示

### 修复

- 修复Android 12下的桌面歌词锁定后还是无法在应用外点击歌词后面下面的内容的问题

### 其他

- 升级React native到v0.67.4

## [0.10.3](https://github.com/lyswhut/lx-music-mobile/compare/v0.10.2...v0.10.3) - 2022-01-28

### 优化

- 优化kw源英文与翻译歌词的匹配

### 修复

- 修复桌面歌词播放器会导致应用崩溃的问题

## [0.10.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.10.1...v0.10.2) - 2022-01-22

### 修复

- 修复某些系统下的虚拟导航栏会导致播放栏隐藏的问题(react-native v0.67.x导致的)

### 其他

- 降级react-native到 v0.66.4

## [0.10.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.10.0...v0.10.1) - 2022-01-22

### 优化

- 优化通知栏的更新机制,尝试修复魅族的通知栏图片不显示的问题
- 我的列表-列表名的右击菜单更新已收藏的在线列表时,将始终重新加载,不再使用缓存,解决在原平台更新歌单后,在LX点击更新可能看到的还是在原平台更新前的歌单的问题

### 修复

- 修复tx源无搜索结果的问题
- 修复小米等设备下面的手势提示线背景颜色为黑色的问题

### 其他

- 升级React native到v0.67.1

## [0.10.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.9.2...v0.10.0) - 2021-12-30

### 新增

- 同步功能新增对列表位置调整的支持(需v1.15.3以上的PC端版本才支持)
- 新增播放详情页歌词字体大小调整设置,可在详情页右上角的按钮进行调整
- 新增同步服务地址历史列表功能
- 横屏播放详情页新增评论入口
- 我的列表歌曲三个点的菜单新增复制歌曲名

### 优化

- 修改对播放模块的调用,杜绝应用显示正在播放的歌曲与实际播放歌曲不一致的问题(这是播放模块歌曲队列与应用内歌曲队列在某些情况下出现不一致时导致的)
- 支持PC端同步功能添加对列表顺序调整的控制,确保手动调整位置后的列表与不同的电脑同步时,列表位置不会被还原
- 调整横屏下的导航栏、播放详情页布局,提高屏幕空间利用率并使其更易操作
- 调整歌单类别、我的列表弹出层界面
- 播放栏移除上一曲按钮,将多出来的空间加给播放、下一曲按钮
- 现在点击、长按播放栏歌曲标题也可以进入详情页、定位当前播放歌曲了

### 修复

- 修复kw源某些歌曲的歌词提取异常的问题

### 其他

- 升级react-native到v0.66.4

## [0.9.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.9.1...v0.9.2) - 2021-11-28

### 优化

- 添加应用初始化出错时的错误捕获输出
- 优化歌词自动换源机制

### 修复

- 修复因kw源歌词接口停用导致该源歌词获取失败的问题

### 其他

- 更新react-native到v0.66.3
- 更新Exoplayer到v2.16.0

## [0.9.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.9.0...v0.9.1) - 2021-10-23

### 修复

- 修复删除列表时会导致应用崩溃的问题
- 修复原生代码导致的错误日志记录

## [0.9.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.8.3...v0.9.0) - 2021-10-22

### 新增

- 新增歌曲评论显示,可在播放详情页进入。(与PC端一样,目前仅支持显示部分评论)
- 新增播放、收藏整个排行榜功能,可长按排行榜名字后在弹出的菜单中操作
- 新增单个列表导入/导出功能,可以方便分享歌曲列表,可在点击“我的列表”里的列表名右侧的按钮后弹出的菜单中使用
- 新增删除列表前的确认弹窗,防止误删列表

### 优化

- 添加更多同步功能的日志记录

### 修复

- 修复kg源的歌单链接无法打开的问题
- 修复同一首歌的URL、歌词等同时需要换源时的处理问题
- 修复在排行榜页面无法时无法通过点击我的列表图标切换到我的列表的问题

### 其他

- 更新react-native到v0.66.1

## [0.8.3](https://github.com/lyswhut/lx-music-mobile/compare/v0.8.2...v0.8.3) - 2021-10-07

### 修复

- 修复我的列表搜索无法搜索小括号、中括号等字符,并会导致应用崩溃的问题
- 修复使用同步功能同步完成后,列表没有被保存,导致下次再连接同步时被同步新增的歌曲被移除的问题(此问题由v0.8.2的存储切片改造引入的)

### 其他

- 更新React native到v0.66.0

## [0.8.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.8.1...v0.8.2) - 2021-10-02

### 优化

- 缓冲进度条颜色
- 优化数据存储,若需要存储的数据过大时会将数据切片后存储,现在存储大列表不会导致列表丢失了

### 修复

- 修复随机播放模式下在同列表切其他歌曲不会清空已播放列表的问题
- 修复歌曲播放出错时的URL刷新问题

## [0.8.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.8.0...v0.8.1) - 2021-09-16

### 优化

- 添加更多错误信息的记录

### 修复

- 修复潜在的获取缓存大小报错问题
- 修复mg排行榜无法加载的问题
- 修复列表导出失败时的提示信息缺失翻译的问题
- 修复 Android 11 导入列表时,不显示备份文件的问题
- 修复其他应用播放声音时,软件临时暂停播放后通知栏的状态仍显示正在播放的问题

## [0.8.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.7.1...v0.8.0) - 2021-09-06

### 新增

- 添加对通知栏歌曲进度条的支持

### 修复

- 修复某些情况下桌面歌词会导致APP崩溃的问题
- 修复从电脑浏览器复制的企鹅歌单链接无法打开的问题

### 其他

- 升级React native到v0.65.1
- 升级播放模块`react-native-track-player`到v2版本,优化通知栏歌曲信息显示逻辑

## [0.7.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.7.0...v0.7.1) - 2021-08-23

### 修复

- 修复无法从歌单界面打开网易歌单详情的问题

## [0.7.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.6.2...v0.7.0) - 2021-08-22

如果你喜欢并经常使用洛雪音乐,并想要第一时间尝鲜洛雪的新功能,可以加入测试企鹅群768786588,
注意:测试版的功可能会不稳定,打算潜水的勿加。

### 新增

- 新增横屏状态下的播放详情页
- 新增橙、粉、灰主题色
- 新增桌面歌词的字体大小、透明度设置
- 新增我的列表内歌曲搜索定位功能

### 调整

- 为了与搜索、歌单操作栏位置统一,现将我的列表-收藏的列表操作栏由底部挪到顶部

### 修复

- 修复tx源的歌词无法显示的问题
- 修复随机播放模式下使用稍后播放功能会导致歌曲单曲循环的问题
- 修复某些情况下桌面歌词会导致APP崩溃的问题

## [0.6.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.6.1...v0.6.2) - 2021-08-11

### 优化

- 优化设置界面的输入框输入机制,现在只要键盘收起即可自动保存输入的内容
- 添加在启用桌面歌词时对悬浮层权限的检查,这应该可以修复某些设备上点击启用桌面歌词时不显示无权限弹窗也不显示桌面歌词的情况

### 变更

- 不再自动聚焦定时退出、调整位置弹窗内的输入框,这应该可以修复某些设备无法在这两个地方弹出键盘的问题

### 修复

- 修复启用桌面歌词时的权限提示弹窗会导致应用报错的问题
- 修复我的列表无法更新从收藏的排行榜的问题

## [0.6.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.6.0...v0.6.1) - 2021-08-08

### 修复

- 修复随机播放下无法切歌的问题

## [0.6.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.5.3...v0.6.0) - 2021-08-08

### 新增

- 新增局域网同步功能(实验性,首次使用前建议先备份一次列表),此功能需要配合PC端使用,移动端与PC端处在同一个局域网(路由器的网络)下时,可以多端实时同步歌曲列表,使用问题请看"常见问题"。
- 新增桌面歌词

### 优化

- 优化退出应用的机制,现在在需要退出应用的场景将会完全退出应用

### 修复

- 修复某些情况下出现恢复播放信息失败的问题
- 修复删除列表中正在播放的歌曲时会自动跳到第一首的问题
- 修复因其他应用需要播放声音而暂停播放音乐时歌词不会暂停播放导致恢复播放后歌词与播放进度不一致的问题

## [0.5.3](https://github.com/lyswhut/lx-music-mobile/compare/v0.5.2...v0.5.3) - 2021-07-23

### 修复

- 修复歌曲缓存失效的问题

## [0.5.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.5.1...v0.5.2) - 2021-07-22

### 优化

- 优化mg源打开歌单的链接兼容

### 修复

- 修复单曲循环播放时循环次数为偶数时歌词不重新播放的问题
- 添加针对进入歌词界面时某些情况下会弹出`scrollToIndex out of range: requested index ...`崩溃错误弹窗的处理
- 修复导入kg歌单最多只能加载100、500首歌曲的问题。注:现在可以加载1000+首歌曲的歌单,但出于未知原因会导致部分歌曲无法加载(可能是无版权导致的),目前酷狗码仍然最多只能加载500首歌

## [0.5.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.5.0...v0.5.1) - 2021-07-05

### 优化

- 添加切换播放模式时的文字提示
- 优化单首歌曲的添加弹窗操作,当选择当前歌曲已存在目标列表时(列表名灰色显示),会将当前歌曲从目标列表移除,否则将当前歌曲添加到目标列表,添加在弹窗内对歌曲的添加、移动、删除操作时的文字提示

### 修复

- 修复mg源搜索失效的问题

### 移除

- 因wy源的歌单列表已没有“最新”排序的选项,所以现跟随移除wy源歌单列表按“最新”排序的按钮

## [0.5.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.4.2...v0.5.0) - 2021-06-13

### 新增

- 新增“其他应用播放声音时,自动暂停播放”设置,默认开启
- 新增“添加歌曲到列表时的位置”设置,可选项为列表的“顶部”与“底部”
- 新增“显示歌词翻译设置”,默认关闭

### 变更

- 添加歌曲到列表时从原来的底部改为顶部,若想要恢复原来的行为则可以去更改“添加歌曲到列表时的位置”设置项

## [0.4.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.4.1...v0.4.2) - 2021-06-06

### 优化

- 优化wy源歌单导入匹配,现在存在链接外的其他字符也可以打开歌单了

### 修复

- 修复定时播放开启歌曲播放完毕再停止时,若倒计时已结束会导致无法播放歌曲的问题
- 修复打开歌单失败时会导致应用崩溃的问题
- 修复打开kw歌单失败时会无限重试的问题
- 尝试修复弹出菜单、列表位置不正确的问题
- 修复打开kg源歌单链接失败的问题
- 尝试修复有时候进入播放详情歌词界面时会导致应用UI被冻结的问题
- 修复有时候进入播放详情页时歌曲封面大小显示不正确的问题

## [0.4.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.4.0...v0.4.1) - 2021-05-30

### 修复

- 修复定时播放开启歌曲播放完毕再停止时,若倒计时已结束会导致无法播放歌曲的问题

## [0.4.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.3.3...v0.4.0) - 2021-05-30

### 新增

- 新增我的列表中已收藏的在线列表的更新功能。注意:这将会覆盖本地的目标列表,歌曲将被替换成最新的在线列表(与PC端的同步一样)
- 歌曲添加、移动弹窗新增创建新列表功能
- 新增定时退出播放

### 优化

- 优化应用布局对手机系统字体大小的适配
- 调整歌单详情页,现在在歌单详情页按手机上的返回键将会返回歌单列表,而不是直接退出APP
- 优化进入播放详情页、歌单详情页的动画效果

### 修复

- 尝试修复某些情况下进播放详情歌词界面时报错的问题

## [0.3.3](https://github.com/lyswhut/lx-music-mobile/compare/v0.3.2...v0.3.3) - 2021-05-25

### 修复

- 尝试修复软件启动时恢复上一次播放的歌曲可能导致软件崩溃的问题
- 尝试修复播放详情页歌词导致UI冻结的问题
- 修复企鹅音乐搜索歌曲没有结果的问题

### 其他

- 整合日志记录
- 更新 exoPlayer 到 2.14.0

## [0.3.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.3.1...v0.3.2) - 2021-05-23

### 修复

- 修复手机分享的wy歌单、某些tx、kg歌单无法打开的问题
- 修复打开空的歌单时,点击播放全部会导致应用崩溃的问题
- 修复企鹅音乐搜索歌曲没有结果的问题

## [0.3.1](https://github.com/lyswhut/lx-music-mobile/compare/v0.3.0...v0.3.1) - 2021-05-22

### 修复

- 修复进入播放详情歌词界面后的屏幕常亮不会被取消的问题

## [0.3.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.2.0...v0.3.0) - 2021-05-22

### 新增

- 新增通过歌单链接打开歌单的功能

### 优化

- 切换到播放详情歌词界面时将阻止屏幕息屏

### 修复

- 修复一个导致崩溃日志写入文件前会导致APP崩溃的莫名其妙问题

## [0.2.0](https://github.com/lyswhut/lx-music-mobile/compare/v0.1.7...v0.2.0) - 2021-05-21

### 新增

- 新增竖屏下的播放详情页

## [0.1.7](https://github.com/lyswhut/lx-music-mobile/compare/v0.1.6...v0.1.7) - 2021-05-20

### 优化

- 修改歌单导入流程,添加对歌单导入错误的捕获

### 修复

- 修复在系统暗主题下,应用内文字输入框的字体会变成白色的问题

## [0.1.6](https://github.com/lyswhut/lx-music-mobile/compare/v0.1.5...v0.1.6) - 2021-05-18

### 优化

- 改进软件错误处理,添加对软件崩溃的错误日志记录,可在设置-其他查看错误日志历史。注:清理缓存时日志也将会被清理

### 修复

- 修复显示版本更新弹窗会导致应用崩溃的问题

## [0.1.5](https://github.com/lyswhut/lx-music-mobile/compare/v0.1.4...v0.1.5) - 2021-05-18

### 修复

- 修复修复协议弹窗可以被绕过的问题
- 修复从在线列表使用稍后播放功能播放歌曲时,歌曲封面不显示的问题
- 修复正在播放“稍后播放”的歌曲时,对“稍后播放”前播放的列表进行添加、删除操作会导致切歌的问题

## [0.1.4](https://github.com/lyswhut/lx-music-mobile/compare/v0.1.3...v0.1.4) - 2021-05-16

### 修复

- 修复获取在线列表时快速切换会导致APP闪退的问题

## [0.1.3](https://github.com/lyswhut/lx-music-mobile/compare/v0.1.2...v0.1.3) - 2021-05-16

### 优化

- 添加导入提示,兼容从PC端“全部数据”类型的备份文件中导入歌单
- 添加全局异常错误捕获,现在一般情况下APP崩溃前会弹窗提示错误信息。

## [0.1.2](https://github.com/lyswhut/lx-music-mobile/compare/v0.1.1...v0.1.2) - 2021-05-16

### 优化

- 在搜索、歌单、排行榜列表多选音乐后点菜单中的播放将会把已选的歌曲添加到试听列表播放

### 修复

- 修复播放模块没拉取最新代码导致播放器存在无法从通知栏停止等问题

## [0.1.1] - 2021-05-15

- v0.1.1版本发布 🎊 🎉


================================================
FILE: FAQ.md
================================================
# lx-music-mobile 常见问题

本文档已迁移至:<https://lyswhut.github.io/lx-music-doc/mobile/faq>

<!-- 在阅读本常见问题后,仍然无法解决你的问题,请提交issue或者加企鹅群`830125506`反馈(无事勿加,入群先看群公告),反馈时请**注明**已阅读常见问题!

## LX Music中的音乐播放列表机制

1. 默认情况下,播放搜索列表、歌单列表、排行榜列表的歌曲时会自动将该歌曲添加到“我的列表”的试听列表后再播放,手动将歌曲添加到试听列表,再去试听列表找到这首歌点播放是等价的
2. 如果你想要播放多首歌曲,需要使用多选功能(若不知道如何多选请看常见问题)多选后,将歌曲这些歌曲添加到“我的列表”播放,或使用稍后播放功能播放
3. 第2条适用于搜索列表、歌单列表、排行榜列表、我的列表的歌曲
4. 对于歌单详情列表,除了可以使用第2条的方式播放外,你可以点击详情页上面的播放按钮临时播放当前歌单,或点击收藏将当前歌单收藏到“我的列表”后再去播放
5. 对于排行榜详情列表,除了可以使用第2条的方式播放外,你可以在长按排行榜名字后弹出的菜单中,播放或收藏整个排行榜,这与第四条的歌单中的播放、与收藏按钮功能一致
6. v0.11.0及之后新增了“双击列表里的歌曲时自动切换到当前列表播放”设置,默认关闭,此功能仅对歌单、排行榜有效
7. 将歌曲添加“稍后播放”后,它们会被放在一个优先级最高的特殊队列中,点击“下一曲”时会消耗该队列中的歌曲,并且无法通过“上一曲”功能播放该队列的上一首歌曲
8. 在切歌时若不是通过“上一曲”、“下一曲”功能切歌(例如直接点击“排行榜列表”、“我的列表”中的歌曲切歌),“稍后播放”队列将会被清空

## 歌曲无法试听与下载

### 所有歌曲都提示 `请求异常😮,可以多试几次,若还是不行就换一首吧。。。`

尝试更换网络,如切换到移动网络,若移动网络还是不行则尝试开关下手机的飞行模式后再试,<br>
若使用家庭网络的话,可尝试将光猫断电5分钟左右再通电联网后播放。

### 其他情况

尝试在在浏览器打开这个地址`http://ts.tempmusics.tk`,浏览器显示404是正常的,如果不是404那就证明所在网络无法访问接口服务器,对于此类情况请尝试切换其他网络。

### 通用解决方法

尝试按以下顺序解决:

1. 尝试更新到最新版本
2. 尝试切换其他歌曲(或直接搜索该歌曲),若全部歌曲都无法试听与下载则进行下一步
3. 尝试到 设置-音乐来源 切换到其他接口
4. 尝试切换网络,比如用手机开热点(所有歌曲都提示请求异常时可通过此方法解决,或等一两天后再试)
5. 若还不行请到这个链接查看详情:<https://github.com/lyswhut/lx-music-desktop/issues/5>
6. 若没有在第5条链接中的第一条评论中看到接口无法使用的说明,则应该是你网络无法访问接口服务器的问题,如果接口有问题我会在那里说明。

想要知道是不是自己网络的问题可以看看`http://ts.tempmusics.tk`能不能在浏览器打开,浏览器显示404是正常的,如果不是404那就证明所在网络无法访问接口服务器。
若网页无法打开或打来不是404,则应该是DNS的问题,可以尝试以下办法:

1. 将DNS改成自动获取试试
2. 手动把DNS改一下,不要用360的DNS,可以把DNS改成`223.6.6.6`、`8.8.8.8`

## 列表多选

长按列表将会进入多选模式。

- 例子一:想要选中1-5项,进入多选模式后,取消所有选中的内容,切换到区间,点击第一项,再点击第五项即可完成选择;
- 例子二:想要选中1项与第3项,进入多选模式后,点击第一项,再点击第三项即可完成选择;
- 例子三:想要选中当前列表的全部内容,进入多选模式后,点击全选即可完成选择(注:由于**在线列表**使用分页加载,全选只会选择目前已加载的内容,若要完整选择整个在线列表的内容则需要往下滑动将列表加载完毕再进行全选)。

注:选完后可用歌曲列表三个点的菜单操作已选的内容

## 无法打开外部歌单

不支持垮源打开歌单,请**确认**你需要打开的歌单平台是否与软件标签所写的**歌单源**对应(不一样的话请通过右上角切换歌单源);<br>
对于分享出来的歌单,若打开失败,可尝试先在浏览器中打开后,再从浏览器地址栏复制URL地址到软件打开;<br>
或者如果你知道歌单 id 也可以直接输入歌单 id 打开。<br>

注:网易源的“我喜欢”歌单无法在未登录的情况下打开,所以你需要手动创建一个歌单后将“我喜欢”里的歌曲移动到该歌单打开

## 播放整个歌单或排行榜

播放在线列表内的歌曲需要将它们都添加到我的列表才能播放,你可以全选列表内的歌曲然后添加到现有列表或者新创建的列表,然后去播放该列表内的歌曲。

## 桌面歌词启用后不显示

安卓6及更高的版本需要给予LX Music显示悬浮窗的权限才能使用桌面歌词功能,请确认是否授予此权限(不懂怎么授予的话请自行百度“开启悬浮窗权限”)。

## 下载功能

移动端暂不支持歌曲下载功能。

## 同步功能的使用(实验性,首次使用前建议先备份一次列表)

**注意:由于同步传输时的数据是明文传输,请在受信任的网络下使用此功能!**<br>
此功能需要配合PC端使用,移动端与PC端处在同一个局域网(路由器的网络)下时,可以多端实时同步歌曲列表,使用方法:

1. 在PC端的设置-数据同步开启同步功能(这时如果出现安全软件、防火墙等提示网络连接弹窗时需要点击允许)
2. 在移动端的设置-同步-同步服务器地址输入PC端显示的同步服务器地址(如果显示可以多个,则输入与**移动端上显示的本机地址**最相似的那个),端口号与PC端的同步端口一致(**输入完毕后需要按一下键盘上的回车键使输入的内容生效**)
3. 输入完这两项后点击“启动同步”
4. 若连接成功,对于首次同步时,若两边的设备的列表不为空,则PC端会弹出选择列表同步方式的弹窗,同步方式的说明弹窗下面有介绍

#### 关于同步弹窗的说明

对于首次同步时,若两边的设备的列表不为空,则PC端会弹出选择列表同步方式的弹窗,此弹窗内的同步方式仅针对**首次同步**,<br>
第一次同步成功后,以后再同步时将会自动根据两边设备的列表内容合并同步,不信你可以在同步完成后断开两边的连接,然后在两边增删一些歌曲或列表后再同步试试看~😉

#### 连接同步服务失败的可能原因

- 此功能需要PC端与移动端都连接在同一个路由器下的网络才能使用
- 检查防火墙是否拦截了PC端的服务端口
- 路由器若开启了AP隔离,则此功能无法使用

#### 连接同步服务失败的检查

1. 确保PC端的同步服务已启用成功(若连接码、同步服务地址没有内容,则证明服务启动失败,此时看启用同步功能复选框后面的错误信息自行解决,另外若你不知道端口号是什么意思就不要乱改,或不要改得太大与太小)
2. 在手机浏览器地址栏输入`http://x.x.x.x:23332/hello` **(注:将`x.x.x.x`换成PC端显示的同步服务地址,`23332`为PC端的端口号)** 后回车,若此地址可以打开并显示 `Hello~::^-^::`则证明移动端与PC端网络已互通,
3. 若移动端无法打开第2步的地址,则在PC端的浏览器地址栏输入并打开该地址,若可以打开,则要么是被LX Music PC端被电脑防火墙拦截,要么PC端与移动端不在同一个网络下,或者路由器开启了AP隔离(一般在公共网络下会出现这种情况)

## 更新已收藏的在线歌单

该功能仅对直接从歌单详情页点“收藏”按钮收藏的歌单有效,可右击已收藏的列表名从弹出的菜单中选择“更新”使用该功能,

需要注意的是:这将会覆盖本地的目标列表,歌曲将被替换成最新的在线列表。

## 杀毒软件提示有病毒或恶意行为

本人只能保证我写的代码不包含任何**恶意代码**、**收集用户信息**的行为,并且软件代码已开源,请自行查阅,软件安装包也是由CI拉取源代码构建,构建日志:[GitHub Actions](https://github.com/lyswhut/lx-music-mobile/actions)<br>
尽管如此,但这不意味着软件是100%安全的,由于软件使用了第三方依赖,当这些依赖存在恶意行为时([供应链攻击](https://docs.microsoft.com/zh-cn/windows/security/threat-protection/intelligence/supply-chain-malware)),软件也将会受到牵连,所以我只能尽量选择使用较多人用、信任度较高的依赖。<br>
当然,以上说明建立的前提是在你所用的安装包是从**本项目主页上写的链接**下载的,或者有相关能力者还可以下载源代码自己构建安装包。

最后,若出现杀毒软件报毒、存在恶意行为,请自行判断选择是否继续使用本软件! -->


================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'

# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
ruby ">= 2.6.10"

gem 'cocoapods', '~> 1.12'


================================================
FILE: LICENSE
================================================
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.


================================================
FILE: README.md
================================================
<p align="center"><a href="https://github.com/lyswhut/lx-music-mobile"><img width="200" src="https://github.com/lyswhut/lx-music-mobile/blob/master/doc/images/icon.png" alt="lx-music logo"></a></p>

<h1 align="center">LX Music 移动版</h1>

<p align="center">
  <a href="https://github.com/lyswhut/lx-music-mobile/releases"><img src="https://img.shields.io/github/release/lyswhut/lx-music-mobile" alt="Release version"></a>
  <a href="https://github.com/lyswhut/lx-music-mobile/actions/workflows/release.yml"><img src="https://github.com/lyswhut/lx-music-mobile/workflows/Build/badge.svg" alt="Build status"></a>
  <a href="https://github.com/lyswhut/lx-music-mobile/actions/workflows/beta-pack.yml"><img src="https://github.com/lyswhut/lx-music-mobile/workflows/Build%20Beta/badge.svg" alt="Build status"></a>
  <a href="https://github.com/facebook/react-native"><img src="https://img.shields.io/github/package-json/dependency-version/lyswhut/lx-music-mobile/react-native/master" alt="React native version"></a>
  <!-- <a href="https://github.com/lyswhut/lx-music-mobile/releases"><img src="https://img.shields.io/github/downloads/lyswhut/lx-music-mobile/latest/total" alt="Downloads"></a> -->
  <a href="https://github.com/lyswhut/lx-music-mobile/tree/dev"><img src="https://img.shields.io/github/package-json/v/lyswhut/lx-music-mobile/dev" alt="Dev branch version"></a>
  <!-- <a href="https://github.com/lyswhut/lx-music-mobile/blob/master/LICENSE"><img src="https://img.shields.io/github/license/lyswhut/lx-music-mobile" alt="License"></a> -->
</p>

<p align="center">一个基于 React Native 开发的音乐软件</p>

## 说明

所用技术栈:

- React Native
- Redux

已支持的平台:

- Android 5 及以上

***注:目前没有计划支持 iOS 和 HarmonyOS NEXT**。*<br>
*桌面版项目地址:<https://github.com/lyswhut/lx-music-desktop>*<br>
*LX Music 项目发展调整与新项目计划:https://github.com/lyswhut/lx-music-desktop/issues/1912*

软件变化请查看[更新日志](https://github.com/lyswhut/lx-music-mobile/blob/master/CHANGELOG.md)。

软件下载请查看 [GitHub Releases](https://github.com/lyswhut/lx-music-mobile/releases)。

使用常见问题请参阅[移动版常见问题](https://lyswhut.github.io/lx-music-doc/mobile/faq)。

目前本项目的原始发布地址只有 [**GitHub**](https://github.com/lyswhut/lx-music-mobile/releases),其他渠道均为第三方转载发布,与本项目无关!

为了提高使用门槛,本软件内的默认设置、UI 操作不以新手友好为目标,所以使用前建议先根据你的喜好浏览调整一遍软件设置,阅读一遍[音乐播放列表机制](https://lyswhut.github.io/lx-music-doc/mobile/faq/playlist)。

### 数据同步服务

从 v1.0.0 起,我们发布了一个独立的[数据同步服务](https://github.com/lyswhut/lx-music-sync-server#readme)。如果你有服务器,可以将其部署到服务器上作为私人多端同步服务使用,详情看该项目说明。

## 贡献代码

本项目欢迎 PR,但为了 PR 能顺利合并,需要注意以下几点:

- 对于添加新功能的 PR,建议在提交 PR 前先创建 Issue 进行说明,以确认该功能是否确实需要;
- 对于修复 bug 的 PR,请提供修复前后的说明及重现方式;
- 对于其他类型的 PR,则适当附上说明。

贡献代码步骤:

1. 参照[源码使用方法](https://lyswhut.github.io/lx-music-doc/mobile/use-source-code)设置开发环境;
2. 克隆本仓库代码并切换至 `dev` 分支进行开发;
3. 提交 PR 至 `dev` 分支。

<!--
## 用户界面

<p><img width="100%" src="https://github.com/lyswhut/lx-music-mobile/blob/master/doc/images/app.png" alt="lx-music mobile UI"></p> -->

## 项目协议

本项目基于 [Apache License 2.0](https://github.com/lyswhut/lx-music-mobile/blob/master/LICENSE) 许可证发行,以下协议是对于 Apache License 2.0 的补充,如有冲突,以以下协议为准。

---

*词语约定:本协议中的“本项目”指 LX Music(洛雪音乐)移动版项目;“使用者”指签署本协议的使用者;“官方音乐平台”指对本项目内置的包括酷我、酷狗、咪咕等音乐源的官方平台统称;“版权数据”指包括但不限于图像、音频、名字等在内的他人拥有所属版权的数据。*

### 一、数据来源

1.1 本项目的各官方平台在线数据来源原理是从其公开服务器中拉取数据(与未登录状态在官方平台 APP 获取的数据相同),经过对数据简单地筛选与合并后进行展示,因此本项目不对数据的合法性、准确性负责。

1.2 本项目本身没有获取某个音频数据的能力,本项目使用的在线音频数据来源来自软件设置内“自定义源”设置所选择的“源”返回的在线链接。例如播放某首歌,本项目所做的只是将希望播放的歌曲名、艺术家等信息传递给“源”,若“源”返回了一个链接,则本项目将认为这就是该歌曲的音频数据而进行使用,至于这是不是正确的音频数据本项目无法校验其准确性,所以使用本项目的过程中可能会出现希望播放的音频与实际播放的音频不对应或者无法播放的问题。

1.3 本项目的非官方平台数据(例如“我的列表”内列表)来自使用者本地系统或者使用者连接的同步服务,本项目不对这些数据的合法性、准确性负责。

### 二、版权数据

2.1 使用本项目的过程中可能会产生版权数据。对于这些版权数据,本项目不拥有它们的所有权。为了避免侵权,使用者务必在 **24 小时内** 清除使用本项目的过程中所产生的版权数据。

### 三、音乐平台别名

3.1 本项目内的官方音乐平台别名为本项目内对官方音乐平台的一个称呼,不包含恶意。如果官方音乐平台觉得不妥,可联系本项目更改或移除。

### 四、资源使用

4.1 本项目内使用的部分包括但不限于字体、图片等资源来源于互联网。如果出现侵权可联系本项目移除。

### 五、免责声明

5.1 由于使用本项目产生的包括由于本协议或由于使用或无法使用本项目而引起的任何性质的任何直接、间接、特殊、偶然或结果性损害(包括但不限于因商誉损失、停工、计算机故障或故障引起的损害赔偿,或任何及所有其他商业损害或损失)由使用者负责。

### 六、使用限制

6.1 本项目完全免费,且开源发布于 GitHub 面向全世界人用作对技术的学习交流。本项目不对项目内的技术可能存在违反当地法律法规的行为作保证。

6.2 **禁止在违反当地法律法规的情况下使用本项目。** 对于使用者在明知或不知当地法律法规不允许的情况下使用本项目所造成的任何违法违规行为由使用者承担,本项目不承担由此造成的任何直接、间接、特殊、偶然或结果性责任。

### 七、版权保护

7.1 音乐平台不易,请尊重版权,支持正版。

### 八、非商业性质

8.1 本项目仅用于对技术可行性的探索及研究,不接受任何商业(包括但不限于广告等)合作及捐赠。

### 九、接受协议

9.1 若你使用了本项目,即代表你接受本协议。

---

若对此有疑问请 mail to: lyswhut+qq.com (请将 `+` 替换成 `@`)


================================================
FILE: android/app/build.gradle
================================================
apply plugin: "com.android.application"
apply plugin: "org.jetbrains.kotlin.android"
apply plugin: "com.facebook.react"

import com.android.build.OutputFile
import groovy.json.JsonSlurper
import org.apache.tools.ant.taskdefs.condition.Os

/**
 * This is the configuration block to customize your React Native Android app.
 * By default you don't need to apply any configuration, just uncomment the lines you need.
 */
react {
    /* Folders */
    //   The root of your project, i.e. where "package.json" lives. Default is '..'
    // root = file("../")
    //   The folder where the react-native NPM package is. Default is ../node_modules/react-native
    // reactNativeDir = file("../node_modules/react-native")
    //   The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
    // codegenDir = file("../node_modules/@react-native/codegen")
    //   The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
    // cliFile = file("../node_modules/react-native/cli.js")

    /* Variants */
    //   The list of variants to that are debuggable. For those we're going to
    //   skip the bundling of the JS bundle and the assets. By default is just 'debug'.
    //   If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
    // debuggableVariants = ["liteDebug", "prodDebug"]

    /* Bundling */
    //   A list containing the node command and its flags. Default is just 'node'.
    // nodeExecutableAndArgs = ["node"]
    //
    //   The command to run when bundling. By default is 'bundle'
    // bundleCommand = "ram-bundle"
    //
    //   The path to the CLI configuration file. Default is empty.
    // bundleConfig = file(../rn-cli.config.js)
    //
    //   The name of the generated asset file containing your JS bundle
    // bundleAssetName = "MyApplication.android.bundle"
    //
    //   The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
    // entryFile = file("../js/MyApplication.android.js")
    //
    //   A list of extra flags to pass to the 'bundle' commands.
    //   See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
    // extraPackagerArgs = []

    /* Hermes Commands */
    //   The hermes compiler command to run. By default it is 'hermesc'
    // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
    //
    //   The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
    // hermesFlags = ["-O", "-output-source-map"]
}

/**
 * Set this to true to create four separate APKs instead of one,
 * one for each native architecture. This is useful if you don't
 * use App Bundles (https://developer.android.com/guide/app-bundle/)
 * and want to have separate APKs to upload to the Play Store.
 */
def enableSeparateBuildPerCPUArchitecture = true

/**
 * Set this to true to Run Proguard on Release builds to minify the Java bytecode.
 */
def enableProguardInReleaseBuilds = true

/**
 * The preferred build flavor of JavaScriptCore (JSC)
 *
 * For example, to use the international variant, you can use:
 * `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
 *
 * The international variant includes ICU i18n library and necessary data
 * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
 * give correct results when using with locales other than en-US. Note that
 * this variant is about 6MiB larger per architecture than default.
 */
def jscFlavor = 'org.webkit:android-jsc:+'

/**
 * Private function to get the list of Native Architectures you want to build.
 * This reads the value from reactNativeArchitectures in your gradle.properties
 * file and works together with the --active-arch-only flag of react-native run-android.
 */
def reactNativeArchitectures() {
    def value = project.getProperties().get("reactNativeArchitectures")
    return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
}

// Get version number
def getNpmPackageJson() {
    def inputFile = new File("../package.json")
    def packageJson = new JsonSlurper().parseText(inputFile.text)
    return packageJson
}
def npmPackageJson = getNpmPackageJson()
def verCode = npmPackageJson["versionCode"]
def verName = npmPackageJson["version"]
def applicationName = npmPackageJson["name"]

android {
    ndkVersion rootProject.ext.ndkVersion

    buildToolsVersion rootProject.ext.buildToolsVersion
    compileSdk rootProject.ext.compileSdkVersion

    namespace "cn.toside.music.mobile"
    defaultConfig {
        applicationId "cn.toside.music.mobile"
        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode verCode
        versionName verName
    }

    splits {
        abi {
            reset()
            enable enableSeparateBuildPerCPUArchitecture
            universalApk true  // If true, also generate a universal APK
            include (*reactNativeArchitectures())
        }
    }
    signingConfigs {
        release {
            if (project.hasProperty('MYAPP_UPLOAD_STORE_FILE')) {
                storeFile file(MYAPP_UPLOAD_STORE_FILE)
                storePassword MYAPP_UPLOAD_STORE_PASSWORD
                keyAlias MYAPP_UPLOAD_KEY_ALIAS
                keyPassword MYAPP_UPLOAD_KEY_PASSWORD
            } else {
                def keystorePropertiesFile = rootProject.file("keystore.properties")
                def keystoreProperties = new Properties()
                keystoreProperties.load(new FileInputStream(keystorePropertiesFile))

                storeFile file(keystoreProperties['storeFile'])
                storePassword keystoreProperties['storePassword']
                keyAlias keystoreProperties['keyAlias']
                keyPassword keystoreProperties['keyPassword']
            }
        }
        debug {
            storeFile file('debug.keystore')
            storePassword 'android'
            keyAlias 'androiddebugkey'
            keyPassword 'android'
        }
    }
    buildTypes {
        debug {
            signingConfig signingConfigs.debug
        }
        release {
            // Caution! In production, you need to generate your own keystore file.
            // see https://reactnative.dev/docs/signed-apk-android.
            signingConfig signingConfigs.debug
            minifyEnabled enableProguardInReleaseBuilds
            proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"

            signingConfig signingConfigs.release
        }
    }

    // applicationVariants are e.g. debug, release
    applicationVariants.all { variant ->
        variant.outputs.each { output ->
            // For each separate APK per architecture, set a unique version code as described here:
            // https://developer.android.com/studio/build/configure-apk-splits.html
            // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
            def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
            def abi = output.getFilter(OutputFile.ABI)
            if (abi == null) {  // null for the universal-debug, universal-release variants
                output.outputFileName =
                        "${applicationName}-v${defaultConfig.versionName}-universal.apk"
            } else {
                output.versionCodeOverride =
                        defaultConfig.versionCode * 1000 + versionCodes.get(abi)
                output.outputFileName =
                        "${applicationName}-v${defaultConfig.versionName}-${abi}.apk"
            }

        }
    }
}

dependencies {
    // The version of react-native is set by the React Native Gradle Plugin
    implementation("com.facebook.react:react-android")
    implementation("com.facebook.react:flipper-integration")
    // implementation "androidx.javascriptengine:javascriptengine:1.0.0-alpha07"
    // implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
    implementation 'wang.harlon.quickjs:wrapper-android:2.4.0'

  if (hermesEnabled.toBoolean()) {
        implementation("com.facebook.react:hermes-android")
    } else {
        implementation jscFlavor
    }
}

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)


================================================
FILE: android/app/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html

# Add any project specific keep options here:

-keep class com.reactnativenavigation.views.element.animators.** { *; }
# -keepclassmembers class com.reactnativenavigation.views.element.animators.** { *; }


-keep class org.jaudiotagger.tag.** { *; }


-keep public class com.dylanvann.fastimage.* {*;}
-keep public class com.dylanvann.fastimage.** {*;}
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}


================================================
FILE: android/app/src/debug/AndroidManifest.xml
================================================
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:usesCleartextTraffic="true"
        tools:targetApi="28"
        tools:ignore="GoogleAppIndexingWarning" />
</manifest>


================================================
FILE: android/app/src/main/AndroidManifest.xml
================================================
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  package="cn.toside.music.mobile">

    <!-- 获取读写外置存储权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />

    <application
      android:name=".MainApplication"
      android:label="@string/app_name"
      android:networkSecurityConfig="@xml/network_security_config"
      android:requestLegacyExternalStorage="true"
      android:icon="@mipmap/ic_launcher"
      android:roundIcon="@mipmap/ic_launcher_round"
      android:allowBackup="false"
      android:theme="@style/AppTheme"
      tools:targetApi="n">
      <activity
        android:name=".MainActivity"
        android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
        android:launchMode="singleTask"
        android:windowSoftInputMode="adjustResize"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
        <intent-filter>
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT" />
          <category android:name="android.intent.category.BROWSABLE" />
          <data android:scheme="lxmusic" android:host="*" />
        </intent-filter>
        <intent-filter android:priority="80">
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT"/>
          <category android:name="android.intent.category.OPENABLE"/>

          <data android:scheme="file"/>
          <data android:scheme="content" />
          <data android:mimeType="audio/*" />
          <data android:mimeType="vnd.android.cursor.dir/audio"/>
          <data android:mimeType="application/ogg"/>
          <data android:mimeType="application/x-ogg"/>
        </intent-filter>
        <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.APP_MUSIC" />
        </intent-filter>
        <intent-filter>
          <action android:name="com.cyanogenmod.eleven.AUDIO_PLAYER"/>
          <category android:name="android.intent.category.DEFAULT"/>
        </intent-filter>

        <intent-filter android:priority="80">
          <!-- <action android:name="android.intent.action.PICK"/> -->
          <action android:name="android.intent.action.VIEW" />
          <category android:name="android.intent.category.DEFAULT"/>
          <category android:name="android.intent.category.OPENABLE"/>

          <data android:scheme="file"/>
          <data android:scheme="content" />
          <data android:host="*"/>
          <data android:mimeType="application/octet-stream"/>

          <data android:mimeType="text/javascript"/>
          <data android:mimeType="application/x-javascript"/>
          <data android:mimeType="application/javascript"/>
          <!-- https://stackoverflow.com/a/66402243 -->
          <data android:pathPattern=".*\\.js" />
          <data android:pathPattern=".*\\..*\\.js" />
          <data android:pathPattern=".*\\..*\\..*\\.js" />
          <data android:pathSuffix=".js" tools:targetApi="s" />

          <data android:mimeType="application/json"/>
          <data android:pathPattern=".*\\.json" />
          <data android:pathPattern=".*\\..*\\.json" />
          <data android:pathPattern=".*\\..*\\..*\\.json" />
          <data android:pathSuffix=".json" tools:targetApi="s" />

          <data android:pathPattern=".*\\.lxmc" />
          <data android:pathPattern=".*\\..*\\.lxmc" />
          <data android:pathPattern=".*\\..*\\..*\\.lxmc" />
          <data android:pathSuffix=".lxmc" tools:targetApi="s" />
        </intent-filter>
      </activity>

      <!-- Define a FileProvider for API24+ -->
      <!-- note this is the authority name used by other modules like rn-fetch-blob, easy to have conflicts -->
      <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <!-- you might need the tools:replace thing to workaround rn-fetch-blob or other definitions of provider -->
        <!-- just make sure if you "replace" here that you include all the paths you are replacing *plus* the cache path we use -->
        <meta-data tools:replace="android:resource"
          android:name="android.support.FILE_PROVIDER_PATHS"
          android:resource="@xml/file_paths" />
      </provider>
    </application>
</manifest>


================================================
FILE: android/app/src/main/assets/script/user-api-preload.js
================================================
'use strict'

globalThis.lx_setup = (key, id, name, description, version, author, homepage, rawScript) => {
  delete globalThis.lx_setup
  const _nativeCall = globalThis.__lx_native_call__
  delete globalThis.__lx_native_call__
  const checkLength = (str, length = 1048576) => {
    if (typeof str == 'string' && str.length > length) throw new Error('Input too long')
    return str
  }
  const nativeFuncNames = [
    '__lx_native_call__set_timeout',
    '__lx_native_call__utils_str2b64',
    '__lx_native_call__utils_b642buf',
    '__lx_native_call__utils_str2md5',
    '__lx_native_call__utils_aes_encrypt',
    '__lx_native_call__utils_rsa_encrypt',
  ]
  const nativeFuncs = {}
  for (const name of nativeFuncNames) {
    const nativeFunc = globalThis[name]
    delete globalThis[name]
    nativeFuncs[name.replace('__lx_native_call__', '')] = (...args) => {
      for (const arg of args) checkLength(arg)
      return nativeFunc(...args)
    }
  }
  // const set_timeout = globalThis.__lx_native_call__set_timeout
  // delete globalThis.__lx_native_call__set_timeout
  // const utils_str2b64 = globalThis.__lx_native_call__utils_str2b64
  // delete globalThis.__lx_native_call__utils_str2b64
  // const utils_b642buf = globalThis.__lx_native_call__utils_b642buf
  // delete globalThis.__lx_native_call__utils_b642buf
  // const utils_str2md5 = globalThis.__lx_native_call__utils_str2md5
  // delete globalThis.__lx_native_call__utils_str2md5
  // const utils_aes_encrypt = globalThis.__lx_native_call__utils_aes_encrypt
  // delete globalThis.__lx_native_call__utils_aes_encrypt
  // const utils_rsa_encrypt = globalThis.__lx_native_call__utils_rsa_encrypt
  // delete globalThis.__lx_native_call__utils_rsa_encrypt
  const KEY_PREFIX = {
    publicKeyStart: '-----BEGIN PUBLIC KEY-----',
    publicKeyEnd: '-----END PUBLIC KEY-----',
    privateKeyStart: '-----BEGIN PRIVATE KEY-----',
    privateKeyEnd: '-----END PRIVATE KEY-----',
  }
  const RSA_PADDING = {
    OAEPWithSHA1AndMGF1Padding: 'RSA/ECB/OAEPWithSHA1AndMGF1Padding',
    NoPadding: 'RSA/ECB/NoPadding',
  }
  const AES_MODE = {
    CBC_128_PKCS7Padding: 'AES/CBC/PKCS7Padding',
    ECB_128_NoPadding: 'AES',
  }
  const nativeCall = (action, data) => {
    data = JSON.stringify(data)
    // console.log('nativeCall', action, data)
    checkLength(data, 2097152)
    _nativeCall(key, action, data)
  }

  const callbacks = new Map()
  let timeoutId = 0
  const _setTimeout = (callback, timeout = 0, ...params) => {
    if (typeof callback !== 'function') throw new Error('callback required a function')
    if (typeof timeout !== 'number' || timeout < 0) throw new Error('timeout required a number')
    if (timeoutId > 90000000000) throw new Error('max timeout')
    const id = timeoutId++
    callbacks.set(id, {
      callback(...args) {
        // eslint-disable-next-line n/no-callback-literal
        callback(...args)
      },
      params,
    })
    nativeFuncs.set_timeout(id, parseInt(timeout))
    return id
  }
  const _clearTimeout = (id) => {
    const tagret = callbacks.get(id)
    if (!tagret) return
    callbacks.delete(id)
  }
  const handleSetTimeout = (id) => {
    const tagret = callbacks.get(id)
    if (!tagret) return
    callbacks.delete(id)
    tagret.callback(...tagret.params)
  }

  // 将字节数组解码为字符串(UTF-8)
  function bytesToString(bytes) {
    let result = ''
    let i = 0
    while (i < bytes.length) {
      const byte = bytes[i]
      if (byte < 128) {
        result += String.fromCharCode(byte)
        i++
      } else if (byte >= 192 && byte < 224) {
        result += String.fromCharCode(((byte & 31) << 6) | (bytes[i + 1] & 63))
        i += 2
      } else {
        result += String.fromCharCode(((byte & 15) << 12) | ((bytes[i + 1] & 63) << 6) | (bytes[i + 2] & 63))
        i += 3
      }
    }
    return result
  }
  // 将字符串编码为字节数组(UTF-8)
  function stringToBytes(inputString) {
    const bytes = []
    for (let i = 0; i < inputString.length; i++) {
      const charCode = inputString.charCodeAt(i)
      if (charCode < 128) {
        bytes.push(charCode)
      } else if (charCode < 2048) {
        bytes.push((charCode >> 6) | 192)
        bytes.push((charCode & 63) | 128)
      } else {
        bytes.push((charCode >> 12) | 224)
        bytes.push(((charCode >> 6) & 63) | 128)
        bytes.push((charCode & 63) | 128)
      }
    }
    return bytes
  }

  const NATIVE_EVENTS_NAMES = {
    init: 'init',
    showUpdateAlert: 'showUpdateAlert',
    request: 'request',
    cancelRequest: 'cancelRequest',
    response: 'response',
    // 'utils.crypto.aesEncrypt': 'utils.crypto.aesEncrypt',
    // 'utils.crypto.rsaEncrypt': 'utils.crypto.rsaEncrypt',
    // 'utils.crypto.randomBytes': 'utils.crypto.randomBytes',
    // 'utils.crypto.md5': 'utils.crypto.md5',
    // 'utils.buffer.from': 'utils.buffer.from',
    // 'utils.buffer.bufToString': 'utils.buffer.bufToString',
    // 'utils.zlib.inflate': 'utils.zlib.inflate',
    // 'utils.zlib.deflate': 'utils.zlib.deflate',
  }
  const EVENT_NAMES = {
    request: 'request',
    inited: 'inited',
    updateAlert: 'updateAlert',
  }
  const eventNames = Object.values(EVENT_NAMES)
  const events = {
    request: null,
  }
  const allSources = ['kw', 'kg', 'tx', 'wy', 'mg', 'local']
  const supportQualitys = {
    kw: ['128k', '320k', 'flac', 'flac24bit'],
    kg: ['128k', '320k', 'flac', 'flac24bit'],
    tx: ['128k', '320k', 'flac', 'flac24bit'],
    wy: ['128k', '320k', 'flac', 'flac24bit'],
    mg: ['128k', '320k', 'flac', 'flac24bit'],
    local: [],
  }
  const supportActions = {
    kw: ['musicUrl'],
    kg: ['musicUrl'],
    tx: ['musicUrl'],
    wy: ['musicUrl'],
    mg: ['musicUrl'],
    xm: ['musicUrl'],
    local: ['musicUrl', 'lyric', 'pic'],
  }

  const verifyLyricInfo = (info) => {
    if (typeof info != 'object' || typeof info.lyric != 'string') throw new Error('failed')
    if (info.lyric.length > 51200) throw new Error('failed')
    return {
      lyric: info.lyric,
      tlyric: (typeof info.tlyric == 'string' && info.tlyric.length < 5120) ? info.tlyric : null,
      rlyric: (typeof info.rlyric == 'string' && info.rlyric.length < 5120) ? info.rlyric : null,
      lxlyric: (typeof info.lxlyric == 'string' && info.lxlyric.length < 8192) ? info.lxlyric : null,
    }
  }

  const requestQueue = new Map()
  let isInitedApi = false
  let isShowedUpdateAlert = false

  const sendNativeRequest = (url, options, callback) => {
    const requestKey = Math.random().toString()
    const requestInfo = {
      aborted: false,
      abort: () => {
        nativeCall(NATIVE_EVENTS_NAMES.cancelRequest, requestKey)
      },
    }
    requestQueue.set(requestKey, {
      callback,
      // timeout: setTimeout(() => {
      //   const req = requestQueue.get(requestKey)
      //   if (req) req.timeout = null
      //   nativeCall(NATIVE_EVENTS_NAMES.cancelRequest, requestKey)
      // }, 30000),
      requestInfo,
    })

    nativeCall(NATIVE_EVENTS_NAMES.request, { requestKey, url, options })
    return requestInfo
  }
  const handleNativeResponse = ({ requestKey, error, response }) => {
    const targetRequest = requestQueue.get(requestKey)
    if (!targetRequest) return
    requestQueue.delete(requestKey)
    targetRequest.requestInfo.aborted = true
    // if (targetRequest.timeout) clearTimeout(targetRequest.timeout)
    if (error == null) targetRequest.callback(null, response)
    else targetRequest.callback(new Error(error), null)
  }

  const handleRequest = ({ requestKey, data }) => {
    // console.log(data)
    if (!events.request) return nativeCall(NATIVE_EVENTS_NAMES.response, { requestKey, status: false, errorMessage: 'Request event is not defined' })
    try {
      events.request.call(globalThis.lx, { source: data.source, action: data.action, info: data.info }).then(response => {
        let result
        switch (data.action) {
          case 'musicUrl':
            if (typeof response != 'string' || response.length > 2048 || !/^https?:/.test(response)) throw new Error('failed')
            result = {
              source: data.source,
              action: data.action,
              data: {
                type: data.info.type,
                url: response,
              },
            }
            break
          case 'lyric':
            result = {
              source: data.source,
              action: data.action,
              data: verifyLyricInfo(response),
            }
            break
          case 'pic':
            if (typeof response != 'string' || response.length > 2048 || !/^https?:/.test(response)) throw new Error('failed')
            result = {
              source: data.source,
              action: data.action,
              data: response,
            }
            break
        }
        nativeCall(NATIVE_EVENTS_NAMES.response, { requestKey, status: true, result })
      }).catch(err => {
        // console.log('handleRequest err', err)
        nativeCall(NATIVE_EVENTS_NAMES.response, { requestKey, status: false, errorMessage: err.message })
      })
    } catch (err) {
      // console.log('handleRequest call err', err)
      nativeCall(NATIVE_EVENTS_NAMES.response, { requestKey, status: false, errorMessage: err.message })
    }
  }

  const jsCall = (action, data) => {
    // console.log('jsCall', action, data)
    switch (action) {
      case '__run_error__':
        if (!isInitedApi) isInitedApi = true
        return
      case '__set_timeout__':
        handleSetTimeout(data)
        return
      case 'request':
        handleRequest(data)
        return
      case 'response':
        handleNativeResponse(data)
        return
    }
    return 'Unknown action: ' + action
  }

  Object.defineProperty(globalThis, '__lx_native__', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: (_key, action, data) => {
      if (key != _key) return 'Invalid key'
      return data == null ? jsCall(action) : jsCall(action, JSON.parse(data))
    },
  })


  /**
   *
   * @param {*} info {
   *                    sources: {
   *                         kw: ['128k', '320k', 'flac', 'flac24bit'],
   *                         kg: ['128k', '320k', 'flac', 'flac24bit'],
   *                         tx: ['128k', '320k', 'flac', 'flac24bit'],
   *                         wy: ['128k', '320k', 'flac', 'flac24bit'],
   *                         mg: ['128k', '320k', 'flac', 'flac24bit'],
   *                     }
   *                 }
   */
  const handleInit = (info) => {
    if (!info) {
      nativeCall(NATIVE_EVENTS_NAMES.init, { info: null, status: false, errorMessage: 'Missing required parameter init info' })
      // sendMessage(NATIVE_EVENTS_NAMES.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
      return
    }
    // if (!info.status) {
    //   nativeCall(NATIVE_EVENTS_NAMES.init, { info: null, status: false, errorMessage: 'Init failed' })
    //   // sendMessage(NATIVE_EVENTS_NAMES.init, false, null, typeof info.message === 'string' ? info.message.substring(0, 100) : '')
    //   return
    // }
    const sourceInfo = {
      sources: {},
    }
    try {
      for (const source of allSources) {
        const userSource = info.sources[source]
        if (!userSource || userSource.type !== 'music') continue
        const qualitys = supportQualitys[source]
        const actions = supportActions[source]
        sourceInfo.sources[source] = {
          type: 'music',
          actions: actions.filter(a => userSource.actions.includes(a)),
          qualitys: qualitys.filter(q => userSource.qualitys.includes(q)),
        }
      }
    } catch (error) {
      // console.log(error)
      nativeCall(NATIVE_EVENTS_NAMES.init, { info: null, status: false, errorMessage: error.message })
      return
    }
    nativeCall(NATIVE_EVENTS_NAMES.init, { info: sourceInfo, status: true })
  }
  const handleShowUpdateAlert = (data, resolve, reject) => {
    if (!data || typeof data != 'object') return reject(new Error('parameter format error.'))
    if (!data.log || typeof data.log != 'string') return reject(new Error('log is required.'))
    if (data.updateUrl && !/^https?:\/\/[^\s$.?#].[^\s]*$/.test(data.updateUrl) && data.updateUrl.length > 1024) delete data.updateUrl
    if (data.log.length > 1024) data.log = data.log.substring(0, 1024) + '...'
    nativeCall(NATIVE_EVENTS_NAMES.showUpdateAlert, { log: data.log, updateUrl: data.updateUrl, name })
    resolve()
  }

  const dataToB64 = (data) => {
    if (typeof data === 'string') return nativeFuncs.utils_str2b64(data)
    else if (Array.isArray(data) || ArrayBuffer.isView(data)) return utils.buffer.bufToString(data, 'base64')
    throw new Error('data type error: ' + typeof data + ' raw data: ' + data)
  }
  const utils = {
    crypto: {
      aesEncrypt(buffer, mode, key, iv) {
        // console.log('aesEncrypt', buffer, mode, key, iv)
        switch (mode) {
          case 'aes-128-cbc':
            return utils.buffer.from(nativeFuncs.utils_aes_encrypt(dataToB64(buffer), dataToB64(key), dataToB64(iv), AES_MODE.CBC_128_PKCS7Padding), 'base64')
          case 'aes-128-ecb':
            return utils.buffer.from(nativeFuncs.utils_aes_encrypt(dataToB64(buffer), dataToB64(key), '', AES_MODE.ECB_128_NoPadding), 'base64')
          default:
            throw new Error('Binary encoding is not supported for input strings')
        }
      },
      rsaEncrypt(buffer, key) {
        // console.log('rsaEncrypt', buffer, key)
        if (typeof key !== 'string') throw new Error('Invalid RSA key')
        key = key.replace(KEY_PREFIX.publicKeyStart, '')
          .replace(KEY_PREFIX.publicKeyEnd, '')
        return utils.buffer.from(nativeFuncs.utils_rsa_encrypt(dataToB64(buffer), key, RSA_PADDING.NoPadding), 'base64')
      },
      randomBytes(size) {
        const byteArray = new Uint8Array(size)
        for (let i = 0; i < size; i++) {
          byteArray[i] = Math.floor(Math.random() * 256) // 随机生成一个字节的值(0-255)
        }
        return byteArray
      },
      md5(str) {
        if (typeof str !== 'string') throw new Error('param required a string')
        const md5 = nativeFuncs.utils_str2md5(encodeURIComponent(str))
        // console.log('md5', str, md5)
        return md5
      },
    },
    buffer: {
      from(input, encoding) {
        // console.log('buffer.from', input, encoding)
        if (typeof input === 'string') {
          switch (encoding) {
            case 'binary':
              throw new Error('Binary encoding is not supported for input strings')
            case 'base64':
              return new Uint8Array(JSON.parse(nativeFuncs.utils_b642buf(input)))
            case 'hex':
              return new Uint8Array(input.match(/.{1,2}/g).map(byte => parseInt(byte, 16)))
            default:
              return new Uint8Array(stringToBytes(input))
          }
        } else if (Array.isArray(input)) {
          return new Uint8Array(input)
        } else {
          throw new Error('Unsupported input type: ' + input + ' encoding: ' + encoding)
        }
      },
      bufToString(buf, format) {
        // console.log('buffer.bufToString', buf, format)
        if (Array.isArray(buf) || ArrayBuffer.isView(buf)) {
          switch (format) {
            case 'binary':
              // return new TextDecoder('latin1').decode(new Uint8Array(buf))
              return buf
            case 'hex':
              return new Uint8Array(buf).reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '')
            case 'base64':
              return nativeFuncs.utils_str2b64(bytesToString(Array.from(buf)))
            case 'utf8':
            case 'utf-8':
            default:
              return bytesToString(Array.from(buf))
          }
        } else {
          throw new Error('Input is not a valid buffer: ' + buf + ' format: ' + format)
        }
      },
    },
    // zlib: {
    //   inflate(buf) {
    //     return new Promise((resolve, reject) => {
    //       zlib.inflate(buf, (err, data) => {
    //         if (err) reject(new Error(err.message))
    //         else resolve(data)
    //       })
    //     })
    //   },
    //   deflate(data) {
    //     return new Promise((resolve, reject) => {
    //       zlib.deflate(data, (err, buf) => {
    //         if (err) reject(new Error(err.message))
    //         else resolve(buf)
    //       })
    //     })
    //   },
    // }),
  }

  globalThis.lx = {
    EVENT_NAMES,
    request(url, { method = 'get', timeout, headers, body, form, formData, binary }, callback) {
      let options = { headers, binary: binary === true }
      // let data
      // if (body) {
      //   data = body
      // } else if (form) {
      //   data = form
      //   // data.content_type = 'application/x-www-form-urlencoded'
      //   options.json = false
      // } else if (formData) {
      //   data = formData
      //   // data.content_type = 'multipart/form-data'
      //   options.json = false
      // }
      if (timeout && typeof timeout == 'number' && timeout > 0) options.timeout = Math.min(timeout, 60_000)

      let request = sendNativeRequest(url, { method, body, form, formData, ...options }, (err, resp) => {
        if (err) {
          callback(err, null, null)
        } else {
          callback(err, {
            statusCode: resp.statusCode,
            statusMessage: resp.statusMessage,
            headers: resp.headers,
            // bytes: resp.bytes,
            // raw: resp.raw,
            body: resp.body,
          }, resp.body)
        }
      })

      return () => {
        if (!request.aborted) request.abort()
        request = null
      }
    },
    send(eventName, data) {
      return new Promise((resolve, reject) => {
        if (!eventNames.includes(eventName)) return reject(new Error('The event is not supported: ' + eventName))
        switch (eventName) {
          case EVENT_NAMES.inited:
            if (isInitedApi) return reject(new Error('Script is inited'))
            isInitedApi = true
            handleInit(data)
            resolve()
            break
          case EVENT_NAMES.updateAlert:
            if (isShowedUpdateAlert) return reject(new Error('The update alert can only be called once.'))
            isShowedUpdateAlert = true
            handleShowUpdateAlert(data, resolve, reject)
            break
          default:
            reject(new Error('Unknown event name: ' + eventName))
        }
      })
    },
    on(eventName, handler) {
      if (!eventNames.includes(eventName)) return Promise.reject(new Error('The event is not supported: ' + eventName))
      switch (eventName) {
        case EVENT_NAMES.request:
          events.request = handler
          break
        default: return Promise.reject(new Error('The event is not supported: ' + eventName))
      }
      return Promise.resolve()
    },
    utils,
    currentScriptInfo: {
      name,
      description,
      version,
      author,
      homepage,
      rawScript,
    },
    version: '2.0.0',
    env: 'mobile',
  }

  globalThis.setTimeout = _setTimeout
  globalThis.clearTimeout = _clearTimeout

  const freezeObject = (obj) => {
    if (typeof obj != 'object') return
    Object.freeze(obj)
    for (const subObj of Object.values(obj)) freezeObject(subObj)
  }
  freezeObject(globalThis.lx)

  const _toString = Function.prototype.toString
  // eslint-disable-next-line no-extend-native
  Function.prototype.toString = function() {
    return Object.getOwnPropertyDescriptors(this).name.configurable
      ? _toString.apply(this)
      : `function ${this.name}() { [native code] }`
  }
  // eslint-disable-next-line no-eval
  globalThis.eval = function() {
    throw new Error('eval is not available')
  }
  const proxyFunctionConstructor = new Proxy(Function.prototype.constructor, {
    apply() {
      throw new Error('Dynamic code execution is not allowed.')
    },
    construct() {
      throw new Error('Dynamic code execution is not allowed.')
    },
  })
  // eslint-disable-next-line no-extend-native
  Object.defineProperty(Function.prototype, 'constructor', {
    value: proxyFunctionConstructor,
    writable: false,
    configurable: false,
    enumerable: false,
  })
  globalThis.Function = proxyFunctionConstructor
  // globalThis.Function = function() {
  //   throw new Error('Function is not available')
  // }

  const excludes = [
    Function.prototype.toString,
    Function.prototype.toLocaleString,
    Object.prototype.toString,
  ]
  const freezeObjectProperty = (obj, freezedObj = new Set()) => {
    if (obj == null) return
    switch (typeof obj) {
      case 'object':
      case 'function':
        if (freezedObj.has(obj)) return
        // Object.freeze(obj)
        freezedObj.add(obj)
        for (const [name, { ...config }] of Object.entries(Object.getOwnPropertyDescriptors(obj))) {
          if (!excludes.includes(config.value)) {
            if (config.writable) config.writable = false
            if (config.configurable) config.configurable = false
            Object.defineProperty(obj, name, config)
          }
          freezeObjectProperty(config.value, freezedObj)
        }
    }
  }
  freezeObjectProperty(globalThis)

  console.log('Preload finished.')
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/MainActivity.java
================================================
package cn.toside.music.mobile;

import com.reactnativenavigation.NavigationActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.facebook.react.defaults.DefaultReactActivityDelegate;

public class MainActivity extends NavigationActivity {




}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/MainApplication.java
================================================
package cn.toside.music.mobile;

import com.facebook.react.PackageList;
import com.facebook.react.flipper.ReactNativeFlipper;
import com.reactnativenavigation.NavigationApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
import com.reactnativenavigation.react.NavigationReactNativeHost;
import java.util.List;

import cn.toside.music.mobile.cache.CachePackage;
import cn.toside.music.mobile.crypto.CryptoPackage;
import cn.toside.music.mobile.lyric.LyricPackage;
import cn.toside.music.mobile.userApi.UserApiPackage;
import cn.toside.music.mobile.utils.UtilsPackage;

public class MainApplication extends NavigationApplication {

  private final ReactNativeHost mReactNativeHost =
      new NavigationReactNativeHost(this) {
        @Override
        public boolean getUseDeveloperSupport() {
          return BuildConfig.DEBUG;
        }

        @Override
        protected List<ReactPackage> getPackages() {
          @SuppressWarnings("UnnecessaryLocalVariable")
          List<ReactPackage> packages = new PackageList(this).getPackages();
          // Packages that cannot be autolinked yet can be added manually here, for example:
          // packages.add(new MyReactNativePackage());
          packages.add(new CachePackage());
          packages.add(new LyricPackage());
          packages.add(new UtilsPackage());
          packages.add(new CryptoPackage());
          packages.add(new UserApiPackage());
          return packages;
        }

        @Override
        protected String getJSMainModuleName() {
          return "index";
        }

        @Override
        protected boolean isNewArchEnabled() {
          return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
        }

        @Override
        protected Boolean isHermesEnabled() {
          return BuildConfig.IS_HERMES_ENABLED;
        }
      };

  @Override
  public ReactNativeHost getReactNativeHost() {
    return mReactNativeHost;
  }

  @Override
  public void onCreate() {
    super.onCreate();

    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      // If you opted-in for the New Architecture, we load the native entry point for this app.
      DefaultNewArchitectureEntryPoint.load();
    }
    ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/cache/CacheClearAsyncTask.java
================================================
package cn.toside.music.mobile.cache;

import android.os.AsyncTask;

import com.facebook.react.bridge.Promise;

// https://github.com/midas-gufei/react-native-clear-app-cache/tree/master/android/src/main/java/com/learnta/clear
public class CacheClearAsyncTask extends AsyncTask<Integer,Integer,String> {
  public CacheModule cacheModule = null;
  public Promise promise;
  public CacheClearAsyncTask(CacheModule clearCacheModule, Promise promise) {
    super();
    this.cacheModule = clearCacheModule;
    this.promise = promise;
  }

  @Override
  protected void onPreExecute() {
    super.onPreExecute();
  }

  @Override
  protected void onPostExecute(String s) {
    super.onPostExecute(s);
    promise.resolve(null);
  }

  @Override
  protected String doInBackground(Integer... params) {
    cacheModule.clearCache();
    return null;
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/cache/CacheModule.java
================================================
package cn.toside.music.mobile.cache;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

import java.io.File;

import static cn.toside.music.mobile.cache.Utils.clearCacheFolder;
import static cn.toside.music.mobile.cache.Utils.getDirSize;
import static cn.toside.music.mobile.cache.Utils.isMethodsCompat;

// https://github.com/midas-gufei/react-native-clear-app-cache/tree/master/android/src/main/java/com/learnta/clear
public class CacheModule extends ReactContextBaseJavaModule {
  private final CacheModule cacheModule;

  CacheModule(ReactApplicationContext reactContext) {
    super(reactContext);
    this.cacheModule = this;
  }

  @Override
  public String getName() {
    return "CacheModule";
  }


  @ReactMethod
  public void getAppCacheSize(Promise promise) {
    // 计算缓存大小
    long fileSize = 0;
    // File filesDir = getReactApplicationContext().getFilesDir();// /data/data/package_name/files
    File cacheDir = getReactApplicationContext().getCacheDir();// /data/data/package_name/cache
    // fileSize += getDirSize(filesDir);
    fileSize += getDirSize(cacheDir);
    // 2.2版本才有将应用缓存转移到sd卡的功能
    if (isMethodsCompat(android.os.Build.VERSION_CODES.FROYO)) {
      File externalCacheDir = Utils.getExternalCacheDir(getReactApplicationContext());//"<sdcard>/Android/data/<package_name>/cache/"
      fileSize += getDirSize(externalCacheDir);
    }

    promise.resolve(String.valueOf(fileSize));
  }

  //清除缓存
  @ReactMethod
  public void clearAppCache(Promise promise) {
    CacheClearAsyncTask asyncTask = new CacheClearAsyncTask(cacheModule, promise);
    asyncTask.execute(10);
  }

  /**
   * 清除app缓存
   */
  public void clearCache() {

    getReactApplicationContext().deleteDatabase("webview.db");
    getReactApplicationContext().deleteDatabase("webview.db-shm");
    getReactApplicationContext().deleteDatabase("webview.db-wal");
    getReactApplicationContext().deleteDatabase("webviewCache.db");
    getReactApplicationContext().deleteDatabase("webviewCache.db-shm");
    getReactApplicationContext().deleteDatabase("webviewCache.db-wal");
    //清除数据缓存
    // clearCacheFolder(getReactApplicationContext().getFilesDir(), System.currentTimeMillis());
    clearCacheFolder(getReactApplicationContext().getCacheDir(), System.currentTimeMillis());
    //2.2版本才有将应用缓存转移到sd卡的功能
    if (isMethodsCompat(android.os.Build.VERSION_CODES.FROYO)) {
      clearCacheFolder(Utils.getExternalCacheDir(getReactApplicationContext()), System.currentTimeMillis());
    }

  }

}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/cache/CachePackage.java
================================================
package cn.toside.music.mobile.cache;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CachePackage implements ReactPackage {

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    return Arrays.<NativeModule>asList(new CacheModule(reactContext));
  }

}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/cache/Utils.java
================================================
package cn.toside.music.mobile.cache;

import android.content.Context;

import java.io.File;

// https://github.com/midas-gufei/react-native-clear-app-cache/tree/master/android/src/main/java/com/learnta/clear
public class Utils {
  /**
   * 获取目录文件大小
   *
   * @param dir
   * @return
   */
  static public long getDirSize(File dir) {
    if (dir == null || !dir.isDirectory()) return 0;
    long dirSize = 0;
    File[] files = dir.listFiles();
    if (files == null) return dirSize;
    for (File file : files) {
      if (file.isFile()) {
        dirSize += file.length();
      } else if (file.isDirectory()) {
        dirSize += file.length();
        dirSize += getDirSize(file); // 递归调用继续统计
      }
    }
    return dirSize;
  }

  /**
   * 判断当前版本是否兼容目标版本的方法
   *
   * @param VersionCode
   * @return
   */
  static public boolean isMethodsCompat(int VersionCode) {
    int currentVersion = android.os.Build.VERSION.SDK_INT;
    return currentVersion >= VersionCode;
  }

  static public File getExternalCacheDir(Context context) {

    // return context.getExternalCacheDir(); API level 8

    // e.g. "<sdcard>/Android/data/<package_name>/cache/"

    return context.getExternalCacheDir();
  }

  /**
   * 清除缓存目录
   * 目录
   * 当前系统时间
   */
  static public int clearCacheFolder(File dir, long curTime) {
    int deletedFiles = 0;
    if (dir == null || !dir.isDirectory()) return deletedFiles;
    File[] files = dir.listFiles();
    if (files == null) return deletedFiles;
    try {
      for (File child : files) {
        if (child.isDirectory()) {
          deletedFiles += clearCacheFolder(child, curTime);
        }
        if (child.lastModified() < curTime) {
          if (child.delete()) {
            deletedFiles++;
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }
    return deletedFiles;
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/AES.java
================================================
package cn.toside.music.mobile.crypto;

import android.util.Base64;

import java.nio.charset.StandardCharsets;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class AES {
  private static final String AES_MODE_CBC_PKCS7Padding = "AES/CBC/PKCS7Padding";
  private static final String AES_MODE_ECB_NoPadding = "AES/ECB/NoPadding";

  private static byte[] decodeBase64(String data) {
    return Base64.decode(data, Base64.DEFAULT);
  }

  private static String encodeBase64(byte[] data) {
    return new String(Base64.encode(data, Base64.NO_WRAP), StandardCharsets.UTF_8);
  }

  public static String encrypt(byte[] data, byte[] key, byte[] iv, String mode) {
    String encryptedBase64 = "";
    try {
      Cipher cipher = Cipher.getInstance(mode);
      SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
      byte[] finalIvs = new byte[16];
      int len = Math.min(iv.length, 16);
      System.arraycopy(iv, 0, finalIvs, 0, len);
      IvParameterSpec ivps = new IvParameterSpec(finalIvs);
      cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivps);
      encryptedBase64 = encodeBase64(cipher.doFinal(data));
    } catch (Exception e) {
      e.printStackTrace();
    }

    return encryptedBase64;
  }

  public static String encrypt(byte[] data, byte[] key, String mode) {
    String encryptedBase64 = "";
    try {
      Cipher cipher = Cipher.getInstance(mode);
      SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
      cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
      encryptedBase64 = encodeBase64(cipher.doFinal(data));
    } catch (Exception e) {
      e.printStackTrace();
    }

    return encryptedBase64;
  }

  public static String encrypt(String data, String key, String iv, String mode) {
    return "".equals(iv) ? encrypt(decodeBase64(data), decodeBase64(key), mode)
      : encrypt(decodeBase64(data), decodeBase64(key), decodeBase64(iv), mode);
  }


  public static String decrypt(byte[] data, byte[] key, byte[] iv, String mode) {
    String decryptedString = "";
    try {
      Cipher cipher = Cipher.getInstance(mode);
      SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
      byte[] finalIvs = new byte[16];
      int len = Math.min(iv.length, 16);
      System.arraycopy(iv, 0, finalIvs, 0, len);
      IvParameterSpec ivps = new IvParameterSpec(finalIvs);
      cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivps);
      decryptedString = new String(cipher.doFinal(data), StandardCharsets.UTF_8);
    } catch (Exception e) {
      e.printStackTrace();
    }

    return decryptedString;
  }

  public static String decrypt(byte[] data, byte[] key, String mode) {
    String decryptedString = "";
    try {
      Cipher cipher = Cipher.getInstance(mode);
      SecretKeySpec secretKeySpec = new SecretKeySpec(key, "AES");
      cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
      decryptedString = new String(cipher.doFinal(data), StandardCharsets.UTF_8);
    } catch (Exception e) {
      e.printStackTrace();
    }

    return decryptedString;
  }

  public static String decrypt(String data, String key, String iv, String mode) {
    return "".equals(iv) ? decrypt(decodeBase64(data), decodeBase64(key), mode)
      : decrypt(decodeBase64(data), decodeBase64(key), decodeBase64(iv), mode);
  }

}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/CryptoModule.java
================================================
package cn.toside.music.mobile.crypto;

import android.util.Base64;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;

public class CryptoModule extends ReactContextBaseJavaModule {
  private final ReactApplicationContext reactContext;

  CryptoModule(ReactApplicationContext reactContext) {
    super(reactContext);
    this.reactContext = reactContext;
  }

  @Override
  public String getName() {
    return "CryptoModule";
  }

  @ReactMethod
  public void generateRsaKey(Promise promise) {
    KeyPair kp = RSA.getKeyPair();
    String publicKeyBytesBase64 = new String(Base64.encode(kp.getPublic().getEncoded(), Base64.DEFAULT));

    KeyFactory keyFac;
    try {
      keyFac = KeyFactory.getInstance("RSA");
    } catch (NoSuchAlgorithmException e) {
      promise.reject("-1", e.toString());
      return;
    }
    KeySpec keySpec = new PKCS8EncodedKeySpec(kp.getPrivate().getEncoded());
    Key key;
    try {
      key = keyFac.generatePrivate(keySpec);
    } catch (InvalidKeySpecException e) {
      promise.reject("-1", e.toString());
      return;
    }
    String privateKeyBytesBase64 = new String(Base64.encode(key.getEncoded(), Base64.DEFAULT));
    WritableMap params = Arguments.createMap();
    params.putString("publicKey", publicKeyBytesBase64);
    params.putString("privateKey", privateKeyBytesBase64);
    promise.resolve(params);
  }

  @ReactMethod
  public void rsaEncrypt(String text, String key, String padding, Promise promise) {
    promise.resolve(RSA.encryptRSAToString(text, key, padding));
    //    TaskRunner taskRunner = new TaskRunner();
    //    try {
    //      taskRunner.executeAsync(new GzipModule.UnGzip(source, target, force), (String errMessage) -> {
    //        if ("".equals(errMessage)) {
    //          promise.resolve(null);
    //        } else promise.reject("-2", errMessage);
    //      });
    //    } catch (RuntimeException err) {
    //      promise.reject("-2", err.getMessage());
    //    }
  }

  @ReactMethod
  public void rsaDecrypt(String text, String key, String padding, Promise promise) {
    promise.resolve(RSA.decryptRSAToString(text, key, padding));
    //    TaskRunner taskRunner = new TaskRunner();
    //    try {
    //      taskRunner.executeAsync(new GzipModule.UnGzip(source, target, force), (String errMessage) -> {
    //        if ("".equals(errMessage)) {
    //          promise.resolve(null);
    //        } else promise.reject("-2", errMessage);
    //      });
    //    } catch (RuntimeException err) {
    //      promise.reject("-2", err.getMessage());
    //    }
  }

  @ReactMethod(isBlockingSynchronousMethod = true)
  public String rsaEncryptSync(String text, String key, String padding) {
    return RSA.encryptRSAToString(text, key, padding);
  }

  @ReactMethod(isBlockingSynchronousMethod = true)
  public String rsaDecryptSync(String text, String key, String padding) {
    return RSA.decryptRSAToString(text, key, padding);
  }

  @ReactMethod
  public void aesEncrypt(String text, String key, String iv, String mode, Promise promise) {
    promise.resolve(AES.encrypt(text, key, iv, mode));
    //    TaskRunner taskRunner = new TaskRunner();
    //    try {
    //      taskRunner.executeAsync(new GzipModule.UnGzip(source, target, force), (String errMessage) -> {
    //        if ("".equals(errMessage)) {
    //          promise.resolve(null);
    //        } else promise.reject("-2", errMessage);
    //      });
    //    } catch (RuntimeException err) {
    //      promise.reject("-2", err.getMessage());
    //    }
  }

  @ReactMethod
  public void aesDecrypt(String text, String key, String iv, String mode, Promise promise) {
    promise.resolve(AES.decrypt(text, key, iv, mode));
    //    TaskRunner taskRunner = new TaskRunner();
    //    try {
    //      taskRunner.executeAsync(new GzipModule.UnGzip(source, target, force), (String errMessage) -> {
    //        if ("".equals(errMessage)) {
    //          promise.resolve(null);
    //        } else promise.reject("-2", errMessage);
    //      });
    //    } catch (RuntimeException err) {
    //      promise.reject("-2", err.getMessage());
    //    }
  }

  @ReactMethod(isBlockingSynchronousMethod = true)
  public String aesEncryptSync(String text, String key, String iv, String mode) {
    return AES.encrypt(text, key, iv, mode);
  }

  @ReactMethod(isBlockingSynchronousMethod = true)
  public String aesDecryptSync(String text, String key, String iv, String mode) {
    return AES.decrypt(text, key, iv, mode);
  }

}



================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/CryptoPackage.java
================================================
package cn.toside.music.mobile.crypto;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class CryptoPackage implements ReactPackage {

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    return Arrays.<NativeModule>asList(new CryptoModule(reactContext));
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/RSA.java
================================================
package cn.toside.music.mobile.crypto;

import android.util.Base64;

import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.spec.KeySpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

import javax.crypto.Cipher;

public class RSA {
  static final String PADDING_OAEPWithSHA1AndMGF1Padding = "RSA/ECB/OAEPWithSHA1AndMGF1Padding";
  static final String PADDING_NoPadding = "RSA/ECB/NoPadding";

  // https://stackoverflow.com/a/40978042
//  public void testEncryptData(String dataToEncrypt) throws NoSuchAlgorithmException, InvalidKeySpecException {
//    // generate a new public/private key pair to test with (note. you should only do this once and keep them!)
//    KeyPair kp = getKeyPair();
//
//    PublicKey publicKey = kp.getPublic();
//    byte[] publicKeyBytes = publicKey.getEncoded();
//    String publicKeyBytesBase64 = new String(Base64.encode(publicKeyBytes, Base64.DEFAULT));
//    Log.d("RSATest", "publicKeyBytesBase64: " + publicKeyBytesBase64);
//
//    PrivateKey privateKey = kp.getPrivate();
//    KeyFactory keyFac = KeyFactory.getInstance("RSA");
//    KeySpec keySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
//    Key key = keyFac.generatePrivate(keySpec);
//
//    StringBuilder sb = new StringBuilder();
//    sb.append("-----BEGIN PRIVATE KEY-----");
//    sb.append(new String(Base64.encode(key.getEncoded(), Base64.DEFAULT)));
//    sb.append("-----END PRIVATE KEY-----");
//    Log.d("RSATest", "privateKeyBytesBase64: " + sb);
//
//    //    return new String(Base64.encode(sb.toString().getBytes()));
//
//    byte[] privateKeyBytes = privateKey.getEncoded();
//    String privateKeyBytesBase64 = new String(Base64.encode(privateKeyBytes, Base64.DEFAULT));
//    Log.d("RSATest", "privateKeyBytesBase64: " + privateKeyBytesBase64);
//
//    // test encryption
//    String encrypted = encryptRSAToString(dataToEncrypt, publicKeyBytesBase64);
//
//    Log.d("RSATest", "encrypted: " + encrypted);
//
//    // test decryption
//    String decrypted = decryptRSAToString(encrypted, privateKeyBytesBase64);
//
//    Log.d("RSATest", "decrypted: " + decrypted);
//  }

  public static KeyPair getKeyPair() {
    KeyPair kp = null;
    try {
      KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
      kpg.initialize(2048);
      kp = kpg.generateKeyPair();
    } catch (Exception e) {
      e.printStackTrace();
    }

    return kp;
  }

   public static String encryptRSAToString(String decryptedBase64, String publicKey, String padding) {
    String encryptedBase64 = "";
    try {
      KeyFactory keyFac = KeyFactory.getInstance("RSA");
      KeySpec keySpec = new X509EncodedKeySpec(Base64.decode(publicKey.trim().getBytes(), Base64.DEFAULT));
      Key key = keyFac.generatePublic(keySpec);

      // get an RSA cipher object and print the provider
      final Cipher cipher = Cipher.getInstance(padding);
      // encrypt the plain text using the public key
      cipher.init(Cipher.ENCRYPT_MODE, key);

      byte[] encryptedBytes = cipher.doFinal(Base64.decode(decryptedBase64, Base64.DEFAULT));
      encryptedBase64 = new String(Base64.encode(encryptedBytes, Base64.NO_WRAP));
    } catch (Exception e) {
      e.printStackTrace();
    }

    return encryptedBase64;
  }

  public static String decryptRSAToString(String encryptedBase64, String privateKey, String padding) {

    String decryptedString = "";
    try {
      KeyFactory keyFac = KeyFactory.getInstance("RSA");
      KeySpec keySpec = new PKCS8EncodedKeySpec(Base64.decode(privateKey.trim().getBytes(), Base64.DEFAULT));
      Key key = keyFac.generatePrivate(keySpec);

      // get an RSA cipher object and print the provider
      final Cipher cipher = Cipher.getInstance(padding);
      // encrypt the plain text using the public key
      cipher.init(Cipher.DECRYPT_MODE, key);

      byte[] encryptedBytes = Base64.decode(encryptedBase64, Base64.DEFAULT);
      byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
      decryptedString = new String(decryptedBytes);
    } catch (Exception e) {
      e.printStackTrace();
    }

    return decryptedString;
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/Lyric.java
================================================
package cn.toside.music.mobile.lyric;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;

public class Lyric extends LyricPlayer {
  LyricView lyricView = null;
  LyricEvent lyricEvent = null;
  ReactApplicationContext reactAppContext;

  boolean isRunPlayer = false;
  // String lastText = "LX Music ^-^";
  int lastLine = 0;
  List lines = new ArrayList();
  boolean isShowTranslation;
  boolean isShowRoma;
  boolean isShowLyricView = false;
  boolean isSendLyricTextEvent = false;
  boolean isScreenOff = false;
  String lyricText = "";
  String translationText = "";
  String romaLyricText = "";

  Lyric(ReactApplicationContext reactContext, boolean isShowTranslation, boolean isShowRoma, float playbackRate) {
    this.reactAppContext = reactContext;
    this.isShowTranslation = isShowTranslation;
    this.isShowRoma = isShowRoma;
    this.playbackRate = playbackRate;
    registerScreenBroadcastReceiver();
    // checkA2DPConnection(reactContext);
  }

  private void registerScreenBroadcastReceiver() {
    final IntentFilter theFilter = new IntentFilter();
    /** System Defined Broadcast */
    theFilter.addAction(Intent.ACTION_SCREEN_ON);
    theFilter.addAction(Intent.ACTION_SCREEN_OFF);

    BroadcastReceiver screenOnOffReceiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {
        String strAction = intent.getAction();

        switch (Objects.requireNonNull(strAction)) {
          case Intent.ACTION_SCREEN_OFF:
            Log.d("Lyric", "ACTION_SCREEN_OFF");
            handleScreenOff();
            break;
          case Intent.ACTION_SCREEN_ON:
            Log.d("Lyric", "ACTION_SCREEN_ON");
            handleScreenOn();
            break;
        }
      }
    };

    reactAppContext.registerReceiver(screenOnOffReceiver, theFilter);
  }

  // private void checkA2DPConnection(Context context) {
  //   BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

  //   if (bluetoothAdapter != null && bluetoothAdapter.isEnabled()) {
  //     bluetoothAdapter.getProfileProxy(context, new BluetoothProfile.ServiceListener() {
  //       @Override
  //       public void onServiceConnected(int profile, BluetoothProfile proxy) {
  //         if (profile == BluetoothProfile.A2DP) {
  //           List<BluetoothDevice> connectedDevices = proxy.getConnectedDevices();
  //           if (!connectedDevices.isEmpty()) {
  //             System.out.println("已连接的 A2DP 媒体设备:");
  //             for (BluetoothDevice device : connectedDevices) {
  //               System.out.println("设备名称: " + "地址: " + device.getAddress());
  //             }
  //           } else {
  //             System.out.println("没有连接的 A2DP 媒体设备");
  //           }
  //         }
  //         bluetoothAdapter.closeProfileProxy(profile, proxy);
  //       }

  //       @Override
  //       public void onServiceDisconnected(int profile) {
  //         // 服务断开时的处理
  //         System.out.println("蓝牙服务断开时的处理");
  //       }
  //     }, BluetoothProfile.A2DP);
  //   } else {
  //     System.out.println("蓝牙未开启或设备不支持蓝牙");
  //   }
  // }

  private boolean isDisableAutoPause() {
    return !isRunPlayer || isSendLyricTextEvent;
  }
  private void handleScreenOff() {
    isScreenOff = true;
    if (isDisableAutoPause()) return;
    setTempPause(true);
  }

  private void handleScreenOn() {
    isScreenOff = false;
    if (isDisableAutoPause()) return;
    if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent);
    lyricView.runOnUiThread(() -> {
      handleGetCurrentLyric(lastLine);
      setTempPause(false);
    });
  }

  private void pausePlayer() {
    if (!isRunPlayer || isShowLyricView || isSendLyricTextEvent) return;
    isRunPlayer = false;
    this.pause();
  }

  private void setCurrentLyric(String lyric, ArrayList<String> extendedLyrics) {
    if (isShowLyricView && !isScreenOff && lyricView != null) {
      lyricView.setLyric(lyric, extendedLyrics);
    }
    if (isSendLyricTextEvent) {
      WritableMap params = Arguments.createMap();
      params.putString("text", lyric);
      params.putArray("extendedLyrics", Arguments.makeNativeArray(extendedLyrics));
      lyricEvent.sendEvent(lyricEvent.LYRIC_Line_PLAY, params);
    }
  }
  private void handleGetCurrentLyric(int lineNum) {
    lastLine = lineNum;
    if (lineNum >= 0 && lineNum < lines.size()) {
      HashMap line = (HashMap) lines.get(lineNum);
      if (line != null) {
        setCurrentLyric((String) line.get("text"), (ArrayList<String>) line.get("extendedLyrics"));
        return;
      }
    }
    setCurrentLyric("", new ArrayList<>(0));
  }

  public void setSendLyricTextEvent(boolean isSend) {
    if (isSendLyricTextEvent == isSend) return;
    isSendLyricTextEvent = isSend;
    if (isSend) {
      if (lyricEvent == null) lyricEvent = new LyricEvent(reactAppContext);
      isRunPlayer = true;
    } else {
      pausePlayer();
    }
  }

  public void showDesktopLyric(Bundle options, Promise promise) {
    if (isShowLyricView) return;
    if (lyricEvent == null) lyricEvent = new LyricEvent(reactAppContext);
    isShowLyricView = true;
    if (lyricView == null) lyricView = new LyricView(reactAppContext, lyricEvent);
    try {
      lyricView.showLyricView(options);
    } catch (Exception e) {
      promise.reject(e);
      Log.e("Lyric", e.getMessage());
      return;
    }
    isRunPlayer = true;
    promise.resolve(null);
  }

  public void hideDesktopLyric() {
    if (!isShowLyricView) return;
    isShowLyricView = false;
    pausePlayer();
    if (lyricView != null) {
      lyricView.destroy();
      lyricView = null;
    }
  }

  private void refreshLyric() {
    if (!isRunPlayer) return;
    ArrayList<String> extendedLyrics = new ArrayList<>(2);
    if (isShowTranslation && !"".equals(translationText)) extendedLyrics.add(translationText);
    if (isShowRoma && !"".equals(romaLyricText)) extendedLyrics.add(romaLyricText);
    super.setLyric(lyricText, extendedLyrics);
  }

  public void setLyric(String lyric, String translation, String romaLyric) {
    lyricText = lyric;
    translationText = translation;
    romaLyricText = romaLyric;
    refreshLyric();
  }

  @Override
  public void onSetLyric(List lines) {
    this.lines = lines;
    handleGetCurrentLyric(-1);
    // for (int i = 0; i < lines.size(); i++) {
    //   HashMap line = (HashMap) lines.get(i);
    //   Log.d("Lyric", "onSetLyric: " +(String) line.get("text") + " " + line.get("extendedLyrics"));
    // }
  }

  @Override
  public void onPlay(int lineNum) {
    handleGetCurrentLyric(lineNum);
    // Log.d("Lyric", lineNum + " " + text + " " + (String) line.get("translation"));
  }

  public void pauseLyric() {
    pause();
    if (!isRunPlayer) return;
    handleGetCurrentLyric(-1);
  }

  public void lockLyric() {
    if (lyricView == null) return;
    lyricView.lockView();
  }

  public void unlockLyric() {
    if (lyricView == null) return;
    lyricView.unlockView();
  }

  public void setMaxLineNum(int maxLineNum) {
    if (lyricView == null) return;
    lyricView.setMaxLineNum(maxLineNum);
  }

  public void setWidth(int width) {
    if (lyricView == null) return;
    lyricView.setWidth(width);
  }

  public void setSingleLine(boolean singleLine) {
    if (lyricView == null) return;
    lyricView.setSingleLine(singleLine);
  }

  public void setShowToggleAnima(boolean showToggleAnima) {
    if (lyricView == null) return;
    lyricView.setShowToggleAnima(showToggleAnima);
  }

  public void toggleTranslation(boolean isShowTranslation) {
    this.isShowTranslation = isShowTranslation;
    refreshLyric();
  }

  public void toggleRoma(boolean isShowRoma) {
    this.isShowRoma = isShowRoma;
    refreshLyric();
  }

  public void setPlayedColor(String unplayColor, String playedColor, String shadowColor) {
    if (lyricView == null) return;
    lyricView.setColor(unplayColor, playedColor, shadowColor);
  }

  public void setAlpha(float alpha) {
    if (lyricView == null) return;
    lyricView.setAlpha(alpha);
  }

  public void setTextSize(float size) {
    if (lyricView == null) return;
    lyricView.setTextSize(size);
  }

  public void setLyricTextPosition(String positionX, String positionY) {
    if (lyricView == null) return;
    lyricView.setLyricTextPosition(positionX, positionY);
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricEvent.java
================================================
package cn.toside.music.mobile.lyric;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;

public class LyricEvent {
  final String SET_VIEW_POSITION = "set-position";
  final String LYRIC_Line_PLAY = "lyric-line-play";

  private final ReactApplicationContext reactContext;
  LyricEvent(ReactApplicationContext reactContext) { this.reactContext = reactContext; }

  public void sendEvent(String eventName, @Nullable WritableMap params) {
    // Log.d("Lyric", "senEvent: " + eventName);
    reactContext
      .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
      .emit(eventName, params);
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricModule.java
================================================
package cn.toside.music.mobile.lyric;

import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;

public class LyricModule extends ReactContextBaseJavaModule {
  private final ReactApplicationContext reactContext;
  Lyric lyric;
  // final Map<String, Object> constants = new HashMap<>();

  boolean isShowTranslation = false;
  boolean isShowRoma = false;
  float playbackRate = 1;

  private int listenerCount = 0;

  LyricModule(ReactApplicationContext reactContext) {
    super(reactContext);
    this.reactContext = reactContext;

    // constants.put("THEME_GREEN", "#07c556");
    // constants.put("THEME_YELLOW", "#fffa12");
    // constants.put("THEME_BLUE", "#19b5fe");
    // constants.put("THEME_RED", "#ff1222");
    // constants.put("THEME_PINK", "#f1828d");
    // constants.put("THEME_PURPLE", "#c851d4");
    // constants.put("THEME_ORANGE", "#fffa12");
    // constants.put("THEME_GREY", "#bdc3c7");
  }

  @Override
  public String getName() {
    return "LyricModule";
  }

//  @Override
//  public Map<String, Object> getConstants() {
//    return constants;
//  }

  @ReactMethod
  public void addListener(String eventName) {
    if (listenerCount == 0) {
      // Set up any upstream listeners or background tasks as necessary
    }

    listenerCount += 1;
  }

  @ReactMethod
  public void removeListeners(Integer count) {
    listenerCount -= count;
    if (listenerCount == 0) {
      // Remove upstream listeners, stop unnecessary background tasks
    }
  }

  @ReactMethod
  public void showDesktopLyric(ReadableMap data, Promise promise) {
    if (lyric == null) lyric = new Lyric(reactContext, isShowTranslation, isShowRoma, playbackRate);
    lyric.showDesktopLyric(Arguments.toBundle(data), promise);
  }

  @ReactMethod
  public void hideDesktopLyric(Promise promise) {
    if (lyric != null) lyric.hideDesktopLyric();
    promise.resolve(null);
  }

  @ReactMethod
  public void setSendLyricTextEvent(boolean isSend, Promise promise) {
    if (lyric == null) lyric = new Lyric(reactContext, isShowTranslation, isShowRoma, playbackRate);
    lyric.setSendLyricTextEvent(isSend);
    promise.resolve(null);
  }


  @ReactMethod
  public void setLyric(String lyric, String translation, String romaLyric, Promise promise) {
    // Log.d("Lyric", "set lyric: " + lyric);
    // Log.d("Lyric", "set lyric translation: " + translation);
    if (this.lyric != null) this.lyric.setLyric(lyric, translation, romaLyric);
    promise.resolve(null);
  }

  @ReactMethod
  public void setPlaybackRate(float playbackRate, Promise promise) {
    this.playbackRate = playbackRate;
    if (lyric != null) lyric.setPlaybackRate(playbackRate);
    promise.resolve(null);
  }

  @ReactMethod
  public void toggleTranslation(boolean isShowTranslation, Promise promise) {
    this.isShowTranslation = isShowTranslation;
    if (lyric != null) lyric.toggleTranslation(isShowTranslation);
    promise.resolve(null);
  }

  @ReactMethod
  public void toggleRoma(boolean isShowRoma, Promise promise) {
    this.isShowRoma = isShowRoma;
    if (lyric != null) lyric.toggleRoma(isShowRoma);
    promise.resolve(null);
  }

  @ReactMethod
  public void play(int time, Promise promise) {
    Log.d("Lyric", "play lyric: " + time);
    if (lyric != null) lyric.play(time);
    promise.resolve(null);
  }

  @ReactMethod
  public void pause(Promise promise) {
    Log.d("Lyric", "play pause");
    if (lyric != null) lyric.pauseLyric();
    promise.resolve(null);
  }

  @ReactMethod
  public void toggleLock(boolean isLock, Promise promise) {
    if (lyric != null) {
      if (isLock) {
        lyric.lockLyric();
      } else {
        lyric.unlockLyric();
      }
    }
    promise.resolve(null);
  }

  @ReactMethod
  public void setColor(String unplayColor, String playedColor, String shadowColor, Promise promise) {
    if (lyric != null) lyric.setPlayedColor(unplayColor, playedColor, shadowColor);
    promise.resolve(null);
  }

  @ReactMethod
  public void setAlpha(float alpha, Promise promise) {
    if (lyric != null) lyric.setAlpha(alpha);
    promise.resolve(null);
  }

  @ReactMethod
  public void setTextSize(float size, Promise promise) {
    if (lyric != null) lyric.setTextSize(size);
    promise.resolve(null);
  }

  @ReactMethod
  public void setMaxLineNum(int maxLineNum, Promise promise) {
    if (lyric != null) lyric.setMaxLineNum(maxLineNum);
    promise.resolve(null);
  }

  @ReactMethod
  public void setSingleLine(boolean singleLine, Promise promise) {
    if (lyric != null) lyric.setSingleLine(singleLine);
    promise.resolve(null);
  }

  @ReactMethod
  public void setShowToggleAnima(boolean showToggleAnima, Promise promise) {
    if (lyric != null) lyric.setShowToggleAnima(showToggleAnima);
    promise.resolve(null);
  }

  @ReactMethod
  public void setWidth(int width, Promise promise) {
    if (lyric != null) lyric.setWidth(width);
    promise.resolve(null);
  }

  @ReactMethod
  public void setLyricTextPosition(String positionX, String positionY, Promise promise) {
    if (lyric != null) lyric.setLyricTextPosition(positionX, positionY);
    promise.resolve(null);
  }

  @ReactMethod
  public void checkOverlayPermission(Promise promise) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(reactContext)) {
      promise.reject(new Exception("Permission denied"));
    }
    promise.resolve(null);
  }

  @ReactMethod
  public void openOverlayPermissionActivity(Promise promise) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && !Settings.canDrawOverlays(reactContext)) {
      Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + reactContext.getApplicationContext().getPackageName()));
      reactContext.startActivityForResult(intent, 1, null);
    }
    promise.resolve(null);
  }

}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricPackage.java
================================================
package cn.toside.music.mobile.lyric;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

public class LyricPackage implements ReactPackage {
  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    return Arrays.<NativeModule>asList(new LyricModule(reactContext));
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricPlayer.java
================================================
package cn.toside.music.mobile.lyric;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class LyricPlayer {
  final String timeFieldExp = "^(?:\\[[\\d:.]+])+";
  final String timeExp = "\\d{1,3}(:\\d{1,3}){0,2}(?:\\.\\d{1,3})";
//  HashMap tagRegMap;
  Pattern timeFieldPattern;
  Pattern timePattern;

  String lyric = "";
  ArrayList<String> extendedLyrics = new ArrayList<>();
  List<HashMap> lines = new ArrayList<>();
  HashMap tags = new HashMap();
  boolean isPlay = false;
  float playbackRate = 1;
  int curLineNum = 0;
  int maxLine = 0;
  int offset = 150;
  int performanceTime = 0;
  int startPlayTime = 0;
  // int delay = 0;
  Object tid = null;
  boolean tempPause = false;
  boolean tempPaused = false;

  LyricPlayer() {
//    tagRegMap = new HashMap<String, String>();
//    tagRegMap.put("title", "ti");
//    tagRegMap.put("artist", "ar");
//    tagRegMap.put("album", "al");
//    tagRegMap.put("offset", "offset");
//    tagRegMap.put("by", "by");
//    tags = new HashMap();

    timeFieldPattern = Pattern.compile(timeFieldExp);
    timePattern = Pattern.compile(timeExp);
  }

  public void setTempPause(boolean isPaused) {
    if (isPaused) {
      tempPause = true;
    } else {
      tempPause = false;
      if (tempPaused) {
        tempPaused = false;
        if (isPlay) refresh();
      }
    }
  }

//  @RequiresApi(api = Build.VERSION_CODES.N)
//  private void initTag() {
//    tagRegMap.forEach((tag, value) -> {
//      Pattern pattern = Pattern.compile("\\[" + value + ":([^\\]]*)]", Pattern.CASE_INSENSITIVE);
//      Matcher matcher = pattern.matcher(lyric);
//
//      tags.put(tag, matcher.group(1));
//    });
//  }

  private void startTimeout(Runnable runnable, long delay) {
    if (tid != null) Utils.clearTimeout(tid);
    tid = Utils.setTimeout(runnable, delay);
  }

  private void stopTimeout() {
    if (tid == null) return;
    Utils.clearTimeout(tid);
    tid = null;
  }

  private int getNow() {
    return (int)(System.nanoTime() / 1000000);
  }

  private int getCurrentTime() {
    return (int)((getNow() - this.performanceTime) * this.playbackRate) + startPlayTime;
  }

  private void initTag() {
    tags = new HashMap();
    Matcher matcher = Pattern.compile("\\[(ti|ar|al|offset|by):\\s*(\\S+(?:\\s+\\S+)*)\\s*]").matcher(this.lyric);
    while (matcher.find()) {
      String key = matcher.group(1);
      if (key == null) continue;
      String val = matcher.group(2);
      if (val == null) val = "";
      tags.put(key, val);
    }

    String offsetStr = (String) tags.get("offset");
    if (offsetStr == null || offsetStr.equals("")) {
      tags.put("offset", 0);
    } else {
      int offset;
      try {
        offset = Integer.parseInt(offsetStr);
      } catch (Exception err) {
        offset = 0;
      }
      tags.put("offset", offset);
    }
  }


  final String t_rxp_1 = "^0+(\\d+)";
  final String t_rxp_2 = ":0+(\\d+)";
  final String t_rxp_3 = "\\.0+(\\d+)";
  private String formatTimeLabel(String label) {
    return label.replaceAll(t_rxp_1, "$1")
      .replaceAll(t_rxp_2, ":$1")
      .replaceAll(t_rxp_3, ".$1");
  }

  private void parseExtendedLyric(HashMap linesMap, String extendedLyric) {
    String[] extendedLyricLines = extendedLyric.split("\r\n|\n|\r");
    for (String translationLine : extendedLyricLines) {
      String line = translationLine.trim();
      Matcher timeFieldResult = timeFieldPattern.matcher(line);
      if (timeFieldResult.find()) {
        String timeField = timeFieldResult.group();
        String text = line.replaceAll(timeFieldExp, "").trim();
        if (text.length() > 0) {
          Matcher timeMatchResult = timePattern.matcher(timeField);
          while (timeMatchResult.find()) {
            String timeStr = timeMatchResult.group();
            timeStr = formatTimeLabel(timeStr);
            HashMap targetLine = (HashMap) linesMap.get(timeStr);
            if (targetLine != null) ((ArrayList<String>) targetLine.get("extendedLyrics")).add(text);
          }
        }
      }
    }
  }

  private void initLines() {
    String[] linesStr = lyric.split("\r\n|\n|\r");
    lines = new ArrayList<>();

    HashMap linesMap = new HashMap<String, HashMap>();
    HashMap timeMap = new HashMap<String, Integer>();

    for (String lineStr : linesStr) {
      String line = lineStr.trim();
      Matcher timeFieldResult = timeFieldPattern.matcher(line);
      if (timeFieldResult.find()) {
        String timeField = timeFieldResult.group();
        String text = line.replaceAll(timeFieldExp, "").trim();
        if (text.length() > 0) {
          Matcher timeMatchResult = timePattern.matcher(timeField);
          while (timeMatchResult.find()) {
            String timeStr = formatTimeLabel(timeMatchResult.group());
            if (linesMap.containsKey(timeStr)) {
              ((ArrayList<String>) ((HashMap) linesMap.get(timeStr)).get("extendedLyrics")).add(text);
              continue;
            }
            String[] timeArr = timeStr.split(":");
            String hours;
            String minutes;
            String seconds;
            String milliseconds = "0";
            switch (timeArr.length) {
              case 3:
                hours = timeArr[0];
                minutes = timeArr[1];
                seconds = timeArr[2];
                break;
              case 2:
                hours = "0";
                minutes = timeArr[0];
                seconds = timeArr[1];
                break;
              case 1:
                hours = "0";
                minutes = "0";
                seconds = timeArr[0];
                break;
              default:
                continue;
            }
            if (seconds.contains(".")) {
              timeArr = seconds.split("\\.");
              seconds = timeArr[0];
              if (timeArr.length > 1) milliseconds = timeArr[1];
            }
            HashMap<String, Object> lineInfo = new HashMap<>();
            int time = Integer.parseInt(hours) * 60 * 60 * 1000
              + Integer.parseInt(minutes) * 60 * 1000
              + Integer.parseInt(seconds) * 1000
              + Integer.parseInt(milliseconds);
            lineInfo.put("time", time);
            lineInfo.put("text", text);
            lineInfo.put("extendedLyrics", new ArrayList<String>(extendedLyrics.size()));
            timeMap.put(timeStr, time);
            linesMap.put(timeStr, lineInfo);
          }
        }
      }
    }

    for (String extendedLyric : extendedLyrics) {
      parseExtendedLyric(linesMap, extendedLyric);
    }

    Set<Entry<String, Integer>> set = timeMap.entrySet();
    List<Entry<String, Integer>> list = new ArrayList<Entry<String, Integer>>(set);
    Collections.sort(list, new Comparator<Entry<String, Integer>>() {
      public int compare(Map.Entry<String, Integer> o1,
                         Map.Entry<String, Integer> o2) {
        return o1.getValue().compareTo(o2.getValue());
      }
    });

    // lines = new ArrayList<HashMap>(list.size());
    for (Entry<String, Integer> entry : list) {
      lines.add((HashMap) linesMap.get(entry.getKey()));
    }

    this.maxLine = lines.size() - 1;
  }

  private void  init() {
    if (lyric == null) lyric = "";
    if (extendedLyrics == null) extendedLyrics = new ArrayList<>();
    initTag();
    initLines();
    onSetLyric(lines);
  }

  public void pause() {
    if (!isPlay) return;
    isPlay = false;
    tempPaused = false;
    stopTimeout();
    if (curLineNum == maxLine) return;
    int curLineNum = this.findCurLineNum(getCurrentTime());
    if (this.curLineNum != curLineNum) {
      this.curLineNum = curLineNum;
      this.onPlay(curLineNum);
    }
  }

  public void play(int curTime) {
    if (this.lines.size() == 0) return;
    pause();
    isPlay = true;

    Object tagOffset = tags.get("offset");
    if (tagOffset == null) tagOffset = 0;
    performanceTime = getNow() - (int) tagOffset - offset;
    startPlayTime = curTime;

    curLineNum = findCurLineNum(getCurrentTime()) - 1;

    refresh();
  }

  private int findCurLineNum(int curTime, int startIndex) {
    // Log.d("Lyric", "findCurLineNum: " + startIndex);
    if (curTime <= 0) return 0;
    int length = lines.size();
    for (int index = startIndex; index < length; index++) {
      if (curTime < (int) ((HashMap)lines.get(index)).get("time")) return index == 0 ? 0 : index - 1;
    }
    return length - 1;
  }

  private int findCurLineNum(int curTime) {
    return findCurLineNum(curTime, 0);
  }

  private void handleMaxLine() {
    this.onPlay(this.curLineNum);
    this.pause();
  }

  private void refresh() {
    if (tempPaused) tempPaused = false;

    curLineNum++;
    // Log.d("Lyric", "refresh: " + curLineNum);

    if (curLineNum >= maxLine) {
      handleMaxLine();
      return;
    }
    HashMap curLine = lines.get(curLineNum);

    int currentTime = getCurrentTime();
    int driftTime = currentTime - (int)curLine.get("time");
    // Log.d("Lyric", "driftTime: " + driftTime + "  time: " + curLine.get("time") + "  currentTime: " + currentTime);

    if (driftTime >= 0 || curLineNum == 0) {
      HashMap nextLine = lines.get(curLineNum + 1);
      int delay = (int)(((int)nextLine.get("time") - (int)curLine.get("time") - driftTime) / this.playbackRate);
      // Log.d("Lyric", "delay: " + delay + "  driftTime: " + driftTime);
      if (delay > 0) {
        if (isPlay) {
          startTimeout(() -> {
            if (tempPause) {
              tempPaused = true;
              return;
            }
            if (!isPlay) return;
            refresh();
          }, delay);
        }
        onPlay(curLineNum);
      } else {
        int newCurLineNum = this.findCurLineNum(currentTime, curLineNum + 1);
        if (newCurLineNum > curLineNum) curLineNum = newCurLineNum - 1;
        // Log.d("Lyric", "refresh--: " + curLineNum + "  newCurLineNum: " + newCurLineNum);
        refresh();
      }
      return;
    }

    curLineNum = this.findCurLineNum(currentTime, curLineNum) - 1;
    refresh();
  }

  public void setLyric(String lyric, ArrayList<String> extendedLyrics) {
    if (isPlay) pause();
    this.lyric = lyric;
    this.extendedLyrics = extendedLyrics;
    init();
  }

  public void setPlaybackRate(float playbackRate) {
    this.playbackRate = playbackRate;
    if (this.lines.size() == 0) return;
    if (!this.isPlay) return;
    this.play(this.getCurrentTime());
  }

  public void onPlay(int lineNum) {}

  public void onSetLyric(List lines) {}

}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricSwitchView.java
================================================
package cn.toside.music.mobile.lyric;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.graphics.Typeface;
import android.text.TextPaint;
import android.text.TextUtils;
import android.view.View;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.TranslateAnimation;
import android.widget.TextSwitcher;
import android.widget.TextView;

import androidx.annotation.Nullable;

import java.util.ArrayList;

// https://github.com/Block-Network/StatusBarLyric/blob/main/app/src/main/java/statusbar/lyric/view/LyricSwitchView.kt
@SuppressLint({"ViewConstructor"})
public final class LyricSwitchView extends TextSwitcher {
  private final TextView textView;
  private final TextView textView2;
  private final ArrayList<TextView> viewArray;
  // private final boolean isSingleLine;
  private boolean isShowAnima;

  private boolean isSingleLine;

  public LyricSwitchView(Context context, boolean isSingleLine, boolean isShowAnima) {
    super(context);
    // this.isSingleLine = isSingleLine;
    this.isShowAnima = isShowAnima;
    this.isSingleLine = isSingleLine;

    if (isSingleLine) {
      viewArray = new ArrayList<>(2);
      textView = new LyricTextView(context);
      textView2 = new LyricTextView(context);
      viewArray.add(textView);
      viewArray.add(textView2);
//      for (TextView v : viewArray) {
//        v.setShadowLayer(0.1f, 0, 0, Color.BLACK);
//      }
    } else {
      viewArray = new ArrayList<>(2);
      textView = new TextView(context);
      textView2 = new TextView(context);
      viewArray.add(textView);
      viewArray.add(textView2);
      for (TextView v : viewArray) {
//        v.setShadowLayer(0.2f, 0, 0, Color.BLACK);
        v.setEllipsize(TextUtils.TruncateAt.END);
      }
    }
    setAnima();
    this.addView(textView);
    this.addView(textView2);
  }

  @Nullable
  public Animation inAnim(String str, float height) {
    AnimationSet animationSet = new AnimationSet(true);
    if (str == null) return null;

    TranslateAnimation translateAnimation;
    switch (str) {
      case "top":
        translateAnimation = new TranslateAnimation(0.0F, 0.0F, height, 0.0F);
        break;
      case "bottom":
        translateAnimation = new TranslateAnimation(0.0F, 0.0F, -height, 0.0F);
        break;
      case "left":
        translateAnimation = new TranslateAnimation(100.0F, 0.0F, 0.0F, 0.0F);
        break;
      case "right":
        translateAnimation = new TranslateAnimation(-100.0F, 0.0F, 0.0F, 0.0F);
        break;
      default: return null;
    }

    translateAnimation.setDuration(300L);
    AlphaAnimation alphaAnimation = new AlphaAnimation(0.0F, 1.0F);
    alphaAnimation.setDuration(300L);
    animationSet.addAnimation(translateAnimation);
    animationSet.addAnimation(alphaAnimation);
    return animationSet;
  }

  @Nullable
  public Animation outAnim(String str, float height) {
    AnimationSet animationSet = new AnimationSet(true);
    if (str == null) return null;

    TranslateAnimation translateAnimation;
    switch (str) {
      case "top":
        translateAnimation = new TranslateAnimation(0.0F, 0.0F, 0.0F, -height);
        break;
      case "bottom":
        translateAnimation = new TranslateAnimation(0.0F, 0.0F, 0.0F, height);
        break;
      case "left":
        translateAnimation = new TranslateAnimation(0.0F, -100.0F, 0.0F, 0.0F);
        break;
      case "right":
        translateAnimation = new TranslateAnimation(0.0F, 100.0F, 0.0F, 0.0F);
        break;
      default: return null;
    }
    translateAnimation.setDuration(300L);
    AlphaAnimation alphaAnimation = new AlphaAnimation(1.0F, 0.0F);
    alphaAnimation.setDuration(300L);
    animationSet.addAnimation(translateAnimation);
    animationSet.addAnimation(alphaAnimation);
    return animationSet;
  }

  private void setAnima() {
    if (textView == null) return;
    if (isShowAnima) {
      float size = textView.getTextSize();
      setInAnimation(inAnim("top", size));
      setOutAnimation(outAnim("top", size));
    } else {
      setInAnimation(null);
      setOutAnimation(null);
    }
  }

  public void setShowAnima(boolean showAnima) {
    isShowAnima = showAnima;
    setAnima();
  }

  public CharSequence getText() {
    View currentView = this.getCurrentView();
    return currentView == null ? "" : ((TextView)currentView).getText();
  }

  public TextPaint getPaint() {
    TextView v = (TextView)this.getCurrentView();
    if (v == null) return null;
    return v.getPaint();
  }

  public void setWidth(int i) {
    for (TextView v : viewArray) v.setWidth(i);
  }

  public void setTextColor(int i) {
    for (TextView v : viewArray) v.setTextColor(i);
  }

  public void setShadowColor(int i) {
    // float radius;
    // if (isSingleLine) {
    //   radius = 1.2f;
    // } else {
    //   radius = 2f;
    // }
    // https://stackoverflow.com/a/28367917
    for (TextView v : viewArray) v.setShadowLayer(1.6f, 1.5f, 1.3f, i);
  }

  public void setSourceText(CharSequence str) {
    for (TextView v : viewArray) v.setText(str);
  }

  public void setLetterSpacings(float letterSpacing) {
    for (TextView v : viewArray) v.setLetterSpacing(letterSpacing);
  }

  public void setHeight(int i) {
    for (TextView v : viewArray) v.setHeight(i);
  }

  public void setTypeface(Typeface typeface) {
    for (TextView v : viewArray) v.setTypeface(typeface);
  }

  public void setSingleLine(boolean bool) {
    for (TextView v : viewArray) v.setSingleLine(bool);
  }

  public void setMaxLines(int i) {
    for (TextView v : viewArray) v.setMaxLines(i);
  }

  public void setTextSize(float f) {
    for (TextView v : viewArray) v.setTextSize(f);
    setAnima();
  }

  public void setGravity(int i) {
    for (TextView v : viewArray) v.setGravity(i);
  }

}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricTextView.java
================================================
package cn.toside.music.mobile.lyric;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.Log;
import android.view.Gravity;
import android.widget.TextView;

// https://github.com/Block-Network/StatusBarLyric/blob/main/app/src/main/java/statusbar/lyric/view/LyricTextView.kt
@SuppressLint("AppCompatCustomView")
public class LyricTextView extends TextView {
  private boolean isStop = true;
  private float textLength = 0F;
  private float viewWidth = 0F;
  private float viewHeight = 0F;
  private final float SPEED_LIMIT = 0.135F;
  private float speed;
  private float xx = 0F;
  private int gravityVertical = Gravity.TOP;
  private int gravityHorizontal = Gravity.CENTER;
  private float y = 0F;
  private String text = null;
  private final Paint mPaint;
  private final Runnable mStartScrollRunnable;
  private final Runnable invalidateRunnable;
  public static final int startScrollDelay = 1500;
  public static final int invalidateDelay = 10;

  public LyricTextView(Context context) {
    super(context);
    mStartScrollRunnable = LyricTextView.this::startScroll;
    invalidateRunnable = LyricTextView.this::invalidate;
    mPaint = getPaint();
    speed = SPEED_LIMIT * getTextSize();
  }

  private void init() {
    xx = 0.0F;
    textLength = getTextLength();
    // viewWidth = (float) getWidth();
  }

  @Override
  protected void onDetachedFromWindow() {
    removeCallbacks(mStartScrollRunnable);
    super.onDetachedFromWindow();
  }

  @Override
  protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
    super.onTextChanged(text, start, lengthBefore, lengthAfter);
    stopScroll();
    this.text = text.toString();
    init();
    postInvalidate();
    postDelayed(mStartScrollRunnable, startScrollDelay);
  }

  @Override
  public void setTextColor(int color) {
    if (mPaint != null) mPaint.setColor(color);
    postInvalidate();
  }

  @Override
  public void setShadowLayer(float radius, float dx, float dy, int shadowColor) {
    if (mPaint != null) mPaint.setShadowLayer(radius, dx, dy, shadowColor);
    post(mStartScrollRunnable);
  }

  @Override
  public void setTextSize(float size) {
    super.setTextSize(size);
    speed = SPEED_LIMIT * size;
    if (text == null) return;
    post(mStartScrollRunnable);
  }

  @Override
  public void setWidth(int pixels) {
    super.setWidth(pixels);
    viewWidth = pixels;
    if (text == null) return;
    post(mStartScrollRunnable);
  }

  @Override
  public void setHeight(int pixels) {
    super.setHeight(pixels);
    viewHeight = pixels;
    y = getDrawY();
    if (text == null) return;
    post(mStartScrollRunnable);
  }

  @Override
  public void setGravity(int gravity) {
    if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
      gravity |= Gravity.START;
    }
    if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
      gravity |= Gravity.TOP;
    }

    gravityVertical = gravity & Gravity.VERTICAL_GRAVITY_MASK;
    gravityHorizontal = gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;

    y = getDrawY();
    // Log.d("Lyric", "gravityVertical: " + gravityVertical + " gravityHorizontal: " + gravityHorizontal);

    if (text == null) return;
    post(mStartScrollRunnable);
  }

  @Override
  protected void onDraw(Canvas canvas) {
    float mSpeed = speed;
    if (text != null) {
      Log.d("Lyric", "getHeight: " + getHeight() + " y: " + y);
      canvas.drawText(text, getDrawX(), y, mPaint);
      if (getText().length() >= 20) {
        mSpeed += mSpeed;
      }
    }

    if (!isStop) {
      if (viewWidth - xx + mSpeed >= textLength) {
        xx = viewWidth - textLength - 2;
        stopScroll();
      } else {
        xx -= mSpeed;
      }

      invalidateAfter();
    }

  }

  private void invalidateAfter() {
    removeCallbacks(invalidateRunnable);
    postDelayed(invalidateRunnable, invalidateDelay);
  }

  private void startScroll() {
    init();
    isStop = false;
    postInvalidate();
  }

  private void stopScroll() {
    isStop = true;
    removeCallbacks(mStartScrollRunnable);
    postInvalidate();
  }

  private float getTextLength() {
    return mPaint == null ? 0.0F : mPaint.measureText(text);
  }

  private float getDrawY() {
    Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
    float top = fontMetrics.top;
    float bottom = fontMetrics.bottom;
    float ascent = fontMetrics.ascent;
    // float descent = fontMetrics.descent;

    float y;

    // float y = Math.abs(mPaint.ascent() + mPaint.descent()) / 2;
    switch (gravityVertical) {
      case Gravity.CENTER_VERTICAL:
        y = viewHeight / 2F + (bottom - top) / 2 - bottom;
        break;
      case Gravity.BOTTOM:
        y = viewHeight - bottom;
        break;
      default:
        y = -ascent;
        break;
    }
    return y;
  }

  private float getDrawX() {
    float x;
    if (textLength < viewWidth) {
      switch (gravityHorizontal) {
        case Gravity.CENTER_HORIZONTAL:
          x = (viewWidth - textLength) / 2;
          break;
        case Gravity.END:
          x = viewWidth - textLength;
          break;
        default:
          x = 0;
          break;
      }
      isStop = true;
    } else {
      x = xx;
    }
    return x;
  }

  // public void setSpeed(float speed) {
  //  this.speed = speed;
  // }

}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricView.java
================================================
package cn.toside.music.mobile.lyric;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.hardware.SensorManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Display;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.View;
import android.view.WindowManager;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableMap;

import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import cn.toside.music.mobile.R;

public class LyricView extends Activity implements View.OnTouchListener {
  LyricSwitchView textView = null;
  WindowManager windowManager = null;
  WindowManager.LayoutParams layoutParams = null;
  final private ReactApplicationContext reactContext;
  final private LyricEvent lyricEvent;

  // private int winWidth = 0;

  private float lastX; //上一次位置的X.Y坐标
  private float lastY;
  private float nowX;  //当前移动位置的X.Y坐标
  private float nowY;
  private float tranX; //悬浮窗移动位置的相对值
  private float tranY;
  private float prevViewPercentageX = 0;
  private float prevViewPercentageY = 0;
  private float widthPercentage = 1f;

  private float preY = 0;
  // private static boolean isVibrated = false;

  private boolean isLock = false;
  private boolean isSingleLine = false;
  private boolean isShowToggleAnima = false;
  private String unplayColor = "rgba(255, 255, 255, 1)";
  private String playedColor = "rgba(7, 197, 86, 1)";
  private String shadowColor = "rgba(0, 0, 0, 0.15)";
  // private String lastText = "LX Music ^-^";
  private String textX = "LEFT";
  private String textY = "TOP";
  private float alpha = 1f;
  private float textSize = 18f;
  private int maxWidth = 0;
  private int maxHeight = 0;

  private int maxLineNum = 5;
  // private float lineHeight = 1;
  private String currentLyric = "LX Music ^-^";
  private ArrayList<String> currentExtendedLyrics = new ArrayList<>();

  private int mLastRotation;
  private OrientationEventListener orientationEventListener = null;

  final Handler fixViewPositionHandler;
  final Runnable fixViewPositionRunnable = this::updateViewPosition;

  LyricView(ReactApplicationContext reactContext, LyricEvent lyricEvent) {
    this.reactContext = reactContext;
    this.lyricEvent = lyricEvent;
    fixViewPositionHandler = new Handler();
  }

  private void listenOrientationEvent() {
    if (orientationEventListener == null) {
      orientationEventListener = new OrientationEventListener(reactContext, SensorManager.SENSOR_DELAY_NORMAL) {
        @Override
        public void onOrientationChanged(int orientation) {
          Display display = windowManager.getDefaultDisplay();
          int rotation = display.getRotation();
          if(rotation != mLastRotation){
            //rotation changed
            // if (rotation == Surface.ROTATION_90){} // check rotations here
            // if (rotation == Surface.ROTATION_270){} //
            // Log.d("Lyric", "rotation: " + rotation);
            fixViewPositionHandler.postDelayed(fixViewPositionRunnable, 300);
          }
          mLastRotation = rotation;
        }
      };
    }
    // Log.d("Lyric", "orientationEventListener: " + orientationEventListener.canDetectOrientation());
    if (orientationEventListener.canDetectOrientation()) {
      orientationEventListener.enable();
    }
  }
  private void removeOrientationEvent() {
    if (orientationEventListener == null) return;
    orientationEventListener.disable();
    // orientationEventListener = null;
  }

  private int getLayoutParamsFlags() {
    int flag = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
      WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
      WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
      WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;

    if (isLock) {
      flag = flag | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
    }

    return flag;
  }

  /**
   * update screen width and height
   * @return has updated
   */
  private boolean updateWH() {
    Display display = windowManager.getDefaultDisplay();
    Point size = new Point();
    display.getRealSize(size);
    if (maxWidth == size.x && maxHeight == size.y) return false;
    maxWidth = size.x;
    maxHeight = size.y;
    return true;
  }

  private void setLayoutParamsHeight() {
    if (textView == null) return;
    int height = textView.getPaint().getFontMetricsInt(null) * maxLineNum;
    if (height > maxHeight - 100) height = maxHeight - 100;
    layoutParams.height = height;
    textView.setHeight(height);
  }

  private void fixViewPosition() {
    int maxX = maxWidth - layoutParams.width;
    int x = (int)(maxWidth * prevViewPercentageX);
    if (x < 0) x = 0;
    else if (x > maxX) x = maxX;
    if (layoutParams.x != x) layoutParams.x = x;

    setLayoutParamsHeight();

    int maxY = maxHeight - layoutParams.height;
    int y = (int)(maxHeight * prevViewPercentageY);
    if (y < 0) y = 0;
    else if (y > maxY) y = maxY;
    if (layoutParams.y != y) layoutParams.y = y;
  }

  private void updateViewPosition() {
    if (!updateWH()) return;

    int width = (int)(maxWidth * widthPercentage);
    if (layoutParams.width != width) {
      layoutParams.width = width;
      if (textView != null) textView.setWidth(width);
    }

    fixViewPosition();
    // Log.d("Lyric", "widthPercentage: " + widthPercentage + "  prevViewPercentageX: " + prevViewPercentageX);
    // Log.d("Lyric", "prevViewPercentageY: " + prevViewPercentageY + "  layoutParams.x: " + layoutParams.x);
    // Log.d("Lyric", "layoutParams.y: " + layoutParams.y + "  layoutParams.width: " + layoutParams.width);

    windowManager.updateViewLayout(textView, layoutParams);
  }

  public void sendPositionEvent(float x, float y) {
    WritableMap params = Arguments.createMap();
    params.putDouble("x", x);
    params.putDouble("y", y);
    lyricEvent.sendEvent(lyricEvent.SET_VIEW_POSITION, params);
  }

//  public void permission(){
//    if (Build.VERSION.SDK_INT >= 23) {
//      if(!Settings.canDrawOverlays(this)) {
//        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
//        startActivity(intent);
//        return;
//      } else {
//        //Android6.0以上
//        if (mFloatView!=null && mFloatView.isShow()==false) {
//          mFloatView.show();
//        }
//      }
//    } else {
//      //Android6.0以下,不用动态声明权限
//      if (mFloatView!=null && mFloatView.isShow()==false) {
//        mFloatView.show();
//      }
//    }
//  }
// boolean isLock, String themeColor, float alpha, int lyricViewX, int lyricViewY, String textX, String textY
  public void showLyricView(Bundle options) {
    isLock = options.getBoolean("isLock", isLock);
    isSingleLine = options.getBoolean("isSingleLine", isSingleLine);
    isShowToggleAnima = options.getBoolean("isShowToggleAnima", isShowToggleAnima);
    unplayColor = options.getString("unplayColor", unplayColor);
    playedColor = options.getString("playedColor", playedColor);
    shadowColor = options.getString("shadowColor", shadowColor);
    prevViewPercentageX = (float) options.getDouble("lyricViewX", 0f) / 100f;
    prevViewPercentageY = (float) options.getDouble("lyricViewY", 0f) / 100f;
    textX = options.getString("textX", textX);
    textY = options.getString("textY", textY);
    alpha = (float) options.getDouble("alpha", alpha);
    textSize = (float) options.getDouble("textSize", textSize);
    widthPercentage = (float) options.getDouble("width", 100) / 100f;
    maxLineNum = (int) options.getDouble("maxLineNum", maxLineNum);
    handleShowLyric();
    listenOrientationEvent();
  }
  public void showLyricView() {
    try {
      handleShowLyric();
    } catch (Exception e) {
      Log.e("Lyric", e.getMessage());
      return;
    }
    listenOrientationEvent();
  }
  public static int parseColor(String input) {
    if (input.startsWith("#")) return Color.parseColor(input);
    Pattern c = Pattern.compile("rgba? *\\( *(\\d+), *(\\d+), *(\\d+)(?:, *([\\d.]+))? *\\)");
    Matcher m = c.matcher(input);
    if (m.matches()) {
      int red = Integer.parseInt(m.group(1));
      int green = Integer.parseInt(m.group(2));
      int blue = Integer.parseInt(m.group(3));
      float a = 1;
      if (m.group(4) != null) a = Float.parseFloat(m.group(4));
      return Color.argb((int) (a * 255), red, green, blue);
    }
    return Color.parseColor("#000000");
  }

  private void createTextView() {
    textView = new LyricSwitchView(reactContext, isSingleLine, isShowToggleAnima);
    textView.setText("");
    textView.setText(currentLyric);

    textView.setTextColor(parseColor(playedColor));
    textView.setShadowColor(parseColor(shadowColor));
    textView.setAlpha(alpha);
    textView.setTextSize(textSize);
    // Log.d("Lyric", "alpha: " + alpha + " text size: " + textSize);

    //监听 OnTouch 事件 为了实现"移动歌词"功能
    textView.setOnTouchListener(this);

    int textPositionX;
    int textPositionY;
    switch (textX) {
      case "CENTER":
        textPositionX = Gravity.CENTER;
        break;
      case "RIGHT":
        textPositionX = Gravity.END;
        break;
      case "Left":
      default:
        textPositionX = Gravity.START;
        break;
    }
    switch (textY) {
      case "CENTER":
        textPositionY = Gravity.CENTER;
        break;
      case "BOTTOM":
        textPositionY = Gravity.BOTTOM;
        break;
      case "TOP":
      default:
        textPositionY = Gravity.TOP;
        break;
    }
    textView.setGravity(textPositionX | textPositionY);

    if (!isSingleLine) {
      textView.setMaxLines(maxLineNum);
    }
  }
  private void handleShowLyric() {
    if (windowManager == null) {
      windowManager = (WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE);
      //设置TextView的属性
      layoutParams = new WindowManager.LayoutParams();

      DisplayMetrics outMetrics = new DisplayMetrics();
      windowManager.getDefaultDisplay().getMetrics(outMetrics);
      // winWidth = (int)(outMetrics.widthPixels * 0.92);
    }

    // 注意,悬浮窗只有一个,而当打开应用的时候才会产生悬浮窗,所以要判断悬浮窗是否已经存在,
    if (textView != null) {
      windowManager.removeView(textView);
    }

    // 使用Application context
    // 创建UI控件,避免Activity销毁导致上下文出现问题,因为现在的悬浮窗是系统级别的,不依赖与Activity存在
    //创建自定义的TextView
    createTextView();

    // layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT | WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
    // layoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY;
    layoutParams.type = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ?
      WindowManager.LayoutParams.TYPE_SYSTEM_ALERT :
      WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;

    // layoutParams.flags = isLock
    //  ? WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
    //  : WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
    layoutParams.flags = getLayoutParamsFlags();
    if (isLock) {
      textView.setBackgroundColor(Color.TRANSPARENT);

      // 修复 Android 12 的穿透点击问题
      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
        layoutParams.alpha = 0.8f;
      }
    } else {
      textView.setBackgroundResource(R.drawable.rounded_corner);

      if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
        layoutParams.alpha = 1.0f;
      }
    }

    // TYPE_SYSTEM_ALERT  系统提示,它总是出现在应用程序窗口之上
    // TYPE_SYSTEM_OVERLAY   系统顶层窗口。显示在其他一切内容之上。此窗口不能获得输入焦点,否则影响锁屏
    // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
    // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
    layoutParams.gravity = Gravity.TOP | Gravity.START;  //显示在屏幕上中部

    updateWH();

    //悬浮窗的宽高
    // layoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
    // layoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
    // layoutParams.width= DisplayUtil.dp2px(mContext,55);
    // layoutParams.height= DisplayUtil.dp2px(mContext,55);
    layoutParams.width = (int)(maxWidth * widthPercentage);
    textView.setWidth(layoutParams.width);
    setLayoutParamsHeight();

    //显示位置与指定位置的相对位置差
    layoutParams.x = (int)(maxWidth * prevViewPercentageX);
    layoutParams.y = (int)(maxHeight * prevViewPercentageY);

    fixViewPosition();

    //设置透明
    layoutParams.format = PixelFormat.TRANSPARENT;

    //添加到window中
    windowManager.addView(textView, layoutParams);
  }

  public void setLyric(String text, ArrayList<String> extendedLyrics) {
    if (text.equals("") && text.equals(currentLyric) && extendedLyrics.size() == 0) return;
    currentLyric = text;
    currentExtendedLyrics = extendedLyrics;
    if (textView == null) return;
    if (extendedLyrics.size() > 0 && maxLineNum > 1 && !isSingleLine) {
      int num = maxLineNum - 1;
      StringBuilder textBuilder = new StringBuilder(text);
      for (String lrc : extendedLyrics) {
        textBuilder.append("\n").append(lrc);
        if (--num < 1) break;
      }
      text = textBuilder.toString();
    }
    if (textView == null) return;
    textView.setText(text);
  }

  public void setMaxLineNum(int maxLineNum) {
    this.maxLineNum = maxLineNum;
    if (textView == null) return;
    if (!isSingleLine) textView.setMaxLines(maxLineNum);
    setLayoutParamsHeight();

    int maxY = maxHeight - layoutParams.height;
    int y = layoutParams.y;
    if (y < 0) y = 0;
    else if (y > maxY) y = maxY;
    if (layoutParams.y != y) layoutParams.y = y;

    windowManager.updateViewLayout(textView, layoutParams);
  }

  public void setWidth(int width) {
    if (textView == null) return;
    widthPercentage = width / 100f;
    layoutParams.width = (int)(maxWidth * widthPercentage);
    textView.setWidth(layoutParams.width);

    int maxX = maxWidth - layoutParams.width;
    int x = layoutParams.x;
    if (x < 0) x = 0;
    else if (x > maxX) x = maxX;
    if (layoutParams.x != x) layoutParams.x = x;

    windowManager.updateViewLayout(textView, layoutParams);
  }

  @Override
  public boolean onTouch(View v, MotionEvent event) {
    int maxX = maxWidth - layoutParams.width;
    int maxY = maxHeight - layoutParams.height;

    switch (event.getAction()){
      case MotionEvent.ACTION_DOWN:
        // 获取按下时的X,Y坐标
        lastX = event.getRawX();
        lastY = event.getRawY();

        preY = lastY;
        break;
      case MotionEvent.ACTION_MOVE:
        // 获取移动时的X,Y坐标
        nowX = event.getRawX();
        nowY = event.getRawY();
        if (preY == 0){
          preY = nowY;
        }
        // 计算XY坐标偏移量
        tranX = nowX - lastX;
        tranY = nowY - lastY;

        int x = layoutParams.x + (int)tranX;
        if (x < 0) x = 0;
        else if (x > maxX) x = maxX;
        int y = layoutParams.y + (int)tranY;
        if (y < 0) y = 0;
        else if (y > maxY) y = maxY;

        // 移动悬浮窗
        layoutParams.x = x;
        layoutParams.y = y;
        //更新悬浮窗位置
        windowManager.updateViewLayout(textView, layoutParams);
        //记录当前坐标作为下一次计算的上一次移动的位置坐标
        lastX = nowX;
        lastY = nowY;
        break;
      case MotionEvent.ACTION_UP:
        // float dy = nowY - preY;
        // Log.d("Lyric","dy: " + dy);
        // if (isVibrated){
        //   if (dy > 10){
        //     //down
        //     actions(AppHolder.actions[3]);
        //   }else if (dy<-10){
        //     //up
        //     actions(AppHolder.actions[4]);
        //   }else {
        //     //longClick
        //     actions(AppHolder.actions[2]);
        //   }
        //   isVibrated =false;
        // }
        //根据移动的位置来判断
        // dy = 0;
        tranY = 0;
        float percentageX = (float)layoutParams.x / (float) maxWidth * 100f;
        float percentageY = (float)layoutParams.y / (float) maxHeight * 100f;
        if (percentageX != prevViewPercentageX || percentageY != prevViewPercentageY) {
          prevViewPercentageX = percentageX / 100f;
          prevViewPercentageY = percentageY / 100f;
          sendPositionEvent(percentageX, percentageY);
        }
        break;
    }
    return true;
  }

  public void lockView() {
    isLock = true;
    if (windowManager == null || textView == null) return;
    layoutParams.flags = getLayoutParamsFlags();

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
      layoutParams.alpha = 0.8f;
    }
    textView.setBackgroundColor(Color.TRANSPARENT);
    windowManager.updateViewLayout(textView, layoutParams);
  }

  public void unlockView() {
    isLock = false;
    if (windowManager == null || textView == null) return;
    layoutParams.flags = getLayoutParamsFlags();

    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.R) {
      layoutParams.alpha = 1.0f;
    }
    textView.setBackgroundResource(R.drawable.rounded_corner);
    windowManager.updateViewLayout(textView, layoutParams);
  }

  public void setColor(String unplayColor, String playedColor, String shadowColor) {
    this.unplayColor = unplayColor;
    this.playedColor = playedColor;
    this.shadowColor = shadowColor;
    if (textView == null) return;
    textView.setTextColor(parseColor(playedColor));
    textView.setShadowColor(parseColor(shadowColor));
    // windowManager.updateViewLayout(textView, layoutParams);
  }

  public void setLyricTextPosition(String textX, String textY) {
    this.textX = textX;
    this.textY = textY;
    if (windowManager == null || textView == null) return;
    int textPositionX;
    int textPositionY;
    // Log.d("Lyric", "textX: " + textX + "  textY: " + textY);
    switch (textX) {
      case "CENTER":
        textPositionX = Gravity.CENTER_HORIZONTAL;
        break;
      case "RIGHT":
        textPositionX = Gravity.END;
        break;
      case "LEFT":
      default:
        textPositionX = Gravity.START;
        break;
    }
    switch (textY) {
      case "CENTER":
        textPositionY = Gravity.CENTER_VERTICAL;
        break;
      case "BOTTOM":
        textPositionY = Gravity.BOTTOM;
        break;
      case "TOP":
      default:
        textPositionY = Gravity.TOP;
        break;
    }
    textView.setGravity(textPositionX | textPositionY);
    windowManager.updateViewLayout(textView, layoutParams);
  }

  public void setAlpha(float alpha) {
    this.alpha = alpha;
    if (textView == null) return;
    textView.setAlpha(alpha);
  }

  public void setSingleLine(boolean isSingleLine) {
    this.isSingleLine = isSingleLine;
    if (textView == null) return;
    windowManager.removeView(textView);
    createTextView();
    textView.setWidth(layoutParams.width);
    textView.setHeight(layoutParams.height);
    windowManager.addView(textView, layoutParams);

    if (isLock) lockView();
    else unlockView();

    setLyric(currentLyric, currentExtendedLyrics);
  }

  public void setShowToggleAnima(boolean showToggleAnima) {
    isShowToggleAnima = showToggleAnima;
    if (textView == null) return;
    textView.setShowAnima(showToggleAnima);
  }

  public void setTextSize(float size) {
    this.textSize = size;
    if (windowManager == null || textView == null) return;
    textView.setTextSize(size);
    setLayoutParamsHeight();
    windowManager.updateViewLayout(textView, layoutParams);
  }

  public void destroyView() {
    if (textView == null || windowManager == null) return;
    windowManager.removeView(textView);
    textView = null;
    removeOrientationEvent();
  }

  public void destroy() {
    destroyView();
    windowManager = null;
    layoutParams = null;
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/Utils.java
================================================
package cn.toside.music.mobile.lyric;

import android.os.Handler;

public class Utils {
  // https://gist.github.com/mathew-kurian/2bd2b8b3a2f6438d6786
  public static Object setTimeout(Runnable runnable, long delay) {
    return new TimeoutEvent(runnable, delay);
  }
  public static void clearTimeout(Object timeoutEvent) {
    if (timeoutEvent instanceof TimeoutEvent) {
      ((TimeoutEvent) timeoutEvent).cancelTimeout();
    }
  }
  private static class TimeoutEvent {
    private static final Handler handler = new Handler();
    private volatile Runnable runnable;

    private TimeoutEvent(Runnable task, long delay) {
      runnable = task;
      handler.postDelayed(() -> {
        if (runnable != null) {
          runnable.run();
        }
      }, delay);
    }

    private void cancelTimeout() {
      runnable = null;
    }
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/Console.java
================================================
package cn.toside.music.mobile.userApi;

import android.os.Handler;
import android.os.Message;
import android.util.Log;

import com.whl.quickjs.wrapper.QuickJSContext;

public class Console implements QuickJSContext.Console {
  private final Handler eventHandler;

  Console(Handler eventHandler) {
    this.eventHandler = eventHandler;
  }

  private void sendLog(String type, String log) {
    Message message = this.eventHandler.obtainMessage();
    message.what = HandlerWhat.LOG;
    message.obj = new Object[]{type, log};
    Log.d("UserApi Log", "[" + type + "]" + log);
    this.eventHandler.sendMessage(message);
  }

  @Override
  public void log(String info) {
    sendLog("log", info);
  }

  @Override
  public void info(String info) {
    sendLog("info", info);
  }

  @Override
  public void warn(String info) {
    sendLog("warn", info);
  }

  @Override
  public void error(String info) {
    sendLog("error", info);
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/HandlerWhat.java
================================================
package cn.toside.music.mobile.userApi;

public class HandlerWhat {
  public static final int ACTION = 1000;
  public static final int INIT = 99;
  public static final int INIT_FAILED = 500;
  public static final int INIT_SUCCESS = 200;
  public static final int DESTROY = 98;
  public static final int LOG = 1001;
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/JavaScriptThread.java
================================================
package cn.toside.music.mobile.userApi;

import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.util.Log;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.ReactApplicationContext;

public class JavaScriptThread extends HandlerThread {
  private Handler handler;
  private QuickJS javaScriptExecutor;
  private final ReactApplicationContext reactContext;
  private final Bundle scriptInfo;

  JavaScriptThread(ReactApplicationContext context, Bundle info) {
    super("JavaScriptThread");
    this.reactContext = context;
    this.scriptInfo = info;
  }

  public void prepareHandler(final Handler mainHandler) {
    start();
    Log.d("UserApi [thread]", "running 2");
    this.handler = new Handler(getLooper()) {
      @Override
      public void handleMessage(@NonNull Message message) {
        if (javaScriptExecutor == null) {
          javaScriptExecutor = new QuickJS(reactContext, mainHandler);
          Log.d("UserApi [thread]", "javaScript executor created");
          String result = javaScriptExecutor.loadScript(scriptInfo);
          if ("".equals(result)) {
            Log.d("UserApi [thread]", "script loaded");
            mainHandler.sendEmptyMessage(HandlerWhat.INIT_SUCCESS);
          } else {
            Log.w("UserApi [thread]", "script load failed: " + result);
            mainHandler.sendMessage(mainHandler.obtainMessage(HandlerWhat.INIT_FAILED, result));
          }
        }
        switch (message.what) {
          case HandlerWhat.INIT: break;
          case HandlerWhat.ACTION: {
            Object[] data = (Object[]) message.obj;
            // Log.d("UserApi [handler]", "handler action: " + data[0]);
            javaScriptExecutor.callJS((String) data[0], data[1]);
            return;
          }
          case HandlerWhat.DESTROY:
            javaScriptExecutor.destroy();
            javaScriptExecutor = null;
            break;
          default:
            Log.w("UserApi [handler]", "Unknown message what: " + message.what);
            break;
        }
      }
    };
  }

  public Handler getHandler() {
    return this.handler;
  }

  public void stopThread() {
    quit();
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/JsHandler.java
================================================
package cn.toside.music.mobile.userApi;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;

import java.util.Objects;

public class JsHandler extends Handler {
  private final UtilsEvent utilsEvent;

  JsHandler(Looper looper, UtilsEvent utilsEvent) {
    super(looper);
    this.utilsEvent = utilsEvent;
  }

  private void sendInitFailedEvent(String errorMessage) {
    WritableMap params = Arguments.createMap();
    params.putString("action", "init");
    params.putString("errorMessage", errorMessage);
    params.putString("data", "{ \"info\": null, \"status\": false, \"errorMessage\": \"Create JavaScript Env Failed\" }");
    this.utilsEvent.sendEvent(utilsEvent.API_ACTION, params);
    sendLogEvent(new Object[]{"error", errorMessage});
  }

  private void sendLogEvent(Object[] data) {
    WritableMap params = Arguments.createMap();
    params.putString("action", "log");
    params.putString("type", (String) data[0]);
    params.putString("log", (String) data[1]);
    this.utilsEvent.sendEvent(utilsEvent.API_ACTION, params);
  }

  private void sendActionEvent(String action, String data) {
    WritableMap params = Arguments.createMap();
    params.putString("action", action);
    params.putString("data", data);
    this.utilsEvent.sendEvent(utilsEvent.API_ACTION, params);
  }

  @Override
  public void handleMessage(Message msg) {
    switch (msg.what) {
      case HandlerWhat.INIT_SUCCESS: break;
      case HandlerWhat.INIT_FAILED:
        sendInitFailedEvent((String) msg.obj);
        break;
      case HandlerWhat.ACTION:
        Object[] action = (Object[]) msg.obj;
        sendActionEvent((String) action[0], (String) action[1]);
        // Log.d("UserApi [api call]", "action: " + action[0]);
        break;
      case HandlerWhat.LOG:
        sendLogEvent((Object[]) msg.obj);
        break;
      default:
        Log.w("UserApi [api call]", "Unknown message what: " + msg.what);
        break;
    }
  }
}


================================================
FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/QuickJS.java
================================================
package cn.toside.music.mobile.userApi;

import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Base64;
import android.util.Log;
import cn.toside.music.mobile.crypto.AES;
import cn.toside.music.mobile.crypto.RSA;
import com.facebook.react.bridge.ReactApplicationContext;

import com.whl.quickjs.android.QuickJSLoader;
import com.whl.quickjs.wrapper.QuickJSContext;
import java.io.InputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.UUID;

public class QuickJS {
  private final Handler eventHandler;
  private String key;
  private final ReactApplicationContext reactContext;
  private boolean isInited = false;
  private QuickJSContext jsContext = null;
  final Handler timeoutHandler = new Handler();
  private boolean inited = false;

  public QuickJS(ReactApplicationContext context, Handler eventHandler) {
    this.reactContext = context;
    this.eventHandler = eventHandler;
  }

  private void init() {
    if (this.isInited) return;
    QuickJSLoader.init();
    this.key = UUID.randomUUID().toString();
    this.isInited = true;
  }

  private String getPreloadScript() {
    try {
      InputStream inputStream = this.reactContext.getAssets().open("script/user-api-preload.js");
      byte[] buffer = new byte[inputStream.available()];
      inputStream.read(buffer);
      inputStream.close();
      return new String(buffer, StandardCharsets.UTF_8);
    } catch (Exception e) {
      return null;
    }
  }

  private void createEnvObj(QuickJSContext jsContext) {
    jsContext.getGlobalObject().setProperty("__lx_native_call__", args -> {
      if (this.key.equals(args[0])) {
        callNative((String) args[1], (String) args[2]);
        return null;
      }
      return null;
    });
    jsContext.getGlobalObject().setProperty("__lx_native_call__utils_str2b64", args -> {
      try {
        return new String(Base64.encode(((String) args[0]).getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP));
      } catch (Exception e) {
        Log.e("UserApi [utils]", "utils_str2b64 error: " + e.getMessage());
        return "";
      }
    });
    jsContext.getGlobalObject().setProperty("__lx_native_call__utils_b642buf", args -> {
      try {
        byte[] byteArray = Base64.decode(((String) args[0]).getBytes(StandardCharsets.UTF_8), Base64.NO_WRAP);
        StringBuilder jsonArrayString = new StringBuilder("[");
        for (int i = 0; i < byteArray.length; i++) {
          jsonArrayString.append((int) byteArray[i]);
          if (i < byteArray.length - 1) {
            jsonArrayString.append(",");
          }
        }
        jsonArrayString.append("]");
        return jsonArrayString.toString();
      } catch (Exception e) {
        Log.e("UserApi [utils]", "utils_b642buf error: " + e.getMessage());
        return "";
      }
    });
    jsContext.getGlobalObject().setProperty("__lx_native_call__utils_str2md5", args -> {
      try {
        // Log.d("UserApi [script call]", "utils_str2md5: " + args[0]);
        String str;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
          str = URLDecoder.decode((String) args[0], StandardCharsets.UTF_8);
        } else {
          str = URLDecoder.decode((String) args[0], "UTF-8");
        }
        // Log.d("UserApi [script call]", "utils_str2md5: " + str);
        MessageDigest md = MessageDigest.getInstance("MD5");
Download .txt
gitextract_rqtblcgl/

├── .bundle/
│   └── config
├── .editorconfig
├── .eslintrc.cjs
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug.yml
│   │   └── feature.yml
│   ├── actions/
│   │   ├── setup/
│   │   │   └── action.yml
│   │   └── upload-artifact/
│   │       └── action.yml
│   └── workflows/
│       ├── beta-pack.yml
│       ├── build-test.yml
│       ├── publish-version-info.yml
│       └── release.yml
├── .gitignore
├── .ncurc.js
├── .nvmrc
├── .vscode/
│   ├── i18n-ally-custom-framework.yml
│   ├── javascript.code-snippets
│   └── settings.json
├── CHANGELOG.md
├── FAQ.md
├── Gemfile
├── LICENSE
├── README.md
├── android/
│   ├── app/
│   │   ├── build.gradle
│   │   ├── debug.keystore
│   │   ├── proguard-rules.pro
│   │   └── src/
│   │       ├── debug/
│   │       │   └── AndroidManifest.xml
│   │       ├── main/
│   │       │   ├── AndroidManifest.xml
│   │       │   ├── assets/
│   │       │   │   └── script/
│   │       │   │       └── user-api-preload.js
│   │       │   ├── java/
│   │       │   │   └── cn/
│   │       │   │       └── toside/
│   │       │   │           └── music/
│   │       │   │               └── mobile/
│   │       │   │                   ├── MainActivity.java
│   │       │   │                   ├── MainApplication.java
│   │       │   │                   ├── cache/
│   │       │   │                   │   ├── CacheClearAsyncTask.java
│   │       │   │                   │   ├── CacheModule.java
│   │       │   │                   │   ├── CachePackage.java
│   │       │   │                   │   └── Utils.java
│   │       │   │                   ├── crypto/
│   │       │   │                   │   ├── AES.java
│   │       │   │                   │   ├── CryptoModule.java
│   │       │   │                   │   ├── CryptoPackage.java
│   │       │   │                   │   └── RSA.java
│   │       │   │                   ├── lyric/
│   │       │   │                   │   ├── Lyric.java
│   │       │   │                   │   ├── LyricEvent.java
│   │       │   │                   │   ├── LyricModule.java
│   │       │   │                   │   ├── LyricPackage.java
│   │       │   │                   │   ├── LyricPlayer.java
│   │       │   │                   │   ├── LyricSwitchView.java
│   │       │   │                   │   ├── LyricTextView.java
│   │       │   │                   │   ├── LyricView.java
│   │       │   │                   │   └── Utils.java
│   │       │   │                   ├── userApi/
│   │       │   │                   │   ├── Console.java
│   │       │   │                   │   ├── HandlerWhat.java
│   │       │   │                   │   ├── JavaScriptThread.java
│   │       │   │                   │   ├── JsHandler.java
│   │       │   │                   │   ├── QuickJS.java
│   │       │   │                   │   ├── UserApiModule.java
│   │       │   │                   │   ├── UserApiPackage.java
│   │       │   │                   │   └── UtilsEvent.java
│   │       │   │                   └── utils/
│   │       │   │                       ├── AsyncTask.java
│   │       │   │                       ├── BatteryOptimizationUtil.java
│   │       │   │                       ├── NotificationPermissionUtil.java
│   │       │   │                       ├── Utils.java
│   │       │   │                       ├── UtilsEvent.java
│   │       │   │                       ├── UtilsModule.java
│   │       │   │                       └── UtilsPackage.java
│   │       │   └── res/
│   │       │       ├── drawable/
│   │       │       │   ├── ic_launcher_foreground.xml
│   │       │       │   ├── rn_edit_text_material.xml
│   │       │       │   └── rounded_corner.xml
│   │       │       ├── mipmap-anydpi-v26/
│   │       │       │   ├── ic_launcher.xml
│   │       │       │   └── ic_launcher_round.xml
│   │       │       ├── values/
│   │       │       │   ├── ic_launcher_background.xml
│   │       │       │   ├── strings.xml
│   │       │       │   └── styles.xml
│   │       │       └── xml/
│   │       │           ├── file_paths.xml
│   │       │           └── network_security_config.xml
│   │       └── release/
│   │           └── java/
│   │               └── cn/
│   │                   └── toside/
│   │                       └── music/
│   │                           └── ReactNativeFlipper.java
│   ├── build.gradle
│   ├── gradle/
│   │   └── wrapper/
│   │       ├── gradle-wrapper.jar
│   │       └── gradle-wrapper.properties
│   ├── gradle.properties
│   ├── gradlew
│   ├── gradlew.bat
│   └── settings.gradle
├── app.json
├── babel.config.js
├── dependencies-patch.js
├── index.js
├── ios/
│   ├── .xcode.env
│   ├── LxMusicMobile/
│   │   ├── AppDelegate.h
│   │   ├── AppDelegate.mm
│   │   ├── Images.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   └── Contents.json
│   │   │   └── Contents.json
│   │   ├── Info.plist
│   │   ├── LaunchScreen.storyboard
│   │   └── main.m
│   ├── LxMusicMobile.xcodeproj/
│   │   ├── project.pbxproj
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── LxMusicMobile.xcscheme
│   ├── LxMusicMobileTests/
│   │   ├── Info.plist
│   │   └── LxMusicMobileTests.m
│   └── Podfile
├── metro.config.js
├── package.json
├── publish/
│   ├── changeLog.md
│   ├── index.js
│   ├── utils/
│   │   ├── index.js
│   │   ├── parseChangelog.js
│   │   └── updateChangeLog.js
│   └── version.json
├── shim.js
├── src/
│   ├── app.ts
│   ├── components/
│   │   ├── DesktopLyricEnable.tsx
│   │   ├── MetadataEditModal/
│   │   │   ├── InputItem.tsx
│   │   │   ├── MetadataForm.tsx
│   │   │   ├── ParseName.tsx
│   │   │   ├── PicItem.tsx
│   │   │   ├── TextAreaItem.tsx
│   │   │   └── index.tsx
│   │   ├── MusicAddModal/
│   │   │   ├── CreateUserList.tsx
│   │   │   ├── List.tsx
│   │   │   ├── ListItem.tsx
│   │   │   ├── MusicAddModal.tsx
│   │   │   ├── Title.tsx
│   │   │   └── index.tsx
│   │   ├── MusicMultiAddModal/
│   │   │   ├── List.tsx
│   │   │   ├── ListItem.tsx
│   │   │   ├── MusicMultiAddModal.tsx
│   │   │   ├── Title.tsx
│   │   │   └── index.tsx
│   │   ├── OnlineList/
│   │   │   ├── List.tsx
│   │   │   ├── ListItem.tsx
│   │   │   ├── ListMenu.tsx
│   │   │   ├── MultipleModeBar.tsx
│   │   │   ├── index.tsx
│   │   │   └── listAction.ts
│   │   ├── PageContent.tsx
│   │   ├── SearchTipList/
│   │   │   ├── List.tsx
│   │   │   └── index.tsx
│   │   ├── SizeView.tsx
│   │   ├── SourceSelector.tsx
│   │   ├── TimeoutExitEditModal.tsx
│   │   ├── common/
│   │   │   ├── Badge.tsx
│   │   │   ├── Button.tsx
│   │   │   ├── ButtonPrimary.tsx
│   │   │   ├── CheckBox/
│   │   │   │   ├── Checkbox.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── ChoosePath/
│   │   │   │   ├── List.tsx
│   │   │   │   ├── components/
│   │   │   │   │   ├── Footer.tsx
│   │   │   │   │   ├── Header.tsx
│   │   │   │   │   ├── ListItem.tsx
│   │   │   │   │   ├── Main.tsx
│   │   │   │   │   ├── NewFolderModal.tsx
│   │   │   │   │   └── OpenStorageModal.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── ConfirmAlert.tsx
│   │   │   ├── Dialog.tsx
│   │   │   ├── DorpDownMenu.tsx
│   │   │   ├── DorpDownPanel/
│   │   │   │   ├── Panel.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── DrawerLayoutFixed.tsx
│   │   │   ├── FileSelect.tsx
│   │   │   ├── Icon.tsx
│   │   │   ├── Image.tsx
│   │   │   ├── ImageBackground.tsx
│   │   │   ├── Input.tsx
│   │   │   ├── Loading.tsx
│   │   │   ├── LoadingMask.tsx
│   │   │   ├── Menu.tsx
│   │   │   ├── Modal.tsx
│   │   │   ├── Popup.tsx
│   │   │   ├── ScaledImage.tsx
│   │   │   ├── Slider.tsx
│   │   │   ├── StatusBar.tsx
│   │   │   └── Text.tsx
│   │   └── player/
│   │       ├── PlayerBar/
│   │       │   ├── components/
│   │       │   │   ├── ControlBtn.tsx
│   │       │   │   ├── Pic.tsx
│   │       │   │   ├── PlayInfo.tsx
│   │       │   │   ├── Status.tsx
│   │       │   │   └── Title.tsx
│   │       │   └── index.tsx
│   │       ├── Progress.tsx
│   │       └── ProgressBar.tsx
│   ├── config/
│   │   ├── constant.ts
│   │   ├── defaultSetting.ts
│   │   ├── globalData.ts
│   │   ├── index.js
│   │   ├── migrate.ts
│   │   ├── migrateSetting.ts
│   │   └── setting.ts
│   ├── core/
│   │   ├── apiSource.ts
│   │   ├── common.ts
│   │   ├── desktopLyric.ts
│   │   ├── dislikeList.ts
│   │   ├── hotSearch.ts
│   │   ├── init/
│   │   │   ├── common.ts
│   │   │   ├── dataInit.ts
│   │   │   ├── deeplink/
│   │   │   │   ├── fileAction.ts
│   │   │   │   ├── index.ts
│   │   │   │   ├── musicAction.js
│   │   │   │   ├── playSonglist.ts
│   │   │   │   ├── playerAction.ts
│   │   │   │   ├── songlistAction.js
│   │   │   │   └── utils.js
│   │   │   ├── i18n.ts
│   │   │   ├── index.ts
│   │   │   ├── player/
│   │   │   │   ├── index.ts
│   │   │   │   ├── lyric.ts
│   │   │   │   ├── playInfo.ts
│   │   │   │   ├── playProgress.ts
│   │   │   │   ├── playStatus.ts
│   │   │   │   ├── player.ts
│   │   │   │   ├── playerEvent.ts
│   │   │   │   ├── preloadNextMusic.ts
│   │   │   │   └── watchList.ts
│   │   │   ├── sync.ts
│   │   │   ├── theme.ts
│   │   │   └── userApi/
│   │   │       ├── index.ts
│   │   │       └── request.js
│   │   ├── leaderboard.ts
│   │   ├── list.ts
│   │   ├── lyric.ts
│   │   ├── music/
│   │   │   ├── download.ts
│   │   │   ├── index.ts
│   │   │   ├── local.ts
│   │   │   ├── online.ts
│   │   │   └── utils.ts
│   │   ├── player/
│   │   │   ├── playInfo.ts
│   │   │   ├── playStatus.ts
│   │   │   ├── playedList.ts
│   │   │   ├── player.ts
│   │   │   ├── progress.ts
│   │   │   ├── tempPlayList.ts
│   │   │   ├── timeoutExit.ts
│   │   │   └── utils.ts
│   │   ├── search/
│   │   │   ├── music.ts
│   │   │   ├── search.ts
│   │   │   └── songlist.ts
│   │   ├── songlist.ts
│   │   ├── sync.ts
│   │   ├── syncSourceList.ts
│   │   ├── theme.ts
│   │   ├── userApi.ts
│   │   └── version.ts
│   ├── event/
│   │   ├── Event.ts
│   │   ├── appEvent.ts
│   │   ├── dislikeEvent.ts
│   │   ├── listEvent.ts
│   │   └── stateEvent.ts
│   ├── lang/
│   │   ├── Readme.md
│   │   ├── en-us.json
│   │   ├── i18n.ts
│   │   ├── index.ts
│   │   ├── zh-cn.json
│   │   └── zh-tw.json
│   ├── navigation/
│   │   ├── components/
│   │   │   ├── ModalContent.tsx
│   │   │   ├── PactModal.tsx
│   │   │   ├── SyncModeModal.tsx
│   │   │   ├── Toast.js
│   │   │   └── VersionModal.tsx
│   │   ├── event.ts
│   │   ├── hooks.ts
│   │   ├── index.ts
│   │   ├── navigation.ts
│   │   ├── regLaunchedEvent.ts
│   │   ├── registerScreens.tsx
│   │   ├── screenNames.ts
│   │   └── utils.ts
│   ├── plugins/
│   │   ├── lyric.ts
│   │   ├── player/
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   ├── playList.ts
│   │   │   ├── service.ts
│   │   │   └── utils.ts
│   │   ├── storage.ts
│   │   └── sync/
│   │       ├── client/
│   │       │   ├── auth.ts
│   │       │   ├── client.ts
│   │       │   ├── index.ts
│   │       │   ├── modules/
│   │       │   │   ├── dislike/
│   │       │   │   │   ├── handler.ts
│   │       │   │   │   ├── index.ts
│   │       │   │   │   └── localEvent.ts
│   │       │   │   ├── index.ts
│   │       │   │   └── list/
│   │       │   │       ├── handler.ts
│   │       │   │       ├── index.ts
│   │       │   │       └── localEvent.ts
│   │       │   ├── sync/
│   │       │   │   ├── handler.ts
│   │       │   │   └── index.ts
│   │       │   └── utils.ts
│   │       ├── constants.ts
│   │       ├── data.ts
│   │       ├── dislikeEvent.ts
│   │       ├── index.ts
│   │       ├── listEvent.ts
│   │       ├── log.ts
│   │       └── utils.ts
│   ├── resources/
│   │   └── fonts/
│   │       └── selection.json
│   ├── screens/
│   │   ├── Comment/
│   │   │   ├── CommentHot.tsx
│   │   │   ├── CommentNew.tsx
│   │   │   ├── components/
│   │   │   │   ├── CommentFloor.tsx
│   │   │   │   ├── CommentImage.tsx
│   │   │   │   ├── CommentText.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   └── List.tsx
│   │   │   ├── index.tsx
│   │   │   └── utils.ts
│   │   ├── Home/
│   │   │   ├── Horizontal/
│   │   │   │   ├── Aside.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── Main.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── Vertical/
│   │   │   │   ├── Content.tsx
│   │   │   │   ├── DrawerNav.tsx
│   │   │   │   ├── Header.tsx
│   │   │   │   ├── Main.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── Views/
│   │   │   │   ├── Download/
│   │   │   │   │   └── index.js
│   │   │   │   ├── Leaderboard/
│   │   │   │   │   ├── BoardsList/
│   │   │   │   │   │   ├── List.tsx
│   │   │   │   │   │   ├── ListItem.tsx
│   │   │   │   │   │   ├── ListMenu.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── Horizontal/
│   │   │   │   │   │   ├── LeftBar.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── MusicList.tsx
│   │   │   │   │   ├── Vertical/
│   │   │   │   │   │   ├── HeaderBar/
│   │   │   │   │   │   │   ├── ActiveListName.tsx
│   │   │   │   │   │   │   ├── SourceSelector.tsx
│   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── listAction.ts
│   │   │   │   ├── Mylist/
│   │   │   │   │   ├── MusicList/
│   │   │   │   │   │   ├── ActiveList.tsx
│   │   │   │   │   │   ├── List.tsx
│   │   │   │   │   │   ├── ListItem.tsx
│   │   │   │   │   │   ├── ListMenu.tsx
│   │   │   │   │   │   ├── ListMusicSearch.tsx
│   │   │   │   │   │   ├── ListSearchBar.tsx
│   │   │   │   │   │   ├── MultipleModeBar.tsx
│   │   │   │   │   │   ├── MusicPositionModal.tsx
│   │   │   │   │   │   ├── MusicToggleModal.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   └── listAction.ts
│   │   │   │   │   ├── MyList/
│   │   │   │   │   │   ├── DuplicateMusic.tsx
│   │   │   │   │   │   ├── List.tsx
│   │   │   │   │   │   ├── ListImportExport.tsx
│   │   │   │   │   │   ├── ListMenu.tsx
│   │   │   │   │   │   ├── ListMusicSort.tsx
│   │   │   │   │   │   ├── ListNameEdit.tsx
│   │   │   │   │   │   ├── index.tsx
│   │   │   │   │   │   ├── listAction.ts
│   │   │   │   │   │   └── utils.ts
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── Search/
│   │   │   │   │   ├── BlankView/
│   │   │   │   │   │   ├── HistorySearch.tsx
│   │   │   │   │   │   ├── HotSearch.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── HeaderBar/
│   │   │   │   │   │   ├── SearchInput.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── List.tsx
│   │   │   │   │   ├── MusicList.tsx
│   │   │   │   │   ├── SearchTypeSelector.tsx
│   │   │   │   │   ├── SonglistList.tsx
│   │   │   │   │   ├── TipList.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── Setting/
│   │   │   │   │   ├── Horizontal/
│   │   │   │   │   │   ├── NavList.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── Main.tsx
│   │   │   │   │   ├── Vertical/
│   │   │   │   │   │   ├── Header.tsx
│   │   │   │   │   │   ├── Main.tsx
│   │   │   │   │   │   ├── NavList.tsx
│   │   │   │   │   │   └── index.tsx
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── Button.tsx
│   │   │   │   │   │   ├── CheckBoxItem.tsx
│   │   │   │   │   │   ├── InputItem.tsx
│   │   │   │   │   │   ├── Section.tsx
│   │   │   │   │   │   ├── Slider.tsx
│   │   │   │   │   │   └── SubTitle.tsx
│   │   │   │   │   ├── index.tsx
│   │   │   │   │   └── settings/
│   │   │   │   │       ├── About.tsx
│   │   │   │   │       ├── Backup/
│   │   │   │   │       │   ├── All.js
│   │   │   │   │       │   ├── ListImportExport.tsx
│   │   │   │   │       │   ├── Part.tsx
│   │   │   │   │       │   ├── actions.ts
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Basic/
│   │   │   │   │       │   ├── DrawerLayoutPosition.tsx
│   │   │   │   │       │   ├── FontSize.tsx
│   │   │   │   │       │   ├── IsAllowProgressBarSeek.tsx
│   │   │   │   │       │   ├── IsAlwaysKeepStatusbarHeight.tsx
│   │   │   │   │       │   ├── IsAutoHidePlayBar.tsx
│   │   │   │   │       │   ├── IsHomePageScroll.tsx
│   │   │   │   │       │   ├── IsShowBackBtn.tsx
│   │   │   │   │       │   ├── IsShowExitBtn.tsx
│   │   │   │   │       │   ├── IsStartupAutoPlay.tsx
│   │   │   │   │       │   ├── IsStartupPushPlayDetailScreen.tsx
│   │   │   │   │       │   ├── IsUseSystemFileSelector.tsx
│   │   │   │   │       │   ├── Language.tsx
│   │   │   │   │       │   ├── ShareType.tsx
│   │   │   │   │       │   ├── Source.tsx
│   │   │   │   │       │   ├── SourceName.tsx
│   │   │   │   │       │   ├── UserApiEditModal/
│   │   │   │   │       │   │   ├── ImportBtn.tsx
│   │   │   │   │       │   │   ├── List.tsx
│   │   │   │   │       │   │   ├── ScriptImportExport.tsx
│   │   │   │   │       │   │   ├── ScriptImportOnline.tsx
│   │   │   │   │       │   │   ├── action.ts
│   │   │   │   │       │   │   └── index.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── List/
│   │   │   │   │       │   ├── AddMusicLocationType.tsx
│   │   │   │   │       │   ├── IsClickPlayList.tsx
│   │   │   │   │       │   ├── IsShowAlbumName.tsx
│   │   │   │   │       │   ├── IsShowInterval.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── LyricDesktop/
│   │   │   │   │       │   ├── IsLockLyric.tsx
│   │   │   │   │       │   ├── IsShowLyric.tsx
│   │   │   │   │       │   ├── IsShowToggleAnima.tsx
│   │   │   │   │       │   ├── IsSingleLine.tsx
│   │   │   │   │       │   ├── MaxLineNum.tsx
│   │   │   │   │       │   ├── TextOpacity.tsx
│   │   │   │   │       │   ├── TextPositionX.tsx
│   │   │   │   │       │   ├── TextPositionY.tsx
│   │   │   │   │       │   ├── TextSize.tsx
│   │   │   │   │       │   ├── Theme.tsx
│   │   │   │   │       │   ├── ViewWidth.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Other/
│   │   │   │   │       │   ├── DislikeEditModal.tsx
│   │   │   │   │       │   ├── DislikeList.tsx
│   │   │   │   │       │   ├── Log.tsx
│   │   │   │   │       │   ├── MetaCache.tsx
│   │   │   │   │       │   ├── ResourceCache.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Player/
│   │   │   │   │       │   ├── IsAutoCleanPlayedList.tsx
│   │   │   │   │       │   ├── IsEnableAudioOffload.tsx
│   │   │   │   │       │   ├── IsHandleAudioFocus.tsx
│   │   │   │   │       │   ├── IsS2T.tsx
│   │   │   │   │       │   ├── IsSavePlayTime.tsx
│   │   │   │   │       │   ├── IsShowBluetoothLyric.tsx
│   │   │   │   │       │   ├── IsShowLyricRoma.tsx
│   │   │   │   │       │   ├── IsShowLyricTranslation.tsx
│   │   │   │   │       │   ├── IsShowNotificationImage.tsx
│   │   │   │   │       │   ├── MaxCache.tsx
│   │   │   │   │       │   ├── PlayHighQuality.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Search/
│   │   │   │   │       │   ├── IsShowHistorySearch.tsx
│   │   │   │   │       │   ├── IsShowHotSearch.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       ├── Sync/
│   │   │   │   │       │   ├── History.tsx
│   │   │   │   │       │   ├── IsEnable.tsx
│   │   │   │   │       │   ├── index.tsx
│   │   │   │   │       │   └── isEnable.tsx.bak
│   │   │   │   │       ├── Theme/
│   │   │   │   │       │   ├── IsAutoTheme.tsx
│   │   │   │   │       │   ├── IsDynamicBg.tsx
│   │   │   │   │       │   ├── IsFontShadow.tsx
│   │   │   │   │       │   ├── IsHideBgDark.tsx
│   │   │   │   │       │   ├── Theme.tsx
│   │   │   │   │       │   └── index.tsx
│   │   │   │   │       └── Version.tsx
│   │   │   │   └── SongList/
│   │   │   │       ├── Content.tsx
│   │   │   │       ├── HeaderBar/
│   │   │   │       │   ├── OpenList/
│   │   │   │       │   │   ├── Modal.tsx
│   │   │   │       │   │   └── index.tsx
│   │   │   │       │   ├── SortTab.tsx
│   │   │   │       │   ├── SourceSelector.tsx
│   │   │   │       │   ├── Tag/
│   │   │   │       │   │   ├── CurrentTagBtn.tsx
│   │   │   │       │   │   └── index.tsx
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── List.tsx
│   │   │   │       ├── TagList/
│   │   │   │       │   ├── List.tsx
│   │   │   │       │   ├── TagGroup.tsx
│   │   │   │       │   └── index.tsx
│   │   │   │       ├── components/
│   │   │   │       │   └── Songlist/
│   │   │   │       │       ├── List.tsx
│   │   │   │       │       ├── ListItem.tsx
│   │   │   │       │       └── index.tsx
│   │   │   │       └── index.tsx
│   │   │   └── index.tsx
│   │   ├── PlayDetail/
│   │   │   ├── Horizontal/
│   │   │   │   ├── Lyric.tsx
│   │   │   │   ├── MoreBtn/
│   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   ├── MusicAddBtn.tsx
│   │   │   │   │   ├── PlayModeBtn.tsx
│   │   │   │   │   ├── TimeoutExitBtn.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── Pic.tsx
│   │   │   │   ├── Player/
│   │   │   │   │   ├── ControlBtn.tsx
│   │   │   │   │   ├── PlayInfo.tsx
│   │   │   │   │   ├── Status.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── components/
│   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   ├── CommentBtn.tsx
│   │   │   │   │   ├── DesktopLyricBtn.tsx
│   │   │   │   │   └── Header.tsx
│   │   │   │   ├── constant.ts
│   │   │   │   └── index.tsx
│   │   │   ├── Vertical/
│   │   │   │   ├── Lyric.tsx
│   │   │   │   ├── Pic.tsx
│   │   │   │   ├── Player/
│   │   │   │   │   ├── components/
│   │   │   │   │   │   ├── ControlBtn.tsx
│   │   │   │   │   │   ├── MoreBtn/
│   │   │   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   │   │   ├── CommentBtn.tsx
│   │   │   │   │   │   │   ├── DesktopLyricBtn.tsx
│   │   │   │   │   │   │   ├── MusicAddBtn.tsx
│   │   │   │   │   │   │   ├── PlayModeBtn.tsx
│   │   │   │   │   │   │   ├── TimeoutExitBtn.tsx
│   │   │   │   │   │   │   └── index.tsx
│   │   │   │   │   │   ├── PlayInfo.tsx
│   │   │   │   │   │   └── Status.tsx
│   │   │   │   │   └── index.tsx
│   │   │   │   ├── components/
│   │   │   │   │   ├── Btn.tsx
│   │   │   │   │   ├── Header.tsx
│   │   │   │   │   └── TimeoutExitBtn.tsx
│   │   │   │   └── index.tsx
│   │   │   ├── components/
│   │   │   │   ├── PlayLine.tsx
│   │   │   │   └── SettingPopup/
│   │   │   │       ├── index.tsx
│   │   │   │       └── settings/
│   │   │   │           ├── SettingLrcAlign.tsx
│   │   │   │           ├── SettingLrcFontSize.tsx
│   │   │   │           ├── SettingLyricProgress.tsx
│   │   │   │           ├── SettingPlaybackRate.tsx
│   │   │   │           ├── SettingVolume.tsx
│   │   │   │           └── style.ts
│   │   │   └── index.tsx
│   │   ├── SonglistDetail/
│   │   │   ├── ActionBar.tsx
│   │   │   ├── Header.tsx
│   │   │   ├── MusicList.tsx
│   │   │   ├── index.tsx
│   │   │   ├── listAction.ts
│   │   │   └── state.ts
│   │   └── index.ts
│   ├── store/
│   │   ├── Provider/
│   │   │   ├── Provider.tsx
│   │   │   ├── ThemeProvider.tsx
│   │   │   └── index.ts
│   │   ├── common/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── dislikeList/
│   │   │   ├── action.ts
│   │   │   ├── event.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   └── state.ts
│   │   ├── hotSearch/
│   │   │   ├── action.ts
│   │   │   └── state.ts
│   │   ├── index.ts
│   │   ├── leaderboard/
│   │   │   ├── action.ts
│   │   │   └── state.ts
│   │   ├── list/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── player/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── search/
│   │   │   ├── action.ts
│   │   │   ├── music/
│   │   │   │   ├── action.ts
│   │   │   │   └── state.ts
│   │   │   ├── songlist/
│   │   │   │   ├── action.ts
│   │   │   │   └── state.ts
│   │   │   └── state.ts
│   │   ├── setting/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── songlist/
│   │   │   ├── action.ts
│   │   │   └── state.ts
│   │   ├── sync/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── theme/
│   │   │   ├── action.ts
│   │   │   ├── hook.ts
│   │   │   └── state.ts
│   │   ├── userApi/
│   │   │   ├── action.ts
│   │   │   ├── event.ts
│   │   │   ├── hook.ts
│   │   │   ├── index.ts
│   │   │   └── state.ts
│   │   └── version/
│   │       ├── action.ts
│   │       ├── hook.ts
│   │       └── state.ts
│   ├── theme/
│   │   ├── Colors.js
│   │   ├── Typography.js
│   │   ├── index.js
│   │   └── themes/
│   │       ├── colorUtils.js
│   │       ├── createThemes.js
│   │       ├── index.ts
│   │       ├── themes.ts
│   │       └── utils.js
│   ├── types/
│   │   ├── app.d.ts
│   │   ├── app_setting.d.ts
│   │   ├── common.d.ts
│   │   ├── config_files.d.ts
│   │   ├── dislike_list.d.ts
│   │   ├── dislike_list_sync.d.ts
│   │   ├── download_list.d.ts
│   │   ├── list.d.ts
│   │   ├── list_sync.d.ts
│   │   ├── music.d.ts
│   │   ├── player.d.ts
│   │   ├── sync.d.ts
│   │   ├── sync_common.d.ts
│   │   ├── theme.d.ts
│   │   ├── user_api.d.ts
│   │   └── utils.d.ts
│   └── utils/
│       ├── bootLog.ts
│       ├── common.ts
│       ├── data.ts
│       ├── dislikeManage.ts
│       ├── errorHandle.ts
│       ├── fs.ts
│       ├── hooks/
│       │   ├── index.js
│       │   ├── useAnimateColor.ts
│       │   ├── useAnimateNumber.ts
│       │   ├── useAssertApiSupport.js
│       │   ├── useBackHandler.ts
│       │   ├── useDeviceOrientation.js
│       │   ├── useDrag.ts
│       │   ├── useHorizontalMode.ts
│       │   ├── useKeyboard.js
│       │   ├── useLayout.tsx
│       │   ├── usePlayTime.js
│       │   ├── useUnmounted.tsx
│       │   └── useWindowSize.ts
│       ├── index.ts
│       ├── listManage.ts
│       ├── localMediaMetadata.ts
│       ├── log.ts
│       ├── lrcTools.ts
│       ├── message.ts
│       ├── music.ts
│       ├── musicSdk/
│       │   ├── api-source-info.ts
│       │   ├── api-source.js
│       │   ├── bd/
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   └── songList.js
│       │   ├── index.js
│       │   ├── kg/
│       │   │   ├── album.js
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── pic.js
│       │   │   ├── singer.js
│       │   │   ├── songList.js
│       │   │   ├── temp/
│       │   │   │   ├── musicSearch-new.js
│       │   │   │   └── songList-new.js
│       │   │   ├── tipSearch.js
│       │   │   └── util.js
│       │   ├── kw/
│       │   │   ├── album.js
│       │   │   ├── comment.js
│       │   │   ├── decodeLyric.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicSearch.js
│       │   │   ├── pic.js
│       │   │   ├── songList.js
│       │   │   ├── tipSearch.js
│       │   │   └── util.js
│       │   ├── mg/
│       │   │   ├── album.js
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── pic.js
│       │   │   ├── songId.js
│       │   │   ├── songList.js
│       │   │   ├── temp/
│       │   │   │   └── leaderboard-old.js
│       │   │   ├── tipSearch.js
│       │   │   └── utils/
│       │   │       ├── index.js
│       │   │       └── mrc.js
│       │   ├── options.js
│       │   ├── tx/
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── songList.js
│       │   │   └── tipSearch.js
│       │   ├── utils.js
│       │   ├── wy/
│       │   │   ├── comment.js
│       │   │   ├── hotSearch.js
│       │   │   ├── index.js
│       │   │   ├── leaderboard.js
│       │   │   ├── lyric.js
│       │   │   ├── musicDetail.js
│       │   │   ├── musicInfo.js
│       │   │   ├── musicSearch.js
│       │   │   ├── songList.js
│       │   │   ├── tipSearch.js
│       │   │   └── utils/
│       │   │       ├── crypto.js
│       │   │       └── index.js
│       │   └── xm.js
│       ├── nativeModules/
│       │   ├── cache.ts
│       │   ├── crypto.ts
│       │   ├── cryptoTest.ts
│       │   ├── lyricDesktop.ts
│       │   ├── userApi.ts
│       │   └── utils.ts
│       ├── pixelRatio.ts
│       ├── request.js
│       ├── scroll.ts
│       ├── simplify-chinese-main/
│       │   ├── .gitignore
│       │   ├── LICENSE.md
│       │   ├── README.md
│       │   ├── chinese.js
│       │   ├── index.d.ts
│       │   ├── index.js
│       │   └── package.json
│       ├── tools.ts
│       ├── version.js
│       └── windowSizeTools.ts
├── test.js
└── tsconfig.json
Download .txt
SYMBOL INDEX (1736 symbols across 356 files)

FILE: android/app/src/main/assets/script/user-api-preload.js
  method callback (line 69) | callback(...args) {
  function bytesToString (line 91) | function bytesToString(bytes) {
  function stringToBytes (line 110) | function stringToBytes(inputString) {
  method aesEncrypt (line 353) | aesEncrypt(buffer, mode, key, iv) {
  method rsaEncrypt (line 364) | rsaEncrypt(buffer, key) {
  method randomBytes (line 371) | randomBytes(size) {
  method md5 (line 378) | md5(str) {
  method from (line 386) | from(input, encoding) {
  method bufToString (line 405) | bufToString(buf, format) {
  method request (line 448) | request(url, { method = 'get', timeout, headers, body, form, formData, b...
  method send (line 484) | send(eventName, data) {
  method on (line 504) | on(eventName, handler) {
  method apply (line 549) | apply() {
  method construct (line 552) | construct() {

FILE: android/app/src/main/java/cn/toside/music/mobile/MainActivity.java
  class MainActivity (line 8) | public class MainActivity extends NavigationActivity {

FILE: android/app/src/main/java/cn/toside/music/mobile/MainApplication.java
  class MainApplication (line 18) | public class MainApplication extends NavigationApplication {
    method getUseDeveloperSupport (line 22) | @Override
    method getPackages (line 27) | @Override
    method getJSMainModuleName (line 41) | @Override
    method isNewArchEnabled (line 46) | @Override
    method isHermesEnabled (line 51) | @Override
    method getReactNativeHost (line 57) | @Override
    method onCreate (line 62) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/cache/CacheClearAsyncTask.java
  class CacheClearAsyncTask (line 8) | public class CacheClearAsyncTask extends AsyncTask<Integer,Integer,Strin...
    method CacheClearAsyncTask (line 11) | public CacheClearAsyncTask(CacheModule clearCacheModule, Promise promi...
    method onPreExecute (line 17) | @Override
    method onPostExecute (line 22) | @Override
    method doInBackground (line 28) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/cache/CacheModule.java
  class CacheModule (line 15) | public class CacheModule extends ReactContextBaseJavaModule {
    method CacheModule (line 18) | CacheModule(ReactApplicationContext reactContext) {
    method getName (line 23) | @Override
    method getAppCacheSize (line 29) | @ReactMethod
    method clearAppCache (line 47) | @ReactMethod
    method clearCache (line 56) | public void clearCache() {

FILE: android/app/src/main/java/cn/toside/music/mobile/cache/CachePackage.java
  class CachePackage (line 12) | public class CachePackage implements ReactPackage {
    method createViewManagers (line 14) | @Override
    method createNativeModules (line 19) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/cache/Utils.java
  class Utils (line 8) | public class Utils {
    method getDirSize (line 15) | static public long getDirSize(File dir) {
    method isMethodsCompat (line 37) | static public boolean isMethodsCompat(int VersionCode) {
    method getExternalCacheDir (line 42) | static public File getExternalCacheDir(Context context) {
    method clearCacheFolder (line 56) | static public int clearCacheFolder(File dir, long curTime) {

FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/AES.java
  class AES (line 11) | public class AES {
    method decodeBase64 (line 15) | private static byte[] decodeBase64(String data) {
    method encodeBase64 (line 19) | private static String encodeBase64(byte[] data) {
    method encrypt (line 23) | public static String encrypt(byte[] data, byte[] key, byte[] iv, Strin...
    method encrypt (line 41) | public static String encrypt(byte[] data, byte[] key, String mode) {
    method encrypt (line 55) | public static String encrypt(String data, String key, String iv, Strin...
    method decrypt (line 61) | public static String decrypt(byte[] data, byte[] key, byte[] iv, Strin...
    method decrypt (line 79) | public static String decrypt(byte[] data, byte[] key, String mode) {
    method decrypt (line 93) | public static String decrypt(String data, String key, String iv, Strin...

FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/CryptoModule.java
  class CryptoModule (line 20) | public class CryptoModule extends ReactContextBaseJavaModule {
    method CryptoModule (line 23) | CryptoModule(ReactApplicationContext reactContext) {
    method getName (line 28) | @Override
    method generateRsaKey (line 33) | @ReactMethod
    method rsaEncrypt (line 60) | @ReactMethod
    method rsaDecrypt (line 75) | @ReactMethod
    method rsaEncryptSync (line 90) | @ReactMethod(isBlockingSynchronousMethod = true)
    method rsaDecryptSync (line 95) | @ReactMethod(isBlockingSynchronousMethod = true)
    method aesEncrypt (line 100) | @ReactMethod
    method aesDecrypt (line 115) | @ReactMethod
    method aesEncryptSync (line 130) | @ReactMethod(isBlockingSynchronousMethod = true)
    method aesDecryptSync (line 135) | @ReactMethod(isBlockingSynchronousMethod = true)

FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/CryptoPackage.java
  class CryptoPackage (line 12) | public class CryptoPackage implements ReactPackage {
    method createViewManagers (line 14) | @Override
    method createNativeModules (line 19) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/crypto/RSA.java
  class RSA (line 15) | public class RSA {
    method getKeyPair (line 57) | public static KeyPair getKeyPair() {
    method encryptRSAToString (line 70) | public static String encryptRSAToString(String decryptedBase64, String...
    method decryptRSAToString (line 91) | public static String decryptRSAToString(String encryptedBase64, String...

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/Lyric.java
  class Lyric (line 20) | public class Lyric extends LyricPlayer {
    method Lyric (line 38) | Lyric(ReactApplicationContext reactContext, boolean isShowTranslation,...
    method registerScreenBroadcastReceiver (line 47) | private void registerScreenBroadcastReceiver() {
    method isDisableAutoPause (line 106) | private boolean isDisableAutoPause() {
    method handleScreenOff (line 109) | private void handleScreenOff() {
    method handleScreenOn (line 115) | private void handleScreenOn() {
    method pausePlayer (line 125) | private void pausePlayer() {
    method setCurrentLyric (line 131) | private void setCurrentLyric(String lyric, ArrayList<String> extendedL...
    method handleGetCurrentLyric (line 142) | private void handleGetCurrentLyric(int lineNum) {
    method setSendLyricTextEvent (line 154) | public void setSendLyricTextEvent(boolean isSend) {
    method showDesktopLyric (line 165) | public void showDesktopLyric(Bundle options, Promise promise) {
    method hideDesktopLyric (line 181) | public void hideDesktopLyric() {
    method refreshLyric (line 191) | private void refreshLyric() {
    method setLyric (line 199) | public void setLyric(String lyric, String translation, String romaLyri...
    method onSetLyric (line 206) | @Override
    method onPlay (line 216) | @Override
    method pauseLyric (line 222) | public void pauseLyric() {
    method lockLyric (line 228) | public void lockLyric() {
    method unlockLyric (line 233) | public void unlockLyric() {
    method setMaxLineNum (line 238) | public void setMaxLineNum(int maxLineNum) {
    method setWidth (line 243) | public void setWidth(int width) {
    method setSingleLine (line 248) | public void setSingleLine(boolean singleLine) {
    method setShowToggleAnima (line 253) | public void setShowToggleAnima(boolean showToggleAnima) {
    method toggleTranslation (line 258) | public void toggleTranslation(boolean isShowTranslation) {
    method toggleRoma (line 263) | public void toggleRoma(boolean isShowRoma) {
    method setPlayedColor (line 268) | public void setPlayedColor(String unplayColor, String playedColor, Str...
    method setAlpha (line 273) | public void setAlpha(float alpha) {
    method setTextSize (line 278) | public void setTextSize(float size) {
    method setLyricTextPosition (line 283) | public void setLyricTextPosition(String positionX, String positionY) {

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricEvent.java
  class LyricEvent (line 9) | public class LyricEvent {
    method LyricEvent (line 14) | LyricEvent(ReactApplicationContext reactContext) { this.reactContext =...
    method sendEvent (line 16) | public void sendEvent(String eventName, @Nullable WritableMap params) {

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricModule.java
  class LyricModule (line 16) | public class LyricModule extends ReactContextBaseJavaModule {
    method LyricModule (line 27) | LyricModule(ReactApplicationContext reactContext) {
    method getName (line 41) | @Override
    method addListener (line 51) | @ReactMethod
    method removeListeners (line 60) | @ReactMethod
    method showDesktopLyric (line 68) | @ReactMethod
    method hideDesktopLyric (line 74) | @ReactMethod
    method setSendLyricTextEvent (line 80) | @ReactMethod
    method setLyric (line 88) | @ReactMethod
    method setPlaybackRate (line 96) | @ReactMethod
    method toggleTranslation (line 103) | @ReactMethod
    method toggleRoma (line 110) | @ReactMethod
    method play (line 117) | @ReactMethod
    method pause (line 124) | @ReactMethod
    method toggleLock (line 131) | @ReactMethod
    method setColor (line 143) | @ReactMethod
    method setAlpha (line 149) | @ReactMethod
    method setTextSize (line 155) | @ReactMethod
    method setMaxLineNum (line 161) | @ReactMethod
    method setSingleLine (line 167) | @ReactMethod
    method setShowToggleAnima (line 173) | @ReactMethod
    method setWidth (line 179) | @ReactMethod
    method setLyricTextPosition (line 185) | @ReactMethod
    method checkOverlayPermission (line 191) | @ReactMethod
    method openOverlayPermissionActivity (line 199) | @ReactMethod

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricPackage.java
  class LyricPackage (line 12) | public class LyricPackage implements ReactPackage {
    method createViewManagers (line 13) | @Override
    method createNativeModules (line 18) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricPlayer.java
  class LyricPlayer (line 14) | public class LyricPlayer {
    method LyricPlayer (line 37) | LyricPlayer() {
    method setTempPause (line 50) | public void setTempPause(boolean isPaused) {
    method startTimeout (line 72) | private void startTimeout(Runnable runnable, long delay) {
    method stopTimeout (line 77) | private void stopTimeout() {
    method getNow (line 83) | private int getNow() {
    method getCurrentTime (line 87) | private int getCurrentTime() {
    method initTag (line 91) | private void initTag() {
    method formatTimeLabel (line 120) | private String formatTimeLabel(String label) {
    method parseExtendedLyric (line 126) | private void parseExtendedLyric(HashMap linesMap, String extendedLyric) {
    method initLines (line 147) | private void initLines() {
    method init (line 233) | private void  init() {
    method pause (line 241) | public void pause() {
    method play (line 254) | public void play(int curTime) {
    method findCurLineNum (line 269) | private int findCurLineNum(int curTime, int startIndex) {
    method findCurLineNum (line 279) | private int findCurLineNum(int curTime) {
    method handleMaxLine (line 283) | private void handleMaxLine() {
    method refresh (line 288) | private void refresh() {
    method setLyric (line 333) | public void setLyric(String lyric, ArrayList<String> extendedLyrics) {
    method setPlaybackRate (line 340) | public void setPlaybackRate(float playbackRate) {
    method onPlay (line 347) | public void onPlay(int lineNum) {}
    method onSetLyric (line 349) | public void onSetLyric(List lines) {}

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricSwitchView.java
  class LyricSwitchView (line 22) | @SuppressLint({"ViewConstructor"})
    method LyricSwitchView (line 32) | public LyricSwitchView(Context context, boolean isSingleLine, boolean ...
    method inAnim (line 63) | @Nullable
    method outAnim (line 93) | @Nullable
    method setAnima (line 122) | private void setAnima() {
    method setShowAnima (line 134) | public void setShowAnima(boolean showAnima) {
    method getText (line 139) | public CharSequence getText() {
    method getPaint (line 144) | public TextPaint getPaint() {
    method setWidth (line 150) | public void setWidth(int i) {
    method setTextColor (line 154) | public void setTextColor(int i) {
    method setShadowColor (line 158) | public void setShadowColor(int i) {
    method setSourceText (line 169) | public void setSourceText(CharSequence str) {
    method setLetterSpacings (line 173) | public void setLetterSpacings(float letterSpacing) {
    method setHeight (line 177) | public void setHeight(int i) {
    method setTypeface (line 181) | public void setTypeface(Typeface typeface) {
    method setSingleLine (line 185) | public void setSingleLine(boolean bool) {
    method setMaxLines (line 189) | public void setMaxLines(int i) {
    method setTextSize (line 193) | public void setTextSize(float f) {
    method setGravity (line 198) | public void setGravity(int i) {

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricTextView.java
  class LyricTextView (line 12) | @SuppressLint("AppCompatCustomView")
    method LyricTextView (line 31) | public LyricTextView(Context context) {
    method init (line 39) | private void init() {
    method onDetachedFromWindow (line 45) | @Override
    method onTextChanged (line 51) | @Override
    method setTextColor (line 61) | @Override
    method setShadowLayer (line 67) | @Override
    method setTextSize (line 73) | @Override
    method setWidth (line 81) | @Override
    method setHeight (line 89) | @Override
    method setGravity (line 98) | @Override
    method onDraw (line 117) | @Override
    method invalidateAfter (line 141) | private void invalidateAfter() {
    method startScroll (line 146) | private void startScroll() {
    method stopScroll (line 152) | private void stopScroll() {
    method getTextLength (line 158) | private float getTextLength() {
    method getDrawY (line 162) | private float getDrawY() {
    method getDrawX (line 186) | private float getDrawX() {

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/LyricView.java
  class LyricView (line 31) | public class LyricView extends Activity implements View.OnTouchListener {
    method LyricView (line 78) | LyricView(ReactApplicationContext reactContext, LyricEvent lyricEvent) {
    method listenOrientationEvent (line 84) | private void listenOrientationEvent() {
    method removeOrientationEvent (line 107) | private void removeOrientationEvent() {
    method getLayoutParamsFlags (line 113) | private int getLayoutParamsFlags() {
    method updateWH (line 130) | private boolean updateWH() {
    method setLayoutParamsHeight (line 140) | private void setLayoutParamsHeight() {
    method fixViewPosition (line 148) | private void fixViewPosition() {
    method updateViewPosition (line 164) | private void updateViewPosition() {
    method sendPositionEvent (line 181) | public void sendPositionEvent(float x, float y) {
    method showLyricView (line 208) | public void showLyricView(Bundle options) {
    method showLyricView (line 226) | public void showLyricView() {
    method parseColor (line 235) | public static int parseColor(String input) {
    method createTextView (line 250) | private void createTextView() {
    method handleShowLyric (line 296) | private void handleShowLyric() {
    method setLyric (line 372) | public void setLyric(String text, ArrayList<String> extendedLyrics) {
    method setMaxLineNum (line 390) | public void setMaxLineNum(int maxLineNum) {
    method setWidth (line 405) | public void setWidth(int width) {
    method onTouch (line 420) | @Override
    method lockView (line 491) | public void lockView() {
    method unlockView (line 503) | public void unlockView() {
    method setColor (line 515) | public void setColor(String unplayColor, String playedColor, String sh...
    method setLyricTextPosition (line 525) | public void setLyricTextPosition(String textX, String textY) {
    method setAlpha (line 560) | public void setAlpha(float alpha) {
    method setSingleLine (line 566) | public void setSingleLine(boolean isSingleLine) {
    method setShowToggleAnima (line 581) | public void setShowToggleAnima(boolean showToggleAnima) {
    method setTextSize (line 587) | public void setTextSize(float size) {
    method destroyView (line 595) | public void destroyView() {
    method destroy (line 602) | public void destroy() {

FILE: android/app/src/main/java/cn/toside/music/mobile/lyric/Utils.java
  class Utils (line 5) | public class Utils {
    method setTimeout (line 7) | public static Object setTimeout(Runnable runnable, long delay) {
    method clearTimeout (line 10) | public static void clearTimeout(Object timeoutEvent) {
    class TimeoutEvent (line 15) | private static class TimeoutEvent {
      method TimeoutEvent (line 19) | private TimeoutEvent(Runnable task, long delay) {
      method cancelTimeout (line 28) | private void cancelTimeout() {

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/Console.java
  class Console (line 9) | public class Console implements QuickJSContext.Console {
    method Console (line 12) | Console(Handler eventHandler) {
    method sendLog (line 16) | private void sendLog(String type, String log) {
    method log (line 24) | @Override
    method info (line 29) | @Override
    method warn (line 34) | @Override
    method error (line 39) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/HandlerWhat.java
  class HandlerWhat (line 3) | public class HandlerWhat {

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/JavaScriptThread.java
  class JavaScriptThread (line 13) | public class JavaScriptThread extends HandlerThread {
    method JavaScriptThread (line 19) | JavaScriptThread(ReactApplicationContext context, Bundle info) {
    method prepareHandler (line 25) | public void prepareHandler(final Handler mainHandler) {
    method getHandler (line 63) | public Handler getHandler() {
    method stopThread (line 67) | public void stopThread() {

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/JsHandler.java
  class JsHandler (line 12) | public class JsHandler extends Handler {
    method JsHandler (line 15) | JsHandler(Looper looper, UtilsEvent utilsEvent) {
    method sendInitFailedEvent (line 20) | private void sendInitFailedEvent(String errorMessage) {
    method sendLogEvent (line 29) | private void sendLogEvent(Object[] data) {
    method sendActionEvent (line 37) | private void sendActionEvent(String action, String data) {
    method handleMessage (line 44) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/QuickJS.java
  class QuickJS (line 21) | public class QuickJS {
    method QuickJS (line 30) | public QuickJS(ReactApplicationContext context, Handler eventHandler) {
    method init (line 35) | private void init() {
    method getPreloadScript (line 42) | private String getPreloadScript() {
    method createEnvObj (line 54) | private void createEnvObj(QuickJSContext jsContext) {
    method createJSEnv (line 133) | private boolean createJSEnv(String id, String name, String desc, Strin...
    method callNative (line 147) | private void callNative(String action, String data) {
    method loadScript (line 159) | public String loadScript(Bundle scriptInfo) {
    method callJS (line 185) | public Object callJS(String action) {
    method callJS (line 189) | public Object callJS(String action, Object... args) {
    method callJS (line 196) | public Object callJS(Object[] params) {
    method destroy (line 216) | public void destroy () {

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/UserApiModule.java
  class UserApiModule (line 14) | public class UserApiModule extends ReactContextBaseJavaModule {
    method UserApiModule (line 21) | UserApiModule(ReactApplicationContext reactContext) {
    method getName (line 28) | @Override
    method addListener (line 33) | @ReactMethod
    method removeListeners (line 42) | @ReactMethod
    method loadScript (line 50) | @ReactMethod
    method sendAction (line 69) | @ReactMethod
    method destroy (line 81) | @ReactMethod

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/UserApiPackage.java
  class UserApiPackage (line 11) | public class UserApiPackage implements ReactPackage {
    method createViewManagers (line 12) | @Override
    method createNativeModules (line 17) | @Override

FILE: android/app/src/main/java/cn/toside/music/mobile/userApi/UtilsEvent.java
  class UtilsEvent (line 11) | public class UtilsEvent {
    method UtilsEvent (line 15) | UtilsEvent(ReactApplicationContext reactContext) {
    method sendEvent (line 19) | public void sendEvent(String eventName, @Nullable WritableMap params) {

FILE: android/app/src/main/java/cn/toside/music/mobile/utils/AsyncTask.java
  class AsyncTask (line 13) | public class AsyncTask {
    class TaskRunner (line 15) | private static class TaskRunner {
      type Callback (line 19) | public interface Callback<Object> {
        method onComplete (line 20) | void onComplete(Object result);
      method executeAsync (line 23) | public <Object> void executeAsync(Callable<Object> callable, Callbac...
      method shutdown (line 34) | public void shutdown() {
    method runTask (line 39) | public static void runTask(Callable<Object> callable, Promise promise) {

FILE: android/app/src/main/java/cn/toside/music/mobile/utils/BatteryOptimizationUtil.java
  class BatteryOptimizationUtil (line 16) | public class BatteryOptimizationUtil {
    method isIgnoringBatteryOptimization (line 18) | public static boolean isIgnoringBatteryOptimization(Context context, S...
    method requestIgnoreBatteryOptimization (line 33) | public static boolean requestIgnoreBatteryOptimization(Context context...

FILE: android/app/src/main/java/cn/toside/music/mobile/utils/NotificationPermissionUtil.java
  class NotificationPermissionUtil (line 17) | public class NotificationPermissionUtil {
    method isNotificationsEnabled (line 20) | public static boolean isNotificationsEnabled(Context context) {
    method openNotificationPermissionActivity (line 53) | public static boolean openNotificationPermissionActivity(Context conte...

FILE: android/app/src/main/java/cn/toside/music/mobile/utils/Utils.java
  class Utils (line 24) | public class Utils {

FILE: android/app/src/main/java/cn/toside/music/mobile/utils/UtilsEvent.java
  class UtilsEvent (line 11) | public class UtilsEvent {
    method UtilsEvent (line 16) | UtilsEvent(ReactApplicationContext reactContext) { this.reactContext =...
    method sendEvent (line 18) | public void sendEvent(String eventName, @Nullable WritableMap params) {

FILE: android/app/src/main/java/cn/toside/music/mobile/utils/UtilsModule.java
  class UtilsModule (line 35) | public class UtilsModule extends ReactContextBaseJavaModule {
    method UtilsModule (line 42) | UtilsModule(ReactApplicationContext reactContext) {
    method getName (line 49) | @Override
    method addListener (line 54) | @ReactMethod
    method removeListeners (line 63) | @ReactMethod
    method registerScreenBroadcastReceiver (line 71) | private void registerScreenBroadcastReceiver() {
    method exitApp (line 101) | @ReactMethod
    method getSupportedAbis (line 118) | @ReactMethod
    method installApk (line 128) | @ReactMethod
    method screenkeepAwake (line 176) | @ReactMethod
    method screenUnkeepAwake (line 188) | @ReactMethod
    method getWIFIIPV4Address (line 204) | @ReactMethod
    method getDeviceName (line 224) | @ReactMethod
    method capitalize (line 234) | private String capitalize(String s) {
    method isNotificationsEnabled (line 246) | @ReactMethod
    method openNotificationPermissionActivity (line 255) | @ReactMethod
    method shareText (line 264) | @ReactMethod
    method getSystemLocales (line 274) | @ReactMethod
    method getWindowSize (line 336) | @ReactMethod
    method isIgnoringBatteryOptimization (line 359) | @ReactMethod
    method requestIgnoreBatteryOptimization (line 370) | @ReactMethod

FILE: android/app/src/main/java/cn/toside/music/mobile/utils/UtilsPackage.java
  class UtilsPackage (line 12) | public class UtilsPackage implements ReactPackage {
    method createViewManagers (line 14) | @Override
    method createNativeModules (line 19) | @Override

FILE: android/app/src/release/java/cn/toside/music/ReactNativeFlipper.java
  class ReactNativeFlipper (line 16) | public class ReactNativeFlipper {
    method initializeFlipper (line 17) | public static void initializeFlipper(Context context, ReactInstanceMan...

FILE: publish/utils/index.js
  function fm (line 33) | function fm(value) {

FILE: src/components/DesktopLyricEnable.tsx
  type DesktopLyricEnableType (line 11) | interface DesktopLyricEnableType {
  method setEnabled (line 22) | setEnabled(enabled) {

FILE: src/components/MetadataEditModal/InputItem.tsx
  type InputItemProps (line 10) | interface InputItemProps extends InputProps {

FILE: src/components/MetadataEditModal/MetadataForm.tsx
  type Metadata (line 16) | interface Metadata {
  type MetadataFormType (line 33) | interface MetadataFormType {
  method setForm (line 49) | setForm(path, data) {
  method getForm (line 58) | getForm() {

FILE: src/components/MetadataEditModal/ParseName.tsx
  type ParseNameProps (line 10) | interface ParseNameProps {

FILE: src/components/MetadataEditModal/PicItem.tsx
  type PicItemProps (line 12) | interface PicItemProps {

FILE: src/components/MetadataEditModal/TextAreaItem.tsx
  type TextAreaItemProps (line 11) | interface TextAreaItemProps extends InputProps {

FILE: src/components/MetadataEditModal/index.tsx
  type MetadataEditType (line 24) | interface MetadataEditType {
  type MetadataEditProps (line 27) | interface MetadataEditProps {
  method show (line 64) | show(path) {

FILE: src/components/MusicAddModal/List.tsx
  constant MIN_WIDTH (line 25) | const MIN_WIDTH = scaleSizeW(150)
  constant PADDING (line 26) | const PADDING = styles.list.paddingLeft + styles.list.paddingRight

FILE: src/components/MusicAddModal/MusicAddModal.tsx
  type SelectInfo (line 10) | interface SelectInfo {
  type MusicAddModalProps (line 18) | interface MusicAddModalProps {
  type MusicAddModalType (line 26) | interface MusicAddModalType {
  method show (line 36) | show(selectInfo) {

FILE: src/components/MusicAddModal/index.tsx
  type MusicAddModalProps (line 4) | interface MusicAddModalProps {
  type MusicAddModalType (line 7) | interface MusicAddModalType {
  method show (line 16) | show(listInfo) {

FILE: src/components/MusicMultiAddModal/List.tsx
  constant MIN_WIDTH (line 24) | const MIN_WIDTH = scaleSizeW(140)
  constant PADDING (line 25) | const PADDING = styles.list.paddingLeft + styles.list.paddingRight

FILE: src/components/MusicMultiAddModal/MusicMultiAddModal.tsx
  type SelectInfo (line 10) | interface SelectInfo {
  type MusicMultiAddModalProps (line 18) | interface MusicMultiAddModalProps {
  type MusicMultiAddModalType (line 26) | interface MusicMultiAddModalType {
  method show (line 36) | show(selectInfo) {

FILE: src/components/MusicMultiAddModal/index.tsx
  type MusicAddModalProps (line 4) | interface MusicAddModalProps {
  type MusicMultiAddModalType (line 7) | interface MusicMultiAddModalType {
  method show (line 16) | show(listInfo) {

FILE: src/components/OnlineList/List.tsx
  type FlatListType (line 17) | type FlatListType = FlatListProps<LX.Music.MusicInfoOnline>
  type ListProps (line 23) | interface ListProps {
  type ListType (line 35) | interface ListType {
  type Status (line 44) | type Status = 'loading' | 'refreshing' | 'end' | 'error' | 'idle'
  method setList (line 78) | setList(list, isAppend, showSource) {
  method setIsMultiSelectMode (line 83) | setIsMultiSelectMode(isMultiSelectMode) {
  method setSelectMode (line 91) | setSelectMode(mode) {
  method selectAll (line 94) | selectAll(isAll) {
  method getSelectedList (line 104) | getSelectedList() {
  method getList (line 107) | getList() {
  method setStatus (line 110) | setStatus(val) {
  type FooterLabel (line 259) | type FooterLabel = 'list_loading' | 'list_end' | 'list_error' | null

FILE: src/components/OnlineList/ListItem.tsx
  constant ITEM_HEIGHT (line 13) | const ITEM_HEIGHT = scaleSizeH(LIST_ITEM_HEIGHT)

FILE: src/components/OnlineList/ListMenu.tsx
  type SelectInfo (line 6) | interface SelectInfo {
  type ListMenuProps (line 14) | interface ListMenuProps {
  type ListMenuType (line 22) | interface ListMenuType {
  method show (line 38) | show(selectInfo, position) {

FILE: src/components/OnlineList/MultipleModeBar.tsx
  type SelectMode (line 11) | type SelectMode = 'single' | 'range'
  constant MULTI_SELECT_BAR_HEIGHT (line 13) | const MULTI_SELECT_BAR_HEIGHT = scaleSizeH(40)
  type MultipleModeBarProps (line 15) | interface MultipleModeBarProps {
  type MultipleModeBarType (line 20) | interface MultipleModeBarType {
  method show (line 38) | show() {
  method setIsSelectAll (line 41) | setIsSelectAll(isAll) {
  method setSwitchMode (line 44) | setSwitchMode(mode: SelectMode) {
  method exitSelectMode (line 47) | exitSelectMode() {

FILE: src/components/OnlineList/index.tsx
  type OnlineListProps (line 12) | interface OnlineListProps {
  type OnlineListType (line 21) | interface OnlineListType {
  method setList (line 43) | setList(list, isAppend = false, showSource = false) {
  method setStatus (line 47) | setStatus(val) {

FILE: src/components/PageContent.tsx
  type Props (line 12) | interface Props {
  constant BLUR_RADIUS (line 16) | const BLUR_RADIUS = Math.max(scaleSizeAbsHR(18), 10)

FILE: src/components/SearchTipList/List.tsx
  type ItemT (line 6) | type ItemT<T> = FlatListProps<T>['data']
  type ListProps (line 8) | type ListProps<T> = Pick<FlatListProps<T>,
  type ListType (line 18) | interface ListType<T> {
  method setList (line 25) | setList(list) {

FILE: src/components/SearchTipList/index.tsx
  type SearchTipListProps (line 9) | interface SearchTipListProps<T> extends ListProps<T> {
  type SearchTipListType (line 12) | interface SearchTipListType<T> {
  method setList (line 30) | setList(list) {
  method setHeight (line 39) | setHeight(height) {

FILE: src/components/SourceSelector.tsx
  type Sources (line 11) | type Sources = Readonly<Array<LX.OnlineSource | 'all'>>
  type SourceSelectorProps (line 13) | interface SourceSelectorProps<S extends Sources> {
  type SourceSelectorType (line 19) | interface SourceSelectorType<S extends Sources> {
  method setSourceList (line 39) | setSourceList(list, activeSource) {
  type DorpDownMenuProps (line 47) | type DorpDownMenuProps = _DorpDownMenuProps<typeof sourceList_t>

FILE: src/components/TimeoutExitEditModal.tsx
  constant MAX_MIN (line 15) | const MAX_MIN = 1440
  type TimeInputType (line 49) | interface TimeInputType {
  method getText (line 61) | getText() {
  method setText (line 64) | setText(text) {
  method focus (line 67) | focus() {
  type TimeoutExitEditModalType (line 141) | interface TimeoutExitEditModalType {
  type TimeoutExitEditModalProps (line 144) | interface TimeoutExitEditModalProps {
  method show (line 164) | show() {

FILE: src/components/common/Badge.tsx
  type BadgeType (line 24) | type BadgeType = 'normal' | 'secondary' | 'tertiary'

FILE: src/components/common/Button.tsx
  type BtnProps (line 7) | interface BtnProps extends PressableProps {
  type BtnType (line 16) | interface BtnType {
  method measure (line 29) | measure(callback) {

FILE: src/components/common/ButtonPrimary.tsx
  type ButtonProps (line 8) | interface ButtonProps extends BtnProps {

FILE: src/components/common/CheckBox/Checkbox.tsx
  type Props (line 14) | interface Props {
  constant ANIMATION_DURATION (line 39) | const ANIMATION_DURATION = 200
  constant PADDING (line 40) | const PADDING = scaleSizeW(4)

FILE: src/components/common/CheckBox/index.tsx
  type CheckBoxProps (line 11) | interface CheckBoxProps {

FILE: src/components/common/ChoosePath/List.tsx
  type ReadOptions (line 70) | interface ReadOptions {
  type ListProps (line 76) | interface ListProps {
  type ListType (line 81) | interface ListType {
  method show (line 99) | show(title, dir = '', dirOnly = false, filter) {
  method hide (line 113) | hide() {

FILE: src/components/common/ChoosePath/components/ListItem.tsx
  type PathItem (line 8) | interface PathItem {

FILE: src/components/common/ChoosePath/components/NewFolderModal.tsx
  type NameInputType (line 13) | interface NameInputType {
  method getText (line 24) | getText() {
  method setName (line 27) | setName(text) {
  method focus (line 30) | focus() {
  type NewFolderType (line 46) | interface NewFolderType {
  method show (line 55) | show(path) {

FILE: src/components/common/ChoosePath/components/OpenStorageModal.tsx
  type PathInputType (line 18) | interface PathInputType {
  method getText (line 29) | getText() {
  method setPath (line 32) | setPath(text) {
  method focus (line 35) | focus() {
  type OpenDirModalType (line 54) | interface OpenDirModalType {
  method show (line 68) | show(paths) {

FILE: src/components/common/ChoosePath/index.tsx
  type ReadOptions (line 16) | interface ReadOptions {
  type ChoosePathProps (line 24) | interface ChoosePathProps {
  type ChoosePathType (line 28) | interface ChoosePathType {
  method show (line 54) | show(options) {

FILE: src/components/common/ConfirmAlert.tsx
  type ConfirmAlertProps (line 54) | interface ConfirmAlertProps {
  type ConfirmAlertType (line 71) | interface ConfirmAlertType {
  method setVisible (line 97) | setVisible(visible: boolean) {

FILE: src/components/common/Dialog.tsx
  constant HEADER_HEIGHT (line 12) | const HEADER_HEIGHT = 20
  type DialogProps (line 59) | interface DialogProps {
  type DialogType (line 69) | interface DialogType {
  method setVisible (line 87) | setVisible(visible: boolean) {

FILE: src/components/common/DorpDownMenu.tsx
  type DorpDownMenuProps (line 8) | interface DorpDownMenuProps<T extends Menus> extends Omit<MenuProps<T>, ...

FILE: src/components/common/DorpDownPanel/Panel.tsx
  type Position (line 11) | interface Position { w: number, h: number, x: number, y: number }
  type PanelProps (line 87) | interface PanelProps {
  type PanelType (line 97) | interface PanelType {
  method show (line 107) | show(newPosition) {
  method hide (line 111) | hide() {

FILE: src/components/common/DorpDownPanel/index.tsx
  type DorpDownPanelProps (line 8) | interface DorpDownPanelProps {
  type DorpDownPanelType (line 13) | interface DorpDownPanelType {

FILE: src/components/common/DrawerLayoutFixed.tsx
  type Props (line 7) | interface Props extends DrawerLayoutAndroidProps {
  type DrawerLayoutFixedType (line 13) | interface DrawerLayoutFixedType {
  method openDrawer (line 39) | openDrawer() {
  method closeDrawer (line 42) | closeDrawer() {
  method fixWidth (line 45) | fixWidth() {

FILE: src/components/common/FileSelect.tsx
  type FileSelectType (line 4) | interface FileSelectType {
  method show (line 15) | show(options, onSelect) {

FILE: src/components/common/Icon.tsx
  type IconType (line 29) | type IconType = ReturnType<typeof createIconSetFromIcoMoon>
  type IconProps (line 31) | interface IconProps extends Omit<ComponentProps<IconType>, 'style'> {

FILE: src/components/common/Image.tsx
  type ImageProps (line 11) | interface ImageProps extends ViewProps {

FILE: src/components/common/ImageBackground.tsx
  type ImageBackgroundType (line 46) | type ImageBackgroundType = View
  type ImageBackgroundProps (line 48) | interface ImageBackgroundProps extends Omit<_ImageBackgroundProps, 'sour...

FILE: src/components/common/Input.tsx
  type InputProps (line 46) | interface InputProps extends TextInputProps {
  type InputType (line 54) | interface InputType {
  method blur (line 67) | blur() {
  method focus (line 70) | focus() {
  method clear (line 73) | clear() {
  method isFocused (line 76) | isFocused() {

FILE: src/components/common/Loading.tsx
  type LoadingProps (line 9) | interface LoadingProps extends Omit<ActivityIndicatorProps, 'size'> {

FILE: src/components/common/LoadingMask.tsx
  type LoadingMaskType (line 12) | interface LoadingMaskType {
  method setVisible (line 23) | setVisible(visible: boolean) {

FILE: src/components/common/Menu.tsx
  type Position (line 15) | interface Position { w: number, h: number, x: number, y: number, menuWid...
  type MenuSize (line 16) | interface MenuSize { width?: number, height?: number }
  type Menus (line 17) | type Menus = Readonly<Array<{ action: string, label: string, disabled?: ...
  type Props (line 52) | interface Props<M extends Menus = Menus> {
  type MenuProps (line 170) | interface MenuProps<M extends Menus = Menus> {
  type MenuType (line 181) | interface MenuType {
  method show (line 195) | show(newPosition, menuSize) {
  method hide (line 200) | hide() {

FILE: src/components/common/Modal.tsx
  type ModalProps (line 22) | interface ModalProps extends Omit<_ModalProps, 'visible'> {
  type ModalType (line 43) | interface ModalType {
  method setVisible (line 73) | setVisible(_visible) {

FILE: src/components/common/Popup.tsx
  type PopupProps (line 50) | interface PopupProps {
  type PopupType (line 60) | interface PopupType {
  method setVisible (line 80) | setVisible(visible: boolean) {

FILE: src/components/common/ScaledImage.tsx
  type ScaledImageProps (line 5) | interface ScaledImageProps extends Pick<ImageProps, 'style'> {

FILE: src/components/common/Slider.tsx
  type SliderProps (line 7) | type SliderProps = Pick<_SliderProps,

FILE: src/components/common/Text.tsx
  type TextProps (line 9) | interface TextProps extends _TextProps {
  type AnimatedTextProps (line 56) | interface AnimatedTextProps extends _AnimatedTextProps {
  type _AnimatedTextProps (line 86) | type _AnimatedTextProps = ComponentProps<(typeof Animated)['Text']>
  type AnimatedColorTextProps (line 87) | interface AnimatedColorTextProps extends _AnimatedTextProps {

FILE: src/components/player/PlayerBar/components/ControlBtn.tsx
  constant BTN_SIZE (line 9) | const BTN_SIZE = 24

FILE: src/components/player/PlayerBar/components/Pic.tsx
  constant PIC_HEIGHT (line 12) | const PIC_HEIGHT = scaleSizeH(46)

FILE: src/components/player/PlayerBar/components/PlayInfo.tsx
  constant FONT_SIZE (line 16) | const FONT_SIZE = 13
  constant PADDING_TOP_RAW (line 17) | const PADDING_TOP_RAW = 1.8
  constant PADDING_TOP (line 18) | const PADDING_TOP = Math.round(scaleSizeWR(PADDING_TOP_RAW))
  constant MARGIN_TOP (line 19) | const MARGIN_TOP = Math.round(scaleSizeH(2))
  constant PADDING_TOP_PROGRESS (line 20) | const PADDING_TOP_PROGRESS = PADDING_TOP + MARGIN_TOP

FILE: src/config/constant.ts
  constant HEADER_HEIGHT (line 1) | const HEADER_HEIGHT = 42
  constant LIST_ITEM_HEIGHT (line 2) | const LIST_ITEM_HEIGHT = 54
  constant LIST_SCROLL_POSITION_KEY (line 3) | const LIST_SCROLL_POSITION_KEY = '__LIST_SCROLL_POSITION_KEY__'
  constant SPLIT_CHAR (line 5) | const SPLIT_CHAR = {
  constant LIST_IDS (line 10) | const LIST_IDS = {
  type COMPONENT_IDS (line 23) | enum COMPONENT_IDS {
  type NAV_SHEAR_NATIVE_IDS (line 30) | enum NAV_SHEAR_NATIVE_IDS {
  constant APP_PROVIDER_NAME (line 98) | const APP_PROVIDER_NAME = 'cn.toside.music.mobile.provider'
  constant NAV_MENUS (line 101) | const NAV_MENUS = [
  type NAV_ID_Type (line 110) | type NAV_ID_Type = typeof NAV_MENUS[number]['id']
  constant LXM_FILE_EXT_RXP (line 112) | const LXM_FILE_EXT_RXP = ['json', 'lxmc', 'bin']
  constant USER_API_SOURCE_FILE_EXT_RXP (line 113) | const USER_API_SOURCE_FILE_EXT_RXP = ['js']
  constant MUSIC_TOGGLE_MODE (line 115) | const MUSIC_TOGGLE_MODE = {
  constant MUSIC_TOGGLE_MODE_LIST (line 123) | const MUSIC_TOGGLE_MODE_LIST = [
  constant DEFAULT_SETTING (line 131) | const DEFAULT_SETTING = {

FILE: src/config/migrate.ts
  type OldUserListInfo (line 8) | interface OldUserListInfo {

FILE: src/core/init/deeplink/playerAction.ts
  type PlayerAction (line 3) | type PlayerAction = 'play' | 'pause' | 'skipNext' | 'skipPrev' | 'toggle...

FILE: src/core/init/userApi/index.ts
  method canceleFn (line 91) | canceleFn() {
  method canceleFn (line 119) | canceleFn() {
  method canceleFn (line 147) | canceleFn() {

FILE: src/core/init/userApi/request.js
  method abort (line 118) | abort() {

FILE: src/core/leaderboard.ts
  type PageCache (line 29) | interface PageCache { data: ListDetailInfo, sourcePage: number }
  type CacheValue (line 30) | type CacheValue = Map<string, PageCache | ListDetailInfo['list']>
  constant LIST_LOAD_LIMIT (line 33) | const LIST_LOAD_LIMIT = 30

FILE: src/core/music/utils.ts
  constant TRY_QUALITYS_LIST (line 216) | const TRY_QUALITYS_LIST = ['flac24bit', 'flac', '320k'] as const
  type TryQualityType (line 217) | type TryQualityType = typeof TRY_QUALITYS_LIST[number]

FILE: src/core/player/player.ts
  method onToggleSource (line 112) | onToggleSource(mInfo) {

FILE: src/core/player/timeoutExit.ts
  type Hook (line 7) | type Hook = (time: number, isPlayedStop: boolean) => void
  method exit (line 15) | exit() {
  method getTime (line 23) | getTime() {
  method callHooks (line 26) | callHooks() {
  method clearTimeout (line 32) | clearTimeout() {
  method start (line 41) | start(time: number) {
  method addTimeHook (line 53) | addTimeHook(hook: Hook) {
  method removeTimeHook (line 57) | removeTimeHook(hook: Hook) {

FILE: src/core/songlist.ts
  type DetailPageCache (line 7) | interface DetailPageCache { data: ListDetailInfo, sourcePage: number }
  type LimitDetailCache (line 8) | type LimitDetailCache = Map<string, DetailPageCache | ListDetailInfo['li...
  type CacheValue (line 9) | type CacheValue = LimitDetailCache | ListInfo
  constant LIST_LOAD_LIMIT (line 12) | const LIST_LOAD_LIMIT = 30

FILE: src/core/sync.ts
  type RemoveListener (line 5) | type RemoveListener = (() => void) | null

FILE: src/core/userApi.ts
  method r_info (line 46) | r_info(...params: any[]) {
  method r_warn (line 50) | r_warn(...params: any[]) {
  method r_error (line 54) | r_error(...params: any[]) {
  method log (line 58) | log(...params: any[]) {
  method info (line 62) | info(...params: any[]) {
  method warn (line 66) | warn(...params: any[]) {
  method error (line 70) | error(...params: any[]) {

FILE: src/event/Event.ts
  class Event (line 4) | class Event {
    method constructor (line 6) | constructor() {
    method on (line 10) | on(eventName: string, listener: (...args: any[]) => any) {
    method off (line 16) | off(eventName: string, listener: (...args: any[]) => any) {
    method emit (line 24) | emit(eventName: string, ...args: any[]) {
    method offAll (line 35) | offAll(eventName: string) {

FILE: src/event/appEvent.ts
  class AppEvent (line 17) | class AppEvent extends Event {
    method focus (line 22) | focus() {
    method mylistUpdated (line 29) | mylistUpdated(lists: Array<LX.List.MyDefaultListInfo | LX.List.MyLoveL...
    method mylistToggled (line 36) | mylistToggled(id: string) {
    method musicToggled (line 43) | musicToggled() {
    method setProgress (line 51) | setProgress(progress: number, maxPlayTime?: number) {
    method setVolume (line 59) | setVolume(volume: number) {
    method setVolumeIsMute (line 67) | setVolumeIsMute(isMute: boolean) {
    method play (line 72) | play() {
    method pause (line 76) | pause() {
    method stop (line 80) | stop() {
    method error (line 84) | error() {
    method playerPlaying (line 89) | playerPlaying() {
    method playerPause (line 93) | playerPause() {
    method playerEnded (line 101) | playerEnded() {
    method playerError (line 105) | playerError() {
    method playerLoadstart (line 113) | playerLoadstart() {
    method playerEmptied (line 121) | playerEmptied() {
    method playerWaiting (line 125) | playerWaiting() {
    method picUpdated (line 131) | picUpdated() {
    method lyricUpdated (line 136) | lyricUpdated() {
    method lyricOffsetUpdate (line 141) | lyricOffsetUpdate() {
    method myListMusicUpdate (line 146) | myListMusicUpdate(ids: string[]) {
    method downloadListUpdate (line 152) | downloadListUpdate() {
    method musicInfoUpdate (line 157) | musicInfoUpdate(musicInfo: LX.Music.MusicInfo) {
    method changeMenuVisible (line 161) | changeMenuVisible(visible: boolean) {
    method searchTypeChanged (line 169) | searchTypeChanged(type: SearchType) {
    method jumpListPosition (line 173) | jumpListPosition() {
    method changeLoveListVisible (line 185) | changeLoveListVisible(visible: boolean) {
    method showSonglistTagList (line 189) | showSonglistTagList(source: SonglistSource, activeId: string) {
    method hideSonglistTagList (line 193) | hideSonglistTagList() {
    method songlistTagInfoChange (line 197) | songlistTagInfoChange(name: string, id: string) {
    method selectSyncMode (line 201) | selectSyncMode(mode: LX.Sync.ModeType) {
  type EventMethods (line 207) | type EventMethods = Omit<EventType, keyof Event>
  class EventType (line 210) | class EventType extends AppEvent {
  type AppEventTypes (line 215) | type AppEventTypes = Omit<EventType, keyof Omit<Event, 'on' | 'off'>>

FILE: src/event/dislikeEvent.ts
  class DislikeEvent (line 11) | class DislikeEvent extends Event {
    method dislike_changed (line 12) | dislike_changed() {
    method dislike_data_overwrite (line 21) | async dislike_data_overwrite(dislikeData: LX.Dislike.DislikeRules, isR...
    method dislike_music_add (line 35) | async dislike_music_add(musicInfo: LX.Dislike.DislikeMusicInfo[], isRe...
    method dislike_music_clear (line 47) | async dislike_music_clear(isRemote: boolean = false) {
  type EventMethods (line 56) | type EventMethods = Omit<EventType, keyof Event>
  class EventType (line 59) | class EventType extends DislikeEvent {
  type DislikeEventTypes (line 64) | type DislikeEventTypes = Omit<EventType, keyof Omit<Event, 'on' | 'off'>>

FILE: src/event/listEvent.ts
  class ListEvent (line 64) | class ListEvent extends Event {
    method list_data_overwrite (line 78) | async list_data_overwrite(listData: MakeOptional<LX.List.ListDataFull,...
    method list_create (line 101) | async list_create(position: number, lists: LX.List.UserListInfo[], isR...
    method list_remove (line 117) | async list_remove(ids: string[], isRemote: boolean = false) {
    method list_update (line 132) | async list_update(lists: LX.List.UserListInfo[], isRemote: boolean = f...
    method list_update_position (line 144) | async list_update_position(position: number, ids: string[], isRemote: ...
    method list_music_overwrite (line 156) | async list_music_overwrite(listId: string, musicInfos: LX.Music.MusicI...
    method list_music_add (line 169) | async list_music_add(listId: string, musicInfos: LX.Music.MusicInfo[],...
    method list_music_move (line 183) | async list_music_move(fromId: string, toId: string, musicInfos: LX.Mus...
    method list_music_remove (line 196) | async list_music_remove(listId: string, ids: string[], isRemote: boole...
    method list_music_update (line 208) | async list_music_update(musicInfos: LX.List.ListActionMusicUpdate, isR...
    method list_music_clear (line 219) | async list_music_clear(ids: string[], isRemote: boolean = false) {
    method list_music_update_position (line 232) | async list_music_update_position(listId: string, position: number, ids...
  type EventMethods (line 240) | type EventMethods = Omit<EventType, keyof Event>
  class EventType (line 243) | class EventType extends ListEvent {
  type ListEventTypes (line 248) | type ListEventTypes = Omit<EventType, keyof Omit<Event, 'on' | 'off'>>

FILE: src/event/stateEvent.ts
  class StateEvent (line 18) | class StateEvent extends Event {
    method configUpdated (line 19) | configUpdated(keys: Array<keyof LX.AppSetting>, setting: Partial<LX.Ap...
    method languageChanged (line 23) | languageChanged(locale: I18n['locale']) {
    method fontSizeUpdated (line 27) | fontSizeUpdated(size: number) {
    method statusbarHeightUpdated (line 31) | statusbarHeightUpdated(size: number) {
    method apiSourceUpdated (line 35) | apiSourceUpdated(source: LX.AppSetting['common.apiSource']) {
    method themeUpdated (line 39) | themeUpdated(theme: LX.ActiveTheme) {
    method bgPicUpdated (line 43) | bgPicUpdated(bgPic: string | null) {
    method playerMusicInfoChanged (line 47) | playerMusicInfoChanged(musicInfo: PlayerState['musicInfo']) {
    method playMusicInfoChanged (line 51) | playMusicInfoChanged(playMusicInfo: PlayerState['playMusicInfo']) {
    method playInfoChanged (line 55) | playInfoChanged(playInfo: PlayerState['playInfo']) {
    method playStateTextChanged (line 59) | playStateTextChanged(text: PlayerState['statusText']) {
    method playStateChanged (line 63) | playStateChanged(state: PlayerState['isPlay']) {
    method playProgressChanged (line 67) | playProgressChanged(progress: PlayerState['progress']) {
    method playPlayedListChanged (line 71) | playPlayedListChanged(playedList: PlayerState['playedList']) {
    method playTempPlayListChanged (line 75) | playTempPlayListChanged(tempPlayList: PlayerState['tempPlayList']) {
    method mylistUpdated (line 82) | mylistUpdated(lists: Array<LX.List.MyDefaultListInfo | LX.List.MyLoveL...
    method mylistToggled (line 89) | mylistToggled(id: string) {
    method fetchingListStatusUpdated (line 93) | fetchingListStatusUpdated(fetchingListStatus: ListState['fetchingListS...
    method syncStatusUpdated (line 97) | syncStatusUpdated(status: LX.Sync.Status) {
    method versionInfoUpdated (line 101) | versionInfoUpdated(info: VersionState['versionInfo']) {
    method versionInfoIgnoreVersionUpdated (line 105) | versionInfoIgnoreVersionUpdated(version: VersionState['ignoreVersion']) {
    method versionDownloadProgressUpdated (line 109) | versionDownloadProgressUpdated(progress: VersionState['progress']) {
    method componentIdsUpdated (line 113) | componentIdsUpdated(ids: CommonState['componentIds']) {
    method navActiveIdUpdated (line 117) | navActiveIdUpdated(id: CommonState['navActiveId']) {
    method sourceNamesUpdated (line 121) | sourceNamesUpdated(names: CommonState['sourceNames']) {
  type EventMethods (line 127) | type EventMethods = Omit<EventType, keyof Event>
  class EventType (line 130) | class EventType extends StateEvent {
  type StateEventTypes (line 135) | type StateEventTypes = Omit<EventType, keyof Omit<Event, 'on' | 'off'>>

FILE: src/lang/i18n.ts
  type TranslateValues (line 6) | type TranslateValues = Record<string, string | number | boolean>
  type Langs (line 8) | type Langs = keyof Messages
  type Hook (line 10) | type Hook = (locale: Langs) => void
  type I18n (line 12) | interface I18n {
  method add (line 31) | add(hook: Hook) {
  method remove (line 34) | remove(hook: Hook) {
  method update (line 37) | update(locale: Parameters<Hook>[0]) {
  method setLanguage (line 72) | setLanguage(_locale: Langs) {
  method fillMessage (line 77) | fillMessage(message: string, vals: TranslateValues): string {
  method getMessage (line 83) | getMessage(key: keyof Message, val?: TranslateValues): string {
  method t (line 87) | t(key: keyof Message, val?: TranslateValues): string {

FILE: src/lang/index.ts
  type Message (line 5) | type Message = Record<keyof typeof zh_cn, string>
  type Messages (line 39) | type Messages = Record<(typeof langs)[number]['locale'], Message>

FILE: src/navigation/components/ModalContent.tsx
  constant HEADER_HEIGHT (line 5) | const HEADER_HEIGHT = 20
  type Props (line 7) | interface Props {

FILE: src/navigation/components/PactModal.tsx
  method start (line 110) | start() {
  method clear (line 119) | clear() {

FILE: src/navigation/navigation.ts
  function pushHomeScreen (line 21) | async function pushHomeScreen() {
  function pushPlayDetailScreen (line 88) | function pushPlayDetailScreen(componentId: string, skipAnimation = false) {
  function pushSonglistDetailScreen (line 202) | function pushSonglistDetailScreen(componentId: string, info: ListInfoIte...
  function pushCommentScreen (line 306) | function pushCommentScreen(componentId: string) {

FILE: src/navigation/registerScreens.tsx
  function WrappedComponent (line 28) | function WrappedComponent(Component: any) {

FILE: src/navigation/screenNames.ts
  constant HOME_SCREEN (line 1) | const HOME_SCREEN = 'lxm.HomeScreen'
  constant PLAY_DETAIL_SCREEN (line 2) | const PLAY_DETAIL_SCREEN = 'lxm.PlayDetailScreen'
  constant SONGLIST_DETAIL_SCREEN (line 3) | const SONGLIST_DETAIL_SCREEN = 'lxm.SonglistDetailScreen'
  constant COMMENT_SCREEN (line 4) | const COMMENT_SCREEN = 'lxm.CommentScreen'
  constant VERSION_MODAL (line 5) | const VERSION_MODAL = 'lxm.VersionModal'
  constant PACT_MODAL (line 6) | const PACT_MODAL = 'lxm.PactModal'
  constant SYNC_MODE_MODAL (line 7) | const SYNC_MODE_MODAL = 'lxm.SyncModeModal'

FILE: src/plugins/lyric.ts
  type Line (line 4) | type Line = Lines[number]
  type PlayHook (line 5) | type PlayHook = (line: number, text: string) => void
  type SetLyricHook (line 6) | type SetLyricHook = (lines: Lines) => void
  method init (line 21) | init() {
  method onPlay (line 30) | onPlay(line: number, text: string) {
  method onSetLyric (line 36) | onSetLyric(lines: Lines) {
  method addPlayHook (line 43) | addPlayHook(hook: PlayHook) {
  method removePlayHook (line 47) | removePlayHook(hook: PlayHook) {
  method addSetLyricHook (line 50) | addSetLyricHook(hook: SetLyricHook) {
  method removeSetLyricHook (line 54) | removeSetLyricHook(hook: SetLyricHook) {
  method setLyric (line 57) | setLyric() {

FILE: src/plugins/player/hook.ts
  function setPlayerState (line 9) | async function setPlayerState() {
  function useProgress (line 69) | function useProgress(updateInterval: number) {
  function useBufferProgress (line 115) | function useBufferProgress() {

FILE: src/plugins/player/playList.ts
  method debounce (line 229) | debounce(fn: (musicInfo: LX.Player.MusicInfo, lyric?: string) => void | ...
  method init (line 268) | init() {

FILE: src/plugins/player/utils.ts
  type PlayStatus (line 201) | type PlayStatus = 'None' | 'Ready' | 'Playing' | 'Paused' | 'Stopped' | ...

FILE: src/plugins/storage.ts
  type RawData (line 134) | type RawData = { [K in keyof T]: [T[K], string | null] }

FILE: src/plugins/sync/client/client.ts
  method handleOpen (line 37) | handleOpen() {
  method heartbeat (line 42) | heartbeat() {
  method reConnnect (line 53) | reConnnect() {
  method clearTimeout (line 85) | clearTimeout() {
  method connect (line 99) | connect(socket: LX.Sync.Socket) {
  method finished (line 161) | finished() {
  method sendMessage (line 172) | sendMessage(data) {
  method onCallBeforeParams (line 181) | onCallBeforeParams(rawArgs) {
  method onError (line 184) | onError(error, path, groupName) {

FILE: src/plugins/sync/client/modules/dislike/handler.ts
  method onDislikeSyncAction (line 16) | async onDislikeSyncAction(socket, action) {
  method dislike_sync_get_md5 (line 21) | async dislike_sync_get_md5(socket) {
  method dislike_sync_get_sync_mode (line 27) | async dislike_sync_get_sync_mode(socket) {
  method dislike_sync_get_list_data (line 35) | async dislike_sync_get_list_data(socket) {
  method dislike_sync_set_list_data (line 40) | async dislike_sync_set_list_data(socket, data) {
  method dislike_sync_finished (line 45) | async dislike_sync_finished(socket) {

FILE: src/plugins/sync/client/modules/list/handler.ts
  method onListSyncAction (line 16) | async onListSyncAction(socket, action) {
  method list_sync_get_md5 (line 21) | async list_sync_get_md5(socket) {
  method list_sync_get_sync_mode (line 26) | async list_sync_get_sync_mode(socket) {
  method list_sync_get_list_data (line 34) | async list_sync_get_list_data(socket) {
  method list_sync_set_list_data (line 39) | async list_sync_set_list_data(socket, data) {
  method list_sync_finished (line 44) | async list_sync_finished(socket) {

FILE: src/plugins/sync/client/sync/handler.ts
  method getEnabledFeatures (line 9) | async getEnabledFeatures(socket, serverType, supportedFeatures) {

FILE: src/plugins/sync/constants.ts
  constant ENV_PARAMS (line 1) | const ENV_PARAMS = [
  constant LIST_IDS (line 14) | const LIST_IDS = {
  constant SYNC_CODE (line 22) | const SYNC_CODE = {
  constant SYNC_CLOSE_CODE (line 41) | const SYNC_CLOSE_CODE = {
  constant TRANS_MODE (line 46) | const TRANS_MODE: Readonly<Record<LX.Sync.List.SyncMode, LX.Sync.List.Sy...

FILE: src/plugins/sync/log.ts
  method r_info (line 5) | r_info(...params: any[]) {
  method r_warn (line 8) | r_warn(...params: any[]) {
  method r_error (line 11) | r_error(...params: any[]) {
  method info (line 14) | info(...params: any[]) {
  method warn (line 17) | warn(...params: any[]) {
  method error (line 20) | error(...params: any[]) {

FILE: src/screens/Comment/components/CommentFloor.tsx
  constant GAP (line 18) | const GAP = 12

FILE: src/screens/Comment/components/CommentImage.tsx
  constant MAX_IMAGE_HEIGHT (line 10) | const MAX_IMAGE_HEIGHT = scaleSizeH(260)

FILE: src/screens/Comment/components/CommentText.tsx
  constant TEXT_LIMIT (line 7) | const TEXT_LIMIT = 160
  constant SUB_TEXT_LIMIT (line 8) | const SUB_TEXT_LIMIT = TEXT_LIMIT * 1.2

FILE: src/screens/Comment/components/Header.tsx
  constant HEADER_HEIGHT (line 16) | const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)

FILE: src/screens/Comment/components/List.tsx
  type FlatListType (line 12) | type FlatListType = FlatListProps<Comment>
  type ListProps (line 14) | interface ListProps {
  type ListType (line 18) | interface ListType {
  type Status (line 23) | type Status = 'loading' | 'refreshing' | 'end' | 'error' | 'idle'
  method setList (line 38) | setList(list) {
  method getList (line 41) | getList() {
  method setStatus (line 44) | setStatus(val) {
  type FooterLabel (line 107) | type FooterLabel = 'list_loading' | 'list_end' | 'list_error' | null

FILE: src/screens/Comment/index.tsx
  type ActiveId (line 19) | type ActiveId = 'hot' | 'new'
  constant BAR_HEIGHT (line 21) | const BAR_HEIGHT = scaleSizeH(34)
  constant TABS (line 72) | const TABS = [

FILE: src/screens/Comment/utils.ts
  type Comment (line 4) | interface Comment {
  type CommentInfo (line 17) | interface CommentInfo {

FILE: src/screens/Home/Horizontal/Aside.tsx
  constant NAV_WIDTH (line 14) | const NAV_WIDTH = 68
  type IdType (line 79) | type IdType = InitState['navActiveId'] | 'nav_exit' | 'back_home'

FILE: src/screens/Home/Horizontal/Header.tsx
  constant HEADER_HEIGHT (line 20) | const HEADER_HEIGHT = _HEADER_HEIGHT * 0.8

FILE: src/screens/Home/Vertical/Content.tsx
  constant MAX_WIDTH (line 11) | const MAX_WIDTH = scaleSizeW(300)

FILE: src/screens/Home/Vertical/DrawerNav.tsx
  type IdType (line 73) | type IdType = InitState['navActiveId'] | 'nav_exit' | 'back_home'

FILE: src/screens/Home/Views/Leaderboard/BoardsList/List.tsx
  type ListProps (line 9) | interface ListProps {
  type ListType (line 13) | interface ListType {
  method setList (line 24) | setList(list, activeId) {
  method hideMenu (line 28) | hideMenu() {

FILE: src/screens/Home/Views/Leaderboard/BoardsList/ListItem.tsx
  type ListItemProps (line 14) | interface ListItemProps {

FILE: src/screens/Home/Views/Leaderboard/BoardsList/ListMenu.tsx
  type SelectInfo (line 5) | interface SelectInfo {
  type ListMenuProps (line 12) | interface ListMenuProps {
  type ListMenuType (line 17) | interface ListMenuType {
  method show (line 32) | show(selectInfo, position) {

FILE: src/screens/Home/Views/Leaderboard/BoardsList/index.tsx
  type BoardsListProps (line 8) | interface BoardsListProps {
  type BoardsListType (line 13) | interface BoardsListType {
  method setList (line 22) | setList(list, listId) {

FILE: src/screens/Home/Views/Leaderboard/Horizontal/LeftBar.tsx
  type Sources (line 16) | type Sources = Readonly<InitState['sources']>
  type SourceSelectorType (line 18) | type SourceSelectorType = _SourceSelectorType<Sources>
  type LeftBarProps (line 20) | interface LeftBarProps {
  type LeftBarType (line 24) | interface LeftBarType {
  method setBound (line 34) | setBound(source, listId) {

FILE: src/screens/Home/Views/Leaderboard/MusicList.tsx
  type MusicListType (line 13) | interface MusicListType {
  method loadList (line 21) | async loadList(source, id) {

FILE: src/screens/Home/Views/Leaderboard/Vertical/HeaderBar/ActiveListName.tsx
  type ActiveListNameProps (line 8) | interface ActiveListNameProps {
  type ActiveListNameType (line 11) | interface ActiveListNameType {
  method setBound (line 20) | setBound(id, name) {

FILE: src/screens/Home/Views/Leaderboard/Vertical/HeaderBar/SourceSelector.tsx
  type Sources (line 11) | type Sources = Readonly<InitState['sources']>
  type SourceSelectorCommonProps (line 12) | type SourceSelectorCommonProps = _SourceSelectorProps<Sources>
  type SourceSelectorCommonType (line 13) | type SourceSelectorCommonType = _SourceSelectorType<Sources>
  type SourceSelectorProps (line 15) | interface SourceSelectorProps {
  type SourceSelectorType (line 20) | interface SourceSelectorType {
  method setSource (line 28) | setSource(source) {

FILE: src/screens/Home/Views/Leaderboard/Vertical/HeaderBar/index.tsx
  type HeaderBarProps (line 17) | interface HeaderBarProps {
  type HeaderBarType (line 22) | interface HeaderBarType {
  method setBound (line 33) | setBound(source, id, name) {

FILE: src/screens/Home/Views/Leaderboard/Vertical/index.tsx
  constant MAX_WIDTH (line 22) | const MAX_WIDTH = scaleSizeW(200)

FILE: src/screens/Home/Views/Mylist/MusicList/ActiveList.tsx
  type ActiveListProps (line 17) | interface ActiveListProps {
  type ActiveListType (line 21) | interface ActiveListType {
  method setVisibleBar (line 46) | setVisibleBar(visible) {

FILE: src/screens/Home/Views/Mylist/MusicList/List.tsx
  type FlatListType (line 18) | type FlatListType = FlatListProps<LX.Music.MusicInfo>
  type ListProps (line 20) | interface ListProps {
  type ListType (line 25) | interface ListType {
  method setIsMultiSelectMode (line 65) | setIsMultiSelectMode(isMultiSelectMode) {
  method setSelectMode (line 72) | setSelectMode(mode) {
  method selectAll (line 75) | selectAll(isAll) {
  method getSelectedList (line 85) | getSelectedList() {
  method scrollToInfo (line 88) | scrollToInfo(info) {
  method scrollToTop (line 95) | scrollToTop() {

FILE: src/screens/Home/Views/Mylist/MusicList/ListItem.tsx
  constant ITEM_HEIGHT (line 13) | const ITEM_HEIGHT = scaleSizeH(LIST_ITEM_HEIGHT)

FILE: src/screens/Home/Views/Mylist/MusicList/ListMenu.tsx
  type SelectInfo (line 7) | interface SelectInfo {
  type ListMenuProps (line 16) | interface ListMenuProps {
  type ListMenuType (line 29) | interface ListMenuType {
  method show (line 49) | show(selectInfo, position) {

FILE: src/screens/Home/Views/Mylist/MusicList/ListMusicSearch.tsx
  type SearchTipListProps (line 14) | type SearchTipListProps = _SearchTipListProps<LX.Music.MusicInfo>
  type ListMusicSearchProps (line 15) | interface ListMusicSearchProps {
  constant ITEM_HEIGHT (line 18) | const ITEM_HEIGHT = scaleSizeH(46)
  type ListMusicSearchType (line 20) | interface ListMusicSearchType {
  method search (line 55) | search(keyword, height) {
  method hide (line 64) | hide() {

FILE: src/screens/Home/Views/Mylist/MusicList/ListSearchBar.tsx
  type SearchInputProps (line 12) | interface SearchInputProps {
  type SearchInputType (line 15) | type SearchInputType = InputType
  type ListSearchBarProps (line 39) | interface ListSearchBarProps {
  type ListSearchBarType (line 43) | interface ListSearchBarType {
  method show (line 60) | show() {
  method hide (line 66) | hide() {

FILE: src/screens/Home/Views/Mylist/MusicList/MultipleModeBar.tsx
  type SelectMode (line 10) | type SelectMode = 'single' | 'range'
  type MultipleModeBarProps (line 12) | interface MultipleModeBarProps {
  type MultipleModeBarType (line 17) | interface MultipleModeBarType {
  method show (line 37) | show() {
  method setVisibleBar (line 40) | setVisibleBar(visible) {
  method setIsSelectAll (line 43) | setIsSelectAll(isAll) {
  method setSwitchMode (line 46) | setSwitchMode(mode: SelectMode) {
  method exitSelectMode (line 49) | exitSelectMode() {

FILE: src/screens/Home/Views/Mylist/MusicList/MusicPositionModal.tsx
  type TitleType (line 11) | interface TitleType {
  method updateTitle (line 18) | updateTitle(musicInfo, selectedList) {
  type PositionInputType (line 30) | interface PositionInputType {
  method getText (line 42) | getText() {
  method setText (line 45) | setText(text) {
  method focus (line 48) | focus() {
  type SelectInfo (line 65) | interface SelectInfo {
  type MusicPositionModalProps (line 74) | interface MusicPositionModalProps {
  type MusicPositionModalType (line 78) | interface MusicPositionModalType {
  method show (line 100) | show(listInfo) {

FILE: src/screens/Home/Views/Mylist/MusicList/MusicToggleModal.tsx
  type FlatListProps (line 22) | type FlatListProps = _FlatListProps<LX.Music.MusicInfoOnline>
  constant ITEM_HEIGHT (line 23) | const ITEM_HEIGHT = scaleSizeH(56)
  type ModalType (line 290) | interface ModalType {
  method show (line 337) | show(info) {
  type SelectInfo (line 377) | interface SelectInfo {
  type MusicToggleModalType (line 381) | interface MusicToggleModalType {
  method show (line 390) | show(musicInfo) {

FILE: src/screens/Home/Views/Mylist/MyList/DuplicateMusic.tsx
  type FlatListProps (line 17) | type FlatListProps = _FlatListProps<DuplicateMusicItem>
  constant ITEM_HEIGHT (line 18) | const ITEM_HEIGHT = scaleSizeH(56)
  type ModalType (line 205) | interface ModalType {
  method show (line 214) | show(info) {
  type DuplicateMusicType (line 243) | interface DuplicateMusicType {
  method show (line 252) | show(listInfo) {

FILE: src/screens/Home/Views/Mylist/MyList/List.tsx
  type FlatListType (line 17) | type FlatListType = FlatListProps<LX.List.MyListInfo>
  constant ITEM_HEIGHT (line 19) | const ITEM_HEIGHT = scaleSizeH(40)

FILE: src/screens/Home/Views/Mylist/MyList/ListImportExport.tsx
  type SelectInfo (line 6) | interface SelectInfo {
  type ListImportExportType (line 23) | interface ListImportExportType {
  method import (line 36) | import(listInfo, index) {
  method export (line 59) | export(listInfo, index) {
  method selectFile (line 82) | selectFile(listInfo, index) {

FILE: src/screens/Home/Views/Mylist/MyList/ListMenu.tsx
  type SelectInfo (line 9) | interface SelectInfo {
  type ListMenuProps (line 21) | interface ListMenuProps {
  type ListMenuType (line 32) | interface ListMenuType {
  method show (line 58) | show(selectInfo, position) {

FILE: src/screens/Home/Views/Mylist/MyList/ListMusicSort.tsx
  type FormType (line 21) | interface FormType {
  type FieldName (line 27) | type FieldName = typeof fieldNames[number]
  type FieldType (line 28) | type FieldType = typeof fieldTypes[number]
  method reset (line 50) | reset() {
  method getForm (line 54) | getForm() {
  type ListMusicSortType (line 77) | interface ListMusicSortType {
  method show (line 104) | show(listInfo) {

FILE: src/screens/Home/Views/Mylist/MyList/ListNameEdit.tsx
  type NameInputType (line 11) | interface NameInputType {
  method getText (line 23) | getText() {
  method setName (line 26) | setName(text) {
  method focus (line 30) | focus() {
  type ListNameEditType (line 47) | interface ListNameEditType {
  method showCreate (line 72) | showCreate(position) {
  method show (line 82) | show(listInfo) {

FILE: src/screens/Home/Views/Mylist/MyList/utils.ts
  type DuplicateMusicItem (line 94) | interface DuplicateMusicItem {

FILE: src/screens/Home/Views/Mylist/index.tsx
  constant MAX_WIDTH (line 11) | const MAX_WIDTH = scaleSizeW(400)

FILE: src/screens/Home/Views/Search/BlankView/HistorySearch.tsx
  type List (line 13) | type List = NonNullable<InitState['sourceList'][keyof InitState['sourceL...
  type HistorySearchProps (line 33) | interface HistorySearchProps {
  type HistorySearchType (line 36) | interface HistorySearchType {
  method show (line 54) | show() {

FILE: src/screens/Home/Views/Search/BlankView/HotSearch.tsx
  type ListProps (line 12) | interface ListProps {
  type HotSearchType (line 15) | interface HotSearchType {
  type List (line 20) | type List = NonNullable<InitState['sourceList'][keyof InitState['sourceL...
  method show (line 50) | show(source) {

FILE: src/screens/Home/Views/Search/BlankView/index.tsx
  type BlankViewProps (line 11) | interface BlankViewProps {
  type Source (line 14) | type Source = LX.OnlineSource | 'all'
  type BlankViewType (line 16) | interface BlankViewType {
  method show (line 36) | show(source) {

FILE: src/screens/Home/Views/Search/HeaderBar/SearchInput.tsx
  type SearchInputProps (line 5) | interface SearchInputProps {
  type SearchInputType (line 12) | interface SearchInputType {
  method setText (line 28) | setText(text) {
  method focus (line 31) | focus() {
  method blur (line 34) | blur() {

FILE: src/screens/Home/Views/Search/HeaderBar/index.tsx
  type Sources (line 17) | type Sources = Readonly<Array<MusicSource | SonglistSource>>
  type SourceSelectorProps (line 18) | type SourceSelectorProps = _SourceSelectorProps<Sources>
  type SourceSelectorType (line 19) | type SourceSelectorType = _SourceSelectorType<Sources>
  type HeaderBarProps (line 21) | interface HeaderBarProps {
  type HeaderBarType (line 29) | interface HeaderBarType {
  method setSourceList (line 42) | setSourceList(list, source) {
  method setText (line 45) | setText(text) {
  method blur (line 48) | blur() {

FILE: src/screens/Home/Views/Search/List.tsx
  type ListProps (line 9) | interface ListProps {
  type ListType (line 12) | interface ListType {
  method loadList (line 23) | loadList(text, source, type) {

FILE: src/screens/Home/Views/Search/MusicList.tsx
  type MusicListType (line 12) | interface MusicListType {
  method loadList (line 21) | async loadList(text, source) {

FILE: src/screens/Home/Views/Search/SearchTypeSelector.tsx
  constant SEARCH_TYPE_LIST (line 12) | const SEARCH_TYPE_LIST = [

FILE: src/screens/Home/Views/Search/SonglistList.tsx
  type MusicListType (line 13) | interface MusicListType {
  method loadList (line 22) | async loadList(text, source) {

FILE: src/screens/Home/Views/Search/TipList.tsx
  constant ITEM_HEIGHT (line 12) | const ITEM_HEIGHT = scaleSizeH(36)
  type SearchTipListProps (line 20) | type SearchTipListProps = _SearchTipListProps<string>
  type SearchTipListType (line 21) | type SearchTipListType = _SearchTipListType<string>
  type TipListProps (line 23) | interface TipListProps {
  type TipListType (line 26) | interface TipListType {
  method search (line 72) | search(keyword, height) {
  method show (line 81) | show(height) {
  method hide (line 91) | hide() {

FILE: src/screens/Home/Views/Search/index.tsx
  type SearchInfo (line 19) | interface SearchInfo {

FILE: src/screens/Home/Views/Setting/Horizontal/NavList.tsx
  type FlatListType (line 13) | type FlatListType = FlatListProps<SettingScreenIds>
  constant ITEM_HEIGHT (line 15) | const ITEM_HEIGHT = scaleSizeH(40)

FILE: src/screens/Home/Views/Setting/Main.tsx
  constant SETTING_SCREENS (line 14) | const SETTING_SCREENS = [
  type SettingScreenIds (line 27) | type SettingScreenIds = typeof SETTING_SCREENS[number]
  type MainType (line 32) | interface MainType {
  method setActiveId (line 40) | setActiveId(id) {

FILE: src/screens/Home/Views/Setting/Vertical/Header.tsx
  type HeaderProps (line 12) | interface HeaderProps {
  type HeaderType (line 15) | interface HeaderType {
  method setActiveId (line 25) | setActiveId(id) {

FILE: src/screens/Home/Views/Setting/Vertical/Main.tsx
  type FlatListType (line 17) | type FlatListType = FlatListProps<SettingScreenIds>

FILE: src/screens/Home/Views/Setting/components/Button.tsx
  type ButtonProps (line 8) | type ButtonProps = BtnProps

FILE: src/screens/Home/Views/Setting/components/InputItem.tsx
  type InputItemProps (line 10) | interface InputItemProps extends InputProps {

FILE: src/screens/Home/Views/Setting/components/Section.tsx
  type Props (line 8) | interface Props {

FILE: src/screens/Home/Views/Setting/settings/Backup/ListImportExport.tsx
  type SelectInfo (line 6) | interface SelectInfo {
  type ListImportExportType (line 23) | interface ListImportExportType {
  method import (line 35) | import() {
  method export (line 54) | export() {

FILE: src/screens/Home/Views/Setting/settings/Basic/DrawerLayoutPosition.tsx
  constant LIST (line 11) | const LIST = [

FILE: src/screens/Home/Views/Setting/settings/Basic/FontSize.tsx
  constant LIST (line 14) | const LIST = [
  type SIZE_TYPE (line 41) | type SIZE_TYPE = typeof LIST[number]['size']

FILE: src/screens/Home/Views/Setting/settings/Basic/ShareType.tsx
  type ShareType (line 11) | type ShareType = LX.AppSetting['common.shareType']

FILE: src/screens/Home/Views/Setting/settings/Basic/SourceName.tsx
  type SourceNameType (line 11) | type SourceNameType = LX.AppSetting['common.sourceNameType']

FILE: src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/ImportBtn.tsx
  type BtnProps (line 13) | interface BtnProps {
  type DorpDownMenuProps (line 31) | type DorpDownMenuProps = _DorpDownMenuProps<typeof importTypes>

FILE: src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/List.tsx
  type UserApiEditModalProps (line 67) | interface UserApiEditModalProps {
  type UserApiEditModalType (line 71) | interface UserApiEditModalType {

FILE: src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/ScriptImportExport.tsx
  type SelectInfo (line 6) | interface SelectInfo {
  type ScriptImportExportType (line 23) | interface ScriptImportExportType {
  method import (line 35) | import() {

FILE: src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/ScriptImportOnline.tsx
  type UrlInputType (line 12) | interface UrlInputType {
  method getText (line 24) | getText() {
  method setText (line 27) | setText(text) {
  method focus (line 31) | focus() {
  type ScriptImportOnlineType (line 48) | interface ScriptImportOnlineType {
  method show (line 71) | show() {

FILE: src/screens/Home/Views/Setting/settings/Basic/UserApiEditModal/index.tsx
  type UserApiEditModalType (line 60) | interface UserApiEditModalType {
  method show (line 83) | show() {

FILE: src/screens/Home/Views/Setting/settings/LyricDesktop/TextPositionX.tsx
  type X_TYPE (line 13) | type X_TYPE = LX.AppSetting['desktopLyric.textPosition.x']
  constant X_LIST (line 15) | const X_LIST = [

FILE: src/screens/Home/Views/Setting/settings/LyricDesktop/TextPositionY.tsx
  type Y_TYPE (line 12) | type Y_TYPE = LX.AppSetting['desktopLyric.textPosition.y']
  constant Y_LIST (line 14) | const Y_LIST = [

FILE: src/screens/Home/Views/Setting/settings/LyricDesktop/Theme.tsx
  type Theme (line 20) | type Theme = typeof themes[number]

FILE: src/screens/Home/Views/Setting/settings/Other/DislikeEditModal.tsx
  type RuleInputType (line 11) | interface RuleInputType {
  method getText (line 24) | getText() {
  method setText (line 27) | setText(text) {
  method focus (line 30) | focus() {
  type DislikeEditModalProps (line 56) | interface DislikeEditModalProps {
  type DislikeEditModalType (line 60) | interface DislikeEditModalType {
  method show (line 83) | show(rules) {

FILE: src/screens/Home/Views/Setting/settings/Player/MaxCache.tsx
  constant MAX_SIZE (line 10) | const MAX_SIZE = 1024 * 1024 * 1024

FILE: src/screens/Home/Views/Setting/settings/Sync/History.tsx
  type SyncHistoryItem (line 16) | type SyncHistoryItem = Awaited<ReturnType<typeof getSyncHostHistory>>[nu...
  type HistoryListProps (line 52) | interface HistoryListProps {
  type HistoryListType (line 55) | interface HistoryListType {
  method show (line 75) | show() {

FILE: src/screens/Home/Views/Setting/settings/Theme/Theme.tsx
  type ThemeInfo (line 70) | interface ThemeInfo {
  constant ITEM_HEIGHT (line 123) | const ITEM_HEIGHT = 62
  constant COLOR_ITEM_HEIGHT (line 124) | const COLOR_ITEM_HEIGHT = 36
  constant IMAGE_HEIGHT (line 125) | const IMAGE_HEIGHT = 29

FILE: src/screens/Home/Views/SongList/Content.tsx
  type SonglistInfo (line 11) | interface SonglistInfo {

FILE: src/screens/Home/Views/SongList/HeaderBar/OpenList/Modal.tsx
  type IdInputType (line 12) | interface IdInputType {
  method getText (line 24) | getText() {
  method setText (line 27) | setText(text) {
  method focus (line 30) | focus() {
  type ModalProps (line 47) | interface ModalProps {
  type ModalType (line 51) | interface ModalType {
  method show (line 74) | show(source) {

FILE: src/screens/Home/Views/SongList/HeaderBar/OpenList/index.tsx
  type OpenListType (line 17) | interface OpenListType {
  method setInfo (line 27) | setInfo(source) {

FILE: src/screens/Home/Views/SongList/HeaderBar/SortTab.tsx
  type SortTabProps (line 10) | interface SortTabProps {
  type SortTabType (line 14) | interface SortTabType {
  method setSource (line 27) | setSource(source, activeTab) {

FILE: src/screens/Home/Views/SongList/HeaderBar/SourceSelector.tsx
  type Sources (line 11) | type Sources = Readonly<InitState['sources']>
  type SourceSelectorCommonProps (line 12) | type SourceSelectorCommonProps = _SourceSelectorProps<Sources>
  type SourceSelectorCommonType (line 13) | type SourceSelectorCommonType = _SourceSelectorType<Sources>
  type SourceSelectorProps (line 15) | interface SourceSelectorProps {
  type SourceSelectorType (line 20) | interface SourceSelectorType {
  method setSource (line 28) | setSource(source) {

FILE: src/screens/Home/Views/SongList/HeaderBar/Tag/CurrentTagBtn.tsx
  type CurrentTagBtnProps (line 8) | interface CurrentTagBtnProps {
  type CurrentTagBtnType (line 12) | interface CurrentTagBtnType {
  method setCurrentTagInfo (line 21) | setCurrentTagInfo(name) {

FILE: src/screens/Home/Views/SongList/HeaderBar/Tag/index.tsx
  type TagProps (line 8) | interface TagProps {
  type TagType (line 12) | interface TagType {
  method setSelectedTagInfo (line 36) | setSelectedTagInfo(source, name, activeId) {

FILE: src/screens/Home/Views/SongList/HeaderBar/index.tsx
  type HeaderBarProps (line 20) | interface HeaderBarProps {
  type HeaderBarType (line 26) | interface HeaderBarType {
  method setSource (line 39) | setSource(source, sortId, tagName, tagId) {

FILE: src/screens/Home/Views/SongList/List.tsx
  type ListType (line 8) | interface ListType {
  method loadList (line 16) | async loadList(source, sortId, tagId) {

FILE: src/screens/Home/Views/SongList/TagList/List.tsx
  type ListProps (line 12) | interface ListProps {
  type ListType (line 16) | interface ListType {
  method loadTag (line 36) | loadTag(source, id) {

FILE: src/screens/Home/Views/SongList/TagList/TagGroup.tsx
  type TagGroupProps (line 9) | interface TagGroupProps {

FILE: src/screens/Home/Views/SongList/components/Songlist/List.tsx
  type FlatListType (line 14) | type FlatListType = FlatListProps<ListInfoItem>
  constant MIN_WIDTH (line 17) | const MIN_WIDTH = scaleSizeW(110)
  constant GAP (line 18) | const GAP = scaleSizeW(20)
  type ListProps (line 20) | interface ListProps {
  type Status (line 25) | type Status = 'loading' | 'refreshing' | 'end' | 'error' | 'idle'
  type ListType (line 27) | interface ListType {
  method setList (line 42) | setList(list, showSource = false) {
  method setStatus (line 47) | setStatus(val) {
  type FooterLabel (line 180) | type FooterLabel = 'list_loading' | 'list_end' | 'list_error' | null

FILE: src/screens/Home/Views/SongList/components/Songlist/index.tsx
  type SonglistProps (line 8) | interface SonglistProps {
  type SonglistType (line 12) | interface SonglistType {
  method setList (line 25) | setList(list, showSource) {
  method setStatus (line 28) | setStatus(val) {

FILE: src/screens/Home/Views/SongList/index.tsx
  constant MAX_WIDTH (line 11) | const MAX_WIDTH = scaleSizeW(560)

FILE: src/screens/Home/index.tsx
  type Props (line 12) | interface Props {

FILE: src/screens/PlayDetail/Horizontal/Lyric.tsx
  type FlatListType (line 18) | type FlatListType = FlatListProps<Line>
  type LineProps (line 20) | interface LineProps {

FILE: src/screens/PlayDetail/Horizontal/MoreBtn/Btn.tsx
  constant BTN_WIDTH (line 7) | const BTN_WIDTH = scaleSizeW(32)
  constant BTN_ICON_SIZE (line 8) | const BTN_ICON_SIZE = 22

FILE: src/screens/PlayDetail/Horizontal/Player/ControlBtn.tsx
  constant MIN_SIZE (line 47) | const MIN_SIZE = BTN_WIDTH * 1.1

FILE: src/screens/PlayDetail/Horizontal/components/Btn.tsx
  constant BTN_WIDTH (line 8) | const BTN_WIDTH = scaleSizeW(HEADER_HEIGHT)
  constant BTN_ICON_SIZE (line 9) | const BTN_ICON_SIZE = 20

FILE: src/screens/PlayDetail/Horizontal/components/Header.tsx
  constant HEADER_HEIGHT (line 18) | const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)

FILE: src/screens/PlayDetail/Vertical/Lyric.tsx
  type FlatListType (line 18) | type FlatListType = FlatListProps<Line>
  type LineProps (line 59) | interface LineProps {

FILE: src/screens/PlayDetail/Vertical/Player/components/ControlBtn.tsx
  constant MAX_SIZE (line 45) | const MAX_SIZE = BTN_WIDTH * 1.6
  constant MIN_SIZE (line 46) | const MIN_SIZE = BTN_WIDTH * 1.2

FILE: src/screens/PlayDetail/Vertical/Player/components/MoreBtn/Btn.tsx
  constant BTN_WIDTH (line 7) | const BTN_WIDTH = scaleSizeW(36)
  constant BTN_ICON_SIZE (line 8) | const BTN_ICON_SIZE = 24

FILE: src/screens/PlayDetail/Vertical/components/Btn.tsx
  constant HEADER_HEIGHT (line 7) | const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)

FILE: src/screens/PlayDetail/Vertical/components/Header.tsx
  constant HEADER_HEIGHT (line 18) | const HEADER_HEIGHT = scaleSizeH(_HEADER_HEIGHT)

FILE: src/screens/PlayDetail/components/PlayLine.tsx
  type PlayLineType (line 12) | interface PlayLineType {
  type PlayLineProps (line 19) | interface PlayLineProps {
  constant ANIMATION_DURATION (line 23) | const ANIMATION_DURATION = 300
  method updateScrollInfo (line 46) | updateScrollInfo(scrollInfo) {
  method updateLayoutInfo (line 49) | updateLayoutInfo(listLayoutInfo) {
  method updateLyricLines (line 52) | updateLyricLines(lyricLines) {
  method setVisible (line 55) | setVisible(visible) {

FILE: src/screens/PlayDetail/components/SettingPopup/index.tsx
  type SettingPopupProps (line 12) | interface SettingPopupProps extends Omit<PopupProps, 'children'> {
  type SettingPopupType (line 16) | interface SettingPopupType {
  method show (line 27) | show() {

FILE: src/screens/PlayDetail/components/SettingPopup/settings/SettingLrcAlign.tsx
  type Align_Type (line 11) | type Align_Type = LX.AppSetting['playDetail.style.align']
  constant ALIGN_LIST (line 13) | const ALIGN_LIST = [

FILE: src/screens/PlayDetail/components/SettingPopup/settings/SettingPlaybackRate.tsx
  constant MIN_VALUE (line 17) | const MIN_VALUE = 60
  constant MAX_VALUE (line 18) | const MAX_VALUE = 200

FILE: src/screens/SonglistDetail/Header.tsx
  constant IMAGE_WIDTH (line 16) | const IMAGE_WIDTH = scaleSizeW(70)
  type HeaderProps (line 60) | interface HeaderProps {
  type HeaderType (line 64) | interface HeaderType {
  type DetailInfo (line 67) | interface DetailInfo {
  method setInfo (line 81) | setInfo(info) {

FILE: src/screens/SonglistDetail/MusicList.tsx
  type MusicListProps (line 9) | interface MusicListProps {
  type MusicListType (line 13) | interface MusicListType {
  method loadList (line 24) | async loadList(source, id) {

FILE: src/store/common/action.ts
  method setFontSize (line 6) | setFontSize(size: number) {
  method setStatusbarHeight (line 10) | setStatusbarHeight(size: number) {
  method setComponentId (line 15) | setComponentId(name: COMPONENT_IDS, id: string) {
  method removeComponentId (line 19) | removeComponentId(id: string) {
  method setNavActiveId (line 25) | setNavActiveId(id: InitState['navActiveId']) {
  method setLastNavActiveId (line 30) | setLastNavActiveId(id: InitState['navActiveId']) {
  method setBgPic (line 33) | setBgPic(pic: string | null) {
  method setSourceNames (line 37) | setSourceNames(names: InitState['sourceNames']) {

FILE: src/store/common/state.ts
  type InitState (line 4) | interface InitState {

FILE: src/store/dislikeList/event.ts
  class DislikeEvent (line 4) | class DislikeEvent extends Event {
    method dislike_changed (line 5) | dislike_changed() {
  type EventMethods (line 11) | type EventMethods = Omit<EventType, keyof Event>
  class EventType (line 14) | class EventType extends DislikeEvent {
  type DislikeEventTypes (line 19) | type DislikeEventTypes = Omit<EventType, keyof Omit<Event, 'on' | 'off'>>

FILE: src/store/dislikeList/state.ts
  type InitState (line 2) | interface InitState {

FILE: src/store/hotSearch/action.ts
  type Lists (line 3) | type Lists = Array<{ source: LX.OnlineSource, list: string[] }>
  method setList (line 28) | setList(source: Source, list: string[] | Lists) {
  method clearList (line 34) | clearList(source: Source) {

FILE: src/store/hotSearch/state.ts
  type Source (line 5) | type Source = LX.OnlineSource | 'all'
  type SourceLists (line 7) | type SourceLists = Partial<Record<Source, string[]>>
  type InitState (line 10) | interface InitState {

FILE: src/store/leaderboard/action.ts
  method setBoard (line 4) | setBoard(board: Board, source: LX.OnlineSource) {
  method setListDetailInfo (line 7) | setListDetailInfo(source: Source, id: string) {
  method setListDetail (line 11) | setListDetail(result: ListDetailInfo, id: string, page: number) {
  method clearListDetail (line 23) | clearListDetail() {

FILE: src/store/leaderboard/state.ts
  type Source (line 3) | type Source = LX.OnlineSource
  type BoardItem (line 5) | interface BoardItem {
  type Board (line 10) | interface Board {
  type Boards (line 14) | type Boards = Partial<Record<LX.OnlineSource, Board>>
  type ListDetailInfo (line 16) | interface ListDetailInfo {
  type InitState (line 27) | interface InitState {

FILE: src/store/list/action.ts
  method setUserLists (line 5) | setUserLists(userList: LX.List.UserListInfo[]) {
  method setActiveList (line 11) | setActiveList(activeListId: string) {
  method setTempListMeta (line 16) | setTempListMeta(meta: InitState['tempListMeta']) {
  method setFetchingListStatus (line 19) | setFetchingListStatus(id: string, status: boolean) {

FILE: src/store/list/state.ts
  type InitState (line 4) | interface InitState {

FILE: src/store/player/action.ts
  type PlayerMusicInfoKeys (line 4) | type PlayerMusicInfoKeys = keyof LX.Player.MusicInfo
  method updatePlayIndex (line 8) | updatePlayIndex(playIndex: number, playerPlayIndex: number) {
  method setPlayListId (line 14) | setPlayListId(playerListId: string | null) {
  method setPlayMusicInfo (line 19) | setPlayMusicInfo(listId: string | null, musicInfo: LX.Download.ListItem ...
  method setMusicInfo (line 24) | setMusicInfo(_musicInfo: Partial<LX.Player.MusicInfo>) {
  method setIsPlay (line 35) | setIsPlay(isPlay: boolean) {
  method setStatusText (line 40) | setStatusText(statusText: string) {
  method setNowPlayTime (line 44) | setNowPlayTime(time: number) {
  method setMaxplayTime (line 51) | setMaxplayTime(time: number) {
  method setProgress (line 58) | setProgress(currentTime: number, totalTime: number) {
  method addPlayedList (line 67) | addPlayedList(info: LX.Player.PlayMusicInfo) {
  method removePlayedList (line 73) | removePlayedList(index: number) {
  method clearPlayedList (line 78) | clearPlayedList() {
  method addTempPlayList (line 83) | addTempPlayList(list: LX.Player.TempPlayListItem[]) {
  method removeTempPlayList (line 97) | removeTempPlayList(index: number) {
  method clearTempPlayeList (line 102) | clearTempPlayeList() {
  method setLoadErrorPicUrl (line 107) | setLoadErrorPicUrl(url: string) {
  method setLastLyric (line 110) | setLastLyric(lrc?: string) {

FILE: src/store/player/state.ts
  type InitState (line 1) | interface InitState {

FILE: src/store/search/action.ts
  method setSearchType (line 33) | setSearchType(type: InitState['searchType']) {
  method setSearchText (line 36) | setSearchText(text: string) {
  method setTipListInfo (line 39) | setTipListInfo(keyword: InitState['tipListInfo']['text'], source: InitSt...
  method setTipList (line 43) | setTipList(list: InitState['tipListInfo']['list']) {
  method setHistoryWord (line 46) | setHistoryWord(list: string[]) {
  method addHistoryWord (line 49) | addHistoryWord(word: string) {
  method removeHistoryWord (line 57) | removeHistoryWord(index: number) {
  method clearHistoryList (line 61) | clearHistoryList() {

FILE: src/store/search/music/action.ts
  type SearchResult (line 6) | interface SearchResult {
  method setSource (line 77) | setSource(source: InitState['source']) {
  method setSearchText (line 80) | setSearchText(searchText: InitState['searchText']) {
  method setListInfo (line 83) | setListInfo(result: SearchResult | SearchResult[], page: number, text: s...
  method clearListInfo (line 90) | clearListInfo(sourceId: Source) {

FILE: src/store/search/music/state.ts
  type ListInfo (line 3) | interface ListInfo {
  type ListInfos (line 12) | interface ListInfos extends Partial<Record<LX.OnlineSource, ListInfo>> {
  type Source (line 16) | type Source = LX.OnlineSource | 'all'
  type InitState (line 18) | interface InitState {

FILE: src/store/search/songlist/action.ts
  type SearchResult (line 6) | interface SearchResult {
  method setSource (line 73) | setSource(source: InitState['source']) {
  method setSearchText (line 76) | setSearchText(searchText: InitState['searchText']) {
  method setListInfo (line 79) | setListInfo(result: SearchResult | SearchResult[], page: number, text: s...
  method clearListInfo (line 86) | clearListInfo(sourceId: Source) {

FILE: src/store/search/songlist/state.ts
  type SearchListInfo (line 9) | type SearchListInfo = Omit<ListInfo, 'source' | 'maxPage'>
  type ListInfos (line 12) | interface ListInfos extends Partial<Record<LX.OnlineSource, SearchListIn...
  type Source (line 16) | type Source = LX.OnlineSource | 'all'
  type InitState (line 18) | interface InitState {

FILE: src/store/search/state.ts
  type SearchType (line 1) | type SearchType = 'music' | 'songlist'
  type InitState (line 3) | interface InitState {

FILE: src/store/setting/action.ts
  method initSetting (line 12) | initSetting(newSetting: LX.AppSetting) {
  method updateSetting (line 15) | updateSetting(newSetting: Partial<LX.AppSetting>) {

FILE: src/store/setting/state.ts
  type InitState (line 4) | interface InitState {

FILE: src/store/songlist/action.ts
  method setTags (line 5) | setTags(tagInfo: TagInfo, source: LX.OnlineSource) {
  method setListInfo (line 8) | setListInfo(source: Source, tagId: string, sortId: string) {
  method setList (line 13) | setList(result: ListInfo, tagId: string, sortId: string, page: number) {
  method clearList (line 26) | clearList() {
  method setListDetailInfo (line 33) | setListDetailInfo(source: Source, id: string) {
  method setListDetail (line 37) | setListDetail(result: ListDetailInfo, id: string, page: number) {
  method clearListDetail (line 50) | clearListDetail() {

FILE: src/store/songlist/state.ts
  type SortInfo (line 3) | interface SortInfo {
  type TagInfoItem (line 9) | interface TagInfoItem<T extends LX.OnlineSource = LX.OnlineSource> {
  type TagInfoTypeItem (line 16) | interface TagInfoTypeItem<T extends LX.OnlineSource = LX.OnlineSource> {
  type TagInfo (line 20) | interface TagInfo<Source extends LX.OnlineSource = LX.OnlineSource> {
  type Tags (line 26) | type Tags = Partial<Record<LX.OnlineSource, TagInfo>>
  type ListInfoItem (line 28) | interface ListInfoItem {
  type ListInfo (line 40) | interface ListInfo {
  type ListDetailInfo (line 52) | interface ListDetailInfo {
  type Source (line 76) | type Source = LX.OnlineSource
  type InitState (line 77) | interface InitState {

FILE: src/store/sync/action.ts
  method setStatus (line 5) | setStatus(info: LX.Sync.Status) {
  method setMessage (line 11) | setMessage(message: LX.Sync.Status['message']) {
  method setServerInfo (line 16) | setServerInfo(name: string, type: keyof LX.Sync.ModeTypes) {
  method setSyncModeComponentId (line 20) | setSyncModeComponentId(id: string) {

FILE: src/store/sync/state.ts
  type InitState (line 2) | interface InitState {

FILE: src/store/theme/action.ts
  method setTheme (line 6) | setTheme(theme: LX.Theme) {
  method setShouldUseDarkColors (line 11) | setShouldUseDarkColors(shouldUseDarkColors: boolean) {

FILE: src/store/theme/state.ts
  type InitState (line 5) | interface InitState {

FILE: src/store/userApi/event.ts
  class UserApiEvent (line 4) | class UserApiEvent extends Event {
    method status_changed (line 5) | status_changed(status: { status: boolean, message?: string }) {
    method list_changed (line 9) | list_changed(list: LX.UserApi.UserApiInfo[]) {
  type EventMethods (line 15) | type EventMethods = Omit<EventType, keyof Event>
  class EventType (line 18) | class EventType extends UserApiEvent {
  type UserApiEventTypes (line 23) | type UserApiEventTypes = Omit<EventType, keyof Omit<Event, 'on' | 'off'>>

FILE: src/store/userApi/state.ts
  type InitState (line 2) | interface InitState {

FILE: src/store/version/action.ts
  method setVersionInfo (line 5) | setVersionInfo(info: Partial<InitState['versionInfo']>) {
  method setIgnoreVersion (line 9) | setIgnoreVersion(version: InitState['ignoreVersion']) {
  method setProgress (line 13) | setProgress(info: InitState['progress']) {
  method setVisibleModal (line 21) | setVisibleModal(visible: boolean) {

FILE: src/store/version/state.ts
  type ProgressInfo (line 3) | interface ProgressInfo {
  type VersionInfo (line 8) | interface VersionInfo {
  type InitState (line 14) | interface InitState {

FILE: src/theme/themes/index.ts
  constant BG_IMAGES (line 10) | const BG_IMAGES = {
  type LocalTheme (line 44) | type LocalTheme = typeof themes[number]
  type ColorsKey (line 45) | type ColorsKey = keyof LX.Theme['config']['themeColors']
  type ExtInfoKey (line 46) | type ExtInfoKey = keyof LX.Theme['config']['extInfo']

FILE: src/types/app.d.ts
  type GlobalData (line 18) | interface GlobalData {
  type ProcessVersions (line 75) | interface ProcessVersions {

FILE: src/types/app_setting.d.ts
  type AddMusicLocationType (line 5) | type AddMusicLocationType = 'top' | 'bottom'
  type AppSetting (line 7) | interface AppSetting {

FILE: src/types/common.d.ts
  type OnlineSource (line 4) | type OnlineSource = 'kw' | 'kg' | 'tx' | 'wy' | 'mg'
  type Source (line 5) | type Source = OnlineSource | 'local'
  type Quality (line 6) | type Quality = '128k' | '320k' | 'flac' | 'flac24bit' | '192k' | 'ape' |...
  type QualityList (line 7) | type QualityList = Partial<Record<LX.Source, LX.Quality[]>>
  type ShareType (line 9) | type ShareType = 'system' | 'clipboard'
  type UpdateStatus (line 11) | type UpdateStatus = 'downloaded' | 'downloading' | 'error' | 'checking' ...
  type VersionInfo (line 12) | interface VersionInfo {

FILE: src/types/config_files.d.ts
  type MyListInfoPart (line 3) | interface MyListInfoPart {

FILE: src/types/dislike_list.d.ts
  type DislikeMusicInfo (line 21) | interface DislikeMusicInfo {
  type DislikeRules (line 26) | type DislikeRules = string
  type DislikeInfo (line 28) | interface DislikeInfo {

FILE: src/types/dislike_list_sync.d.ts
  type ListInfo (line 5) | interface ListInfo {
  type SyncActionBase (line 10) | interface SyncActionBase <A> {
  type SyncActionData (line 13) | interface SyncActionData<A, D> extends SyncActionBase<A> {
  type SyncAction (line 16) | type SyncAction<A, D = undefined> = D extends undefined ? SyncActionBase...
  type ActionList (line 17) | type ActionList = SyncAction<'dislike_data_overwrite', LX.Dislike.Dislik...
  type SyncMode (line 21) | type SyncMode = 'merge_local_remote'

FILE: src/types/download_list.d.ts
  type DownloadTaskStatus (line 9) | type DownloadTaskStatus = 'run'
  type FileExt (line 15) | type FileExt = 'mp3' | 'flac' | 'wav' | 'ape'
  type ProgressInfo (line 17) | interface ProgressInfo {
  type DownloadTaskActionBase (line 24) | interface DownloadTaskActionBase <A> {
  type DownloadTaskActionData (line 27) | interface DownloadTaskActionData<A, D> extends DownloadTaskActionBase<A> {
  type DownloadTaskAction (line 30) | type DownloadTaskAction<A, D = undefined> = D extends undefined ? Downlo...
  type DownloadTaskActions (line 32) | type DownloadTaskActions = DownloadTaskAction<'start'>
  type ListItem (line 42) | interface ListItem {
  type saveDownloadMusicInfo (line 61) | interface saveDownloadMusicInfo {

FILE: src/types/list.d.ts
  type UserListInfo (line 3) | interface UserListInfo {
  type MyDefaultListInfo (line 13) | interface MyDefaultListInfo {
  type MyLoveListInfo (line 19) | interface MyLoveListInfo {
  type MyTempListInfo (line 25) | interface MyTempListInfo {
  type MyListInfo (line 35) | type MyListInfo = MyDefaultListInfo | MyLoveListInfo | UserListInfo
  type MyAllList (line 37) | interface MyAllList {
  type SearchHistoryList (line 45) | type SearchHistoryList = string[]
  type ListPositionInfo (line 46) | type ListPositionInfo = Record<string, number>
  type ListUpdateInfo (line 47) | type ListUpdateInfo = Record<string, {
  type ListSaveType (line 52) | type ListSaveType = 'myList' | 'downloadList'
  type ListSaveInfo (line 53) | type ListSaveInfo = {
  type ListActionDataOverwrite (line 62) | type ListActionDataOverwrite = MakeOptional<LX.List.ListDataFull, 'tempL...
  type ListActionAdd (line 63) | interface ListActionAdd {
  type ListActionRemove (line 67) | type ListActionRemove = string[]
  type ListActionUpdate (line 68) | type ListActionUpdate = UserListInfo[]
  type ListActionUpdatePosition (line 69) | interface ListActionUpdatePosition {
  type ListActionMusicAdd (line 80) | interface ListActionMusicAdd {
  type ListActionMusicMove (line 86) | interface ListActionMusicMove {
  type ListActionCheckMusicExistList (line 93) | interface ListActionCheckMusicExistList {
  type ListActionMusicRemove (line 98) | interface ListActionMusicRemove {
  type ListActionMusicUpdate (line 103) | type ListActionMusicUpdate = Array<{
  type ListActionMusicUpdatePosition (line 108) | interface ListActionMusicUpdatePosition {
  type ListActionMusicOverwrite (line 114) | interface ListActionMusicOverwrite {
  type ListActionMusicClear (line 119) | type ListActionMusicClear = string[]
  type MyDefaultListInfoFull (line 121) | interface MyDefaultListInfoFull extends MyDefaultListInfo {
  type MyLoveListInfoFull (line 124) | interface MyLoveListInfoFull extends MyLoveListInfo {
  type UserListInfoFull (line 127) | interface UserListInfoFull extends UserListInfo {
  type MyTempListInfoFull (line 130) | interface MyTempListInfoFull extends MyTempListInfo {
  type ListDataFull (line 134) | interface ListDataFull {
  type ListMusics (line 141) | type ListMusics = LX.Music.MusicInfo[]

FILE: src/types/list_sync.d.ts
  type ListInfo (line 5) | interface ListInfo {
  type SyncActionBase (line 10) | interface SyncActionBase <A> {
  type SyncActionData (line 13) | interface SyncActionData<A, D> extends SyncActionBase<A> {
  type SyncAction (line 16) | type SyncAction<A, D = undefined> = D extends undefined ? SyncActionBase...
  type ActionList (line 17) | type ActionList = SyncAction<'list_data_overwrite', LX.List.ListActionDa...
  type ListData (line 30) | type ListData = Omit<LX.List.ListDataFull, 'tempList'>
  type SyncMode (line 31) | type SyncMode = 'merge_local_remote'

FILE: src/types/music.d.ts
  type MusicQualityType (line 3) | interface MusicQualityType { // {"type": "128k", size: "3.56M"}
  type MusicQualityTypeKg (line 7) | interface MusicQualityTypeKg { // {"type": "128k", size: "3.56M"}
  type _MusicQualityType (line 12) | type _MusicQualityType = Partial<Record<Quality, {
  type _MusicQualityTypeKg (line 15) | type _MusicQualityTypeKg = Partial<Record<Quality, {
  type MusicInfoMetaBase (line 21) | interface MusicInfoMetaBase {
  type MusicInfoMeta_online (line 28) | interface MusicInfoMeta_online extends MusicInfoMetaBase {
  type MusicInfoMeta_local (line 34) | interface MusicInfoMeta_local extends MusicInfoMetaBase {
  type MusicInfoBase (line 40) | interface MusicInfoBase<S = LX.Source> {
  type MusicInfoLocal (line 49) | interface MusicInfoLocal extends MusicInfoBase<'local'> {
  type MusicInfo_online_common (line 53) | interface MusicInfo_online_common extends MusicInfoBase<'kw' | 'wy'> {
  type MusicInfoMeta_kg (line 57) | interface MusicInfoMeta_kg extends MusicInfoMeta_online {
  type MusicInfo_kg (line 62) | interface MusicInfo_kg extends MusicInfoBase<'kg'> {
  type MusicInfoMeta_tx (line 66) | interface MusicInfoMeta_tx extends MusicInfoMeta_online {
  type MusicInfo_tx (line 71) | interface MusicInfo_tx extends MusicInfoBase<'tx'> {
  type MusicInfoMeta_mg (line 75) | interface MusicInfoMeta_mg extends MusicInfoMeta_online {
  type MusicInfo_mg (line 81) | interface MusicInfo_mg extends MusicInfoBase<'mg'> {
  type MusicInfoOnline (line 85) | type MusicInfoOnline = MusicInfo_online_common | MusicInfo_kg | MusicInf...
  type MusicInfo (line 86) | type MusicInfo = MusicInfoOnline | MusicInfoLocal
  type LyricInfo (line 88) | interface LyricInfo {
  type LyricInfoSave (line 99) | interface LyricInfoSave {
  type MusicUrlInfo (line 104) | interface MusicUrlInfo {
  type MusicInfoOtherSourceSave (line 109) | interface MusicInfoOtherSourceSave {

FILE: src/types/player.d.ts
  type MusicInfo (line 6) | interface MusicInfo {
  type LyricInfo (line 20) | interface LyricInfo extends LX.Music.LyricInfo {
  type PlayMusic (line 24) | type PlayMusic = LX.Music.MusicInfo | LX.Download.ListItem
  type PlayMusicInfo (line 26) | type PlayMusicInfo = Readonly<{
  type PlayInfo (line 41) | interface PlayInfo {
  type TempPlayListItem (line 56) | interface TempPlayListItem {
  type SavedPlayInfo (line 71) | interface SavedPlayInfo {
  type Track (line 78) | interface Track extends RNTrack {

FILE: src/types/sync.d.ts
  type Status (line 4) | interface Status {
  type KeyInfo (line 9) | interface KeyInfo {
  type Socket (line 15) | interface Socket extends WebSocket {
  type ModeTypes (line 34) | interface ModeTypes {
  type ModeType (line 39) | type ModeType = { [K in keyof ModeTypes]: { type: K, mode: ModeTypes[K] ...
  type UrlInfo (line 41) | interface UrlInfo {
  type ListConfig (line 48) | interface ListConfig {
  type DislikeConfig (line 51) | interface DislikeConfig {
  type ServerType (line 54) | type ServerType = 'desktop-app' | 'server'
  type EnabledFeatures (line 55) | interface EnabledFeatures {
  type SupportedFeatures (line 59) | type SupportedFeatures = Partial<{ [k in keyof EnabledFeatures]: number }>

FILE: src/types/sync_common.d.ts
  type WarpSyncHandlerActions (line 1) | type WarpSyncHandlerActions<Socket, Actions> = {
  type ServerSyncActions (line 7) | type ServerSyncActions = WarpPromiseRecord<{
  type ServerSyncHandlerActions (line 10) | type ServerSyncHandlerActions<Socket> = WarpSyncHandlerActions<Socket, S...
  type ServerSyncListActions (line 12) | type ServerSyncListActions = WarpPromiseRecord<{
  type ServerSyncHandlerListActions (line 15) | type ServerSyncHandlerListActions<Socket> = WarpSyncHandlerActions<Socke...
  type ServerSyncDislikeActions (line 17) | type ServerSyncDislikeActions = WarpPromiseRecord<{
  type ServerSyncHandlerDislikeActions (line 20) | type ServerSyncHandlerDislikeActions<Socket> = WarpSyncHandlerActions<So...
  type ClientSyncActions (line 22) | type ClientSyncActions = WarpPromiseRecord<{
  type ClientSyncHandlerActions (line 26) | type ClientSyncHandlerActions<Socket> = WarpSyncHandlerActions<Socket, C...
  type ClientSyncListActions (line 28) | type ClientSyncListActions = WarpPromiseRecord<{
  type ClientSyncHandlerListActions (line 36) | type ClientSyncHandlerListActions<Socket> = WarpSyncHandlerActions<Socke...
  type ClientSyncDislikeActions (line 38) | type ClientSyncDislikeActions = WarpPromiseRecord<{
  type ClientSyncHandlerDislikeActions (line 46) | type ClientSyncHandlerDislikeActions<Socket> = WarpSyncHandlerActions<So...

FILE: src/types/theme.d.ts
  type ThemeColors (line 5) | interface ThemeColors {
  type ActiveTheme (line 263) | type ActiveTheme = ThemeColors & Omit<Theme['config']['extInfo'], 'bg-im...
  type Theme (line 285) | interface Theme {
  type ThemeInfo (line 307) | interface ThemeInfo {
  type ThemeSetting (line 313) | interface ThemeSetting {

FILE: src/types/user_api.d.ts
  type UserApiSourceInfoType (line 3) | type UserApiSourceInfoType = 'music'
  type UserApiSourceInfoActions (line 4) | type UserApiSourceInfoActions = 'musicUrl' | 'lyric' | 'pic'
  type UserApiSourceInfo (line 6) | interface UserApiSourceInfo {
  type UserApiSources (line 13) | type UserApiSources = Record<LX.Source, UserApiSourceInfo>
  type UserApiInfo (line 16) | interface UserApiInfo {
  type UserApiStatus (line 28) | interface UserApiStatus {
  type UserApiUpdateInfo (line 34) | interface UserApiUpdateInfo {
  type UserApiRequestParams (line 41) | interface UserApiRequestParams {
  type UserApiRequestParams (line 45) | interface UserApiRequestParams {
  type UserApiRequestCancelParams (line 49) | type UserApiRequestCancelParams = string
  type UserApiSetApiParams (line 50) | type UserApiSetApiParams = string
  type UserApiSetAllowUpdateAlertParams (line 52) | interface UserApiSetAllowUpdateAlertParams {
  type ImportUserApi (line 57) | interface ImportUserApi {

FILE: src/types/utils.d.ts
  type MakeOptional (line 1) | type MakeOptional<Type, Key extends keyof Type> = Omit<Type, Key> & Part...
  type DeepPartial (line 3) | type DeepPartial<T> = {
  type Modify (line 7) | type Modify<T, R> = Omit<T, keyof R> & R
  type MakeArrayItemReadOnly (line 9) | type MakeArrayItemReadOnly<T extends any[]> = { [K in keyof T]: Readonly...
  type ForwardRefFn (line 11) | type ForwardRefFn<R> = <P = {}>(p: React.PropsWithChildren<P> & React.Re...
  type Actions (line 14) | type Actions<T extends { action: string, data?: any }> = {
  type WarpPromiseValue (line 18) | type WarpPromiseValue<T> = T extends ((...args: infer P) => Promise<infe...
  type WarpPromiseRecord (line 24) | type WarpPromiseRecord<T extends Record<string, any>> = {

FILE: src/utils/common.ts
  function throttle (line 95) | function throttle<Args extends any[]>(fn: (...args: Args) => void | Prom...
  function debounce (line 114) | function debounce<Args extends any[]>(fn: (...args: Args) => void | Prom...

FILE: src/utils/data.ts
  constant INFO_NAMES (line 518) | const INFO_NAMES = {
  type INFO_NAMES_Type (line 525) | type INFO_NAMES_Type = typeof INFO_NAMES

FILE: src/utils/hooks/useAnimateColor.ts
  constant ANIMATION_DURATION (line 5) | const ANIMATION_DURATION = 800

FILE: src/utils/hooks/useAnimateNumber.ts
  constant DEFAULT_DURATION (line 5) | const DEFAULT_DURATION = 800

FILE: src/utils/hooks/useBackHandler.ts
  function useBackHandler (line 4) | function useBackHandler(handler: () => boolean) {

FILE: src/utils/hooks/useUnmounted.tsx
  function useUnmounted (line 3) | function useUnmounted() {

FILE: src/utils/index.ts
  function compareVer (line 9) | function compareVer(currentVer: string, targetVer: string): -1 | 0 | 1 {

FILE: src/utils/log.ts
  method writeLog (line 8) | writeLog(msg: string) {
  method initLogFile (line 12) | async initLogFile() {
  method info (line 38) | info(...msgs: any[]) {
  method warn (line 47) | warn(...msgs: any[]) {
  method error (line 55) | error(...msgs: any[]) {

FILE: src/utils/musicSdk/bd/hotSearch.js
  method getList (line 5) | async getList(retryNum = 0) {
  method filterList (line 20) | filterList(rawList) {

FILE: src/utils/musicSdk/bd/index.js
  method getMusicUrl (line 14) | getMusicUrl(songInfo, type) {
  method getPic (line 17) | getPic(songInfo) {
  method getLyric (line 21) | getLyric(songInfo) {
  method getMusicInfo (line 32) | getMusicInfo(songInfo) {
  method getMusicDetailPageUrl (line 35) | getMusicDetailPageUrl(songInfo) {

FILE: src/utils/musicSdk/bd/leaderboard.js
  method getUrl (line 75) | getUrl(id, p) {
  method getData (line 82) | getData(url) {
  method filterData (line 86) | filterData(rawList) {
  method parseData (line 124) | parseData(rawData) {
  method getBoards (line 128) | async getBoards(retryNum = 0) {
  method getList (line 135) | getList(bangid, page, retryNum = 0) {

FILE: src/utils/musicSdk/bd/musicInfo.js
  method getMusicInfo (line 5) | getMusicInfo(songmid) {

FILE: src/utils/musicSdk/bd/musicSearch.js
  method musicSearch (line 13) | musicSearch(str, page, limit) {
  method handleResult (line 17) | handleResult(rawData) {
  method search (line 65) | search(str, page = 1, limit, retryNum = 0) {

FILE: src/utils/musicSdk/bd/songList.js
  method aesPassEncod (line 28) | aesPassEncod(jsonData) {
  method createUrl (line 84) | createUrl(param, method) {
  method getTagsUrl (line 88) | getTagsUrl() {
  method getListUrl (line 95) | getListUrl(sortType, tagName, page) {
  method getListDetailUrl (line 105) | getListDetailUrl(list_id, page) {
  method getTags (line 116) | getTags(tryNum = 0) {
  method filterInfoHotTag (line 129) | filterInfoHotTag(rawList) {
  method filterTagInfo (line 136) | filterTagInfo(rawList) {
  method getList (line 150) | getList(sortId, tagId, page, tryNum = 0) {
  method formatPlayCount (line 171) | formatPlayCount(num) {
  method filterList (line 176) | filterList(rawData) {
  method getListDetail (line 191) | getListDetail(id, page, tryNum = 0) {
  method filterData (line 216) | filterData(rawList) {

FILE: src/utils/musicSdk/kg/album.js
  method getAlbumInfo (line 9) | async getAlbumInfo(id) {
  method getAlbumDetail (line 40) | async getAlbumDetail(id, page = 1, limit = 200) {

FILE: src/utils/musicSdk/kg/comment.js
  method getComment (line 9) | async getComment({ hash }, page = 1, limit = 20) {
  method getHotComment (line 30) | async getHotComment({ hash }, page = 1, limit = 20) {
  method getReplyComment (line 48) | async getReplyComment({ songmid, audioId }, replyId, page = 1, limit = 1...
  method replaceAt (line 65) | replaceAt(raw, atList) {
  method filterComment (line 71) | filterComment(rawList) {

FILE: src/utils/musicSdk/kg/hotSearch.js
  method getList (line 6) | async getList(retryNum = 0) {
  method filterList (line 27) | filterList(rawList) {

FILE: src/utils/musicSdk/kg/index.js
  method getMusicUrl (line 18) | getMusicUrl(songInfo, type) {
  method getLyric (line 21) | getLyric(songInfo) {
  method getPic (line 27) | getPic(songInfo) {
  method getMusicDetailPageUrl (line 30) | getMusicDetailPageUrl(songInfo) {

FILE: src/utils/musicSdk/kg/leaderboard.js
  method getUrl (line 66) | getUrl(p, id, limit) {
  method getBoardsData (line 76) | getBoardsData() {
  method getData (line 81) | getData(url) {
  method getSinger (line 85) | getSinger(singers) {
  method filterData (line 92) | filterData(rawList) {
  method filterBoardsData (line 148) | filterBoardsData(rawList) {
  method getBoards (line 161) | async getBoards(retryNum = 0) {
  method getList (line 185) | async getList(bangid, page, retryNum = 0) {
  method getDetailPageUrl (line 204) | getDetailPageUrl(id) {

FILE: src/utils/musicSdk/kg/lyric.js
  method getIntv (line 59) | getIntv(interval) {
  method searchLyric (line 92) | searchLyric(name, hash, time, tryNum = 0) {
  method getLyricDownload (line 115) | getLyricDownload(id, accessKey, fmt, tryNum = 0) {
  method getLyric (line 148) | getLyric(songInfo, tryNum = 0) {

FILE: src/utils/musicSdk/kg/musicSearch.js
  method musicSearch (line 10) | musicSearch(str, page, limit) {
  method filterData (line 14) | filterData(rawData) {
  method handleResult (line 67) | handleResult(rawData) {
  method search (line 84) | search(str, page = 1, limit, retryNum = 0) {

FILE: src/utils/musicSdk/kg/pic.js
  method getPic (line 4) | getPic(songInfo) {

FILE: src/utils/musicSdk/kg/singer.js
  method filterAlbum (line 7) | filterAlbum(rawList) {
  method getSingerInfo (line 18) | async getSingerInfo(singerid) {
  method getSingerSongList (line 33) | async getSingerSongList(singerid, page, limit) {
  method getSingerAlbumList (line 48) | async getSingerAlbumList(singerid, page, limit) {

FILE: src/utils/musicSdk/kg/songList.js
  method callback (line 10) | callback(i) {
  method parseHtmlDesc (line 105) | parseHtmlDesc(html) {
  method getListDetailBySpecialId (line 114) | async getListDetailBySpecialId(id, page, tryNum = 0) {
  method getInfoUrl (line 167) | getInfoUrl(tagId) {
  method getSongListUrl (line 172) | getSongListUrl(sortId, tagId, page) {
  method getSongListDetailUrl (line 176) | getSongListDetailUrl(id) {
  method filterInfoHotTag (line 180) | filterInfoHotTag(rawData) {
  method filterTagInfo (line 193) | filterTagInfo(rawData) {
  method getSongList (line 210) | getSongList(sortId, tagId, page, tryNum = 0) {
  method getSongListRecommend (line 221) | getSongListRecommend(tryNum = 0) {
  method filterList (line 249) | filterList(rawData) {
  method createHttp (line 264) | async createHttp(url, options, retryNum = 0) {
  method createTask (line 289) | createTask(hashs) {
  method getMusicInfos (line 324) | async getMusicInfos(list) {
  method getUserListDetailByCode (line 334) | async getUserListDetailByCode(id) {
  method getUserListDetail3 (line 386) | async getUserListDetail3(chain, page) {
  method deDuplication (line 414) | deDuplication(datas) {
  method decodeGcid (line 423) | async decodeGcid(gcid) {
  method getUserListDetailByLink (line 445) | async getUserListDetailByLink({ info }, link) {
  method createGetListDetail2Task (line 479) | createGetListDetail2Task(id, total) {
  method getUserListDetail2 (line 499) | async getUserListDetail2(global_collection_id) {
  method getListInfoByChain (line 531) | async getListInfoByChain(chain) {
  method getUserListDetailByPcChain (line 544) | async getUserListDetailByPcChain(chain) {
  method getUserListDetail4 (line 560) | async getUserListDetail4(songInfo, chain, page) {
  method getUserListDetail5 (line 582) | async getUserListDetail5(chain) {
  method getUserListDetailById (line 603) | async getUserListDetailById(id, page, limit) {
  method getUserListDetail (line 619) | async getUserListDetail(link, page, retryNum = 0) {
  method getListDetail (line 681) | async getListDetail(id, page) { // 获取歌曲列表内的音乐
  method filterData (line 697) | filterData(rawList) {
  method filterData2 (line 815) | filterData2(rawList) {
  method getListInfo (line 878) | getListInfo(tagId, tryNum = 0) {
  method getList (line 894) | getList(sortId, tagId, page) {
  method getTags (line 916) | getTags(tryNum = 0) {
  method getDetailPageUrl (line 930) | getDetailPageUrl(id) {
  method search (line 938) | search(text, page, limit = 20) {

FILE: src/utils/musicSdk/kg/temp/musicSearch-new.js
  method musicSearch (line 10) | musicSearch(str, page, limit) {
  method filterList (line 19) | filterList(raw) {
  method handleResult (line 82) | handleResult(rawData) {
  method search (line 91) | search(str, page = 1, limit, retryNum = 0) {

FILE: src/utils/musicSdk/kg/temp/songList-new.js
  method getListDetail (line 54) | async getListDetail(id, page) {
  method getUserListDetailBySpecialId (line 86) | async getUserListDetailBySpecialId(id, page, tryNum = 0) {
  method parseHtmlDesc (line 118) | parseHtmlDesc(html) {
  method getCollectionIdBySpecialId (line 132) | async getCollectionIdBySpecialId(specialId) {
  method getSongListUrl (line 150) | getSongListUrl(sortId, tagId, page) {
  method getInfoUrl (line 154) | getInfoUrl(tagId) {
  method getSongListDetailUrl (line 159) | getSongListDetailUrl(id) {
  method filterInfoHotTag (line 163) | filterInfoHotTag(rawData) {
  method filterTagInfo (line 177) | filterTagInfo(rawData) {
  method filterSongList (line 193) | filterSongList(rawData) {
  method getSongList (line 208) | getSongList(sortId, tagId, page, tryNum = 0) {
  method getSongListRecommend (line 219) | getSongListRecommend(tryNum = 0) {
  method getUserListInfoByCollectionId (line 252) | async getUserListInfoByCollectionId(id) {
  method getUserListDetailByCollectionId (line 319) | async getUserListDetailByCollectionId(id, page = 1, limit = 300) {
  method filterListByCollectionId (line 352) | filterListByCollectionId(rawData) {
  method getUserListDetailByCode (line 420) | async getUserListDetailByCode(id, page = 1) {
  method getUserListDetail3 (line 472) | async getUserListDetail3(chain, page) {
  method getUserListDetailByLink (line 500) | async getUserListDetailByLink({ info }, link) {
  method createGetListDetail2Task (line 534) | createGetListDetail2Task(id, total) {
  method getUserListDetail2 (line 554) | async getUserListDetail2(global_collection_id) {
  method getListInfoByChain (line 586) | async getListInfoByChain(chain) {
  method getUserListDetailByPcChain (line 600) | async getUserListDetailByPcChain(chain) {
  method getUserListDetail4 (line 616) | async getUserListDetail4(songInfo, chain, page) {
  method getUserListDetail5 (line 638) | async getUserListDetail5(chain) {
  method getUserListDetail (line 659) | async getUserListDetail(link, page, retryNum = 0) {
  method getListInfo (line 701) | getListInfo(tagId, tryNum = 0) {
  method getList (line 717) | getList(sortId, tagId, page) {
  method getTags (line 739) | getTags(tryNum = 0) {
  method getDetailPageUrl (line 753) | getDetailPageUrl(id) {
  method search (line 761) | search(text, page, limit = 20) {

FILE: src/utils/musicSdk/kg/tipSearch.js
  method cancelTipSearch (line 5) | cancelTipSearch() {
  method tipSearchBySong (line 8) | tipSearchBySong(str) {
  method handleResult (line 19) | handleResult(rawData) {
  method search (line 22) | async search(str) {

FILE: src/utils/musicSdk/kw/album.js
  method filterListDetail (line 9) | filterListDetail(rawList, albumName, albumId) {
  method formatPlayCount (line 74) | formatPlayCount(num) {
  method getAlbumListDetail (line 79) | getAlbumListDetail(id, page, retryNum = 0) {

FILE: src/utils/musicSdk/kw/comment.js
  method getComment (line 7) | async getComment({ songmid }, page = 1, limit = 20) {
  method getHotComment (line 29) | async getHotComment({ songmid }, page = 1, limit = 100) {
  method filterComment (line 51) | filterComment(rawList) {

FILE: src/utils/musicSdk/kw/hotSearch.js
  method getList (line 5) | async getList(retryNum = 0) {
  method filterList (line 19) | filterList(rawList) {

FILE: src/utils/musicSdk/kw/index.js
  method getLyric (line 41) | getLyric(songInfo, isGetLyricx) {
  method handleMusicInfo (line 45) | handleMusicInfo(songInfo) {
  method getMusicUrl (line 62) | getMusicUrl(songInfo, type) {
  method getMusicInfo (line 66) | getMusicInfo(songInfo) {
  method getMusicUrls (line 74) | getMusicUrls(musicInfo, cb) {
  method getPic (line 89) | getPic(songInfo) {
  method getMusicDetailPageUrl (line 93) | getMusicDetailPageUrl(songInfo) {

FILE: src/utils/musicSdk/kw/leaderboard.js
  method getBoardsData (line 98) | getBoardsData() {
  method getData (line 103) | getData(url) {
  method filterData (line 107) | filterData(rawList) {
  method filterBoardsData (line 162) | filterBoardsData(rawList) {
  method getBoards (line 175) | async getBoards(retryNum = 0) {
  method getList (line 200) | getList(id, page, retryNum = 0) {

FILE: src/utils/musicSdk/kw/lyric.js
  method sortLrcArr (line 149) | sortLrcArr(arr) {
  method transformLrc (line 186) | transformLrc(tags, lrclist) {
  method parseLrc (line 189) | parseLrc(lrc) {
  method getLyric (line 236) | getLyric(musicInfo, isGetLyricx = true) {

FILE: src/utils/musicSdk/kw/musicSearch.js
  method musicSearch (line 17) | musicSearch(str, page, limit) {
  method handleResult (line 27) | handleResult(rawData) {
  method search (line 102) | search(str, page = 1, limit, retryNum = 0) {

FILE: src/utils/musicSdk/kw/pic.js
  method getPic (line 4) | getPic({ songmid }) {

FILE: src/utils/musicSdk/kw/songList.js
  method getListUrl (line 33) | getListUrl({ sortId, id, type, page }) {
  method getListDetailUrl (line 41) | getListDetailUrl(id, page) {
  method getTag (line 49) | getTag(tryNum = 0) {
  method getHotTag (line 59) | getHotTag(tryNum = 0) {
  method filterInfoHotTag (line 68) | filterInfoHotTag(rawList) {
  method filterTagInfo (line 75) | filterTagInfo(rawList) {
  method getList (line 89) | getList(sortId, tagId, page, tryNum = 0) {
  method formatPlayCount (line 130) | formatPlayCount(num) {
  method filterList (line 135) | filterList(rawData) {
  method filterList2 (line 149) | filterList2(rawData) {
  method getListDetailDigest8 (line 170) | getListDetailDigest8(id, page, tryNum = 0) {
  method getListDetailDigest5Info (line 192) | getListDetailDigest5Info(id, tryNum = 0) {
  method getListDetailDigest5Music (line 201) | getListDetailDigest5Music(id, page, tryNum = 0) {
  method getListDetailDigest5 (line 223) | async getListDetailDigest5(id, page, retryNum) {
  method filterBDListDetail (line 228) | filterBDListDetail(rawList) {
  method getReqId (line 281) | getReqId() {
  method getListDetailMusicListByBDListInfo (line 287) | async getListDetailMusicListByBDListInfo(id, source) {
  method getListDetailMusicListByBDUserPub (line 305) | async getListDetailMusicListByBDUserPub(id) {
  method getListDetailMusicListByBDList (line 324) | async getListDetailMusicListByBDList(id, source, page, tryNum = 0) {
  method getListDetailMusicListByBD (line 345) | async getListDetailMusicListByBD(id, page) {
  method getListDetail (line 373) | getListDetail(id, page, retryNum = 0) {
  method filterListDetail (line 393) | filterListDetail(rawData) {
  method getTags (line 449) | getTags() {
  method getDetailPageUrl (line 452) | getDetailPageUrl(id) {
  method search (line 461) | search(text, page, limit = 20) {

FILE: src/utils/musicSdk/kw/tipSearch.js
  method tipSearchBySong (line 10) | async tipSearchBySong(str) {
  method handleResult (line 23) | handleResult(rawData) {
  method cancelTipSearch (line 26) | cancelTipSearch() {
  method search (line 29) | async search(str) {

FILE: src/utils/musicSdk/kw/util.js
  method getWordInfo (line 99) | getWordInfo(str, str2, prevWord) {
  method parseLine (line 121) | parseLine(line) {
  method parse (line 161) | parse(lrc) {
  method decodeData (line 200) | decodeData(base64Result) {
  method createSign (line 206) | createSign(data, time) {
  method buildParam (line 210) | buildParam(jsonData) {

FILE: src/utils/musicSdk/mg/album.js
  method getAlbumDetail (line 11) | async getAlbumDetail(id, page = 1) {
  method getAlbumInfo (line 38) | async getAlbumInfo(id) {

FILE: src/utils/musicSdk/mg/comment.js
  method getComment (line 10) | async getComment(musicInfo, page = 1, limit = 20) {
  method getHotComment (line 35) | async getHotComment(musicInfo, page = 1, limit = 20) {
  method getReplyComment (line 57) | async getReplyComment(musicInfo, replyId, page = 1, limit = 10) {
  method filterComment (line 72) | filterComment(rawList) {

FILE: src/utils/musicSdk/mg/hotSearch.js
  method getList (line 5) | async getList(retryNum = 0) {
  method filterList (line 15) | filterList(rawList) {

FILE: src/utils/musicSdk/mg/index.js
  method getMusicUrl (line 18) | getMusicUrl(songInfo, type) {
  method getLyric (line 21) | getLyric(songInfo) {
  method getPic (line 24) | getPic(songInfo) {
  method getMusicDetailPageUrl (line 27) | getMusicDetailPageUrl(songInfo) {

FILE: src/utils/musicSdk/mg/leaderboard.js
  method getUrl (line 139) | getUrl(id, page) {
  method getBoardsData (line 145) | getBoardsData() {
  method getData (line 158) | getData(url) {
  method getBoards (line 194) | async getBoards(retryNum = 0) {
  method getList (line 218) | getList(bangid, page, retryNum = 0) {
  method getDetailPageUrl (line 234) | getDetailPageUrl(id) {

FILE: src/utils/musicSdk/mg/lyric.js
  method parseLyric (line 11) | parseLyric(str) {
  method getText (line 50) | getText(url, tryNum = 0) {
  method getMrc (line 64) | getMrc(url) {
  method getLrc (line 69) | getLrc(url) {
  method getTrc (line 72) | getTrc(url) {
  method getMusicInfo (line 76) | async getMusicInfo(songInfo) {
  method getLyric (line 81) | getLyric(songInfo) {
  method getLyric (line 99) | getLyric(songInfo) {

FILE: src/utils/musicSdk/mg/musicSearch.js
  method musicSearch (line 112) | musicSearch(str, page, limit) {
  method filterData (line 127) | filterData(rawData) {
  method search (line 198) | search(str, page = 1, limit, retryNum = 0) {

FILE: src/utils/musicSdk/mg/pic.js
  method getPicUrl (line 5) | async getPicUrl(songId, tryNum = 0) {
  method getPic (line 24) | async getPic(songInfo) {

FILE: src/utils/musicSdk/mg/songList.js
  method getSongListUrl (line 41) | getSongListUrl(sortId, tagId, page) {
  method getSongListDetailUrl (line 59) | getSongListDetailUrl(id, page) {
  method getListDetailList (line 72) | getListDetailList(id, page, tryNum = 0) {
  method getListDetailInfo (line 95) | getListDetailInfo(id, tryNum = 0) {
  method getDetailUrl (line 117) | async getDetailUrl(link, page, retryNum = 0) {
  method getListDetail (line 136) | getListDetail(id, page, retryNum = 0) { // 获取歌曲列表内的音乐
  method getList (line 160) | getList(sortId, tagId, page, tryNum = 0) {
  method filterList2 (line 232) | filterList2(listData, list = [], ids = new Set()) {
  method filterList (line 252) | filterList(rawData) {
  method getTag (line 269) | getTag(tryNum = 0) {
  method filterTagInfo (line 280) | filterTagInfo(rawList) {
  method getTags (line 318) | getTags() {
  method getDetailPageUrl (line 322) | getDetailPageUrl(id) {
  method filterSongListResult (line 331) | filterSongListResult(raw) {
  method search (line 349) | search(text, page, limit = 20) {

FILE: src/utils/musicSdk/mg/temp/leaderboard-old.js
  method getUrl (line 53) | getUrl(id, page) {
  method getData (line 63) | getData(url) {
  method getSinger (line 67) | getSinger(singers) {
  method getIntv (line 74) | getIntv(interval) {
  method formateIntv (line 85) | formateIntv() {
  method filterData (line 88) | filterData(rawData) {
  method filterBoardsData (line 135) | filterBoardsData(rawList) {
  method getBoards (line 153) | async getBoards(retryNum = 0) {
  method getList (line 177) | getList(bangid, page, retryNum = 0) {

FILE: src/utils/musicSdk/mg/tipSearch.js
  method cancelTipSearch (line 5) | cancelTipSearch() {
  method tipSearchBySong (line 8) | tipSearchBySong(str) {
  method handleResult (line 19) | handleResult(rawData) {
  method search (line 22) | async search(str) {

FILE: src/utils/musicSdk/mg/utils/mrc.js
  constant DELTA (line 3) | const DELTA = 2654435769n
  constant MIN_LENGTH (line 4) | const MIN_LENGTH = 32
  constant MAX (line 76) | const MAX = 9223372036854775807n
  constant MIN (line 77) | const MIN = -9223372036854775808n

FILE: src/utils/musicSdk/tx/comment.js
  method getSongId (line 80) | async getSongId({ songId, songmid }) {
  method getComment (line 91) | async getComment(mInfo, page = 1, limit = 20) {
  method getHotComment (line 126) | async getHotComment(mInfo, page = 1, limit = 20) {
  method filterNewComment (line 196) | filterNewComment(rawList) {
  method filterHotComment (line 236) | filterHotComment(rawList) {
  method replaceEmoji (line 268) | replaceEmoji(msg) {
  method formatTime (line 279) | formatTime(time) {

FILE: src/utils/musicSdk/tx/hotSearch.js
  method getList (line 5) | async getList(retryNum = 0) {
  method filterList (line 51) | filterList(rawList) {

FILE: src/utils/musicSdk/tx/index.js
  method getMusicUrl (line 18) | getMusicUrl(songInfo, type) {
  method getLyric (line 21) | getLyric(songInfo) {
  method getPic (line 25) | async getPic(songInfo) {
  method getMusicDetailPageUrl (line 28) | getMusicDetailPageUrl(songInfo) {

FILE: src/utils/musicSdk/tx/leaderboard.js
  method listDetailRequest (line 66) | listDetailRequest(id, period, limit) {
  method getBoardsData (line 99) | getBoardsData() {
  method getData (line 104) | getData(url) {
  method filterData (line 108) | filterData(rawList) {
  method getPeriods (line 164) | getPeriods(bangid) {
  method filterBoardsData (line 181) | filterBoardsData(rawList) {
  method getBoards (line 200) | async getBoards(retryNum = 0) {
  method getList (line 224) | getList(bangid, page, retryNum = 0) {
  method getDetailPageUrl (line 243) | getDetailPageUrl(id) {

FILE: src/utils/musicSdk/tx/lyric.js
  method getLyric (line 8) | getLyric(songmid) {

FILE: src/utils/musicSdk/tx/musicSearch.js
  method musicSearch (line 11) | musicSearch(str, page, limit, retryNum = 0) {
  method handleResult (line 76) | handleResult(rawList) {
  method search (line 142) | search(str, page = 1, limit) {

FILE: src/utils/musicSdk/tx/songList.js
  method getListUrl (line 35) | getListUrl(sortId, id, page) {
  method getListDetailUrl (line 63) | getListDetailUrl(id) {
  method getTag (line 69) | getTag(tryNum = 0) {
  method getHotTag (line 79) | getHotTag(tryNum = 0) {
  method filterInfoHotTag (line 88) | filterInfoHotTag(html) {
  method filterTagInfo (line 104) | filterTagInfo(rawList) {
  method getList (line 118) | getList(sortId, tagId, page, tryNum = 0) {
  method filterList (line 131) | filterList(data, page) {
  method filterList2 (line 151) | filterList2({ content }, page) {
  method handleParseId (line 172) | async handleParseId(link, retryNum = 0) {
  method getListId (line 182) | async getListId(id) {
  method getListDetail (line 198) | async getListDetail(id, tryNum = 0) {
  method filterListDetail (line 228) | filterListDetail(rawList) {
  method getTags (line 284) | getTags() {
  method getDetailPageUrl (line 288) | async getDetailPageUrl(id) {
  method search (line 294) | search(text, page, limit = 20, retryNum = 0) {

FILE: src/utils/musicSdk/tx/tipSearch.js
  method tipSearch (line 8) | tipSearch(str) {
  method handleResult (line 20) | handleResult(rawData) {
  method cancelTipSearch (line 23) | cancelTipSearch() {
  method search (line 26) | async search(str) {

FILE: src/utils/musicSdk/utils.js
  constant QUALITYS (line 10) | const QUALITYS = ['flac24bit', 'flac', 'wav', 'ape', '320k', '192k', '12...

FILE: src/utils/musicSdk/wy/comment.js
  method getCursor (line 75) | getCursor(id, page, limit) {
  method setCursor (line 106) | setCursor(id, cursor, orderType, offset, page) {
  method getComment (line 120) | async getComment({ songmid }, page = 1, limit = 20) {
  method getHotComment (line 150) | async getHotComment({ songmid }, page = 1, limit = 100) {
  method filterComment (line 174) | filterComment(rawList) {

FILE: src/utils/musicSdk/wy/hotSearch.js
  method getList (line 5) | async getList(retryNum = 0) {
  method filterList (line 17) | filterList(rawList) {

FILE: src/utils/musicSdk/wy/index.js
  method getMusicUrl (line 18) | getMusicUrl(songInfo, type) {
  method getLyric (line 21) | getLyric(songInfo) {
  method getPic (line 24) | getPic(songInfo) {
  method getMusicDetailPageUrl (line 28) | getMusicDetailPageUrl(songInfo) {

FILE: src/utils/musicSdk/wy/leaderboard.js
  method getUrl (line 103) | getUrl(id) {
  method getBoardsData (line 110) | getBoardsData() {
  method getData (line 118) | getData(id) {
  method filterBoardsData (line 130) | filterBoardsData(rawList) {
  method getBoards (line 144) | async getBoards(retryNum = 0) {
  method getList (line 168) | async getList(bangid, page, retryNum = 0) {
  method getDetailPageUrl (line 204) | getDetailPageUrl(id) {

FILE: src/utils/musicSdk/wy/lyric.js
  method msFormat (line 64) | msFormat(timeMs) {
  method parseLyric (line 73) | parseLyric(lines) {
  method parseHeaderInfo (line 112) | parseHeaderInfo(str) {
  method getIntv (line 128) | getIntv(interval) {
  method fixTimeTag (line 136) | fixTimeTag(lrc, targetlrc) {
  method parse (line 167) | parse(ylrc, ytlrc, yrlrc, lrc, tlrc, rlrc) {

FILE: src/utils/musicSdk/wy/musicDetail.js
  method getSinger (line 7) | getSinger(singers) {
  method filterList (line 14) | filterList({ songs, privileges }) {
  method getList (line 93) | async getList(ids = [], retryNum = 0) {

FILE: src/utils/musicSdk/wy/musicSearch.js
  method musicSearch (line 12) | musicSearch(str, page, limit) {
  method getSinger (line 31) | getSinger(singers) {
  method handleResult (line 38) | handleResult(rawList) {
  method search (line 95) | search(str, page = 1, limit, retryNum = 0) {

FILE: src/utils/musicSdk/wy/songList.js
  method handleParseId (line 38) | async handleParseId(link, retryNum = 0) {
  method getListId (line 50) | async getListId(id) {
  method getListDetail (line 69) | async getListDetail(rawId, page, tryNum = 0) { // 获取歌曲列表内的音乐
  method filterListDetail (line 129) | filterListDetail({ playlist: { tracks }, privileges }) {
  method getList (line 209) | getList(sortId, tagId, page, tryNum = 0) {
  method filterList (line 234) | filterList(rawData) {
  method getTag (line 251) | getTag(tryNum = 0) {
  method filterTagInfo (line 264) | filterTagInfo({ sub, categories }) {
  method getHotTag (line 289) | getHotTag(tryNum = 0) {
  method filterHotTagInfo (line 302) | filterHotTagInfo(rawList) {
  method getTags (line 310) | getTags() {
  method getDetailPageUrl (line 314) | async getDetailPageUrl(rawId) {
  method search (line 319) | search(text, page, limit = 20) {

FILE: src/utils/musicSdk/wy/tipSearch.js
  method cancelTipSearch (line 7) | cancelTipSearch() {
  method tipSearchBySong (line 10) | tipSearchBySong(str) {
  method handleResult (line 27) | handleResult(rawData) {
  method search (line 30) | async search(str) {

FILE: src/utils/musicSdk/xm.js
  method getComment (line 19) | getComment() {
  method getHotComment (line 22) | getHotComment() {
  method getMusicUrl (line 26) | getMusicUrl(songInfo, type) {
  method getLyric (line 32) | getLyric(songInfo) {
  method getPic (line 38) | getPic(songInfo) {

FILE: src/utils/nativeModules/crypto.ts
  type KEY_PREFIX (line 10) | enum KEY_PREFIX {
  type RSA_PADDING (line 17) | enum RSA_PADDING {
  type AES_MODE (line 22) | enum AES_MODE {

FILE: src/utils/nativeModules/userApi.ts
  type SendResponseParams (line 19) | interface SendResponseParams {
  type SendActions (line 29) | interface SendActions {
  type InitParams (line 39) | interface InitParams {
  type ResponseParams (line 45) | interface ResponseParams {
  type UpdateInfoParams (line 51) | interface UpdateInfoParams {
  type RequestParams (line 56) | interface RequestParams {
  type CancelRequestParams (line 67) | type CancelRequestParams = string
  type Actions (line 69) | interface Actions {
  type ActionsEvent (line 77) | type ActionsEvent = { [K in keyof Actions]: { action: K, data: Actions[K...

FILE: src/utils/pixelRatio.ts
  function getTextSize (line 42) | function getTextSize(size: number) {
  function setSpText (line 53) | function setSpText(size: number) {
  function scaleSizeH (line 62) | function scaleSizeH(size: number) {
  function scaleSizeW (line 75) | function scaleSizeW(size: number) {

FILE: src/utils/request.js
  method cancelHttp (line 36) | cancelHttp() {
  method abort (line 213) | abort() {

FILE: src/utils/scroll.ts
  type Noop (line 10) | type Noop = () => void
  type ScrollElement (line 13) | type ScrollElement<T> = {

FILE: src/utils/simplify-chinese-main/index.js
  function tranditionalize (line 21) | function tranditionalize(source) {

FILE: src/utils/tools.ts
  constant TEMP_FILE_PATH (line 33) | const TEMP_FILE_PATH = temporaryDirectoryPath + '/tempFile'
  method onPress (line 197) | onPress() {
  method onPress (line 203) | onPress() {
  method onDismiss (line 209) | onDismiss() {
  method onPress (line 226) | onPress() {
  method onDismiss (line 232) | onDismiss() {
  function throttleBackgroundTimer (line 415) | function throttleBackgroundTimer<Args extends any[]>(fn: (...args: Args)...
  function debounceBackgroundTimer (line 434) | function debounceBackgroundTimer<Args extends any[]>(fn: (...args: Args)...
  type Styles (line 448) | type Styles = StyleSheet.NamedStyles<Record<string, {}>>
  type Style (line 449) | type Style = Styles[keyof Styles]
  type RowInfo (line 526) | interface RowInfo {
  type RowInfoType (line 531) | type RowInfoType = 'full' | 'medium'

FILE: src/utils/version.js
  method begin (line 98) | begin({ statusCode, contentLength }) {
  method progress (line 109) | progress({ contentLength, bytesWritten }) {

FILE: src/utils/windowSizeTools.ts
  type SizeHandler (line 5) | type SizeHandler = (size: { width: number, height: number }) => void
  method getSize (line 21) | getSize() {
  method onSizeChanged (line 24) | onSizeChanged(handler: SizeHandler) {
  method init (line 31) | async init() {
  method setWindowSize (line 57) | setWindowSize(width: number, height: number) {
Condensed preview — 706 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (2,630K chars).
[
  {
    "path": ".bundle/config",
    "chars": 59,
    "preview": "BUNDLE_PATH: \"vendor/bundle\"\nBUNDLE_FORCE_RUBY_PLATFORM: 1\n"
  },
  {
    "path": ".editorconfig",
    "chars": 147,
    "preview": "root = true\n\n[*]\ncharset = utf-8\nend_of_line = lf\nindent_size = 2\nindent_style = space\ninsert_final_newline = true\ntrim_"
  },
  {
    "path": ".eslintrc.cjs",
    "chars": 2396,
    "preview": "const baseRule = {\n  'no-new': 'off',\n  camelcase: 'off',\n  'no-return-assign': 'off',\n  'space-before-function-paren': "
  },
  {
    "path": ".github/ISSUE_TEMPLATE/bug.yml",
    "chars": 1453,
    "preview": "name: 🐞 报告错误\ndescription: 报告一个错误(Bug),请先查看常见问题及搜索 Issue 列表中有无你要提的问题。\ntitle: \"[Bug]: \"\nbody:\n- type: checkboxes\n  id: che"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature.yml",
    "chars": 1069,
    "preview": "name: ✨ 功能请求\ndescription: 为这个项目提出一个想法,请先查看常见问题及搜索 Issue 列表中有无你要提的问题。\ntitle: \"[Feature]: \"\nbody:\n- type: checkboxes\n  id:"
  },
  {
    "path": ".github/actions/setup/action.yml",
    "chars": 908,
    "preview": "name: Setup\ndescription: Setup Env\n\nruns:\n  using: composite\n  steps:\n    - name: Setup Node.js\n      uses: actions/setu"
  },
  {
    "path": ".github/actions/upload-artifact/action.yml",
    "chars": 1558,
    "preview": "name: Setup\ndescription: Setup Env\n\nruns:\n  using: composite\n  steps:\n    - name: Upload Artifact arm64-v8a\n      if: en"
  },
  {
    "path": ".github/workflows/beta-pack.yml",
    "chars": 1381,
    "preview": "name: Build Beta\n\non:\n  push:\n    branches:\n      - beta\n\njobs:\n  Android:\n    name: Android\n    runs-on: ubuntu-latest\n"
  },
  {
    "path": ".github/workflows/build-test.yml",
    "chars": 471,
    "preview": "name: Run build test\n\non:\n  pull_request:\n    branches:\n      - dev\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps"
  },
  {
    "path": ".github/workflows/publish-version-info.yml",
    "chars": 374,
    "preview": "name: Publish NPM Version Info\n\non:\n  workflow_dispatch:\n  release:\n    types: [published]\n\njobs:\n  dispatch:\n    runs-o"
  },
  {
    "path": ".github/workflows/release.yml",
    "chars": 7178,
    "preview": "name: Build\n\non:\n  push:\n    branches:\n      - master\n\njobs:\n  Android:\n    name: Android\n    runs-on: ubuntu-latest\n   "
  },
  {
    "path": ".gitignore",
    "chars": 939,
    "preview": "# OSX\n#\n.DS_Store\n\n# Xcode\n#\nbuild/\n*.pbxuser\n!default.pbxuser\n*.mode1v3\n!default.mode1v3\n*.mode2v3\n!default.mode2v3\n*.p"
  },
  {
    "path": ".ncurc.js",
    "chars": 906,
    "preview": "module.exports = {\n  upgrade: true,\n  reject: [\n    '@types/react',\n    '@types/react-native',\n    'message2call',\n    '"
  },
  {
    "path": ".nvmrc",
    "chars": 4,
    "preview": "v18\n"
  },
  {
    "path": ".vscode/i18n-ally-custom-framework.yml",
    "chars": 1123,
    "preview": "# .vscode/i18n-ally-custom-framework.yml\n\n# An array of strings which contain Language Ids defined by VS Code\n# You can "
  },
  {
    "path": ".vscode/javascript.code-snippets",
    "chars": 2105,
    "preview": "{\n\t// Place your LxMusicMobile 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, "
  },
  {
    "path": ".vscode/settings.json",
    "chars": 386,
    "preview": "{\n  \"i18n-ally.localesPaths\": [\n    \"src/lang\"\n  ],\n  // \"i18n-ally.fullReloadOnChanged\": true,\n  \"i18n-ally.keystyle\": "
  },
  {
    "path": "CHANGELOG.md",
    "chars": 23537,
    "preview": "# lx-music-mobile change log\n\nAll notable changes to this project will be documented in this file.\n\nProject versioning a"
  },
  {
    "path": "FAQ.md",
    "chars": 3820,
    "preview": "# lx-music-mobile 常见问题\n\n本文档已迁移至:<https://lyswhut.github.io/lx-music-doc/mobile/faq>\n\n<!-- 在阅读本常见问题后,仍然无法解决你的问题,请提交issue或"
  },
  {
    "path": "Gemfile",
    "chars": 159,
    "preview": "source 'https://rubygems.org'\n\n# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version\nruby \""
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "                                 Apache License\n                           Version 2.0, January 2004\n                   "
  },
  {
    "path": "README.md",
    "chars": 4367,
    "preview": "<p align=\"center\"><a href=\"https://github.com/lyswhut/lx-music-mobile\"><img width=\"200\" src=\"https://github.com/lyswhut/"
  },
  {
    "path": "android/app/build.gradle",
    "chars": 8415,
    "preview": "apply plugin: \"com.android.application\"\napply plugin: \"org.jetbrains.kotlin.android\"\napply plugin: \"com.facebook.react\"\n"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "chars": 977,
    "preview": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /u"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "chars": 314,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:to"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "chars": 5245,
    "preview": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n  xmlns:tools=\"http://schemas.android.com/tools\"\n  "
  },
  {
    "path": "android/app/src/main/assets/script/user-api-preload.js",
    "chars": 21265,
    "preview": "'use strict'\n\nglobalThis.lx_setup = (key, id, name, description, version, author, homepage, rawScript) => {\n  delete glo"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/MainActivity.java",
    "chars": 331,
    "preview": "package cn.toside.music.mobile;\n\nimport com.reactnativenavigation.NavigationActivity;\nimport com.facebook.react.ReactAct"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/MainApplication.java",
    "chars": 2394,
    "preview": "package cn.toside.music.mobile;\n\nimport com.facebook.react.PackageList;\nimport com.facebook.react.flipper.ReactNativeFli"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/cache/CacheClearAsyncTask.java",
    "chars": 848,
    "preview": "package cn.toside.music.mobile.cache;\n\nimport android.os.AsyncTask;\n\nimport com.facebook.react.bridge.Promise;\n\n// https"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/cache/CacheModule.java",
    "chars": 2648,
    "preview": "package cn.toside.music.mobile.cache;\n\nimport com.facebook.react.bridge.Promise;\nimport com.facebook.react.bridge.ReactA"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/cache/CachePackage.java",
    "chars": 683,
    "preview": "package cn.toside.music.mobile.cache;\n\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.bridge.NativeMo"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/cache/Utils.java",
    "chars": 1856,
    "preview": "package cn.toside.music.mobile.cache;\n\nimport android.content.Context;\n\nimport java.io.File;\n\n// https://github.com/mida"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/crypto/AES.java",
    "chars": 3354,
    "preview": "package cn.toside.music.mobile.crypto;\n\nimport android.util.Base64;\n\nimport java.nio.charset.StandardCharsets;\n\nimport j"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/crypto/CryptoModule.java",
    "chars": 5037,
    "preview": "package cn.toside.music.mobile.crypto;\n\nimport android.util.Base64;\n\nimport com.facebook.react.bridge.Arguments;\nimport "
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/crypto/CryptoPackage.java",
    "chars": 685,
    "preview": "package cn.toside.music.mobile.crypto;\n\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.bridge.NativeM"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/crypto/RSA.java",
    "chars": 4242,
    "preview": "package cn.toside.music.mobile.crypto;\n\nimport android.util.Base64;\n\nimport java.security.Key;\nimport java.security.KeyF"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/Lyric.java",
    "chars": 8780,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport android.content.BroadcastReceiver;\nimport android.content.Context;\nimport "
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/LyricEvent.java",
    "chars": 772,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport androidx.annotation.Nullable;\n\nimport com.facebook.react.bridge.ReactAppli"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/LyricModule.java",
    "chars": 6205,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Build;\ni"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/LyricPackage.java",
    "chars": 681,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.bridge.NativeMo"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/LyricPlayer.java",
    "chars": 10723,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport java.util.ArrayList;\nimport java.util.Collections;\nimport java.util.Compar"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/LyricSwitchView.java",
    "chars": 5933,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport an"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/LyricTextView.java",
    "chars": 5421,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport an"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/LyricView.java",
    "chars": 19794,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport android.app.Activity;\nimport android.content.Context;\nimport android.graph"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/lyric/Utils.java",
    "chars": 847,
    "preview": "package cn.toside.music.mobile.lyric;\n\nimport android.os.Handler;\n\npublic class Utils {\n  // https://gist.github.com/mat"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/Console.java",
    "chars": 940,
    "preview": "package cn.toside.music.mobile.userApi;\n\nimport android.os.Handler;\nimport android.os.Message;\nimport android.util.Log;\n"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/HandlerWhat.java",
    "chars": 317,
    "preview": "package cn.toside.music.mobile.userApi;\n\npublic class HandlerWhat {\n  public static final int ACTION = 1000;\n  public st"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/JavaScriptThread.java",
    "chars": 2231,
    "preview": "package cn.toside.music.mobile.userApi;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.HandlerT"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/JsHandler.java",
    "chars": 2087,
    "preview": "package cn.toside.music.mobile.userApi;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.os.Message;"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/QuickJS.java",
    "chars": 8021,
    "preview": "package cn.toside.music.mobile.userApi;\n\nimport android.os.Build;\nimport android.os.Bundle;\nimport android.os.Handler;\ni"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/UserApiModule.java",
    "chars": 3087,
    "preview": "package cn.toside.music.mobile.userApi;\n\nimport android.os.Bundle;\nimport android.os.Handler;\nimport android.os.Message;"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/UserApiPackage.java",
    "chars": 672,
    "preview": "package cn.toside.music.mobile.userApi;\n\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.bridge.Native"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/userApi/UtilsEvent.java",
    "chars": 696,
    "preview": "package cn.toside.music.mobile.userApi;\n\nimport android.util.Log;\n\nimport androidx.annotation.Nullable;\n\nimport com.face"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/utils/AsyncTask.java",
    "chars": 1636,
    "preview": "package cn.toside.music.mobile.utils;\n\nimport android.os.Handler;\nimport android.os.Looper;\nimport android.util.Log;\n\nim"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/utils/BatteryOptimizationUtil.java",
    "chars": 2052,
    "preview": "package cn.toside.music.mobile.utils;\n\nimport android.annotation.SuppressLint;\nimport android.content.Context;\nimport an"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/utils/NotificationPermissionUtil.java",
    "chars": 2783,
    "preview": "package cn.toside.music.mobile.utils;\n\nimport android.app.NotificationChannel;\nimport android.app.NotificationManager;\ni"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/utils/Utils.java",
    "chars": 1054,
    "preview": "package cn.toside.music.mobile.utils;\n\n\nimport android.content.Context;\nimport android.os.storage.StorageManager;\n\nimpor"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/utils/UtilsEvent.java",
    "chars": 752,
    "preview": "package cn.toside.music.mobile.utils;\n\nimport android.util.Log;\n\nimport androidx.annotation.Nullable;\n\nimport com.facebo"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/utils/UtilsModule.java",
    "chars": 13436,
    "preview": "package cn.toside.music.mobile.utils;\n\nimport android.annotation.SuppressLint;\nimport android.app.Activity;\nimport andro"
  },
  {
    "path": "android/app/src/main/java/cn/toside/music/mobile/utils/UtilsPackage.java",
    "chars": 682,
    "preview": "package cn.toside.music.mobile.utils;\n\nimport com.facebook.react.ReactPackage;\nimport com.facebook.react.bridge.NativeMo"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_launcher_foreground.xml",
    "chars": 1305,
    "preview": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n"
  },
  {
    "path": "android/app/src/main/res/drawable/rn_edit_text_material.xml",
    "chars": 1909,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Copyright (C) 2014 The Android Open Source Project\n\n     Licensed under the "
  },
  {
    "path": "android/app/src/main/res/drawable/rounded_corner.xml",
    "chars": 189,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n  <solid andro"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "chars": 267,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "chars": 267,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <b"
  },
  {
    "path": "android/app/src/main/res/values/ic_launcher_background.xml",
    "chars": 120,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"ic_launcher_background\">#FFFFFF</color>\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values/strings.xml",
    "chars": 71,
    "preview": "<resources>\n    <string name=\"app_name\">LX Music</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "chars": 282,
    "preview": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.DayNight.NoActionBa"
  },
  {
    "path": "android/app/src/main/res/xml/file_paths.xml",
    "chars": 1403,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<paths xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n\n  <!-- Selec"
  },
  {
    "path": "android/app/src/main/res/xml/network_security_config.xml",
    "chars": 148,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?> \n<network-security-config> \n    <base-config cleartextTrafficPermitted=\"true\" /> "
  },
  {
    "path": "android/app/src/release/java/cn/toside/music/ReactNativeFlipper.java",
    "chars": 688,
    "preview": "/**\n * Copyright (c) Meta Platforms, Inc. and affiliates.\n *\n * <p>This source code is licensed under the MIT license fo"
  },
  {
    "path": "android/build.gradle",
    "chars": 886,
    "preview": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    e"
  },
  {
    "path": "android/gradle/wrapper/gradle-wrapper.properties",
    "chars": 250,
    "preview": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributi"
  },
  {
    "path": "android/gradle.properties",
    "chars": 2287,
    "preview": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will o"
  },
  {
    "path": "android/gradlew",
    "chars": 8683,
    "preview": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"Lice"
  },
  {
    "path": "android/gradlew.bat",
    "chars": 2826,
    "preview": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \""
  },
  {
    "path": "android/settings.gradle",
    "chars": 266,
    "preview": "rootProject.name = 'cn.toside.music.mobile'\napply from: file(\"../node_modules/@react-native-community/cli-platform-andro"
  },
  {
    "path": "app.json",
    "chars": 52,
    "preview": "{\n  \"name\": \"LX Music\",\n  \"displayName\": \"洛雪音乐助手\"\n}\n"
  },
  {
    "path": "babel.config.js",
    "chars": 833,
    "preview": "module.exports = {\n  presets: ['module:@react-native/babel-preset'],\n  plugins: [\n    '@babel/plugin-proposal-export-nam"
  },
  {
    "path": "dependencies-patch.js",
    "chars": 609,
    "preview": "// 修补依赖源码以使构建的依赖恢复正常工作\n\nconst fs = require('node:fs')\nconst path = require('node:path')\n\nconst rootPath = path.join(__di"
  },
  {
    "path": "index.js",
    "chars": 577,
    "preview": "/**\n * @format\n */\nimport './shim'\nimport './src/app'\n// import './test'\n// import '@/utils/errorHandle'\n// import { Nav"
  },
  {
    "path": "ios/.xcode.env",
    "chars": 482,
    "preview": "# This `.xcode.env` file is versioned and is used to source the environment\n# used when running script phases inside Xco"
  },
  {
    "path": "ios/LxMusicMobile/AppDelegate.h",
    "chars": 98,
    "preview": "#import <RCTAppDelegate.h>\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : RCTAppDelegate\n\n@end\n"
  },
  {
    "path": "ios/LxMusicMobile/AppDelegate.mm",
    "chars": 1047,
    "preview": "#import \"AppDelegate.h\"\n#import <ReactNativeNavigation/ReactNativeNavigation.h>\n\n#import <React/RCTBundleURLProvider.h>\n"
  },
  {
    "path": "ios/LxMusicMobile/Images.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 849,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"iphone\",\n      \"scale\" : \"2x\",\n      \"size\" : \"20x20\"\n    },\n    {\n      \"idiom\""
  },
  {
    "path": "ios/LxMusicMobile/Images.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "ios/LxMusicMobile/Info.plist",
    "chars": 1619,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/LxMusicMobile/LaunchScreen.storyboard",
    "chars": 4237,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3"
  },
  {
    "path": "ios/LxMusicMobile/main.m",
    "chars": 199,
    "preview": "#import <UIKit/UIKit.h>\n\n#import \"AppDelegate.h\"\n\nint main(int argc, char *argv[])\n{\n  @autoreleasepool {\n    return UIA"
  },
  {
    "path": "ios/LxMusicMobile.xcodeproj/project.pbxproj",
    "chars": 28408,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "ios/LxMusicMobile.xcodeproj/xcshareddata/xcschemes/LxMusicMobile.xcscheme",
    "chars": 3342,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1210\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "ios/LxMusicMobileTests/Info.plist",
    "chars": 733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "ios/LxMusicMobileTests/LxMusicMobileTests.m",
    "chars": 2002,
    "preview": "#import <UIKit/UIKit.h>\n#import <XCTest/XCTest.h>\n\n#import <React/RCTLog.h>\n#import <React/RCTRootView.h>\n\n#define TIMEO"
  },
  {
    "path": "ios/Podfile",
    "chars": 1821,
    "preview": "# Resolve react_native_pods.rb with node to allow for hoisting\nrequire Pod::Executable.execute_command('node', ['-p',\n  "
  },
  {
    "path": "metro.config.js",
    "chars": 535,
    "preview": "const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config')\n\n/**\n * Metro configuration\n * https://f"
  },
  {
    "path": "package.json",
    "chars": 4036,
    "preview": "{\n  \"name\": \"lx-music-mobile\",\n  \"version\": \"1.8.1\",\n  \"versionCode\": 73,\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \""
  },
  {
    "path": "publish/changeLog.md",
    "chars": 292,
    "preview": "我们很高兴地宣布新项目 Any Listen 的桌面版已发布,目前已支持列表跟随本地文件自动更新、加载并播放WebDAV上的歌曲等功能,更多功能仍在积极开发中,桌面版与Web版将同步更新。\n对于有播放本地音乐或播放服务器上音乐需求的人可以试"
  },
  {
    "path": "publish/index.js",
    "chars": 1827,
    "preview": "// const fs = require('fs')\n// const path = require('path')\nconst chalk = require('chalk')\n// const clearAssets = requir"
  },
  {
    "path": "publish/utils/index.js",
    "chars": 1807,
    "preview": "const fs = require('fs')\nconst path = require('path')\n\nexports.jp = (...p) => p.length ? path.join(__dirname, ...p) : __"
  },
  {
    "path": "publish/utils/parseChangelog.js",
    "chars": 875,
    "preview": "/**\n *\n * @param {string} text\n * @returns\n */\nexport const parseChangelog = async(text) => {\n  const versions = []\n  co"
  },
  {
    "path": "publish/utils/updateChangeLog.js",
    "chars": 1788,
    "preview": "const fs = require('fs')\nconst { jp, formatTime } = require('./index')\nconst pkgDir = '../../package.json'\nconst pkg = r"
  },
  {
    "path": "publish/version.json",
    "chars": 18599,
    "preview": "{\"version\":\"1.8.1\",\"desc\":\"我们很高兴地宣布新项目 Any Listen 的桌面版已发布,目前已支持列表跟随本地文件自动更新、加载并播放WebDAV上的歌曲等功能,更多功能仍在积极开发中,桌面版与Web版将同步更新"
  },
  {
    "path": "shim.js",
    "chars": 41,
    "preview": "global.Buffer = require('buffer').Buffer\n"
  },
  {
    "path": "src/app.ts",
    "chars": 2095,
    "preview": "import '@/utils/errorHandle'\nimport { init as initLog } from '@/utils/log'\nimport { bootLog, getBootLog } from '@/utils/"
  },
  {
    "path": "src/components/DesktopLyricEnable.tsx",
    "chars": 2239,
    "preview": "import { forwardRef, useImperativeHandle, useRef, useState } from 'react'\n\nimport ConfirmAlert, { type ConfirmAlertType "
  },
  {
    "path": "src/components/MetadataEditModal/InputItem.tsx",
    "chars": 1106,
    "preview": "import { memo } from 'react'\n\nimport { StyleSheet, View } from 'react-native'\nimport type { InputProps } from '@/compone"
  },
  {
    "path": "src/components/MetadataEditModal/MetadataForm.tsx",
    "chars": 7067,
    "preview": "import { useImperativeHandle, forwardRef, useState, useCallback, useRef } from 'react'\nimport { View } from 'react-nativ"
  },
  {
    "path": "src/components/MetadataEditModal/ParseName.tsx",
    "chars": 1900,
    "preview": "import { memo } from 'react'\n\nimport { StyleSheet, View } from 'react-native'\nimport { useTheme } from '@/store/theme/ho"
  },
  {
    "path": "src/components/MetadataEditModal/PicItem.tsx",
    "chars": 2690,
    "preview": "import { memo, useCallback, useRef } from 'react'\n\nimport { TouchableOpacity, View } from 'react-native'\nimport { useThe"
  },
  {
    "path": "src/components/MetadataEditModal/TextAreaItem.tsx",
    "chars": 2218,
    "preview": "import { memo, useCallback } from 'react'\n\nimport { StyleSheet, TouchableOpacity, View } from 'react-native'\nimport type"
  },
  {
    "path": "src/components/MetadataEditModal/index.tsx",
    "chars": 4448,
    "preview": "import { useRef, useImperativeHandle, forwardRef, useState } from 'react'\nimport ConfirmAlert, { type ConfirmAlertType }"
  },
  {
    "path": "src/components/MusicAddModal/CreateUserList.tsx",
    "chars": 1770,
    "preview": "import { useState, useRef, useEffect } from 'react'\nimport { View } from 'react-native'\nimport Input, { type InputType }"
  },
  {
    "path": "src/components/MusicAddModal/List.tsx",
    "chars": 2347,
    "preview": "import { useMemo, useState } from 'react'\nimport { ScrollView, TouchableOpacity, View } from 'react-native'\n\nimport Text"
  },
  {
    "path": "src/components/MusicAddModal/ListItem.tsx",
    "chars": 1556,
    "preview": "import { View } from 'react-native'\nimport Button from '@/components/common/Button'\nimport Text from '@/components/commo"
  },
  {
    "path": "src/components/MusicAddModal/MusicAddModal.tsx",
    "chars": 2677,
    "preview": "import { forwardRef, useImperativeHandle, useRef, useState } from 'react'\nimport Dialog, { type DialogType } from '@/com"
  },
  {
    "path": "src/components/MusicAddModal/Title.tsx",
    "chars": 660,
    "preview": "import Text from '@/components/common/Text'\nimport { createStyle } from '@/utils/tools'\nimport { useTheme } from '@/stor"
  },
  {
    "path": "src/components/MusicAddModal/index.tsx",
    "chars": 925,
    "preview": "import { useRef, useImperativeHandle, forwardRef, useState } from 'react'\nimport Modal, { type MusicAddModalType as Moda"
  },
  {
    "path": "src/components/MusicMultiAddModal/List.tsx",
    "chars": 2323,
    "preview": "import { useMemo, useState } from 'react'\nimport { ScrollView, TouchableOpacity, View } from 'react-native'\n\nimport Text"
  },
  {
    "path": "src/components/MusicMultiAddModal/ListItem.tsx",
    "chars": 1231,
    "preview": "import { View } from 'react-native'\nimport Button from '@/components/common/Button'\nimport Text from '@/components/commo"
  },
  {
    "path": "src/components/MusicMultiAddModal/MusicMultiAddModal.tsx",
    "chars": 2747,
    "preview": "import { forwardRef, useImperativeHandle, useRef, useState } from 'react'\nimport Dialog, { type DialogType } from '@/com"
  },
  {
    "path": "src/components/MusicMultiAddModal/Title.tsx",
    "chars": 711,
    "preview": "import Text from '@/components/common/Text'\nimport { createStyle } from '@/utils/tools'\nimport { useTheme } from '@/stor"
  },
  {
    "path": "src/components/MusicMultiAddModal/index.tsx",
    "chars": 970,
    "preview": "import { useRef, useImperativeHandle, forwardRef, useState } from 'react'\nimport Modal, { type MusicMultiAddModalType as"
  },
  {
    "path": "src/components/OnlineList/List.tsx",
    "chars": 9007,
    "preview": "import { useMemo, useRef, useState, forwardRef, useImperativeHandle } from 'react'\nimport { FlatList, type FlatListProps"
  },
  {
    "path": "src/components/OnlineList/ListItem.tsx",
    "chars": 5614,
    "preview": "import { memo, useRef } from 'react'\nimport { View, TouchableOpacity } from 'react-native'\n// import Button from '@/comp"
  },
  {
    "path": "src/components/OnlineList/ListMenu.tsx",
    "chars": 2820,
    "preview": "import { useMemo, useRef, useImperativeHandle, forwardRef, useState } from 'react'\nimport { useI18n } from '@/lang'\nimpo"
  },
  {
    "path": "src/components/OnlineList/MultipleModeBar.tsx",
    "chars": 5042,
    "preview": "import { useState, useRef, useCallback, useMemo, forwardRef, useImperativeHandle } from 'react'\nimport { Animated, View,"
  },
  {
    "path": "src/components/OnlineList/index.tsx",
    "chars": 4856,
    "preview": "import { useRef, forwardRef, useImperativeHandle } from 'react'\nimport { View } from 'react-native'\n// import LoadingMas"
  },
  {
    "path": "src/components/OnlineList/listAction.ts",
    "chars": 2393,
    "preview": "import { LIST_IDS } from '@/config/constant'\nimport { addListMusics } from '@/core/list'\nimport { playList, playNext } f"
  },
  {
    "path": "src/components/PageContent.tsx",
    "chars": 2863,
    "preview": "// import { useEffect, useState } from 'react'\nimport { View } from 'react-native'\nimport { useTheme } from '@/store/the"
  },
  {
    "path": "src/components/SearchTipList/List.tsx",
    "chars": 945,
    "preview": "import { useState, forwardRef, useImperativeHandle, type Ref } from 'react'\nimport { FlatList, type FlatListProps } from"
  },
  {
    "path": "src/components/SearchTipList/index.tsx",
    "chars": 4044,
    "preview": "import { useRef, useState, useCallback, useMemo, forwardRef, useImperativeHandle, type Ref } from 'react'\nimport { Style"
  },
  {
    "path": "src/components/SizeView.tsx",
    "chars": 2862,
    "preview": "import { memo, useCallback, useRef, useEffect } from 'react'\nimport { type LayoutChangeEvent, StyleSheet, View, StatusBa"
  },
  {
    "path": "src/components/SourceSelector.tsx",
    "chars": 2612,
    "preview": "import { forwardRef, type Ref, useImperativeHandle, useMemo, useState } from 'react'\nimport { View } from 'react-native'"
  },
  {
    "path": "src/components/TimeoutExitEditModal.tsx",
    "chars": 6871,
    "preview": "import { useRef, useImperativeHandle, forwardRef, useState, useEffect } from 'react'\nimport ConfirmAlert, { type Confirm"
  },
  {
    "path": "src/components/common/Badge.tsx",
    "chars": 1358,
    "preview": "import { memo, useMemo } from 'react'\nimport { createStyle } from '@/utils/tools'\nimport { useTheme } from '@/store/them"
  },
  {
    "path": "src/components/common/Button.tsx",
    "chars": 1311,
    "preview": "import { useTheme } from '@/store/theme/hook'\nimport { useMemo, useRef, useImperativeHandle, forwardRef } from 'react'\ni"
  },
  {
    "path": "src/components/common/ButtonPrimary.tsx",
    "chars": 785,
    "preview": "import { memo } from 'react'\n\nimport Button, { type BtnProps } from '@/components/common/Button'\nimport Text from '@/com"
  },
  {
    "path": "src/components/common/CheckBox/Checkbox.tsx",
    "chars": 2772,
    "preview": "import * as React from 'react'\nimport {\n  Animated,\n  type GestureResponderEvent,\n  StyleSheet,\n  View,\n  Pressable,\n} f"
  },
  {
    "path": "src/components/common/CheckBox/index.tsx",
    "chars": 3575,
    "preview": "import { useCallback, useEffect, useMemo, useState } from 'react'\nimport { View, TouchableOpacity } from 'react-native'\n"
  },
  {
    "path": "src/components/common/ChoosePath/List.tsx",
    "chars": 6183,
    "preview": "import { useEffect, useState, useRef, forwardRef, useImperativeHandle } from 'react'\nimport { View } from 'react-native'"
  },
  {
    "path": "src/components/common/ChoosePath/components/Footer.tsx",
    "chars": 1203,
    "preview": "import { memo } from 'react'\nimport { View, StyleSheet } from 'react-native'\nimport Button from '@/components/common/But"
  },
  {
    "path": "src/components/common/ChoosePath/components/Header.tsx",
    "chars": 4080,
    "preview": "import { memo, useCallback, useEffect, useRef } from 'react'\nimport { View, TouchableOpacity } from 'react-native'\nimpor"
  },
  {
    "path": "src/components/common/ChoosePath/components/ListItem.tsx",
    "chars": 3120,
    "preview": "import { memo } from 'react'\nimport { View, TouchableOpacity } from 'react-native'\nimport { Icon } from '@/components/co"
  },
  {
    "path": "src/components/common/ChoosePath/components/Main.tsx",
    "chars": 2002,
    "preview": "import { useI18n } from '@/lang'\nimport { useTheme } from '@/store/theme/hook'\nimport { createStyle, getRowInfo } from '"
  },
  {
    "path": "src/components/common/ChoosePath/components/NewFolderModal.tsx",
    "chars": 3285,
    "preview": "import { forwardRef, useImperativeHandle, useRef, useState } from 'react'\nimport { View } from 'react-native'\nimport Inp"
  },
  {
    "path": "src/components/common/ChoosePath/components/OpenStorageModal.tsx",
    "chars": 6447,
    "preview": "import { forwardRef, useImperativeHandle, useRef, useState } from 'react'\nimport { View } from 'react-native'\nimport Inp"
  },
  {
    "path": "src/components/common/ChoosePath/index.tsx",
    "chars": 4326,
    "preview": "import { useState, useRef, forwardRef, useImperativeHandle } from 'react'\n// import { StyleSheet, View, Text, StatusBar,"
  },
  {
    "path": "src/components/common/ConfirmAlert.tsx",
    "chars": 3302,
    "preview": "import { forwardRef, useImperativeHandle, useRef } from 'react'\nimport { View, ScrollView } from 'react-native'\nimport D"
  },
  {
    "path": "src/components/common/Dialog.tsx",
    "chars": 3199,
    "preview": "import { useImperativeHandle, forwardRef, useMemo, useRef } from 'react'\nimport { View, TouchableHighlight } from 'react"
  },
  {
    "path": "src/components/common/DorpDownMenu.tsx",
    "chars": 1262,
    "preview": "import { useRef } from 'react'\n// import { View } from 'react-native'\n\nimport Menu, { type MenuType, type MenuProps, typ"
  },
  {
    "path": "src/components/common/DorpDownPanel/Panel.tsx",
    "chars": 3388,
    "preview": "import { useMemo, useRef, useImperativeHandle, forwardRef, useState } from 'react'\nimport { View, TouchableWithoutFeedba"
  },
  {
    "path": "src/components/common/DorpDownPanel/index.tsx",
    "chars": 1071,
    "preview": "import { useRef, forwardRef } from 'react'\n// import { View } from 'react-native'\n\nimport Panel, { type PanelType } from"
  },
  {
    "path": "src/components/common/DrawerLayoutFixed.tsx",
    "chars": 3012,
    "preview": "import { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react'\nimport { DrawerLayoutAndroid, typ"
  },
  {
    "path": "src/components/common/FileSelect.tsx",
    "chars": 1014,
    "preview": "import ChoosePath, { type ReadOptions, type ChoosePathType } from '@/components/common/ChoosePath'\nimport { forwardRef, "
  },
  {
    "path": "src/components/common/Icon.tsx",
    "chars": 2497,
    "preview": "import { createIconSetFromIcoMoon } from 'react-native-vector-icons'\nimport icoMoonConfig from '@/resources/fonts/select"
  },
  {
    "path": "src/components/common/Image.tsx",
    "chars": 3206,
    "preview": "import { useTheme } from '@/store/theme/hook'\nimport { BorderRadius } from '@/theme'\nimport { createStyle } from '@/util"
  },
  {
    "path": "src/components/common/ImageBackground.tsx",
    "chars": 2915,
    "preview": "// https://github.com/facebook/react-native/blob/main/packages/react-native/Libraries/Image/ImageBackground.js\n/**\n * Co"
  },
  {
    "path": "src/components/common/Input.tsx",
    "chars": 3688,
    "preview": "import { useRef, useImperativeHandle, forwardRef, useCallback } from 'react'\nimport { TextInput, View, TouchableOpacity,"
  },
  {
    "path": "src/components/common/Loading.tsx",
    "chars": 1151,
    "preview": "import { memo } from 'react'\n\nimport { createStyle } from '@/utils/tools'\nimport { useTheme } from '@/store/theme/hook'\n"
  },
  {
    "path": "src/components/common/LoadingMask.tsx",
    "chars": 2027,
    "preview": "import { useState, useCallback, useMemo, useRef, forwardRef, useImperativeHandle } from 'react'\nimport { Animated } from"
  },
  {
    "path": "src/components/common/Menu.tsx",
    "chars": 6900,
    "preview": "import { useImperativeHandle, forwardRef, useMemo, useRef, useState, type Ref } from 'react'\nimport { View, Animated, To"
  },
  {
    "path": "src/components/common/Modal.tsx",
    "chars": 2419,
    "preview": "// import { createStyle } from '@/utils/tools'\nimport { useImperativeHandle, forwardRef, useState, useMemo } from 'react"
  },
  {
    "path": "src/components/common/Popup.tsx",
    "chars": 4786,
    "preview": "import { forwardRef, useImperativeHandle, useMemo, useRef } from 'react'\nimport { View, TouchableOpacity } from 'react-n"
  },
  {
    "path": "src/components/common/ScaledImage.tsx",
    "chars": 1402,
    "preview": "import { useEffect, useState } from 'react'\nimport { StyleSheet } from 'react-native'\nimport Image, { getSize, type Imag"
  },
  {
    "path": "src/components/common/Slider.tsx",
    "chars": 1354,
    "preview": "import { memo } from 'react'\n\nimport Slider, { type SliderProps as _SliderProps } from '@react-native-community/slider'\n"
  },
  {
    "path": "src/components/common/StatusBar.tsx",
    "chars": 470,
    "preview": "import { useTheme } from '@/store/theme/hook'\nimport { StatusBar as RNStatusBar } from 'react-native'\n\nconst StatusBar ="
  },
  {
    "path": "src/components/common/Text.tsx",
    "chars": 3589,
    "preview": "import { memo, type ComponentProps } from 'react'\nimport { Text, type TextProps as _TextProps, StyleSheet, Animated, typ"
  },
  {
    "path": "src/components/player/PlayerBar/components/ControlBtn.tsx",
    "chars": 2037,
    "preview": "import { TouchableOpacity } from 'react-native'\nimport { Icon } from '@/components/common/Icon'\nimport { useIsPlay } fro"
  },
  {
    "path": "src/components/player/PlayerBar/components/Pic.tsx",
    "chars": 1732,
    "preview": "import { StyleSheet, TouchableOpacity } from 'react-native'\nimport { navigations } from '@/navigation'\nimport { usePlaye"
  },
  {
    "path": "src/components/player/PlayerBar/components/PlayInfo.tsx",
    "chars": 3663,
    "preview": "import { memo, useCallback, useState } from 'react'\nimport { View, StyleSheet } from 'react-native'\n\nimport Progress, { "
  },
  {
    "path": "src/components/player/PlayerBar/components/Status.tsx",
    "chars": 716,
    "preview": "import { useLrcPlay } from '@/plugins/lyric'\nimport { useIsPlay, useStatusText } from '@/store/player/hook'\n// import { "
  },
  {
    "path": "src/components/player/PlayerBar/components/Title.tsx",
    "chars": 2548,
    "preview": "import { TouchableOpacity } from 'react-native'\nimport { navigations } from '@/navigation'\nimport { usePlayerMusicInfo }"
  },
  {
    "path": "src/components/player/PlayerBar/index.tsx",
    "chars": 2409,
    "preview": "import { memo, useMemo } from 'react'\nimport { View } from 'react-native'\nimport { useKeyboard } from '@/utils/hooks'\n\ni"
  },
  {
    "path": "src/components/player/Progress.tsx",
    "chars": 5300,
    "preview": "import { memo, useCallback, useEffect, useRef, useState } from 'react'\nimport { View, PanResponder } from 'react-native'"
  },
  {
    "path": "src/components/player/ProgressBar.tsx",
    "chars": 5373,
    "preview": "import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'\nimport { View, PanResponder } from 'reac"
  },
  {
    "path": "src/config/constant.ts",
    "chars": 3929,
    "preview": "export const HEADER_HEIGHT = 42\nexport const LIST_ITEM_HEIGHT = 54\nexport const LIST_SCROLL_POSITION_KEY = '__LIST_SCROL"
  },
  {
    "path": "src/config/defaultSetting.ts",
    "chars": 2806,
    "preview": "const defaultSetting: LX.AppSetting = {\n  version: '2.0',\n  'common.isAutoTheme': false,\n  'common.langId': null,\n  'com"
  },
  {
    "path": "src/config/globalData.ts",
    "chars": 1770,
    "preview": "import { version } from '../../package.json'\nimport { createAppEventHub } from '@/event/appEvent'\nimport { createListEve"
  },
  {
    "path": "src/config/index.js",
    "chars": 402,
    "preview": "// import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'\nimport defaultUrl from '@/resources/"
  },
  {
    "path": "src/config/migrate.ts",
    "chars": 6083,
    "preview": "import { filterMusicList, toNewMusicInfo } from '@/utils'\nimport { LIST_IDS, storageDataPrefix, storageDataPrefixOld } f"
  },
  {
    "path": "src/config/migrateSetting.ts",
    "chars": 2872,
    "preview": "import { compareVer } from '@/utils'\n\nexport default (setting: any): Partial<LX.AppSetting> => {\n  setting = { ...settin"
  },
  {
    "path": "src/config/setting.ts",
    "chars": 3938,
    "preview": "import { storageDataPrefix, storageDataPrefixOld } from '@/config/constant'\nimport defaultSetting from '@/config/default"
  },
  {
    "path": "src/core/apiSource.ts",
    "chars": 1532,
    "preview": "// import { setUserApi as setUserApiAction } from '@renderer/utils/ipc'\nimport musicSdk from '@/utils/musicSdk'\n// impor"
  },
  {
    "path": "src/core/common.ts",
    "chars": 3442,
    "preview": "import { hideDesktopLyric } from './desktopLyric'\nimport { exitApp as utilExitApp } from '@/utils/nativeModules/utils'\ni"
  },
  {
    "path": "src/core/desktopLyric.ts",
    "chars": 4323,
    "preview": "import {\n  hideDesktopLyricView,\n  showDesktopLyricView,\n  setSendLyricTextEvent,\n  setLyric,\n  play,\n  pause,\n  setPlay"
  },
  {
    "path": "src/core/dislikeList.ts",
    "chars": 712,
    "preview": "import { action } from '@/store/dislikeList'\n\n\nexport const addDislikeInfo = async(infos: LX.Dislike.DislikeMusicInfo[])"
  },
  {
    "path": "src/core/hotSearch.ts",
    "chars": 1392,
    "preview": "import hotSearchState, { type Source } from '@/store/hotSearch/state'\nimport hotSearchActions, { type Lists } from '@/st"
  },
  {
    "path": "src/core/init/common.ts",
    "chars": 2633,
    "preview": "// import musicSdk from '@/utils/musicSdk'\n// import commonActions from '@/store/common/action'\nimport playerState from "
  },
  {
    "path": "src/core/init/dataInit.ts",
    "chars": 1470,
    "preview": "// import { getPlayInfo } from '@/utils/data'\n// import { log } from '@/utils/log'\nimport { init as musicSdkInit } from "
  },
  {
    "path": "src/core/init/deeplink/fileAction.ts",
    "chars": 1518,
    "preview": "import { readMetadata } from '@/utils/localMediaMetadata'\nimport { handleImportList } from '@/screens/Home/Views/Setting"
  },
  {
    "path": "src/core/init/deeplink/index.ts",
    "chars": 2873,
    "preview": "import { Linking } from 'react-native'\nimport { errorDialog } from './utils'\nimport { handleMusicAction } from './musicA"
  },
  {
    "path": "src/core/init/deeplink/musicAction.js",
    "chars": 7820,
    "preview": "import { decodeName, toNewMusicInfo } from '@/utils'\nimport { dataVerify, qualityFilter } from './utils'\nimport playerSt"
  },
  {
    "path": "src/core/init/deeplink/playSonglist.ts",
    "chars": 1546,
    "preview": "import { LIST_IDS } from '@/config/constant'\nimport { setTempList } from '@/core/list'\nimport { playList } from '@/core/"
  },
  {
    "path": "src/core/init/deeplink/playerAction.ts",
    "chars": 844,
    "preview": "import { collectMusic, dislikeMusic, pause, play, playNext, playPrev, togglePlay, uncollectMusic } from '@/core/player/p"
  },
  {
    "path": "src/core/init/deeplink/songlistAction.js",
    "chars": 2653,
    "preview": "import { playSonglist } from './playSonglist'\nimport { dataVerify, sourceVerify } from './utils'\n\n// const handleOpenSon"
  },
  {
    "path": "src/core/init/deeplink/utils.js",
    "chars": 1518,
    "preview": "import { tipDialog } from '@/utils/tools'\n\nexport const errorDialog = message => {\n  tipDialog({\n    message: `${global."
  },
  {
    "path": "src/core/init/i18n.ts",
    "chars": 720,
    "preview": "import { createI18n } from '@/lang/i18n'\nimport type { I18n } from '@/lang/i18n'\nimport { getDeviceLanguage } from '@/ut"
  },
  {
    "path": "src/core/init/index.ts",
    "chars": 1901,
    "preview": "import { initSetting, showPactModal } from '@/core/common'\nimport registerPlaybackService from '@/plugins/player/service"
  },
  {
    "path": "src/core/init/player/index.ts",
    "chars": 571,
    "preview": "import initPlayer from './player'\nimport initPlayInfo from './playInfo'\nimport initPlayStatus from './playStatus'\nimport"
  }
]

// ... and 506 more files (download for full content)

About this extraction

This page contains the full source code of the lyswhut/lx-music-mobile GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 706 files (2.4 MB), approximately 657.0k tokens, and a symbol index with 1736 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!