├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── README_ENG.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── hwinzniej │ │ └── musichelper │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── QQMusic │ │ ├── cloudmusic.db │ │ ├── kugou_music_phone_v7.db │ │ ├── kwplayer.db │ │ ├── licenses.html │ │ └── licenses_dark.html │ ├── ic_launcher-playstore.png │ ├── java │ │ └── com │ │ │ └── hwinzniej │ │ │ └── musichelper │ │ │ ├── MainActivity.kt │ │ │ ├── activity │ │ │ ├── ConvertPage.kt │ │ │ ├── PermissionResultHandler.kt │ │ │ ├── ProcessPage.kt │ │ │ ├── ScanPage.kt │ │ │ ├── SettingsPage.kt │ │ │ ├── TagPage.kt │ │ │ └── UnlockPage.kt │ │ │ ├── data │ │ │ ├── DataStoreConstants.kt │ │ │ ├── SourceApp.kt │ │ │ ├── dao │ │ │ │ └── MusicDao.kt │ │ │ ├── database │ │ │ │ └── MusicDatabase.kt │ │ │ └── model │ │ │ │ ├── Music.kt │ │ │ │ └── MusicInfo.kt │ │ │ ├── ui │ │ │ ├── AboutPageUi.kt │ │ │ ├── ConvertPageUi.kt │ │ │ ├── MySaltUi.kt │ │ │ ├── ProcessPageUi.kt │ │ │ ├── ScanPageUi.kt │ │ │ ├── SettingsPageUi.kt │ │ │ ├── TagPageUi.kt │ │ │ ├── UnlockPageUi.kt │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ │ └── utils │ │ │ ├── DoubanMusicApi.kt │ │ │ ├── MyDataStore.kt │ │ │ ├── MyVibrationEffect.kt │ │ │ └── Tools.kt │ └── res │ │ ├── drawable │ │ ├── about.xml │ │ ├── agreement.xml │ │ ├── alipay.jpg │ │ ├── all.xml │ │ ├── android.xml │ │ ├── aplayer.xml │ │ ├── app_theme.xml │ │ ├── auto_check_update.xml │ │ ├── bilibili.xml │ │ ├── caution.xml │ │ ├── check.xml │ │ ├── check_update.xml │ │ ├── chinese.xml │ │ ├── cloudmusic.xml │ │ ├── coffee.xml │ │ ├── color.xml │ │ ├── complete.xml │ │ ├── computer.xml │ │ ├── convert.xml │ │ ├── cookie.xml │ │ ├── coolapk.xml │ │ ├── csv_file.xml │ │ ├── dark_color.xml │ │ ├── database.xml │ │ ├── developer.xml │ │ ├── english.xml │ │ ├── gitee.xml │ │ ├── github.xml │ │ ├── gitlab.xml │ │ ├── haptic.xml │ │ ├── home_screen.xml │ │ ├── ic_arrow_back.xml │ │ ├── ic_arrow_drop_down.xml │ │ ├── ic_check.xml │ │ ├── ic_chevron_right.xml │ │ ├── ic_clear.xml │ │ ├── ic_delete.xml │ │ ├── ic_launcher_border_foreground.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_tips.xml │ │ ├── ic_uncheck.xml │ │ ├── json_file.xml │ │ ├── korean.xml │ │ ├── kugou.xml │ │ ├── kuwo.xml │ │ ├── language.xml │ │ ├── license.xml │ │ ├── light_color.xml │ │ ├── luna_music.xml │ │ ├── manual.xml │ │ ├── microsoft_zune.xml │ │ ├── network.xml │ │ ├── no_items.xml │ │ ├── overall.xml │ │ ├── plus.xml │ │ ├── plus_no_circle.xml │ │ ├── poweramp.xml │ │ ├── proxy.xml │ │ ├── qinalt.xml │ │ ├── qq_group.xml │ │ ├── qqmusic.xml │ │ ├── refresh.xml │ │ ├── result.xml │ │ ├── root_access.xml │ │ ├── saltplayer.xml │ │ ├── scan.xml │ │ ├── search.xml │ │ ├── server.xml │ │ ├── settings.xml │ │ ├── sort.xml │ │ ├── split.xml │ │ ├── spotify.xml │ │ ├── success.xml │ │ ├── tag.xml │ │ ├── text.xml │ │ ├── tune_my_music.xml │ │ ├── um.xml │ │ ├── user.xml │ │ ├── web_page.xml │ │ ├── wechat.png │ │ └── wechat_app.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.webp │ │ └── ic_launcher_round.webp │ │ ├── values-ko │ │ └── strings.xml │ │ ├── values-zh-rCN │ │ └── strings.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml │ └── test │ └── java │ └── com │ └── hwinzniej │ └── musichelper │ └── ExampleUnitTest.kt ├── build.gradle.kts ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── markdownResources ├── Alipay WeChatPay.jpg ├── Questionnaire QR Code.png ├── Sponsorship.png ├── ic_launcher.png ├── img1.png ├── img2.png └── img3.png └── settings.gradle.kts /.gitattributes: -------------------------------------------------------------------------------- 1 | *.html linguist-language=Kotlin 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=android,androidstudio,kotlin 3 | 4 | ### Android ### 5 | # Gradle files 6 | .gradle/ 7 | build/ 8 | 9 | # Local configuration file (sdk path, etc) 10 | local.properties 11 | 12 | # Log/OS Files 13 | *.log 14 | 15 | # Android Studio generated files and folders 16 | captures/ 17 | .externalNativeBuild/ 18 | .cxx/ 19 | *.apk 20 | output.json 21 | 22 | # IntelliJ 23 | *.iml 24 | .idea/ 25 | misc.xml 26 | deploymentTargetDropDown.xml 27 | render.experimental.xml 28 | 29 | # Keystore files 30 | *.jks 31 | *.keystore 32 | 33 | # Google Services (e.g. APIs or Firebase) 34 | google-services.json 35 | 36 | # Android Profiling 37 | *.hprof 38 | 39 | ### Android Patch ### 40 | gen-external-apklibs 41 | 42 | # Replacement of .externalNativeBuild directories introduced 43 | # with Android Studio 3.5. 44 | 45 | ### Kotlin ### 46 | # Compiled class file 47 | *.class 48 | 49 | # Log file 50 | 51 | # BlueJ files 52 | *.ctxt 53 | 54 | # Mobile Tools for Java (J2ME) 55 | .mtj.tmp/ 56 | 57 | # Package Files # 58 | *.jar 59 | *.war 60 | *.nar 61 | *.ear 62 | *.zip 63 | *.tar.gz 64 | *.rar 65 | 66 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 67 | hs_err_pid* 68 | replay_pid* 69 | 70 | ### AndroidStudio ### 71 | # Covers files to be ignored for android development using Android Studio. 72 | 73 | # Built application files 74 | *.ap_ 75 | *.aab 76 | 77 | # Files for the ART/Dalvik VM 78 | *.dex 79 | 80 | # Java class files 81 | 82 | # Generated files 83 | bin/ 84 | gen/ 85 | out/ 86 | 87 | # Gradle files 88 | .gradle 89 | 90 | # Signing files 91 | .signing/ 92 | 93 | # Local configuration file (sdk path, etc) 94 | 95 | # Proguard folder generated by Eclipse 96 | proguard/ 97 | 98 | # Log Files 99 | 100 | # Android Studio 101 | /*/build/ 102 | /*/local.properties 103 | /*/out 104 | /*/*/build 105 | /*/*/production 106 | .navigation/ 107 | *.ipr 108 | *~ 109 | *.swp 110 | 111 | # Keystore files 112 | 113 | # Google Services (e.g. APIs or Firebase) 114 | # google-services.json 115 | 116 | # Android Patch 117 | 118 | # External native build folder generated in Android Studio 2.2 and later 119 | .externalNativeBuild 120 | 121 | # NDK 122 | obj/ 123 | 124 | # IntelliJ IDEA 125 | *.iws 126 | /out/ 127 | 128 | # User-specific configurations 129 | .idea/caches/ 130 | .idea/libraries/ 131 | .idea/shelf/ 132 | .idea/workspace.xml 133 | .idea/tasks.xml 134 | .idea/.name 135 | .idea/compiler.xml 136 | .idea/copyright/profiles_settings.xml 137 | .idea/encodings.xml 138 | .idea/misc.xml 139 | .idea/modules.xml 140 | .idea/scopes/scope_settings.xml 141 | .idea/dictionaries 142 | .idea/vcs.xml 143 | .idea/jsLibraryMappings.xml 144 | .idea/datasources.xml 145 | .idea/dataSources.ids 146 | .idea/sqlDataSources.xml 147 | .idea/dynamic.xml 148 | .idea/uiDesigner.xml 149 | .idea/assetWizardSettings.xml 150 | .idea/gradle.xml 151 | .idea/jarRepositories.xml 152 | .idea/navEditor.xml 153 | 154 | # Legacy Eclipse project files 155 | .classpath 156 | .project 157 | .cproject 158 | .settings/ 159 | 160 | # Mobile Tools for Java (J2ME) 161 | 162 | # Package Files # 163 | 164 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 165 | 166 | ## Plugin-specific files: 167 | 168 | # mpeltonen/sbt-idea plugin 169 | .idea_modules/ 170 | 171 | # JIRA plugin 172 | atlassian-ide-plugin.xml 173 | 174 | # Mongo Explorer plugin 175 | .idea/mongoSettings.xml 176 | 177 | # Crashlytics plugin (for Android Studio and IntelliJ) 178 | com_crashlytics_export_strings.xml 179 | crashlytics.properties 180 | crashlytics-build.properties 181 | fabric.properties 182 | 183 | ### AndroidStudio Patch ### 184 | 185 | !/gradle/wrapper/gradle-wrapper.jar 186 | 187 | # End of https://www.toptal.com/developers/gitignore/api/android,androidstudio,kotlin -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 HWinZnieJ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README_ENG.md: -------------------------------------------------------------------------------- 1 | # 施工中…… 2 | 3 | # Under Construction…… 4 | 5 | # 🛠️🛠️🛠️ 6 | 7 | ## 🛠️🛠️ 8 | 9 | ### 🛠️ -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | id("org.jetbrains.kotlin.android") 4 | id("com.google.devtools.ksp") 5 | id("org.jetbrains.compose") version "1.7.3" 6 | id("org.jetbrains.kotlin.plugin.compose") 7 | } 8 | 9 | android { 10 | namespace = "com.hwinzniej.musichelper" 11 | compileSdk = 35 12 | // useLibrary("org.apache.http.legacy") 13 | 14 | defaultConfig { 15 | applicationId = "com.hwinzniej.musichelper" 16 | minSdk = 26 17 | // targetSdk = 28 18 | targetSdk = 35 19 | versionCode = 78 20 | versionName = "1.6.8" 21 | 22 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 23 | vectorDrawables { 24 | useSupportLibrary = true 25 | } 26 | } 27 | packaging { 28 | resources.excludes.add("META-INF/*") 29 | } 30 | 31 | buildTypes { 32 | release { 33 | isMinifyEnabled = false 34 | proguardFiles( 35 | getDefaultProguardFile("proguard-android-optimize.txt"), 36 | "proguard-rules.pro" 37 | ) 38 | } 39 | } 40 | compileOptions { 41 | sourceCompatibility = JavaVersion.VERSION_21 42 | targetCompatibility = JavaVersion.VERSION_21 43 | } 44 | kotlinOptions { 45 | jvmTarget = "21" 46 | } 47 | buildFeatures { 48 | compose = true 49 | } 50 | packaging { 51 | resources { 52 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 53 | } 54 | } 55 | } 56 | 57 | dependencies { 58 | 59 | implementation("androidx.core:core-ktx:1.15.0") 60 | implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") 61 | implementation("androidx.activity:activity-compose:1.10.0") 62 | implementation(platform("androidx.compose:compose-bom:2025.01.01")) 63 | implementation("androidx.compose.ui:ui") 64 | implementation("androidx.compose.ui:ui-graphics") 65 | implementation("androidx.compose.material:material") 66 | implementation("androidx.compose.material3:material3") 67 | implementation("androidx.annotation:annotation:1.9.1") 68 | implementation("androidx.documentfile:documentfile:1.0.1") 69 | testImplementation("junit:junit:4.13.2") 70 | androidTestImplementation("androidx.test.ext:junit:1.2.1") 71 | androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1") 72 | androidTestImplementation(platform("androidx.compose:compose-bom:2025.01.01")) 73 | 74 | // Android Studio Preview support 75 | implementation("androidx.compose.ui:ui-tooling-preview") 76 | debugImplementation("androidx.compose.ui:ui-tooling") 77 | 78 | // UI Tests 79 | androidTestImplementation("androidx.compose.ui:ui-test-junit4") 80 | debugImplementation("androidx.compose.ui:ui-test-manifest") 81 | 82 | //SaltUI 83 | implementation("io.github.moriafly:salt-ui:2.3.1") 84 | 85 | //JAudioTagger 86 | // implementation("org.bitbucket.ijabz:jaudiotagger:7b004a1") 87 | implementation("com.github.maxbruecken:jaudiotagger-android:master") 88 | 89 | //Kotlin协程 90 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1") 91 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1") 92 | implementation("androidx.activity:activity-ktx:1.10.0") 93 | implementation("androidx.fragment:fragment-ktx:1.8.5") 94 | 95 | //Room数据库 96 | val roomVersion = "2.6.1" 97 | implementation("androidx.room:room-runtime:$roomVersion") 98 | annotationProcessor("androidx.room:room-compiler:$roomVersion") 99 | ksp("androidx.room:room-compiler:$roomVersion") 100 | 101 | //FastJson 102 | implementation("com.alibaba.fastjson2:fastjson2-kotlin:2.0.54") 103 | 104 | //Navigation 105 | implementation("androidx.navigation:navigation-compose:2.8.6") 106 | 107 | //DataStore 108 | implementation("androidx.datastore:datastore-preferences:1.1.2") 109 | 110 | //OkHttp3 111 | implementation("com.squareup.okhttp3:okhttp:4.12.0") 112 | 113 | //Markdown渲染器 114 | implementation("com.github.jeziellago:compose-markdown:0.5.6") 115 | 116 | //Google ZXing二维码生成 117 | implementation("com.google.zxing:core:3.5.3") 118 | 119 | //Splashscreen 120 | implementation("androidx.core:core-splashscreen:1.1.0-rc01") 121 | 122 | //Apache Common Csv 123 | implementation("org.apache.commons:commons-csv:1.12.0") 124 | 125 | //拖拽排序-可注释 126 | // implementation("org.burnoutcrew.composereorderable:reorderable:0.9.6") 127 | 128 | //Jsoup-可注释 129 | // implementation("org.jsoup:jsoup:1.17.2") 130 | 131 | //Ksoup-不可注释,可用于跨平台 132 | implementation("com.fleeksoft.ksoup:ksoup:0.2.1") 133 | 134 | //HtmlUnit-可注释 135 | // implementation("org.htmlunit:htmlunit3-android:3.7.0") 136 | 137 | //XmlAPI-可注释 138 | // implementation("xml-apis:xml-apis:2.0.2") 139 | 140 | 141 | // Optional - Included automatically by material, only add when you need 142 | // the icons but not the material library (e.g. when using Material3 or a 143 | // custom design system based on Foundation) 144 | //implementation("androidx.compose.material:material-icons-core") 145 | // Optional - Add full set of material icons 146 | //implementation("androidx.compose.material:material-icons-extended") 147 | // Optional - Add window size utils 148 | //implementation("androidx.compose.material3:material3-window-size-class") 149 | 150 | // Optional - Integration with activities 151 | //implementation("androidx.activity:activity-compose:1.6.1") 152 | // Optional - Integration with ViewModels 153 | //implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.5.1") 154 | // Optional - Integration with LiveData 155 | //implementation("androidx.compose.runtime:runtime-livedata") 156 | // Optional - Integration with RxJava 157 | //implementation("androidx.compose.runtime:runtime-rxjava2") 158 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /app/src/androidTest/java/com/hwinzniej/musichelper/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.hwinzniej.musichelper", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 24 | 27 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /app/src/main/assets/QQMusic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/assets/QQMusic -------------------------------------------------------------------------------- /app/src/main/assets/cloudmusic.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/assets/cloudmusic.db -------------------------------------------------------------------------------- /app/src/main/assets/kugou_music_phone_v7.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/assets/kugou_music_phone_v7.db -------------------------------------------------------------------------------- /app/src/main/assets/kwplayer.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/assets/kwplayer.db -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/activity/PermissionResultHandler.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.activity 2 | 3 | interface PermissionResultHandler { 4 | fun onPermissionResult( 5 | requestCode: Int, permissions: Array, grantResults: IntArray 6 | ) 7 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/activity/ProcessPage.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.activity 2 | // 3 | //import android.content.Context 4 | //import android.widget.Toast 5 | //import androidx.compose.runtime.mutableStateOf 6 | //import androidx.lifecycle.LifecycleOwner 7 | //import androidx.lifecycle.lifecycleScope 8 | //import com.hwinzniej.musichelper.data.database.MusicDatabase 9 | //import com.hwinzniej.musichelper.data.model.MusicInfo 10 | //import com.hwinzniej.musichelper.utils.DoubanMusicApi 11 | //import kotlinx.coroutines.Dispatchers 12 | //import kotlinx.coroutines.launch 13 | // 14 | //class ProcessPage( 15 | // val context: Context, 16 | // val lifecycleOwner: LifecycleOwner, 17 | // val db: MusicDatabase 18 | //) { 19 | // var processAllScannedMusic = mutableStateOf(true) 20 | // var overwriteOriginalTag = mutableStateOf(true) 21 | // var showSelectTagTypeDialog = mutableStateOf(false) 22 | // var enableAlbumArtist = mutableStateOf(true) 23 | // var enableReleaseYear = mutableStateOf(true) 24 | // var enableGenre = mutableStateOf(true) 25 | // var enableTrackNumber = mutableStateOf(true) 26 | // var showProgressBar = mutableStateOf(false) 27 | // var showSelectSourceDialog = mutableStateOf(false) 28 | // val useDoubanMusicSource = mutableStateOf(true) 29 | // val useMusicBrainzSource = mutableStateOf(true) 30 | // val useBaiduBaikeSource = mutableStateOf(true) 31 | // 32 | // lateinit var musicInfoList: List 33 | // 34 | // fun getMusicList() { 35 | //// showProgressBar.value = true 36 | // lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { 37 | // if (processAllScannedMusic.value) { 38 | // musicInfoList = db.musicDao().getMusic3Info() //应为getMusicInfo 39 | // startProcess() 40 | // } else { 41 | //// db.musicDao().getMusicByPath("") 42 | // } 43 | // } 44 | // 45 | // } 46 | // 47 | // fun startProcess() { 48 | // if (musicInfoList.isEmpty()) { 49 | // lifecycleOwner.lifecycleScope.launch(Dispatchers.Main) { 50 | // Toast.makeText(context, "数据库为空,请先扫描本地歌曲", Toast.LENGTH_SHORT).show() 51 | // } 52 | // return 53 | // } 54 | // if (useDoubanMusicSource.value) { 55 | // getDoubanMusicInfo() 56 | // } 57 | // if (useMusicBrainzSource.value) { 58 | // //TODO 59 | // } 60 | // if (useBaiduBaikeSource.value) { 61 | // //TODO 62 | // } 63 | // } 64 | // 65 | // fun getDoubanMusicInfo() { 66 | // lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { 67 | // musicInfoList.forEach { 68 | // val musicInfo = it 69 | // val musicName = it.song 70 | // val artistName = it.artist 71 | // val albumName = it.album 72 | // val musicInfoFromDouban = 73 | // DoubanMusicApi.getMusicInfo(musicName, artistName, albumName) 74 | // return@launch 75 | //// if (musicInfoFromDouban != null) { 76 | //// musicInfo.releaseYear = musicInfoFromDouban.releaseYear 77 | //// musicInfo.trackNumber = musicInfoFromDouban.trackNumber 78 | //// musicInfo.albumArtist = musicInfoFromDouban.albumArtist 79 | //// musicInfo.genre = musicInfoFromDouban.genre 80 | ////// db.musicDao().updateMusicInfo(musicInfo) 81 | //// } 82 | // } 83 | // } 84 | // } 85 | //} 86 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/activity/SettingsPage.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.activity 2 | 3 | import android.content.Context 4 | import android.net.Uri 5 | import android.widget.Toast 6 | import androidx.activity.result.ActivityResultLauncher 7 | import androidx.compose.runtime.mutableIntStateOf 8 | import androidx.compose.runtime.mutableStateMapOf 9 | import androidx.compose.runtime.mutableStateOf 10 | import androidx.datastore.core.DataStore 11 | import androidx.datastore.preferences.core.Preferences 12 | import androidx.datastore.preferences.core.edit 13 | import androidx.lifecycle.LifecycleOwner 14 | import androidx.lifecycle.lifecycleScope 15 | import com.hwinzniej.musichelper.R 16 | import com.hwinzniej.musichelper.data.DataStoreConstants 17 | import com.hwinzniej.musichelper.utils.Tools 18 | import kotlinx.coroutines.Dispatchers 19 | import kotlinx.coroutines.async 20 | import kotlinx.coroutines.delay 21 | import kotlinx.coroutines.launch 22 | import kotlinx.coroutines.withContext 23 | import java.io.File 24 | 25 | class SettingsPage( 26 | val context: Context, 27 | private val lifecycleOwner: LifecycleOwner, 28 | val openUmExecutableFileLauncher: ActivityResultLauncher>, 29 | val dataStore: DataStore, 30 | ) { 31 | val enableAutoCheckUpdate = mutableStateOf(true) 32 | val encryptServer = mutableStateOf("") 33 | val serverPing = mutableStateMapOf( 34 | 0 to context.getString(R.string.pinging), 35 | 1 to context.getString(R.string.pinging), 36 | 2 to context.getString(R.string.pinging) 37 | ) 38 | val showDialogProgressBar = mutableStateOf(false) 39 | val initialPage = mutableIntStateOf(0) 40 | val githubProxy = mutableIntStateOf(2) 41 | 42 | fun checkServerPing() { 43 | lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { 44 | showDialogProgressBar.value = true 45 | val regex = "\\d+\\.\\d+".toRegex() 46 | val t1 = async { 47 | val text = Tools().execShellCmd("ping -c 1 -q -W 3 'plnb-cf.hwinzniej.top'", true) 48 | delay(300L) 49 | if (text.contains("1 received")) 50 | serverPing[0] = "${regex.findAll(text).toList()[3].value} ms" 51 | else 52 | serverPing[0] = context.getString(R.string.timeout) 53 | } 54 | val t2 = async { 55 | val text = Tools().execShellCmd("ping -c 1 -q -W 3 'plnb1.hwinzniej.top'", true) 56 | delay(300L) 57 | if (text.contains("1 received")) 58 | serverPing[1] = "${regex.findAll(text).toList()[3].value} ms" 59 | else 60 | serverPing[1] = context.getString(R.string.timeout) 61 | } 62 | val t3 = async { 63 | val text = Tools().execShellCmd("ping -c 1 -q -W 3 'plnb2.hwinzniej.top'", true) 64 | delay(300L) 65 | if (text.contains("1 received")) 66 | serverPing[2] = "${regex.findAll(text).toList()[3].value} ms" 67 | else 68 | serverPing[2] = context.getString(R.string.timeout) 69 | } 70 | t1.await() 71 | t2.await() 72 | t3.await() 73 | showDialogProgressBar.value = false 74 | } 75 | } 76 | 77 | fun selectUmFile() { 78 | try { 79 | openUmExecutableFileLauncher.launch(arrayOf("*/*")) 80 | } catch (_: Exception) { 81 | Toast.makeText( 82 | context, 83 | context.getString(R.string.unable_start_documentsui), 84 | Toast.LENGTH_SHORT 85 | ).show() 86 | } 87 | } 88 | 89 | var umFileLegal = mutableStateOf(false) 90 | var umSupportOverWrite = mutableStateOf(false) 91 | 92 | fun checkUmFile(uri: Uri?) { 93 | if (uri == null) 94 | return 95 | lifecycleOwner.lifecycleScope.launch(Dispatchers.IO) { 96 | val inputStream = context.contentResolver.openInputStream(uri) 97 | 98 | val internalFile = File(context.filesDir, "um_executable") 99 | if (internalFile.exists()) 100 | internalFile.delete() 101 | 102 | val outputStream = internalFile.outputStream() 103 | 104 | inputStream?.copyTo(outputStream) 105 | inputStream?.close() 106 | outputStream.close() 107 | 108 | try { 109 | Tools().execShellCmd( 110 | cmd = "chmod 700 ${internalFile.absolutePath}", 111 | withoutRoot = true 112 | ) 113 | } catch (e: Exception) { 114 | e.printStackTrace() 115 | } 116 | 117 | try { 118 | val result = Tools().execShellCmd( 119 | cmd = "${internalFile.absolutePath} -v", 120 | withoutRoot = true 121 | ) 122 | if (result.contains("Unlock Music CLI version", true)) { 123 | dataStore.edit { settings -> 124 | settings[DataStoreConstants.UM_FILE_LEGAL] = true 125 | } 126 | umFileLegal.value = true 127 | withContext(Dispatchers.Main) { 128 | Toast.makeText( 129 | context, 130 | context.getString(R.string.valid_um_file), 131 | Toast.LENGTH_SHORT 132 | ).show() 133 | } 134 | val supportOverwrite = Tools().execShellCmd( 135 | cmd = "${internalFile.absolutePath} -h", 136 | withoutRoot = true 137 | ) 138 | if (supportOverwrite.contains("--overwrite", true)) { 139 | dataStore.edit { settings -> 140 | settings[DataStoreConstants.UM_SUPPORT_OVERWRITE] = true 141 | } 142 | umSupportOverWrite.value = true 143 | } else { 144 | dataStore.edit { settings -> 145 | settings[DataStoreConstants.UM_SUPPORT_OVERWRITE] = false 146 | } 147 | umSupportOverWrite.value = false 148 | } 149 | } else { 150 | dataStore.edit { settings -> 151 | settings[DataStoreConstants.UM_FILE_LEGAL] = false 152 | } 153 | umFileLegal.value = false 154 | internalFile.delete() 155 | withContext(Dispatchers.Main) { 156 | Toast.makeText( 157 | context, 158 | context.getString(R.string.invalid_um_file), 159 | Toast.LENGTH_SHORT 160 | ).show() 161 | } 162 | } 163 | } catch (_: Exception) { 164 | } 165 | } 166 | } 167 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/data/DataStoreConstants.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.data 2 | 3 | import androidx.datastore.preferences.core.booleanPreferencesKey 4 | import androidx.datastore.preferences.core.intPreferencesKey 5 | import androidx.datastore.preferences.core.longPreferencesKey 6 | import androidx.datastore.preferences.core.stringPreferencesKey 7 | 8 | object DataStoreConstants { 9 | const val SETTINGS_PREFERENCES = "settings" 10 | val KEY_LANGUAGE = stringPreferencesKey("language") 11 | val KEY_THEME_MODE = intPreferencesKey("theme_mode") 12 | val KEY_ENABLE_DYNAMIC_COLOR = booleanPreferencesKey("enable_dynamic_color") 13 | val KEY_ENABLE_AUTO_CHECK_UPDATE = booleanPreferencesKey("enable_auto_check_update") 14 | val KEY_USE_ROOT_ACCESS = booleanPreferencesKey("use_root_access") 15 | val KEY_ENABLE_HAPTIC = booleanPreferencesKey("enable_haptic") 16 | val KEY_ENCRYPT_SERVER = stringPreferencesKey("encrypt_server") 17 | val LAST_LOGIN_TIMESTAMP = longPreferencesKey("last_login_timestamp") 18 | val NETEASE_USER_ID = stringPreferencesKey("netease_user_id") 19 | val PLAYLIST_SOURCE_PLATFORM = intPreferencesKey("playlist_source_platform") 20 | val GET_PLAYLIST_METHOD = intPreferencesKey("get_playlist_method") 21 | val KUGOU_TOKEN = stringPreferencesKey("kugou_token") 22 | val KUGOU_USER_ID = stringPreferencesKey("kugou_user_id") 23 | val UM_FILE_LEGAL = booleanPreferencesKey("um_file_legal") 24 | val UM_SUPPORT_OVERWRITE = booleanPreferencesKey("um_support_overwrite") 25 | val HAPTIC_STRENGTH = intPreferencesKey("haptic_strength") 26 | val TAG_OVERWRITE = booleanPreferencesKey("tag_overwrite") 27 | val TAG_LYRICIST = booleanPreferencesKey("tag_lyricist") 28 | val TAG_COMPOSER = booleanPreferencesKey("tag_composer") 29 | val TAG_ARRANGER = booleanPreferencesKey("tag_arranger") 30 | val SORT_METHOD = intPreferencesKey("sort_method") 31 | val SLOW_MODE = booleanPreferencesKey("slow_mode") 32 | val LUNA_DEVICE_ID = stringPreferencesKey("luna_device_id") 33 | val LUNA_INSTALL_ID = stringPreferencesKey("luna_install_id") 34 | val LUNA_COOKIE = stringPreferencesKey("luna_cookie") 35 | val CONVERT_MODE = intPreferencesKey("convert_mode") 36 | val SELECTED_TARGET_APP = intPreferencesKey("selected_target_app") 37 | val SELECTED_SOURCE_APP = intPreferencesKey("selected_source_app") 38 | val INITIAL_PAGE = intPreferencesKey("initial_page") 39 | val SPOTIFY_USER_ID = stringPreferencesKey("spotify_user_id") 40 | val AGREE_USER_AGREEMENT = booleanPreferencesKey("agree_user_agreement") 41 | val WIN_PATH = stringPreferencesKey("win_path") 42 | val MUSIC_DIRNAME = stringPreferencesKey("music_dirName") 43 | val SHOW_ADVANCED_OPTIONS = booleanPreferencesKey("show_advanced_options") 44 | val GITHUB_PROXY = intPreferencesKey("github_proxy") 45 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/data/SourceApp.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.data 2 | 3 | class SourceApp { 4 | lateinit var sourceEng: String 5 | lateinit var sourceChn: String 6 | lateinit var databaseName: String 7 | lateinit var songListTableName: String 8 | lateinit var songListId: String 9 | lateinit var songListName: String 10 | lateinit var songListSongInfoTableName: String 11 | lateinit var songListSongInfoPlaylistId: String 12 | lateinit var songListSongInfoSongId: String 13 | lateinit var songInfoTableName: String 14 | lateinit var sortField: String 15 | lateinit var songInfoSongId: String 16 | lateinit var songInfoSongName: String 17 | lateinit var songInfoSongArtist: String 18 | lateinit var songInfoSongAlbum: String 19 | lateinit var pakageName: String 20 | lateinit var musicNum: String 21 | 22 | fun init(sourceApp: String) { 23 | when (sourceApp) { 24 | "QQMusic" -> { 25 | sourceEng = "QQMusic" 26 | sourceChn = "QQ音乐" 27 | databaseName = "QQMusic" 28 | songListTableName = "User_Folder_table" 29 | songListId = "folderid" 30 | songListName = "foldername" 31 | musicNum = "count" 32 | songListSongInfoTableName = "User_Folder_Song_table" 33 | songListSongInfoPlaylistId = "folderid" 34 | songListSongInfoSongId = "id" 35 | songInfoTableName = "Song_table" 36 | sortField = "position" 37 | songInfoSongId = "id" 38 | songInfoSongName = "name" 39 | songInfoSongArtist = "singername" 40 | songInfoSongAlbum = "albumname" 41 | pakageName = "com.tencent.qqmusic" 42 | } 43 | 44 | "CloudMusic" -> { 45 | sourceEng = "CloudMusic" 46 | sourceChn = "网易云音乐" 47 | databaseName = "cloudmusic.db" 48 | songListTableName = "playlist" 49 | songListId = "_id" 50 | songListName = "name" 51 | musicNum = "track_count" 52 | songListSongInfoTableName = "playlist_track" 53 | songListSongInfoPlaylistId = "playlist_id" 54 | songListSongInfoSongId = "track_id" 55 | songInfoTableName = "track" 56 | sortField = "track_order" 57 | songInfoSongId = "id" 58 | songInfoSongName = "name" 59 | songInfoSongArtist = "artists" 60 | songInfoSongAlbum = "album_name" 61 | pakageName = 62 | "com.netease.cloudmusic|com.hihonor.cloudmusic" 63 | } 64 | 65 | "KugouMusic" -> { 66 | sourceEng = "KuGouMusic" 67 | sourceChn = "酷狗音乐" 68 | databaseName = "kugou_music_phone_v7.db" 69 | songListTableName = "kugou_playlists" 70 | songListId = "_id" 71 | songListName = "name" 72 | musicNum = "num_of_songs" 73 | songListSongInfoTableName = "playlistsong" 74 | songListSongInfoPlaylistId = "plistid" 75 | songListSongInfoSongId = "songid" 76 | songInfoTableName = "kugou_songs" 77 | sortField = "cloudfileorderweight" 78 | songInfoSongId = "_id" 79 | songInfoSongName = "trackName" 80 | songInfoSongArtist = "artistName" 81 | songInfoSongAlbum = "albumName" 82 | pakageName = "com.kugou.android" 83 | } 84 | 85 | "KuwoMusic" -> { 86 | sourceEng = "KuWoMusic" 87 | sourceChn = "酷我音乐" 88 | databaseName = "kwplayer.db" 89 | songListTableName = "v3_list" 90 | songListId = "id" 91 | songListName = "showname" 92 | musicNum = "musicNum" 93 | songListSongInfoTableName = "v3_music" 94 | songListSongInfoPlaylistId = "listid" 95 | songListSongInfoSongId = "rid" 96 | songInfoTableName = "v3_music" 97 | sortField = "id" 98 | songInfoSongId = "rid" 99 | songInfoSongName = "name" 100 | songInfoSongArtist = "artist" 101 | songInfoSongAlbum = "album" 102 | pakageName = "cn.kuwo.player" 103 | } 104 | 105 | "LunaMusic" -> { 106 | sourceEng = "LunaMusic" 107 | sourceChn = "汽水音乐" 108 | databaseName = "QQMusic" 109 | songListTableName = "User_Folder_table" 110 | songListId = "folderid" 111 | songListName = "foldername" 112 | musicNum = "count" 113 | songListSongInfoTableName = "User_Folder_Song_table" 114 | songListSongInfoPlaylistId = "folderid" 115 | songListSongInfoSongId = "id" 116 | songInfoTableName = "Song_table" 117 | sortField = "position" 118 | songInfoSongId = "id" 119 | songInfoSongName = "name" 120 | songInfoSongArtist = "singername" 121 | songInfoSongAlbum = "albumname" 122 | pakageName = "com.luna.music" 123 | } 124 | 125 | "Spotify" -> { 126 | sourceEng = "Spotify" 127 | sourceChn = "Spotify" 128 | databaseName = "QQMusic" 129 | songListTableName = "User_Folder_table" 130 | songListId = "folderid" 131 | songListName = "foldername" 132 | musicNum = "count" 133 | songListSongInfoTableName = "User_Folder_Song_table" 134 | songListSongInfoPlaylistId = "folderid" 135 | songListSongInfoSongId = "id" 136 | songInfoTableName = "Song_table" 137 | sortField = "position" 138 | songInfoSongId = "id" 139 | songInfoSongName = "name" 140 | songInfoSongArtist = "singername" 141 | songInfoSongAlbum = "albumname" 142 | pakageName = "com.spotify.music" 143 | } 144 | 145 | "TuneMyMusic" -> { 146 | sourceEng = "TuneMyMusic" 147 | sourceChn = "TuneMyMusic" 148 | databaseName = "QQMusic" 149 | songListTableName = "User_Folder_table" 150 | songListId = "folderid" 151 | songListName = "foldername" 152 | musicNum = "count" 153 | songListSongInfoTableName = "User_Folder_Song_table" 154 | songListSongInfoPlaylistId = "folderid" 155 | songListSongInfoSongId = "id" 156 | songInfoTableName = "Song_table" 157 | sortField = "position" 158 | songInfoSongId = "id" 159 | songInfoSongName = "name" 160 | songInfoSongArtist = "singername" 161 | songInfoSongAlbum = "albumname" 162 | pakageName = "com.tunemymusic.hwinzniej.idontknow" 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/data/dao/MusicDao.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.data.dao 2 | 3 | import androidx.room.Dao 4 | import androidx.room.Insert 5 | import androidx.room.Query 6 | import com.hwinzniej.musichelper.data.model.Music 7 | import com.hwinzniej.musichelper.data.model.MusicInfo 8 | 9 | @Dao 10 | interface MusicDao { 11 | @Query("SELECT id, song, artist, album, absolutePath FROM music ORDER BY modifyTime ASC") 12 | fun getAll(): List 13 | 14 | @Query("SELECT id, song, artist, album, absolutePath FROM music WHERE id IN (:selection) ORDER BY modifyTime ASC") 15 | fun getSelectedMusic(selection: List): List 16 | 17 | @Insert 18 | fun insertAll(vararg music: Music) 19 | 20 | @Insert 21 | fun insert(music: Music) 22 | 23 | @Query("DELETE FROM music") 24 | fun deleteAll() 25 | 26 | // @Query("SELECT song, artist, album, releaseYear, trackNumber, albumArtist, genre FROM music") 27 | // fun getMusicInfo(): List 28 | 29 | @Query("SELECT song, artist, album, absolutePath, id, modifyTime, albumArtist FROM music") 30 | fun getMusic3Info(): List 31 | 32 | @Query("SELECT COUNT(*) FROM music") 33 | fun getMusicCount(): Int 34 | 35 | @Query("SELECT song, artist, album, absolutePath, id FROM music WHERE song LIKE :keyword OR artist LIKE :keyword OR album LIKE :keyword LIMIT 3") 36 | fun searchMusic(keyword: String): List 37 | 38 | @Query("SELECT song, artist, album, absolutePath, id FROM music WHERE song LIKE :keyword OR artist LIKE :keyword OR album LIKE :keyword OR absolutePath LIKE :keyword") 39 | fun searchMusicAll(keyword: String): List 40 | 41 | @Query("SELECT song, artist, album, absolutePath, id FROM music WHERE id = :id") 42 | fun getMusicById(id: Int): MusicInfo 43 | 44 | @Query("SELECT song, artist, album, absolutePath, id, albumArtist, genre, trackNumber, releaseYear FROM music WHERE id = :id") 45 | fun getMusicAllInfo(id: Int): MusicInfo 46 | 47 | @Query("UPDATE music SET song = :song, artist = :artist, album = :album, albumArtist = :albumArtist, genre = :genre, trackNumber = :trackNumber, releaseYear = :releaseYear, lyricist = :lyricist, composer = :composer, arranger = :arranger, modifyTime = :modifyTime WHERE id = :id") 48 | fun updateMusicInfo( 49 | id: Int, 50 | song: String, 51 | artist: String, 52 | album: String, 53 | albumArtist: String?, 54 | genre: String?, 55 | trackNumber: String?, 56 | releaseYear: String?, 57 | lyricist: String?, 58 | composer: String?, 59 | arranger: String?, 60 | modifyTime: Long, 61 | ) 62 | 63 | @Query( 64 | """ 65 | SELECT * 66 | FROM music 67 | WHERE album IN ( 68 | SELECT album 69 | FROM music 70 | GROUP BY album 71 | HAVING COUNT(*) >= 2 72 | ) 73 | """ 74 | ) 75 | fun searchDuplicateAlbum(): List 76 | 77 | // @Query( 78 | // """ 79 | // SELECT * 80 | // FROM music 81 | // WHERE album IN ( 82 | // SELECT album 83 | // FROM music 84 | // WHERE albumArtist IS NULL OR albumArtist = '' 85 | // GROUP BY album 86 | // HAVING COUNT(*) >= 2 87 | // ) AND (albumArtist IS NULL OR albumArtist = '') 88 | // """ 89 | // ) 90 | @Query("SELECT id, song, artist, album, absolutePath FROM music WHERE albumArtist IS NULL OR albumArtist = '' ORDER BY modifyTime ASC") 91 | fun searchDuplicateAlbumNoOverwrite(): List 92 | 93 | @Query("SELECT id, song, artist, album, absolutePath FROM music WHERE id IN (:selection) AND (albumArtist IS NULL OR albumArtist = '') ORDER BY modifyTime ASC") 94 | fun searchSelectedDuplicateAlbumNoOverwrite(selection: List): List 95 | 96 | @Query("SELECT DISTINCT artist FROM music WHERE album = :album") 97 | fun getDuplicateAlbumArtistList(album: String): List 98 | 99 | @Query("UPDATE music SET albumArtist = :albumArtist, modifyTime = :modifyTime WHERE id = :id") 100 | fun updateAlbumArtist( 101 | id: Int, 102 | albumArtist: String?, 103 | modifyTime: Long 104 | ) 105 | 106 | @Query("SELECT COUNT(*) FROM music WHERE albumArtist IS NULL OR albumArtist = ''") 107 | fun countNullAlbumArtist(): Int 108 | 109 | @Query("SELECT COUNT(*) FROM music WHERE id IN (:selection) AND (albumArtist IS NULL OR albumArtist = '')") 110 | fun countSelectedNullAlbumArtist(selection: List): Int 111 | 112 | @Query("SELECT COUNT(*) FROM music WHERE lyricist IS NULL OR lyricist = ''") 113 | fun countNullLyricist(): Int 114 | 115 | @Query("SELECT COUNT(*) FROM music WHERE composer IS NULL OR composer = ''") 116 | fun countNullComposer(): Int 117 | 118 | @Query("SELECT COUNT(*) FROM music WHERE arranger IS NULL OR arranger = ''") 119 | fun countNullArranger(): Int 120 | 121 | @Query("SELECT COUNT(*) FROM music WHERE id IN (:selection) AND (lyricist IS NULL OR lyricist = '')") 122 | fun countSelectedNullLyricist(selection: List): Int 123 | 124 | @Query("SELECT COUNT(*) FROM music WHERE id IN (:selection) AND (composer IS NULL OR composer = '')") 125 | fun countSelectedNullComposer(selection: List): Int 126 | 127 | @Query("SELECT COUNT(*) FROM music WHERE id IN (:selection) AND (arranger IS NULL OR arranger = '')") 128 | fun countSelectedNullArranger(selection: List): Int 129 | 130 | @Query("UPDATE music SET lyricist = :lyricist, composer = :composer, arranger = :arranger, modifyTime = :modifyTime WHERE id = :id") 131 | fun updateLyricistComposerArranger( 132 | id: Int, 133 | lyricist: String?, 134 | composer: String?, 135 | arranger: String?, 136 | modifyTime: Long 137 | ) 138 | 139 | // @Insert 140 | // fun updateMusicInfo(musicInfo: MusicInfo) 141 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/data/database/MusicDatabase.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.data.database 2 | 3 | import androidx.room.Database 4 | import androidx.room.RoomDatabase 5 | import com.hwinzniej.musichelper.data.dao.MusicDao 6 | import com.hwinzniej.musichelper.data.model.Music 7 | 8 | @Database(entities = [Music::class], version = 3, exportSchema = false) 9 | abstract class MusicDatabase : RoomDatabase() { 10 | abstract fun musicDao(): MusicDao 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/data/model/Music.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.data.model 2 | 3 | import androidx.room.ColumnInfo 4 | import androidx.room.Entity 5 | import androidx.room.PrimaryKey 6 | 7 | @Entity 8 | data class Music( 9 | @PrimaryKey val id: Int, 10 | @ColumnInfo(name = "song") val song: String, //歌名 11 | @ColumnInfo(name = "artist") val artist: String, //歌手 12 | @ColumnInfo(name = "album") val album: String, //专辑 13 | @ColumnInfo(name = "absolutePath") val absolutePath: String, //绝对路径 14 | @ColumnInfo(name = "releaseYear") val releaseYear: String, //发行年份 15 | @ColumnInfo(name = "trackNumber") val trackNumber: String, //音轨号 16 | @ColumnInfo(name = "albumArtist") val albumArtist: String, //专辑艺术家 17 | @ColumnInfo(name = "genre") val genre: String, //风格(流派) 18 | @ColumnInfo(name = "lyricist") val lyricist: String, //作词 19 | @ColumnInfo(name = "composer") val composer: String, //作曲 20 | @ColumnInfo(name = "arranger") val arranger: String, //编曲 21 | @ColumnInfo(name = "modifyTime") val modifyTime: Long //修改时间 22 | ) 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/data/model/MusicInfo.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.data.model 2 | 3 | class MusicInfo( 4 | val id: Int, 5 | var song: String, 6 | var artist: String, 7 | var album: String, 8 | var releaseYear: String?, 9 | var trackNumber: String?, 10 | var albumArtist: String?, 11 | var genre: String?, 12 | var absolutePath: String, 13 | var lyricist: String?, 14 | var composer: String?, 15 | var arranger: String?, 16 | var modifyTime: Long? 17 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/ui/UnlockPageUi.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.ui 2 | 3 | import android.widget.Toast 4 | import androidx.activity.compose.BackHandler 5 | import androidx.compose.animation.AnimatedContent 6 | import androidx.compose.animation.AnimatedVisibility 7 | import androidx.compose.animation.fadeIn 8 | import androidx.compose.animation.fadeOut 9 | import androidx.compose.animation.togetherWith 10 | import androidx.compose.foundation.background 11 | import androidx.compose.foundation.layout.Box 12 | import androidx.compose.foundation.layout.Column 13 | import androidx.compose.foundation.layout.fillMaxSize 14 | import androidx.compose.foundation.layout.fillMaxWidth 15 | import androidx.compose.foundation.layout.heightIn 16 | import androidx.compose.foundation.layout.padding 17 | import androidx.compose.foundation.lazy.LazyColumn 18 | import androidx.compose.foundation.rememberScrollState 19 | import androidx.compose.foundation.shape.RoundedCornerShape 20 | import androidx.compose.foundation.verticalScroll 21 | import androidx.compose.material3.LinearProgressIndicator 22 | import androidx.compose.runtime.Composable 23 | import androidx.compose.runtime.LaunchedEffect 24 | import androidx.compose.runtime.MutableIntState 25 | import androidx.compose.runtime.MutableState 26 | import androidx.compose.runtime.getValue 27 | import androidx.compose.runtime.mutableStateOf 28 | import androidx.compose.runtime.remember 29 | import androidx.compose.runtime.setValue 30 | import androidx.compose.ui.Modifier 31 | import androidx.compose.ui.draw.clip 32 | import androidx.compose.ui.graphics.StrokeCap 33 | import androidx.compose.ui.platform.LocalConfiguration 34 | import androidx.compose.ui.platform.LocalContext 35 | import androidx.compose.ui.res.colorResource 36 | import androidx.compose.ui.res.stringResource 37 | import androidx.compose.ui.unit.dp 38 | import androidx.compose.ui.unit.sp 39 | import androidx.compose.ui.zIndex 40 | import com.hwinzniej.musichelper.R 41 | import com.hwinzniej.musichelper.activity.SettingsPage 42 | import com.hwinzniej.musichelper.activity.UnlockPage 43 | import com.moriafly.salt.ui.ItemContainer 44 | import com.moriafly.salt.ui.RoundedColumn 45 | import com.moriafly.salt.ui.SaltTheme 46 | import com.moriafly.salt.ui.Text 47 | 48 | @Composable 49 | fun UnlockPageUi( 50 | unlockPage: UnlockPage, 51 | enableHaptic: MutableState, 52 | settingsPage: SettingsPage, 53 | hapticStrength: MutableIntState 54 | ) { 55 | val context = LocalContext.current 56 | var inputPath by remember { mutableStateOf("") } 57 | var outputPath by remember { mutableStateOf("") } 58 | var deleteEncryptedFile by remember { mutableStateOf(false) } 59 | var overwriteOutputFile by remember { mutableStateOf(true) } 60 | var umSupportOverWrite by remember { mutableStateOf(false) } 61 | var showInputOutputPathNotSameDialog by remember { mutableStateOf(false) } 62 | 63 | BackHandler(enabled = unlockPage.showLoadingProgressBar.value) { 64 | Toast.makeText( 65 | context, 66 | context.getString(R.string.wait_operate_end), 67 | Toast.LENGTH_SHORT 68 | ).show() 69 | } 70 | 71 | LaunchedEffect(key1 = settingsPage.umSupportOverWrite.value) { 72 | umSupportOverWrite = settingsPage.umSupportOverWrite.value 73 | } 74 | 75 | LaunchedEffect(key1 = unlockPage.selectedInputPath.value) { 76 | inputPath = unlockPage.selectedInputPath.value 77 | } 78 | 79 | LaunchedEffect(key1 = unlockPage.selectedOutputPath.value) { 80 | outputPath = unlockPage.selectedOutputPath.value 81 | } 82 | 83 | if (unlockPage.showUmStdoutDialog.value) { 84 | YesDialog( 85 | onDismissRequest = { unlockPage.showUmStdoutDialog.value = false }, 86 | title = stringResource(id = R.string.um_stdout), 87 | content = null, 88 | enableHaptic = enableHaptic.value, 89 | hapticStrength = hapticStrength.intValue 90 | ) { 91 | ItemContainer { 92 | LazyColumn( 93 | modifier = Modifier 94 | .fillMaxWidth() 95 | .heightIn( 96 | min = 25.dp, 97 | max = (LocalConfiguration.current.screenHeightDp / 2.55).dp 98 | ) 99 | .clip(RoundedCornerShape(12.dp)) 100 | .background(color = SaltTheme.colors.subBackground) 101 | ) { 102 | items(unlockPage.operateResult.size) { index -> 103 | Text( 104 | modifier = Modifier.padding( 105 | horizontal = 16.dp, 106 | vertical = 4.dp 107 | ), 108 | text = unlockPage.operateResult[index].keys.first(), 109 | fontSize = 14.sp, 110 | color = if (unlockPage.operateResult[index].values.first()) 111 | SaltTheme.colors.subText 112 | else 113 | colorResource(id = R.color.unmatched), 114 | ) 115 | } 116 | } 117 | } 118 | } 119 | } 120 | 121 | if (showInputOutputPathNotSameDialog) { 122 | YesDialog( 123 | onDismissRequest = { 124 | showInputOutputPathNotSameDialog = false 125 | }, 126 | title = stringResource(id = R.string.error), 127 | content = if (inputPath.isBlank() || outputPath.isBlank()) 128 | stringResource(id = R.string.input_and_output_path_cant_null) 129 | else 130 | stringResource(id = R.string.input_output_path_cant_same), 131 | fontSize = 14.sp, 132 | enableHaptic = enableHaptic.value, 133 | hapticStrength = hapticStrength.intValue 134 | ) 135 | } 136 | 137 | Column( 138 | modifier = Modifier 139 | .fillMaxSize() 140 | .background(color = SaltTheme.colors.background) 141 | ) { 142 | TitleBar( 143 | onBack = {}, 144 | text = stringResource(id = R.string.unlock_function_name) + if (settingsPage.umFileLegal.value) 145 | "" 146 | else 147 | stringResource(id = R.string.unavailable), 148 | showBackBtn = false 149 | ) 150 | Box { 151 | if (unlockPage.showLoadingProgressBar.value) { 152 | LinearProgressIndicator( 153 | modifier = Modifier 154 | .fillMaxWidth() 155 | .zIndex(1f), 156 | color = SaltTheme.colors.highlight, 157 | trackColor = SaltTheme.colors.background, 158 | strokeCap = StrokeCap.Square 159 | ) 160 | } 161 | Column( 162 | modifier = Modifier 163 | .fillMaxSize() 164 | .background(color = SaltTheme.colors.background) 165 | .verticalScroll(rememberScrollState()) 166 | ) { 167 | RoundedColumn { 168 | ItemTitle(text = stringResource(id = R.string.input_options)) 169 | Item( 170 | enabled = settingsPage.umFileLegal.value, 171 | text = stringResource(id = R.string.select_the_directory_for_input_files), 172 | onClick = { 173 | unlockPage.selectInputDir() 174 | } 175 | ) 176 | AnimatedVisibility( 177 | visible = inputPath != "" 178 | ) { 179 | ItemValue( 180 | text = stringResource(R.string.you_have_selected), 181 | rightSub = inputPath 182 | ) 183 | } 184 | ItemSwitcher( 185 | enabled = settingsPage.umFileLegal.value, 186 | state = deleteEncryptedFile, 187 | onChange = { 188 | deleteEncryptedFile = it 189 | unlockPage.deleteOriginalFile.value = it 190 | }, 191 | text = stringResource(id = R.string.delete_input_file), 192 | sub = stringResource(id = R.string.delete_input_file_sub), 193 | enableHaptic = enableHaptic.value, 194 | hapticStrength = hapticStrength.intValue 195 | ) 196 | } 197 | RoundedColumn { 198 | ItemTitle(text = stringResource(id = R.string.output_options)) 199 | Item( 200 | enabled = settingsPage.umFileLegal.value, 201 | text = stringResource(id = R.string.select_the_directory_for_output_files), 202 | onClick = { 203 | unlockPage.selectOutputDir() 204 | } 205 | ) 206 | AnimatedVisibility( 207 | visible = outputPath != "" 208 | ) { 209 | ItemValue( 210 | text = stringResource(R.string.you_have_selected), 211 | rightSub = outputPath 212 | ) 213 | } 214 | ItemSwitcher( 215 | enabled = umSupportOverWrite && settingsPage.umFileLegal.value, 216 | state = overwriteOutputFile && umSupportOverWrite && settingsPage.umFileLegal.value, 217 | onChange = { 218 | overwriteOutputFile = it 219 | unlockPage.overwriteOutputFile.value = it 220 | }, 221 | text = stringResource(id = R.string.overwrite_output_file), 222 | sub = if (umSupportOverWrite) stringResource(id = R.string.overwrite_output_file_sub) 223 | else if (settingsPage.umFileLegal.value) stringResource(id = R.string.current_um_not_support_this_fun) else "" + "\n" + 224 | stringResource(id = R.string.overwrite_output_file_sub), 225 | enableHaptic = enableHaptic.value, 226 | hapticStrength = hapticStrength.intValue 227 | ) 228 | } 229 | AnimatedContent( 230 | targetState = unlockPage.showLoadingProgressBar.value, 231 | label = "", 232 | transitionSpec = { 233 | fadeIn() togetherWith fadeOut() 234 | }) { 235 | ItemContainer { 236 | TextButton( 237 | onClick = { 238 | if ((inputPath == outputPath) || (inputPath.isBlank() || outputPath.isBlank())) { 239 | showInputOutputPathNotSameDialog = true 240 | } else { 241 | unlockPage.requestPermission() 242 | } 243 | }, text = stringResource(R.string.start_text), 244 | enabled = !it && settingsPage.umFileLegal.value, 245 | enableHaptic = enableHaptic.value, 246 | hapticStrength = hapticStrength.intValue 247 | ) 248 | } 249 | } 250 | } 251 | } 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Purple80, 20 | secondary = PurpleGrey80, 21 | tertiary = Pink80 22 | ) 23 | 24 | private val LightColorScheme = lightColorScheme( 25 | primary = Purple40, 26 | secondary = PurpleGrey40, 27 | tertiary = Pink40 28 | 29 | /* Other default colors to override 30 | background = Color(0xFFFFFBFE), 31 | surface = Color(0xFFFFFBFE), 32 | onPrimary = Color.White, 33 | onSecondary = Color.White, 34 | onTertiary = Color.White, 35 | onBackground = Color(0xFF1C1B1F), 36 | onSurface = Color(0xFF1C1B1F), 37 | */ 38 | ) 39 | 40 | @Composable 41 | fun MusicHelperTheme( 42 | darkTheme: Boolean = isSystemInDarkTheme(), 43 | // Dynamic color is available on Android 12+ 44 | dynamicColor: Boolean = true, 45 | content: @Composable () -> Unit 46 | ) { 47 | val colorScheme = when { 48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 49 | val context = LocalContext.current 50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 51 | } 52 | 53 | darkTheme -> DarkColorScheme 54 | else -> LightColorScheme 55 | } 56 | val view = LocalView.current 57 | if (!view.isInEditMode) { 58 | SideEffect { 59 | val window = (view.context as Activity).window 60 | window.statusBarColor = colorScheme.primary.toArgb() 61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 62 | } 63 | } 64 | 65 | MaterialTheme( 66 | colorScheme = colorScheme, 67 | typography = Typography, 68 | content = content 69 | ) 70 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/utils/DoubanMusicApi.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.utils 2 | // 3 | //import com.hwinzniej.musichelper.data.model.MusicInfo 4 | //import okhttp3.OkHttpClient 5 | //import okio.IOException 6 | //import org.htmlunit.WebClient 7 | //import org.htmlunit.html.HtmlPage 8 | //import org.jsoup.Jsoup 9 | // 10 | //class DoubanMusicApi { 11 | // companion object { 12 | // fun getMusicInfo(musicName: String, artistName: String, albumName: String): MusicInfo? { 13 | // 14 | // val url = "https://search.douban.com/music/subject_search?search_text=Moon halo" 15 | //// val url = 16 | //// "https://music.douban.com/subject_search?search_text=要嫁就嫁灰太狼 周艳泓" 17 | // 18 | // val webClient = WebClient().apply { 19 | // options.isCssEnabled = false 20 | // options.isJavaScriptEnabled = true 21 | // options.isThrowExceptionOnScriptError = false 22 | // options.isThrowExceptionOnFailingStatusCode = false 23 | // } 24 | // 25 | // webClient.waitForBackgroundJavaScript(500) // 等待JavaScript执行完成 26 | // val htmlPage: HtmlPage = webClient.getPage(url) 27 | // 28 | // val musicInfoPage = 29 | // "https://music.douban.com/subject/[0-9]*/".toRegex().find(htmlPage.asXml())?.value 30 | // 31 | // val client = OkHttpClient() 32 | // val request = okhttp3.Request.Builder() 33 | // .url(musicInfoPage.toString()) 34 | // .build() 35 | // 36 | // client.newCall(request).execute().use { response -> 37 | // if (!response.isSuccessful) throw IOException("Unexpected code $response") 38 | // 39 | // val doc = Jsoup.parse(response.body?.string()) 40 | // val title = doc.title() 41 | // 42 | // println("Title: $title") 43 | // } 44 | // 45 | // return null 46 | // } 47 | // } 48 | //} -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/utils/MyDataStore.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.utils 2 | 3 | import android.app.Application 4 | import androidx.datastore.core.DataStore 5 | import androidx.datastore.preferences.core.Preferences 6 | import androidx.datastore.preferences.preferencesDataStore 7 | import com.hwinzniej.musichelper.data.DataStoreConstants 8 | 9 | class MyDataStore : Application() { 10 | val dataStore: DataStore by preferencesDataStore(name = DataStoreConstants.SETTINGS_PREFERENCES) 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/hwinzniej/musichelper/utils/MyVibrationEffect.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper.utils 2 | 3 | import android.content.Context 4 | import android.os.VibrationEffect 5 | import android.os.Vibrator 6 | 7 | class MyVibrationEffect( 8 | context: Context, 9 | private val enable: Boolean, 10 | private val strength: Int 11 | ) { 12 | private val vibrator: Vibrator = context.getSystemService(Vibrator::class.java) 13 | 14 | fun dialog() { 15 | if (enable) { 16 | val vibrationEffect = 17 | VibrationEffect.createWaveform( 18 | longArrayOf( 19 | (18 + (strength - 3) * 3).toLong(), 20 | 100, 21 | (13 + (strength - 3) * 3).toLong() 22 | ), 23 | intArrayOf(200 + (strength - 3) * 15, 0, 120 + (strength - 3) * 15), 24 | -1 25 | ) 26 | vibrator.vibrate(vibrationEffect) 27 | } 28 | } 29 | 30 | fun click() { 31 | if (enable) { 32 | val vibrationEffect = 33 | VibrationEffect.createOneShot( 34 | (12 + (strength - 3) * 3).toLong(), 35 | 180 + (strength - 3) * 15 36 | ) 37 | vibrator.vibrate(vibrationEffect) 38 | } 39 | } 40 | 41 | fun turnOn() { 42 | if (enable) { 43 | val vibrationEffect = 44 | VibrationEffect.createWaveform( 45 | longArrayOf( 46 | (12 + (strength - 3) * 3).toLong(), 47 | 130, 48 | (18 + (strength - 3) * 3).toLong() 49 | ), 50 | intArrayOf(110 + (strength - 3) * 15, 0, 180 + (strength - 3) * 15), 51 | -1 52 | ) 53 | vibrator.vibrate(vibrationEffect) 54 | } 55 | } 56 | 57 | fun turnOff() { 58 | if (enable) { 59 | val vibrationEffect = 60 | VibrationEffect.createWaveform( 61 | longArrayOf( 62 | (18 + (strength - 3) * 3).toLong(), 63 | 130, 64 | (12 + (strength - 3) * 3).toLong() 65 | ), 66 | intArrayOf(180 + (strength - 3) * 15, 0, 110 + (strength - 3) * 15), 67 | -1 68 | ) 69 | vibrator.vibrate(vibrationEffect) 70 | } 71 | } 72 | 73 | fun done() { 74 | if (enable) { 75 | val vibrationEffect = 76 | VibrationEffect.createOneShot( 77 | (20 + (strength - 3) * 3).toLong(), 78 | 225 + (strength - 3) * 15 79 | ) 80 | vibrator.vibrate(vibrationEffect) 81 | } 82 | } 83 | 84 | fun dragMove() { 85 | if (enable) { 86 | val vibrationEffect = 87 | VibrationEffect.createOneShot( 88 | (10 + (strength - 3) * 3).toLong(), 89 | 220 + (strength - 3) * 15 90 | ) 91 | vibrator.vibrate(vibrationEffect) 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/about.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/agreement.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 20 | 26 | 32 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/alipay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/drawable/alipay.jpg -------------------------------------------------------------------------------- /app/src/main/res/drawable/all.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/android.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/aplayer.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 14 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/app_theme.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 16 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/auto_check_update.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/bilibili.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/caution.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/check.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/check_update.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/chinese.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cloudmusic.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/coffee.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/color.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 16 | 20 | 24 | 28 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/complete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/computer.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/convert.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/cookie.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/coolapk.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/csv_file.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/dark_color.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/database.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/developer.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/english.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gitee.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/github.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/gitlab.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/haptic.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/home_screen.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_back.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_arrow_drop_down.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_check.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_chevron_right.xml: -------------------------------------------------------------------------------- 1 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_clear.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_delete.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_border_foreground.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 19 | 27 | 33 | 41 | 49 | 57 | 64 | 72 | 80 | 88 | 96 | 104 | 112 | 120 | 124 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 16 | 20 | 24 | 28 | 31 | 32 | 39 | 42 | 45 | 48 | 49 | 50 | 51 | 59 | 67 | 73 | 81 | 89 | 97 | 104 | 112 | 120 | 128 | 136 | 144 | 152 | 160 | 164 | 168 | 172 | 176 | 180 | 184 | 188 | 196 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_tips.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_uncheck.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/json_file.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/korean.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/kugou.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/kuwo.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 16 | 20 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/language.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/license.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/light_color.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/luna_music.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/manual.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/microsoft_zune.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 24 | 27 | 30 | 33 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/network.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/no_items.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/overall.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/plus.xml: -------------------------------------------------------------------------------- 1 | 6 | 12 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/plus_no_circle.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/poweramp.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/proxy.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/qinalt.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 9 | 13 | 17 | 21 | 25 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/qq_group.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/qqmusic.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/refresh.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/result.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/root_access.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/saltplayer.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 20 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/scan.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/search.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/server.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/settings.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/sort.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/split.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/spotify.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/success.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tag.xml: -------------------------------------------------------------------------------- 1 | 6 | 11 | 14 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/text.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/tune_my_music.xml: -------------------------------------------------------------------------------- 1 | 6 | 14 | 22 | 30 | 34 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/um.xml: -------------------------------------------------------------------------------- 1 | 6 | 10 | 13 | 14 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/user.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/web_page.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/drawable/wechat.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/wechat_app.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | #599E5E 11 | #C94F4F 12 | #3D76A7 13 | #E1A800 14 | -------------------------------------------------------------------------------- /app/src/main/res/values/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFFFFF 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /app/src/test/java/com/hwinzniej/musichelper/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.hwinzniej.musichelper 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id("com.android.application") version "8.8.0" apply false 4 | id("com.android.library") version "8.8.0" apply false 5 | id("org.jetbrains.kotlin.android") version "2.1.0" apply false 6 | id("org.jetbrains.kotlin.plugin.compose") version "2.1.0" apply false 7 | id("com.google.devtools.ksp") version "2.1.0-1.0.29" apply false 8 | id("com.cookpad.android.plugin.license-tools") version "1.2.8" // 需要把生成的yml文件放到项目根目录,才能生成license.html 9 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app's APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Kotlin code style for this project: "official" or "obsolete": 19 | kotlin.code.style=official 20 | # Enables namespacing of each library's R class so that its R class includes only the 21 | # resources declared in the library itself and none from the library's dependencies, 22 | # thereby reducing the size of the R class for that library 23 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Mon Dec 11 19:31:47 CST 2023 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.10.2-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /markdownResources/Alipay WeChatPay.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/markdownResources/Alipay WeChatPay.jpg -------------------------------------------------------------------------------- /markdownResources/Questionnaire QR Code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/markdownResources/Questionnaire QR Code.png -------------------------------------------------------------------------------- /markdownResources/Sponsorship.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/markdownResources/Sponsorship.png -------------------------------------------------------------------------------- /markdownResources/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/markdownResources/ic_launcher.png -------------------------------------------------------------------------------- /markdownResources/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/markdownResources/img1.png -------------------------------------------------------------------------------- /markdownResources/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/markdownResources/img2.png -------------------------------------------------------------------------------- /markdownResources/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Winnie0408/LocalMusicHelper/cf00054963590ee3d10cdef4ea7edc7539923bdb/markdownResources/img3.png -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | // maven { url = uri("https://maven.aliyun.com/repository/central") } 4 | // maven { url = uri("https://maven.aliyun.com/repository/public") } 5 | // maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") } 6 | // maven { url = uri("https://maven.aliyun.com/repository/apache-snapshots") } 7 | maven { url = uri("https://plugins.gradle.org/m2/") } 8 | maven { url = uri("https://jitpack.io") } 9 | google() 10 | mavenCentral() 11 | gradlePluginPortal() 12 | } 13 | } 14 | dependencyResolutionManagement { 15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 16 | repositories { 17 | // maven { url = uri("https://maven.aliyun.com/repository/central") } 18 | // maven { url = uri("https://maven.aliyun.com/repository/public") } 19 | // maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") } 20 | // maven { url = uri("https://maven.aliyun.com/repository/apache-snapshots") } 21 | maven { url = uri("https://plugins.gradle.org/m2/") } 22 | maven { url = uri("https://jitpack.io") } 23 | google() 24 | mavenCentral() 25 | } 26 | } 27 | 28 | rootProject.name = "MusicHelper" 29 | include(":app") 30 | --------------------------------------------------------------------------------