├── .gitignore ├── .idea ├── .gitignore ├── .name ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── compiler.xml ├── deploymentTargetDropDown.xml ├── encodings.xml ├── gradle.xml ├── inspectionProfiles │ └── Project_Default.xml ├── jarRepositories.xml ├── kotlinc.xml ├── misc.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── ftplet-api-1.1.1.jar │ ├── ftpserver-core-1.1.1.jar │ ├── log4j-1.2.17.jar │ ├── mina-core-2.0.16.jar │ └── slf4j-api-1.6.2.jar ├── proguard-rules.pro ├── release │ └── output-metadata.json └── src │ ├── androidTest │ └── java │ │ └── cn │ │ └── wearbbs │ │ └── music │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ │ └── cn │ │ │ ├── jackuxl │ │ │ ├── api │ │ │ │ ├── CloudSongApi.java │ │ │ │ ├── MVApi.java │ │ │ │ ├── MusicListApi.java │ │ │ │ ├── SongApi.kt │ │ │ │ └── UserApi.java │ │ │ ├── model │ │ │ │ ├── Album.kt │ │ │ │ ├── Artist.kt │ │ │ │ ├── CloudSong.kt │ │ │ │ ├── CommentThread.kt │ │ │ │ ├── Song.kt │ │ │ │ ├── SongUrl.kt │ │ │ │ ├── info │ │ │ │ │ ├── ChargeInfo.kt │ │ │ │ │ ├── Info.kt │ │ │ │ │ └── ResourceInfo.kt │ │ │ │ ├── privilege │ │ │ │ │ ├── FreeTimeTrialPrivilege.kt │ │ │ │ │ ├── FreeTrialPrivilege.kt │ │ │ │ │ └── Privilege.kt │ │ │ │ └── result │ │ │ │ │ ├── AlbumResult.kt │ │ │ │ │ ├── PersonalFMResult.kt │ │ │ │ │ ├── Result.kt │ │ │ │ │ ├── SearchHot.kt │ │ │ │ │ ├── SearchHotResult.kt │ │ │ │ │ ├── SearchResult.kt │ │ │ │ │ ├── SongDetailResult.kt │ │ │ │ │ ├── SongResult.kt │ │ │ │ │ ├── SongUrlResult.kt │ │ │ │ │ └── UserCloudResult.kt │ │ │ └── util │ │ │ │ └── NetWorkUtil.java │ │ │ └── wearbbs │ │ │ └── music │ │ │ ├── adapter │ │ │ ├── CommentAdapter.java │ │ │ ├── LocalMusicAdapter.java │ │ │ ├── MusicAdapter.java │ │ │ ├── MusicLibraryAdapter.java │ │ │ ├── SongAdapter.kt │ │ │ └── ViewPagerAdapter.java │ │ │ ├── api │ │ │ └── AppServiceApi.java │ │ │ ├── application │ │ │ └── MainApplication.java │ │ │ ├── event │ │ │ └── MessageEvent.java │ │ │ ├── fragment │ │ │ ├── ConsoleFragment.java │ │ │ ├── LyricsFragment.java │ │ │ └── PlayerFragment.java │ │ │ ├── service │ │ │ └── MusicPlayerService.java │ │ │ ├── ui │ │ │ ├── AboutActivity.java │ │ │ ├── CommentActivity.java │ │ │ ├── FeedbackActivity.java │ │ │ ├── FtpActivity.java │ │ │ ├── LocalMusicActivity.java │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MenuActivity.java │ │ │ ├── MusicLibraryActivity.java │ │ │ ├── MusicListActivity.java │ │ │ ├── MusicPanActivity.java │ │ │ ├── OpenSourceActivity.java │ │ │ ├── PlayListActivity.java │ │ │ ├── QRCodeActivity.java │ │ │ ├── ReplyActivity.java │ │ │ ├── SearchActivity.java │ │ │ ├── SettingsActivity.java │ │ │ ├── SlideBackActivity.java │ │ │ ├── UpdateActivity.java │ │ │ ├── UserProfileActivity.java │ │ │ └── ViewPictureActivity.java │ │ │ ├── util │ │ │ ├── DownloadUtil.java │ │ │ ├── QRCodeUtil.java │ │ │ ├── SharedPreferencesUtil.java │ │ │ └── ToastUtil.java │ │ │ └── view │ │ │ ├── LoadingView.java │ │ │ └── MessageView.java │ └── res │ │ ├── drawable-night │ │ ├── icon_play_order.png │ │ └── icon_play_repeat_one.png │ │ ├── drawable │ │ ├── btn_blue.xml │ │ ├── et_blue.xml │ │ ├── ic_baseline_android_24.xml │ │ ├── ic_baseline_arrow_back_24.xml │ │ ├── ic_baseline_arrow_back_ios_24.xml │ │ ├── ic_baseline_arrow_forward_ios_24.xml │ │ ├── ic_baseline_assignment_24.xml │ │ ├── ic_baseline_assignment_turned_in_24.xml │ │ ├── ic_baseline_brightness_24.xml │ │ ├── ic_baseline_brightness_24_black.xml │ │ ├── ic_baseline_build_24.xml │ │ ├── ic_baseline_check_24.xml │ │ ├── ic_baseline_cloud_24.xml │ │ ├── ic_baseline_cloud_download_24.xml │ │ ├── ic_baseline_desktop_windows_24.xml │ │ ├── ic_baseline_family_restroom_24.xml │ │ ├── ic_baseline_favorite_24.xml │ │ ├── ic_baseline_favorite_border_24.xml │ │ ├── ic_baseline_file_copy_24.xml │ │ ├── ic_baseline_library_music_24.xml │ │ ├── ic_baseline_lock_24.xml │ │ ├── ic_baseline_login_24.xml │ │ ├── ic_baseline_menu_24.xml │ │ ├── ic_baseline_message_24.xml │ │ ├── ic_baseline_music_video_24.xml │ │ ├── ic_baseline_pause_circle_filled_24.xml │ │ ├── ic_baseline_person_24.xml │ │ ├── ic_baseline_phone_android_24.xml │ │ ├── ic_baseline_photo_size_select_actual_24.xml │ │ ├── ic_baseline_play_circle_filled_24.xml │ │ ├── ic_baseline_playlist_play_24.xml │ │ ├── ic_baseline_radio_24.xml │ │ ├── ic_baseline_refresh_24.xml │ │ ├── ic_baseline_save_24.xml │ │ ├── ic_baseline_sd_storage_24.xml │ │ ├── ic_baseline_search_24.xml │ │ ├── ic_baseline_settings_24.xml │ │ ├── ic_baseline_share_24.xml │ │ ├── ic_baseline_star_24.xml │ │ ├── ic_baseline_supervised_user_circle_24.xml │ │ ├── ic_baseline_supervised_user_circle_primary_24.xml │ │ ├── ic_baseline_thumb_up_24.xml │ │ ├── ic_baseline_visibility_24.xml │ │ ├── ic_baseline_visibility_off_24.xml │ │ ├── ic_baseline_volume_down_24.xml │ │ ├── ic_baseline_volume_up_24.xml │ │ ├── ic_baseline_warning_24.xml │ │ ├── ic_launcher_foreground.xml │ │ ├── ic_outline_thumb_up_24.xml │ │ ├── icon_extra.png │ │ ├── icon_play_order.png │ │ ├── icon_play_repeat_one.png │ │ ├── icon_tip.png │ │ ├── middle_ss.xml │ │ ├── qrcode_group.png │ │ ├── qrcode_update.png │ │ ├── qrcode_update_dev.png │ │ ├── ripple_item_normal.xml │ │ ├── shape_progressbar_azure.xml │ │ └── shape_progressbar_loading.xml │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_comment.xml │ │ ├── activity_feedback.xml │ │ ├── activity_ftp.xml │ │ ├── activity_localmusic.xml │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_menu.xml │ │ ├── activity_music.xml │ │ ├── activity_opensource.xml │ │ ├── activity_qrcode.xml │ │ ├── activity_reply.xml │ │ ├── activity_search.xml │ │ ├── activity_settings.xml │ │ ├── activity_update.xml │ │ ├── activity_userprofile.xml │ │ ├── activity_viewpicture.xml │ │ ├── fragment_console.xml │ │ ├── fragment_lyrics.xml │ │ ├── fragment_player.xml │ │ ├── item_comment.xml │ │ ├── item_hot.xml │ │ ├── item_music.xml │ │ ├── view_loading.xml │ │ ├── view_message.xml │ │ ├── widget_musiclist_info.xml │ │ └── widget_reply.xml │ │ ├── mipmap │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ ├── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ └── root_preferences.xml │ └── test │ └── java │ └── cn │ └── wearbbs │ └── music │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | *.apk 17 | *.jks -------------------------------------------------------------------------------- /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | WearMusicPro -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 119 | 120 | 122 | 123 | -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/deploymentTargetDropDown.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 36 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | 34 | 35 | 39 | 40 | -------------------------------------------------------------------------------- /.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 |

WearMusic

3 |

音乐的力量,听你所想。

4 |

5 | 6 | 7 | 8 |

9 | 10 | 11 | 12 | 13 | ## 应用详情 14 | 15 | ### 应用简介 16 | 17 | 一款第三方网易云音乐客户端,支持歌词、音乐云盘、歌单、评论等功能。 18 | 19 | ### 预览图片 20 | 21 | ![预览图片](https://i.loli.net/2021/08/09/PZ8RA1GKEQ7N6rT.png) 22 | 23 | 24 | 25 | ## 立即使用 26 | 27 | ### 如何安装 28 | 29 | #### 手动安装 30 | 31 | - [官方网站](https://wmusic.wearbbs.cn) 32 | - [Github Release](https://github.com/JackuXL/WearMusic/releases) 33 | 34 | > 下载后,请前往 [WearBBS](https://wearbbs.cn) 查看 [ADB 教程](https://wearbbs.cn/threads/adb.110/),以将其安装至手表。 35 | 36 | 37 | ### 了解更多 38 | 39 | > 请前往 [WearBBS 资源中心](https://wearbbs.cn/resources/wearmusic.260/) 。 40 | 41 | 42 | 43 | ## 预览视频 44 | 45 | [手表网易云第三方客户端 WearMusic v3.0 - 从零重置 稳定护航每一首音乐!](https://www.bilibili.com/video/BV1Nv411T7PW/) 46 | 47 | 48 | 49 | ## 关于 50 | 51 | - 哔哩哔哩:@JackuXL 52 | - 软件交流群:133636679 53 | 54 | WearMusic 是完全开源且免费的。同时,维护后台的[API服务器](https://music.wearbbs.cn)和[更新服务器](https://wmu.wearbbs.cn)需要投入大量的资金,您可以自愿捐助我们,保证WearMusic 的正常更新、运行。 55 | 56 | 57 | 58 | 59 | ## Licence 60 | 61 | 本项目在 AGPL-3.0 许可证下开源,详情参阅 [LICENSE 文件](https://github.com/JackuXL/WearMusic/blob/master/LICENSE)。 62 | 63 | **但请注意,您必须同时遵守下面的额外限制:** 64 | **However, please note that you must also observe the following additional restrictions:** 65 | 66 | - 基于(修改)本项目所开发的新项目,必须保留本项目对包括但不限于特定设备、地域、人群的限制,包括源代码和二进制都必须遵守此限制。 67 | - Based on (modifying) the new project developed by this project must retain the limitations of this project including but not limited to specific devices, regions, and crowds, including source code and binary must comply with this restriction. 68 | - 基于(修改)本项目所开发的新项目,其许可证必须包含所有这里列出的额外限制条件。 69 | - Based on (modifying) the new project developed by this project, the license must include all additional restrictions listed here. 70 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | id 'kotlin-android' 4 | } 5 | android { 6 | compileSdkVersion 31 7 | buildToolsVersion "30.0.3" 8 | buildFeatures { 9 | viewBinding true 10 | } 11 | defaultConfig { 12 | applicationId "cn.wearbbs.music" 13 | minSdkVersion 21 14 | targetSdkVersion 31 15 | versionCode 13 16 | versionName "3.5.2" 17 | 18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 19 | 20 | } 21 | packagingOptions { 22 | resources { 23 | excludes += ['META-INF/DEPENDENCIES'] 24 | } 25 | } 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | namespace 'cn.wearbbs.music' 40 | } 41 | 42 | 43 | 44 | dependencies { 45 | implementation fileTree(dir: "libs", include: ["*.aar","*.jar"]) 46 | implementation 'androidx.core:core-ktx:1.7.0' 47 | implementation 'androidx.appcompat:appcompat:1.4.0' 48 | implementation 'com.google.android.material:material:1.4.0' 49 | implementation 'androidx.constraintlayout:constraintlayout:2.1.2' 50 | implementation "androidx.viewpager2:viewpager2:1.0.0" 51 | implementation 'androidx.preference:preference-ktx:1.1.1' 52 | implementation 'androidx.legacy:legacy-support-v4:1.0.0' 53 | testImplementation 'junit:junit:4.13.2' 54 | androidTestImplementation 'androidx.test.ext:junit:1.1.3' 55 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' 56 | implementation 'com.github.bumptech.glide:glide:4.12.0' 57 | implementation 'androidx.viewpager2:viewpager2:1.0.0' 58 | implementation 'androidx.recyclerview:recyclerview:1.2.1' 59 | implementation 'com.hyman:flowlayout-lib:1.1.2' 60 | implementation 'me.wcy:lrcview:2.2' 61 | implementation 'cn.carbs.android:ExpandableTextView:1.0.3' 62 | implementation 'me.panpf:sketch:2.7.1' 63 | implementation 'com.google.zxing:core:3.4.1' 64 | implementation 'com.alibaba:fastjson:1.2.79' 65 | 66 | def appCenterSdkVersion = '4.4.2' 67 | implementation "com.microsoft.appcenter:appcenter-analytics:${appCenterSdkVersion}" 68 | implementation "com.microsoft.appcenter:appcenter-crashes:${appCenterSdkVersion}" 69 | 70 | implementation 'org.greenrobot:eventbus:3.3.1' 71 | implementation 'com.android.support:percent:28.0.0' 72 | implementation 'com.github.youlookwhat:ByRecyclerView:1.1.6' 73 | implementation 'com.squareup.okhttp3:okhttp:4.9.3' 74 | implementation 'com.google.code.gson:gson:2.8.9' 75 | } -------------------------------------------------------------------------------- /app/libs/ftplet-api-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/libs/ftplet-api-1.1.1.jar -------------------------------------------------------------------------------- /app/libs/ftpserver-core-1.1.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/libs/ftpserver-core-1.1.1.jar -------------------------------------------------------------------------------- /app/libs/log4j-1.2.17.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/libs/log4j-1.2.17.jar -------------------------------------------------------------------------------- /app/libs/mina-core-2.0.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/libs/mina-core-2.0.16.jar -------------------------------------------------------------------------------- /app/libs/slf4j-api-1.6.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/libs/slf4j-api-1.6.2.jar -------------------------------------------------------------------------------- /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/release/output-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "artifactType": { 4 | "type": "APK", 5 | "kind": "Directory" 6 | }, 7 | "applicationId": "cn.wearbbs.music", 8 | "variantName": "release", 9 | "elements": [ 10 | { 11 | "type": "SINGLE", 12 | "filters": [], 13 | "attributes": [], 14 | "versionCode": 13, 15 | "versionName": "3.5.1", 16 | "outputFile": "app-release.apk" 17 | } 18 | ], 19 | "elementType": "File" 20 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/cn/wearbbs/music/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.platform.app.InstrumentationRegistry; 6 | import androidx.test.ext.junit.runners.AndroidJUnit4; 7 | 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | /** 14 | * Instrumented test, which will execute on an Android device. 15 | * 16 | * @see Testing documentation 17 | */ 18 | @RunWith(AndroidJUnit4.class) 19 | public class ExampleInstrumentedTest { 20 | @Test 21 | public void useAppContext() { 22 | // Context of the app under test. 23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); 24 | assertEquals("cn.wearbbs.music", appContext.getPackageName()); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 21 | 24 | 27 | 30 | 33 | 36 | 39 | 42 | 45 | 48 | 51 | 54 | 57 | 60 | 63 | 64 | 65 | 66 | 69 | 70 | 71 | 74 | 75 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/api/CloudSongApi.java: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.api; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import com.alibaba.fastjson.JSONObject; 6 | import com.google.gson.Gson; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import java.util.concurrent.LinkedTransferQueue; 11 | 12 | import cn.jackuxl.model.CloudSong; 13 | import cn.jackuxl.model.Song; 14 | import cn.jackuxl.model.SongUrl; 15 | import cn.jackuxl.model.result.PersonalFMResult; 16 | import cn.jackuxl.model.result.SongUrlResult; 17 | import cn.jackuxl.model.result.UserCloudResult; 18 | import cn.jackuxl.util.NetWorkUtil; 19 | 20 | public class CloudSongApi { 21 | private final String cookie; 22 | private final Gson gson = new Gson(); 23 | 24 | public CloudSongApi(String cookie) { 25 | this.cookie = cookie; 26 | } 27 | 28 | public List getSongList() { 29 | UserCloudResult result = gson.fromJson(NetWorkUtil.sendByGetUrl("/user/cloud", cookie), UserCloudResult.class); 30 | if(result.getCode()!=200){ 31 | return null; 32 | } 33 | List res = new ArrayList<>(); 34 | for(int i = 0;i data = gson.fromJson(NetWorkUtil.sendByGetUrl("/song/url?id=" + stringBuilder, cookie),SongUrlResult.class).getData(); 104 | String[] result = new String[data.size()]; 105 | for (int i = 0; i < data.size(); i++) { 106 | result[searchArrayForIndex(ids, String.valueOf(data.get(i).getId()))] = data.get(i).getUrl(); 107 | } 108 | return result; 109 | } 110 | 111 | private int searchArrayForIndex(String[] array, String str) { 112 | for (int i = 0; i < array.length; i++) { 113 | if (array[i].equals(str)) { 114 | return i; 115 | } 116 | } 117 | return -1; 118 | } 119 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/api/MVApi.java: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.api; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import cn.jackuxl.util.NetWorkUtil; 5 | 6 | public class MVApi { 7 | private final String cookie; 8 | 9 | public MVApi(String cookie) { 10 | this.cookie = cookie; 11 | } 12 | 13 | public String getMVUrl(String mvId) { 14 | String obj = NetWorkUtil.sendByGetUrl("/mv/url?id=" + mvId, cookie); 15 | return JSON.parseObject(obj).getJSONObject("data").getString("url"); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/api/MusicListApi.java: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.api; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONArray; 5 | import cn.jackuxl.util.NetWorkUtil; 6 | 7 | public class MusicListApi { 8 | private final String uid; 9 | private final String cookie; 10 | 11 | public MusicListApi(String uid, String cookie) { 12 | this.uid = uid; 13 | this.cookie = cookie; 14 | } 15 | 16 | /** 17 | * 获取喜欢音乐列表 18 | * 19 | * @return ids 20 | */ 21 | @Deprecated 22 | public String[] getLikeList() { 23 | JSONArray ids = JSON.parseObject(NetWorkUtil.sendByGetUrl("/likelist?uid=" + uid, cookie)).getJSONArray("ids"); 24 | String[] result = new String[ids.size()]; 25 | for (int i = 0; i < ids.size(); i++) { 26 | result[i] = ids.getString(i); 27 | } 28 | return result; 29 | } 30 | 31 | /** 32 | * 获取歌单列表 33 | * 34 | * @return data 35 | */ 36 | public JSONArray getMusicList() { 37 | return JSON.parseObject(NetWorkUtil.sendByGetUrl("/user/playlist?uid=" + uid, cookie)).getJSONArray("playlist"); 38 | } 39 | 40 | /** 41 | * 获取歌单详情 42 | * 43 | * @param id 歌单id 44 | * @return detail(ids) 45 | */ 46 | public String[] getMusicListDetail(String id) { 47 | JSONArray trackIds = JSON.parseObject(NetWorkUtil.sendByGetUrl("/playlist/detail?id=" + id, cookie)).getJSONObject("playlist").getJSONArray("trackIds"); 48 | String[] result = new String[trackIds.size()]; 49 | for (int i = 0; i < trackIds.size(); i++) { 50 | result[i] = trackIds.getJSONObject(i).getString("id"); 51 | } 52 | return result; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/Album.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model 2 | 3 | 4 | import cn.jackuxl.model.info.Info 5 | import com.google.gson.annotations.SerializedName 6 | 7 | 8 | data class Album( 9 | @SerializedName("alias") 10 | val alias: List, 11 | @SerializedName("artist") 12 | val artist: Artist, 13 | @SerializedName("artists") 14 | val artists: List, 15 | @SerializedName("blurPicUrl") 16 | val blurPicUrl: String, 17 | @SerializedName("briefDesc") 18 | val briefDesc: String, 19 | @SerializedName("commentThreadId") 20 | val commentThreadId: String, 21 | @SerializedName("company") 22 | val company: Any, 23 | @SerializedName("companyId") 24 | val companyId: Int, 25 | @SerializedName("copyrightId") 26 | val copyrightId: Int, 27 | @SerializedName("description") 28 | val description: String, 29 | @SerializedName("id") 30 | val id: Int, 31 | @SerializedName("info") 32 | val info: Info, 33 | @SerializedName("mark") 34 | val mark: Int, 35 | @SerializedName("name") 36 | val name: String, 37 | @SerializedName("onSale") 38 | val onSale: Boolean, 39 | @SerializedName("paid") 40 | val paid: Boolean, 41 | @SerializedName("pic") 42 | val pic: Long, 43 | @SerializedName("picId") 44 | val picId: Long, 45 | @SerializedName("picId_str") 46 | val picIdStr: String, 47 | @SerializedName("picUrl") 48 | val picUrl: String, 49 | @SerializedName("publishTime") 50 | val publishTime: Long, 51 | @SerializedName("size") 52 | val size: Int, 53 | @SerializedName("songs") 54 | val songs: List, 55 | @SerializedName("status") 56 | val status: Int, 57 | @SerializedName("subType") 58 | val subType: String, 59 | @SerializedName("tags") 60 | val tags: String, 61 | @SerializedName("type") 62 | val type: String 63 | ) 64 | 65 | -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/Artist.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class Artist( 7 | @SerializedName("albumSize") 8 | val albumSize: Int, 9 | @SerializedName("alias") 10 | val alias: List, 11 | @SerializedName("id") 12 | val id: Int, 13 | @SerializedName("img1v1") 14 | val img1v1: Int, 15 | @SerializedName("img1v1Url") 16 | val img1v1Url: String, 17 | @SerializedName("name") 18 | var name: String? = "未知", 19 | @SerializedName("picId") 20 | val picId: Long, 21 | @SerializedName("picUrl") 22 | val picUrl: Any, 23 | @SerializedName("trans") 24 | val trans: Any 25 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/CloudSong.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class CloudSong( 7 | @SerializedName("addTime") 8 | val addTime: Long, 9 | @SerializedName("album") 10 | val album: String, 11 | @SerializedName("artist") 12 | val artist: String, 13 | @SerializedName("bitrate") 14 | val bitrate: Int, 15 | @SerializedName("cover") 16 | val cover: Int, 17 | @SerializedName("coverId") 18 | val coverId: String, 19 | @SerializedName("fileName") 20 | val fileName: String, 21 | @SerializedName("fileSize") 22 | val fileSize: Int, 23 | @SerializedName("lyricId") 24 | val lyricId: String, 25 | @SerializedName("simpleSong") 26 | val simpleSong: Song, 27 | @SerializedName("songId") 28 | val songId: Int, 29 | @SerializedName("songName") 30 | val songName: String, 31 | @SerializedName("version") 32 | val version: Int 33 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/CommentThread.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model 2 | 3 | 4 | import cn.jackuxl.model.info.ResourceInfo 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class CommentThread( 8 | @SerializedName("commentCount") 9 | val commentCount: Int, 10 | @SerializedName("hotCount") 11 | val hotCount: Int, 12 | @SerializedName("id") 13 | val id: String, 14 | @SerializedName("latestLikedUsers") 15 | val latestLikedUsers: Any, 16 | @SerializedName("likedCount") 17 | val likedCount: Int, 18 | @SerializedName("resourceId") 19 | val resourceId: Int, 20 | @SerializedName("resourceInfo") 21 | val resourceInfo: ResourceInfo, 22 | @SerializedName("resourceOwnerId") 23 | val resourceOwnerId: Int, 24 | @SerializedName("resourceTitle") 25 | val resourceTitle: String, 26 | @SerializedName("resourceType") 27 | val resourceType: Int, 28 | @SerializedName("shareCount") 29 | val shareCount: Int 30 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/Song.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class Song( 7 | @SerializedName("album",alternate = ["al"]) 8 | val album: Album, 9 | @SerializedName("alias") 10 | val alias: List, 11 | @SerializedName("artists", alternate = ["ar"]) 12 | val artists: List, 13 | @SerializedName("copyrightId") 14 | val copyrightId: Int, 15 | @SerializedName("duration") 16 | val duration: Int, 17 | @SerializedName("fee") 18 | val fee: Int, 19 | @SerializedName("ftype") 20 | val ftype: Int, 21 | @SerializedName("id") 22 | val id: Int, 23 | @SerializedName("mark") 24 | val mark: Long, 25 | @SerializedName("mvid") 26 | val mvid: Int, 27 | @SerializedName("name") 28 | val name: String, 29 | @SerializedName("rUrl") 30 | val rUrl: Any, 31 | @SerializedName("rtype") 32 | val rtype: Int, 33 | @SerializedName("status") 34 | val status: Int 35 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/SongUrl.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model 2 | 3 | 4 | import cn.jackuxl.model.privilege.FreeTimeTrialPrivilege 5 | import cn.jackuxl.model.privilege.FreeTrialPrivilege 6 | import com.google.gson.annotations.SerializedName 7 | 8 | data class SongUrl( 9 | @SerializedName("br") 10 | val br: Int, 11 | @SerializedName("canExtend") 12 | val canExtend: Boolean, 13 | @SerializedName("code") 14 | val code: Int, 15 | @SerializedName("encodeType") 16 | val encodeType: String, 17 | @SerializedName("expi") 18 | val expi: Int, 19 | @SerializedName("fee") 20 | val fee: Int, 21 | @SerializedName("flag") 22 | val flag: Int, 23 | @SerializedName("freeTimeTrialPrivilege") 24 | val freeTimeTrialPrivilege: FreeTimeTrialPrivilege, 25 | @SerializedName("freeTrialInfo") 26 | val freeTrialInfo: Any, 27 | @SerializedName("freeTrialPrivilege") 28 | val freeTrialPrivilege: FreeTrialPrivilege, 29 | @SerializedName("gain") 30 | val gain: Float, 31 | @SerializedName("id") 32 | val id: Int, 33 | @SerializedName("level") 34 | val level: String, 35 | @SerializedName("md5") 36 | val md5: String, 37 | @SerializedName("payed") 38 | val payed: Int, 39 | @SerializedName("size") 40 | val size: Int, 41 | @SerializedName("type") 42 | val type: String, 43 | @SerializedName("uf") 44 | val uf: Any, 45 | @SerializedName("url") 46 | val url: String, 47 | @SerializedName("urlSource") 48 | val urlSource: Int 49 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/info/ChargeInfo.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.info 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class ChargeInfo( 7 | @SerializedName("chargeMessage") 8 | val chargeMessage: Any, 9 | @SerializedName("chargeType") 10 | val chargeType: Int, 11 | @SerializedName("chargeUrl") 12 | val chargeUrl: Any, 13 | @SerializedName("rate") 14 | val rate: Int 15 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/info/Info.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.info 2 | 3 | 4 | import cn.jackuxl.model.CommentThread 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class Info( 8 | @SerializedName("commentCount") 9 | val commentCount: Int, 10 | @SerializedName("commentThread") 11 | val commentThread: CommentThread, 12 | @SerializedName("comments") 13 | val comments: Any, 14 | @SerializedName("latestLikedUsers") 15 | val latestLikedUsers: Any, 16 | @SerializedName("liked") 17 | val liked: Boolean, 18 | @SerializedName("likedCount") 19 | val likedCount: Int, 20 | @SerializedName("resourceId") 21 | val resourceId: Int, 22 | @SerializedName("resourceType") 23 | val resourceType: Int, 24 | @SerializedName("shareCount") 25 | val shareCount: Int, 26 | @SerializedName("threadId") 27 | val threadId: String 28 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/info/ResourceInfo.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.info 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class ResourceInfo( 7 | @SerializedName("creator") 8 | val creator: Any, 9 | @SerializedName("encodedId") 10 | val encodedId: Any, 11 | @SerializedName("id") 12 | val id: Int, 13 | @SerializedName("imgUrl") 14 | val imgUrl: String, 15 | @SerializedName("name") 16 | val name: String, 17 | @SerializedName("subTitle") 18 | val subTitle: Any, 19 | @SerializedName("userId") 20 | val userId: Int, 21 | @SerializedName("webUrl") 22 | val webUrl: Any 23 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/privilege/FreeTimeTrialPrivilege.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.privilege 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class FreeTimeTrialPrivilege( 7 | @SerializedName("remainTime") 8 | val remainTime: Int, 9 | @SerializedName("resConsumable") 10 | val resConsumable: Boolean, 11 | @SerializedName("type") 12 | val type: Int, 13 | @SerializedName("userConsumable") 14 | val userConsumable: Boolean 15 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/privilege/FreeTrialPrivilege.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.privilege 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class FreeTrialPrivilege( 7 | @SerializedName("listenType") 8 | val listenType: Any, 9 | @SerializedName("resConsumable") 10 | val resConsumable: Boolean, 11 | @SerializedName("userConsumable") 12 | val userConsumable: Boolean 13 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/privilege/Privilege.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.privilege 2 | 3 | 4 | import cn.jackuxl.model.info.ChargeInfo 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class Privilege( 8 | @SerializedName("chargeInfoList") 9 | val chargeInfoList: List, 10 | @SerializedName("cp") 11 | val cp: Long, 12 | @SerializedName("cs") 13 | val cs: Boolean, 14 | @SerializedName("dl") 15 | val dl: Long, 16 | @SerializedName("downloadMaxbr") 17 | val downloadMaxbr: Long, 18 | @SerializedName("fee") 19 | val fee: Long, 20 | @SerializedName("fl") 21 | val fl: Long, 22 | @SerializedName("flag") 23 | val flag: Long, 24 | @SerializedName("freeTrialPrivilege") 25 | val freeTrialPrivilege: FreeTrialPrivilege, 26 | @SerializedName("id") 27 | val id: Long, 28 | @SerializedName("maxbr") 29 | val maxbr: Long, 30 | @SerializedName("payed") 31 | val payed: Long, 32 | @SerializedName("pl") 33 | val pl: Long, 34 | @SerializedName("playMaxbr") 35 | val playMaxbr: Long, 36 | @SerializedName("preSell") 37 | val preSell: Boolean, 38 | @SerializedName("rscl") 39 | val rscl: Long, 40 | @SerializedName("sp") 41 | val sp: Long, 42 | @SerializedName("st") 43 | val st: Long, 44 | @SerializedName("subp") 45 | val subp: Long, 46 | @SerializedName("toast") 47 | val toast: Boolean 48 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/AlbumResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import cn.jackuxl.model.Album 5 | import cn.jackuxl.model.Song 6 | import com.google.gson.annotations.SerializedName 7 | 8 | data class AlbumResult( 9 | @SerializedName("album") 10 | val album: Album, 11 | @SerializedName("code") 12 | val code: Int, 13 | @SerializedName("resourceState") 14 | val resourceState: Boolean, 15 | @SerializedName("songs") 16 | val songs: List 17 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/PersonalFMResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import cn.jackuxl.model.Song 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class PersonalFMResult( 8 | @SerializedName("code") 9 | val code: Int, 10 | @SerializedName("data") 11 | val data: List, 12 | @SerializedName("popAdjust") 13 | val popAdjust: Boolean 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/Result.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class Result( 7 | @SerializedName("hots") 8 | val hots: List 9 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/SearchHot.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class SearchHot( 7 | @SerializedName("first") 8 | val first: String, 9 | @SerializedName("iconType") 10 | val iconType: Int, 11 | @SerializedName("second") 12 | val second: Int, 13 | @SerializedName("third") 14 | val third: Any 15 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/SearchHotResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class SearchHotResult( 7 | @SerializedName("code") 8 | val code: Int, 9 | @SerializedName("result") 10 | val result: Result 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/SearchResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import com.google.gson.annotations.SerializedName 5 | 6 | data class SearchResult( 7 | @SerializedName("code") 8 | val code: Int, 9 | @SerializedName("result") 10 | val result: SongResult 11 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/SongDetailResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import cn.jackuxl.model.Song 5 | import cn.jackuxl.model.privilege.Privilege 6 | import com.google.gson.annotations.SerializedName 7 | 8 | data class SongDetailResult( 9 | @SerializedName("code") 10 | val code: Int, 11 | @SerializedName("privileges") 12 | val privileges: List, 13 | @SerializedName("songs") 14 | val songs: List 15 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/SongResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import cn.jackuxl.model.Song 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class SongResult( 8 | @SerializedName("hasMore") 9 | val hasMore: Boolean, 10 | @SerializedName("songCount") 11 | val songCount: Int, 12 | @SerializedName("songs") 13 | val songs: List 14 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/SongUrlResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import cn.jackuxl.model.SongUrl 5 | import com.google.gson.annotations.SerializedName 6 | 7 | data class SongUrlResult( 8 | @SerializedName("code") 9 | val code: Int, 10 | @SerializedName("data") 11 | val data: List 12 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/model/result/UserCloudResult.kt: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.model.result 2 | 3 | 4 | import cn.jackuxl.api.CloudSongApi 5 | import cn.jackuxl.model.CloudSong 6 | import com.google.gson.annotations.SerializedName 7 | 8 | data class UserCloudResult( 9 | @SerializedName("code") 10 | val code: Int, 11 | @SerializedName("count") 12 | val count: Int, 13 | @SerializedName("data") 14 | val data: List, 15 | @SerializedName("hasMore") 16 | val hasMore: Boolean, 17 | @SerializedName("maxSize") 18 | val maxSize: String, 19 | @SerializedName("size") 20 | val size: String, 21 | @SerializedName("upgradeSign") 22 | val upgradeSign: Int 23 | ) -------------------------------------------------------------------------------- /app/src/main/java/cn/jackuxl/util/NetWorkUtil.java: -------------------------------------------------------------------------------- 1 | package cn.jackuxl.util; 2 | 3 | import okhttp3.OkHttpClient; 4 | import okhttp3.Request; 5 | import okhttp3.Response; 6 | 7 | import java.io.IOException; 8 | import java.io.UnsupportedEncodingException; 9 | import java.net.URLEncoder; 10 | import java.util.Objects; 11 | 12 | /** 13 | * @author JackuXL 14 | */ 15 | public class NetWorkUtil { 16 | public static String domain; 17 | 18 | /** 19 | * 设置主域名 20 | * 添加在请求地址前 21 | * 22 | * @param domain 主域名(格式:http(s)://example.com) 23 | */ 24 | public static void setDomain(String domain) { 25 | NetWorkUtil.domain = domain; 26 | } 27 | 28 | /** 29 | * GET请求 30 | * 31 | * @param url 地址(删除主域名) 32 | * @param cookie Cookie(可为Null) 33 | * @return 请求结果 34 | */ 35 | public static String sendByGetUrl(String url, String cookie) { 36 | if (cookie == null) { 37 | cookie = ""; 38 | } 39 | String result; 40 | OkHttpClient client = new OkHttpClient().newBuilder().build(); 41 | Request request; 42 | request = new Request.Builder() 43 | .url(appendUrl(NetWorkUtil.domain + url,"cookie",cookie)) 44 | .build(); 45 | Response response; 46 | try { 47 | response = client.newCall(request).execute(); 48 | result = Objects.requireNonNull(response.body()).string(); 49 | return result; 50 | } catch (IOException e) { 51 | e.printStackTrace(); 52 | } 53 | return null; 54 | } 55 | /** 56 | * 在指定url后追加参数 57 | * @param url 地址 58 | * @return 处理后 Url 59 | */ 60 | private static String appendUrl(String url, String key,String value) { 61 | String newUrl = url; 62 | try { 63 | value = URLEncoder.encode(value,"utf-8"); 64 | } catch (UnsupportedEncodingException e) { 65 | e.printStackTrace(); 66 | } 67 | String paramStr = key + "=" + value; 68 | if (newUrl.contains("?")) { 69 | newUrl += "&" + paramStr; 70 | } else { 71 | newUrl += "?" + paramStr; 72 | } 73 | return newUrl; 74 | } 75 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/adapter/MusicLibraryAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.adapter; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.LinearLayout; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.NonNull; 13 | import androidx.recyclerview.widget.RecyclerView; 14 | 15 | import com.alibaba.fastjson.JSONArray; 16 | import com.alibaba.fastjson.JSONObject; 17 | import com.bumptech.glide.Glide; 18 | import com.bumptech.glide.load.resource.bitmap.RoundedCorners; 19 | import com.bumptech.glide.request.RequestOptions; 20 | 21 | import org.jetbrains.annotations.NotNull; 22 | 23 | import cn.wearbbs.music.R; 24 | import cn.wearbbs.music.ui.MusicListActivity; 25 | 26 | public class MusicLibraryAdapter extends RecyclerView.Adapter { 27 | private final JSONArray data; 28 | private final Activity activity; 29 | 30 | public MusicLibraryAdapter(JSONArray data, Activity activity) { 31 | this.data = data; 32 | this.activity = activity; 33 | } 34 | 35 | @NotNull 36 | @Override 37 | public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 38 | View view = LayoutInflater.from(activity).inflate(R.layout.item_music, parent, false); 39 | return new ViewHolder(view); 40 | } 41 | 42 | @Override 43 | public void onBindViewHolder(@NonNull final ViewHolder viewHolder, int position) { 44 | JSONObject playListDetail = data.getJSONObject(position); 45 | viewHolder.tv_title.setText(playListDetail.getString("name")); 46 | viewHolder.tv_artists.setText(String.format("by %s", playListDetail.getJSONObject("creator").getString("nickname"))); 47 | RequestOptions options = RequestOptions.bitmapTransform(new RoundedCorners(10)).placeholder(R.drawable.ic_baseline_photo_size_select_actual_24).error(R.drawable.ic_baseline_photo_size_select_actual_24); 48 | viewHolder.iv_cover.setImageResource(R.drawable.ic_baseline_photo_size_select_actual_24); 49 | try{ 50 | Glide.with(activity).load(playListDetail.getString("coverImgUrl").replace("http://", "https://")).apply(options).into(viewHolder.iv_cover); 51 | } 52 | catch (Exception ignored){ 53 | ignored.printStackTrace(); 54 | } 55 | viewHolder.ll_main.setOnClickListener(v -> { 56 | Intent intent = new Intent(activity, MusicListActivity.class); 57 | intent.putExtra("detail", playListDetail.toJSONString()); 58 | activity.startActivity(intent); 59 | }); 60 | 61 | } 62 | 63 | @Override 64 | public int getItemCount() { 65 | return data.size(); 66 | } 67 | 68 | static class ViewHolder extends RecyclerView.ViewHolder { 69 | ImageView iv_cover; 70 | TextView tv_title; 71 | TextView tv_artists; 72 | LinearLayout ll_main; 73 | 74 | public ViewHolder(@NonNull View itemView) { 75 | super(itemView); 76 | iv_cover = itemView.findViewById(R.id.iv_cover); 77 | tv_title = itemView.findViewById(R.id.tv_title); 78 | tv_artists = itemView.findViewById(R.id.tv_artists); 79 | ll_main = itemView.findViewById(R.id.ll_main); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/adapter/ViewPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.adapter; 2 | 3 | import android.content.Intent; 4 | 5 | import androidx.annotation.NonNull; 6 | import androidx.fragment.app.Fragment; 7 | import androidx.fragment.app.FragmentActivity; 8 | import androidx.viewpager2.adapter.FragmentStateAdapter; 9 | 10 | import cn.wearbbs.music.fragment.ConsoleFragment; 11 | import cn.wearbbs.music.fragment.LyricsFragment; 12 | import cn.wearbbs.music.fragment.PlayerFragment; 13 | 14 | public class ViewPagerAdapter extends FragmentStateAdapter { 15 | private final Intent intent; 16 | 17 | public ViewPagerAdapter(@NonNull FragmentActivity fragmentActivity, Intent intent) { 18 | super(fragmentActivity); 19 | this.intent = intent; 20 | } 21 | 22 | @NonNull 23 | @Override 24 | public Fragment createFragment(int position) { 25 | if (position == 0) { 26 | return PlayerFragment.newInstance(intent); 27 | } else if(position==1){ 28 | return LyricsFragment.newInstance(intent); 29 | } 30 | else{ 31 | return ConsoleFragment.newInstance(intent); 32 | } 33 | } 34 | 35 | @Override 36 | public int getItemCount() { 37 | return 3; 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/api/AppServiceApi.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.api; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.alibaba.fastjson.JSONObject; 5 | 6 | import java.net.URLEncoder; 7 | 8 | import cn.wearbbs.music.application.MainApplication; 9 | import cn.wearbbs.music.util.SharedPreferencesUtil; 10 | import cn.jackuxl.util.NetWorkUtil; 11 | 12 | public class AppServiceApi { 13 | public static JSONObject getLatestVersionInfo() { 14 | NetWorkUtil.setDomain(""); 15 | JSONObject result = JSON.parseObject(NetWorkUtil.sendByGetUrl("https://wmusic.wearbbs.cn/AppService/update.json", null)); 16 | resetServer(); 17 | return result; 18 | } 19 | 20 | public static boolean feedback(String content) { 21 | NetWorkUtil.setDomain(""); 22 | String token = "C6ZdL45yZgBjHCmk"; 23 | int code = 400; 24 | try{ 25 | code = JSON.parseObject(NetWorkUtil.sendByGetUrl(String.format("https://wmusic.wearbbs.cn/AppService/feedback/?token=%s&content=%s", token, URLEncoder.encode(content, "utf-8")), null)).getInteger("code"); 26 | } 27 | catch (Exception ignored){ } 28 | resetServer(); 29 | return code == 200; 30 | } 31 | 32 | public static void resetServer(){ 33 | NetWorkUtil.setDomain("https://api.wmusic.pro"); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/application/MainApplication.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.application; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Application; 5 | import android.content.Context; 6 | import android.content.pm.PackageInfo; 7 | import android.content.pm.PackageManager; 8 | 9 | import com.microsoft.appcenter.AppCenter; 10 | import com.microsoft.appcenter.analytics.Analytics; 11 | import com.microsoft.appcenter.crashes.Crashes; 12 | 13 | /** 14 | * @author JackuXL 15 | */ 16 | public class MainApplication extends Application { 17 | 18 | @SuppressLint("StaticFieldLeak") 19 | private static Context context; 20 | @Override 21 | public void onCreate() { 22 | super.onCreate(); 23 | AppCenter.start(this, "2d48333f-2592-446d-a227-7102be9ab4dd", Analytics.class, Crashes.class); 24 | context = getApplicationContext(); 25 | } 26 | public static Context getContext(){ 27 | return context; 28 | } 29 | // 获取应用版本号 30 | public static String getApplicationVersion() { 31 | try { 32 | PackageManager packageManager = context.getPackageManager(); 33 | PackageInfo packageInfo = packageManager.getPackageInfo(context.getPackageName(), 0); 34 | return packageInfo.versionName; 35 | } catch (Exception e) { 36 | e.printStackTrace(); 37 | return "0.0.0"; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/event/MessageEvent.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.event; 2 | 3 | public class MessageEvent { 4 | public final String msg; 5 | 6 | public MessageEvent(String msg) { 7 | this.msg = msg; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.TextView; 8 | 9 | import java.util.Locale; 10 | 11 | import cn.wearbbs.music.R; 12 | import cn.wearbbs.music.application.MainApplication; 13 | 14 | /** 15 | * 关于 16 | */ 17 | public class AboutActivity extends SlideBackActivity { 18 | 19 | @SuppressLint("StringFormatMatches") 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_about); 24 | TextView tv_content = findViewById(R.id.tv_content); 25 | tv_content.setText(String.format(getString(R.string.aboutContent),MainApplication.getApplicationVersion())); 26 | } 27 | 28 | @SuppressLint("NonConstantResourceId") 29 | public void onClick(View view) { 30 | switch (view.getId()) { 31 | case R.id.main_title: 32 | finish(); 33 | break; 34 | case R.id.ll_checkUpdate: 35 | startActivity(new Intent(AboutActivity.this, UpdateActivity.class)); 36 | break; 37 | case R.id.ll_feedback: 38 | startActivity(new Intent(AboutActivity.this, FeedbackActivity.class)); 39 | break; 40 | } 41 | 42 | } 43 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/CommentActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.recyclerview.widget.LinearLayoutManager; 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import com.alibaba.fastjson.JSONArray; 13 | 14 | import cn.jackuxl.api.SongApi; 15 | import cn.wearbbs.music.R; 16 | import cn.wearbbs.music.adapter.CommentAdapter; 17 | import cn.wearbbs.music.util.SharedPreferencesUtil; 18 | import cn.wearbbs.music.view.MessageView; 19 | 20 | public class CommentActivity extends AppCompatActivity { 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_comment); 26 | init(); 27 | } 28 | 29 | public void init(){ 30 | SongApi api = new SongApi(SharedPreferencesUtil.getString("cookie","")); 31 | RecyclerView rv_main = findViewById(R.id.rv_main); 32 | rv_main.setLayoutManager(new LinearLayoutManager(this)); 33 | new Thread(()->{ 34 | try{ 35 | JSONArray data = api.getHotComment(getIntent().getStringExtra("id")); 36 | runOnUiThread(()->{ 37 | findViewById(R.id.lv_loading).setVisibility(View.GONE); 38 | if(SharedPreferencesUtil.getString("cookie","").isEmpty()){ 39 | rv_main.setAdapter(new CommentAdapter(data,getIntent().getStringExtra("id"),this)); 40 | } 41 | else{ 42 | rv_main.setAdapter(new CommentAdapter(data,getIntent().getStringExtra("id"),this,getHeader())); 43 | } 44 | rv_main.setVisibility(View.VISIBLE); 45 | }); 46 | } 47 | catch (Exception e){ 48 | runOnUiThread(this::showErrorMessage); 49 | } 50 | }).start(); 51 | 52 | } 53 | public View getHeader(){ 54 | RecyclerView rv_main = findViewById(R.id.rv_main); 55 | View header = LayoutInflater.from(this).inflate(R.layout.widget_reply, rv_main, false); 56 | header.findViewById(R.id.tv_sendreply).setOnClickListener(v -> { 57 | startActivity(new Intent(CommentActivity.this,ReplyActivity.class).putExtra("id", getIntent().getStringExtra("id"))); 58 | }); 59 | return header; 60 | } 61 | 62 | public void onClick(View view){ 63 | finish(); 64 | } 65 | 66 | public void showErrorMessage(){ 67 | RecyclerView rv_main = findViewById(R.id.rv_main); 68 | rv_main.setVisibility(View.GONE); 69 | 70 | findViewById(R.id.lv_loading).setVisibility(View.GONE); 71 | 72 | MessageView mv_message = findViewById(R.id.mv_message); 73 | mv_message.setVisibility(View.VISIBLE); 74 | mv_message.setContent(MessageView.LOAD_FAILED, v -> { 75 | mv_message.setVisibility(View.GONE); 76 | init(); 77 | }); 78 | } 79 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/FeedbackActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.os.Looper; 7 | import android.view.View; 8 | import android.widget.EditText; 9 | import android.widget.Toast; 10 | 11 | import androidx.appcompat.app.AppCompatActivity; 12 | 13 | import com.alibaba.fastjson.JSONObject; 14 | 15 | import cn.wearbbs.music.R; 16 | import cn.wearbbs.music.api.AppServiceApi; 17 | import cn.wearbbs.music.util.ToastUtil; 18 | import cn.wearbbs.music.view.MessageView; 19 | 20 | public class FeedbackActivity extends AppCompatActivity { 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_feedback); 26 | init(); 27 | } 28 | 29 | @SuppressLint("NonConstantResourceId") 30 | public void onClick(View view) { 31 | switch (view.getId()) { 32 | case R.id.main_title: 33 | case R.id.main_title_info: 34 | finish(); 35 | break; 36 | case R.id.btn_submit: 37 | EditText et_content = findViewById(R.id.et_content); 38 | new Thread(()->{ 39 | if(AppServiceApi.feedback(et_content.getText().toString())){ 40 | ToastUtil.show(FeedbackActivity.this,"提交成功"); 41 | } 42 | else{ 43 | ToastUtil.show(FeedbackActivity.this,"提交失败"); 44 | } 45 | finish(); 46 | }).start(); 47 | break; 48 | } 49 | } 50 | 51 | public void init(){ 52 | findViewById(R.id.ll_info).setVisibility(View.VISIBLE); 53 | new Thread(()->{ 54 | JSONObject data = UpdateActivity.checkUpdate(); 55 | if(data==null){ 56 | runOnUiThread(this::showErrorMessage); 57 | } 58 | else if(data.getBoolean("needUpdate")){ 59 | ToastUtil.show(FeedbackActivity.this,"请先更新"); 60 | Intent intent = new Intent(FeedbackActivity.this, UpdateActivity.class); 61 | startActivity(intent); 62 | finish(); 63 | 64 | 65 | } 66 | else{ 67 | runOnUiThread(()->{ 68 | findViewById(R.id.ll_info).setVisibility(View.GONE); 69 | findViewById(R.id.ll_feedback).setVisibility(View.VISIBLE); 70 | }); 71 | } 72 | }).start(); 73 | } 74 | 75 | public void showErrorMessage(){ 76 | findViewById(R.id.lv_loading).setVisibility(View.GONE); 77 | 78 | MessageView mv_message = findViewById(R.id.mv_message); 79 | mv_message.setVisibility(View.VISIBLE); 80 | mv_message.setContent(MessageView.LOAD_FAILED, v -> { 81 | mv_message.setVisibility(View.GONE); 82 | init(); 83 | }); 84 | } 85 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/MenuActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.appcompat.app.AlertDialog; 11 | 12 | import com.alibaba.fastjson.JSON; 13 | import com.alibaba.fastjson.JSONObject; 14 | import com.bumptech.glide.Glide; 15 | import com.bumptech.glide.request.RequestOptions; 16 | 17 | import cn.jackuxl.api.UserApi; 18 | import cn.wearbbs.music.R; 19 | import cn.wearbbs.music.util.SharedPreferencesUtil; 20 | import cn.jackuxl.util.NetWorkUtil; 21 | 22 | /** 23 | * 菜单 24 | */ 25 | public class MenuActivity extends SlideBackActivity { 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_menu); 31 | JSONObject profile = SharedPreferencesUtil.getJSONObject("profile"); 32 | if (SharedPreferencesUtil.getJSONObject("profile").size() >= 5) { 33 | initUserItem(profile); 34 | } 35 | } 36 | 37 | @Override 38 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 39 | super.onActivityResult(requestCode, resultCode, data); 40 | if(resultCode == RESULT_OK){ 41 | switch (requestCode){ 42 | case 0: 43 | JSONObject profile = JSON.parseObject(data.getStringExtra("profile")); 44 | initUserItem(profile); 45 | startActivity(new Intent(MenuActivity.this,MenuActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)); 46 | finish(); 47 | break; 48 | case 1: 49 | startActivity(new Intent(MenuActivity.this,MenuActivity.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP).addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)); 50 | finish(); 51 | break; 52 | } 53 | } 54 | } 55 | 56 | public void initUserItem(JSONObject profile) { 57 | TextView tv_name = findViewById(R.id.tv_name); 58 | tv_name.setText(profile.getString("nickname")); 59 | TextView tv_id = findViewById(R.id.tv_id); 60 | tv_id.setText(String.format("ID:%s",profile.getString("userId"))); 61 | ImageView iv_avatar = findViewById(R.id.iv_avatar); 62 | RequestOptions options = RequestOptions.circleCropTransform().placeholder(R.drawable.ic_baseline_supervised_user_circle_24).error(R.drawable.ic_baseline_supervised_user_circle_24); 63 | Glide.with(MenuActivity.this).load(profile.getString("avatarUrl").replace("http://", "https://")).apply(options).into(iv_avatar); 64 | } 65 | 66 | @SuppressLint("NonConstantResourceId") 67 | public void onClick(View view) { 68 | switch (view.getId()) { 69 | case R.id.item_fm: 70 | startActivity(new Intent(MenuActivity.this, MainActivity.class) 71 | .putExtra("fm",true) 72 | .setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 73 | .addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)); 74 | break; 75 | case R.id.item_setting: 76 | startActivity(new Intent(MenuActivity.this, SettingsActivity.class)); 77 | break; 78 | case R.id.item_user: 79 | if (SharedPreferencesUtil.getJSONObject("profile").size() <= 5) { 80 | startActivityForResult(new Intent(MenuActivity.this, LoginActivity.class), 0); 81 | } else { 82 | startActivityForResult(new Intent(MenuActivity.this, UserProfileActivity.class), 1); 83 | } 84 | break; 85 | case R.id.item_search: 86 | startActivity(new Intent(MenuActivity.this, SearchActivity.class)); 87 | break; 88 | case R.id.item_musicPan: 89 | startActivity(new Intent(MenuActivity.this, MusicPanActivity.class)); 90 | break; 91 | case R.id.item_likeList: 92 | startActivity(new Intent(MenuActivity.this, MusicListActivity.class)); 93 | break; 94 | case R.id.item_musicLibrary: 95 | startActivity(new Intent(MenuActivity.this, MusicLibraryActivity.class)); 96 | break; 97 | case R.id.item_localmusic: 98 | startActivity(new Intent(MenuActivity.this, LocalMusicActivity.class)); 99 | break; 100 | } 101 | } 102 | 103 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/MusicLibraryActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import androidx.recyclerview.widget.LinearLayoutManager; 10 | import androidx.recyclerview.widget.RecyclerView; 11 | 12 | import com.alibaba.fastjson.JSONArray; 13 | 14 | import cn.jackuxl.api.MusicListApi; 15 | import cn.wearbbs.music.R; 16 | import cn.wearbbs.music.adapter.MusicLibraryAdapter; 17 | import cn.wearbbs.music.util.SharedPreferencesUtil; 18 | import cn.wearbbs.music.view.LoadingView; 19 | import cn.wearbbs.music.view.MessageView; 20 | 21 | /** 22 | * 歌单列表 23 | */ 24 | public class MusicLibraryActivity extends AppCompatActivity { 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_music); 30 | TextView tv_title = findViewById(R.id.tv_title); 31 | tv_title.setText(getString(R.string.musicLibrary)); 32 | init(); 33 | } 34 | 35 | public void init(){ 36 | MusicListApi api = new MusicListApi(SharedPreferencesUtil.getJSONObject("profile").getString("userId"), SharedPreferencesUtil.getString("cookie", "")); 37 | LoadingView lv_loading = findViewById(R.id.lv_loading); 38 | RecyclerView rv_main = findViewById(R.id.rv_main); 39 | MessageView mv_message = findViewById(R.id.mv_message); 40 | if(SharedPreferencesUtil.getString("cookie", "").isEmpty()){ 41 | showNoLoginMessage(); 42 | } 43 | else{ 44 | new Thread(() -> { 45 | try{ 46 | lv_loading.setVisibility(View.VISIBLE); 47 | rv_main.setVisibility(View.GONE); 48 | JSONArray data = api.getMusicList(); 49 | runOnUiThread(() -> { 50 | if(data==null){ 51 | showErrorMessage(); 52 | } 53 | else if (data.size() == 0) { 54 | rv_main.setVisibility(View.GONE); 55 | mv_message.setVisibility(View.VISIBLE); 56 | mv_message.setContent(MessageView.NO_LOGIN,null); 57 | } else { 58 | rv_main.setLayoutManager(new LinearLayoutManager(this)); 59 | rv_main.setAdapter(new MusicLibraryAdapter(data, this)); 60 | lv_loading.setVisibility(View.GONE); 61 | rv_main.setVisibility(View.VISIBLE); 62 | } 63 | }); 64 | } 65 | catch (Exception e){ 66 | runOnUiThread(this::showErrorMessage); 67 | } 68 | }).start(); 69 | } 70 | } 71 | 72 | public void onClick(View view) { 73 | Intent intent = new Intent(MusicLibraryActivity.this, MenuActivity.class); 74 | intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 75 | startActivity(intent); 76 | finish(); 77 | } 78 | 79 | public void showNoLoginMessage() { 80 | RecyclerView rv_main = findViewById(R.id.rv_main); 81 | rv_main.setVisibility(View.GONE); 82 | MessageView mv_message = findViewById(R.id.mv_message); 83 | mv_message.setVisibility(View.VISIBLE); 84 | mv_message.setContent(MessageView.NO_LOGIN,null); 85 | } 86 | 87 | public void showErrorMessage(){ 88 | RecyclerView rv_main = findViewById(R.id.rv_main); 89 | MessageView mv_message = findViewById(R.id.mv_message); 90 | rv_main.setVisibility(View.GONE); 91 | mv_message.setVisibility(View.VISIBLE); 92 | mv_message.setContent(MessageView.LOAD_FAILED, v -> { 93 | mv_message.setVisibility(View.GONE); 94 | init(); 95 | }); 96 | } 97 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/MusicPanActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import androidx.recyclerview.widget.LinearLayoutManager; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | 11 | import com.alibaba.fastjson.JSONArray; 12 | 13 | import java.util.List; 14 | 15 | import cn.jackuxl.api.CloudSongApi; 16 | import cn.jackuxl.model.Song; 17 | import cn.wearbbs.music.R; 18 | import cn.wearbbs.music.adapter.MusicAdapter; 19 | import cn.wearbbs.music.adapter.SongAdapter; 20 | import cn.wearbbs.music.util.SharedPreferencesUtil; 21 | import cn.wearbbs.music.view.LoadingView; 22 | import cn.wearbbs.music.view.MessageView; 23 | import me.jingbin.library.ByRecyclerView; 24 | 25 | /** 26 | * 音乐云盘 27 | */ 28 | public class MusicPanActivity extends SlideBackActivity { 29 | 30 | @Override 31 | protected void onCreate(Bundle savedInstanceState) { 32 | super.onCreate(savedInstanceState); 33 | setContentView(R.layout.activity_music); 34 | TextView tv_title = findViewById(R.id.tv_title); 35 | tv_title.setText(getString(R.string.musicPan)); 36 | init(); 37 | } 38 | 39 | public void init() { 40 | if(SharedPreferencesUtil.getString("cookie", "").isEmpty()){ 41 | showNoLoginMessage(); 42 | } 43 | else{ 44 | CloudSongApi api = new CloudSongApi(SharedPreferencesUtil.getString("cookie", "")); 45 | LoadingView lv_loading = findViewById(R.id.lv_loading); 46 | ByRecyclerView rv_main = findViewById(R.id.rv_main); 47 | MessageView mv_message = findViewById(R.id.mv_message); 48 | new Thread(() -> { 49 | try{ 50 | lv_loading.setVisibility(View.VISIBLE); 51 | rv_main.setVisibility(View.GONE); 52 | List data = api.getSongList(); 53 | runOnUiThread(() -> { 54 | System.out.println(data); 55 | if (data.size() == 0) { 56 | rv_main.setVisibility(View.GONE); 57 | mv_message.setVisibility(View.VISIBLE); 58 | mv_message.setContent(MessageView.NO_MUSIC,null); 59 | } else { 60 | rv_main.setLayoutManager(new LinearLayoutManager(this)); 61 | rv_main.setAdapter(new SongAdapter(data, this)); 62 | rv_main.setOnRefreshListener(() -> { 63 | // 刷新完成 64 | //rv_main.setRefreshing(false); 65 | }); 66 | lv_loading.setVisibility(View.GONE); 67 | rv_main.setVisibility(View.VISIBLE); 68 | } 69 | }); 70 | } 71 | catch (Exception e){ 72 | runOnUiThread(()->{ 73 | mv_message.setVisibility(View.VISIBLE); 74 | mv_message.setContent(MessageView.LOAD_FAILED, v -> { 75 | mv_message.setVisibility(View.GONE); 76 | init(); 77 | }); 78 | }); 79 | } 80 | }).start(); 81 | } 82 | } 83 | 84 | public void showNoLoginMessage() { 85 | RecyclerView rv_main = findViewById(R.id.rv_main); 86 | rv_main.setVisibility(View.GONE); 87 | MessageView mv_message = findViewById(R.id.mv_message); 88 | mv_message.setVisibility(View.VISIBLE); 89 | mv_message.setContent(MessageView.NO_LOGIN,null); 90 | } 91 | 92 | public void onClick(View view) { 93 | Intent intent = new Intent(MusicPanActivity.this, MenuActivity.class); 94 | intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 95 | startActivity(intent); 96 | finish(); 97 | } 98 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/OpenSourceActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | import cn.wearbbs.music.R; 9 | 10 | public class OpenSourceActivity extends AppCompatActivity { 11 | 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_opensource); 16 | } 17 | 18 | public void onClick(View view){ 19 | finish(); 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/PlayListActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | 7 | import androidx.appcompat.app.AppCompatActivity; 8 | import androidx.recyclerview.widget.LinearLayoutManager; 9 | import androidx.recyclerview.widget.RecyclerView; 10 | 11 | import com.alibaba.fastjson.JSON; 12 | import com.alibaba.fastjson.JSONArray; 13 | 14 | import org.greenrobot.eventbus.EventBus; 15 | import org.greenrobot.eventbus.Subscribe; 16 | import org.greenrobot.eventbus.ThreadMode; 17 | 18 | import cn.wearbbs.music.R; 19 | import cn.wearbbs.music.adapter.LocalMusicAdapter; 20 | import cn.wearbbs.music.adapter.MusicAdapter; 21 | import cn.wearbbs.music.event.MessageEvent; 22 | import cn.wearbbs.music.view.LoadingView; 23 | 24 | public class PlayListActivity extends AppCompatActivity { 25 | private JSONArray data; 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_music); 30 | EventBus.getDefault().register(this); 31 | TextView tv_title = findViewById(R.id.tv_title); 32 | tv_title.setText(R.string.playList); 33 | } 34 | public void init(){ 35 | LoadingView lv_loading = findViewById(R.id.lv_loading); 36 | RecyclerView rv_main = findViewById(R.id.rv_main); 37 | rv_main.setLayoutManager(new LinearLayoutManager(this)); 38 | if(getIntent().getBooleanExtra("local",false)){ 39 | rv_main.setAdapter(new LocalMusicAdapter(data, this)); 40 | } 41 | else{ 42 | rv_main.setAdapter(new MusicAdapter(data, this)); 43 | } 44 | lv_loading.setVisibility(View.GONE); 45 | rv_main.setVisibility(View.VISIBLE); 46 | } 47 | 48 | public void onClick(View view){ 49 | finish(); 50 | } 51 | 52 | @Subscribe(threadMode = ThreadMode.MAIN,sticky = true) 53 | public void onMessageEvent(MessageEvent event){ 54 | data = JSON.parseArray(event.msg); 55 | init(); 56 | } 57 | 58 | @Override 59 | protected void onDestroy() { 60 | super.onDestroy(); 61 | //移除所有的黏性事件 62 | EventBus.getDefault().removeAllStickyEvents(); 63 | //避免内存泄漏,防止OOM,优化内存 64 | EventBus.getDefault().unregister(this); 65 | } 66 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/QRCodeActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.ImageView; 6 | 7 | import androidx.appcompat.app.AppCompatActivity; 8 | 9 | import cn.wearbbs.music.R; 10 | import cn.wearbbs.music.util.QRCodeUtil; 11 | 12 | public class QRCodeActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_qrcode); 18 | ImageView iv_qrcode = findViewById(R.id.iv_qrcode); 19 | iv_qrcode.setImageBitmap(QRCodeUtil.createQRCodeBitmap(getIntent().getStringExtra("url"), 512, 512)); 20 | } 21 | 22 | public void onClick(View view) { 23 | finish(); 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/ReplyActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Bundle; 5 | import android.os.Looper; 6 | import android.view.View; 7 | import android.widget.EditText; 8 | import android.widget.Toast; 9 | 10 | import androidx.appcompat.app.AppCompatActivity; 11 | 12 | import cn.jackuxl.api.SongApi; 13 | import cn.wearbbs.music.R; 14 | import cn.wearbbs.music.util.SharedPreferencesUtil; 15 | 16 | public class ReplyActivity extends AppCompatActivity { 17 | 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | setContentView(R.layout.activity_reply); 22 | } 23 | 24 | @SuppressLint("NonConstantResourceId") 25 | public void onClick(View view){ 26 | switch (view.getId()){ 27 | case R.id.main_title: 28 | finish(); 29 | break; 30 | case R.id.btn_send: 31 | EditText et_content = findViewById(R.id.et_content); 32 | new Thread(()->{ 33 | Looper.prepare(); 34 | if(new SongApi(SharedPreferencesUtil.getString("cookie","")).sendComment(getIntent().getStringExtra("id"),et_content.getText().toString())){ 35 | Toast.makeText(this,"发送成功",Toast.LENGTH_SHORT).show(); 36 | finish(); 37 | } 38 | else{ 39 | Toast.makeText(this,"发送失败",Toast.LENGTH_SHORT).show(); 40 | } 41 | Looper.loop(); 42 | }).start(); 43 | break; 44 | 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | 7 | import androidx.appcompat.app.ActionBar; 8 | import androidx.appcompat.app.AppCompatDelegate; 9 | import androidx.preference.Preference; 10 | import androidx.preference.PreferenceFragmentCompat; 11 | 12 | import cn.wearbbs.music.R; 13 | import cn.jackuxl.util.NetWorkUtil; 14 | 15 | /** 16 | * 设置 17 | */ 18 | public class SettingsActivity extends SlideBackActivity { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState) { 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.activity_settings); 24 | if (savedInstanceState == null) { 25 | getSupportFragmentManager() 26 | .beginTransaction() 27 | .replace(R.id.settings, new SettingsFragment()) 28 | .commit(); 29 | } 30 | ActionBar actionBar = getSupportActionBar(); 31 | if (actionBar != null) { 32 | actionBar.setDisplayHomeAsUpEnabled(true); 33 | } 34 | } 35 | 36 | public void onClick(View view) { 37 | Intent intent = new Intent(SettingsActivity.this, MenuActivity.class); 38 | intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); 39 | startActivity(intent); 40 | finish(); 41 | } 42 | 43 | public static class SettingsFragment extends PreferenceFragmentCompat { 44 | @Override 45 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 46 | setPreferencesFromResource(R.xml.root_preferences, rootKey); 47 | findPreference("about").setOnPreferenceClickListener(preference -> { 48 | startActivity(new Intent(getActivity(), AboutActivity.class)); 49 | return true; 50 | }); 51 | 52 | findPreference("dark").setOnPreferenceChangeListener((preference, newValue) -> { 53 | if ((Boolean) newValue) { 54 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES); 55 | } else { 56 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO); 57 | } 58 | return true; 59 | }); 60 | 61 | findPreference("opensource").setOnPreferenceClickListener(preference -> { 62 | startActivity(new Intent(getActivity(), OpenSourceActivity.class)); 63 | return true; 64 | }); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/SlideBackActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.view.MotionEvent; 4 | import android.view.VelocityTracker; 5 | 6 | import androidx.appcompat.app.AppCompatActivity; 7 | 8 | /** 9 | * 支持滑动返回 10 | * 继承该Activity则支持滑动返回 11 | */ 12 | public class SlideBackActivity extends AppCompatActivity { 13 | 14 | //手指上下滑动时的最小速度 15 | private static final int YSPEED_MIN = 1000; 16 | 17 | //手指向右滑动时的最小距离 18 | private static final int XDISTANCE_MIN = 50; 19 | 20 | //手指向上滑或下滑时的最小距离 21 | private static final int YDISTANCE_MIN = 100; 22 | 23 | //记录手指按下时的横坐标。 24 | private float xDown; 25 | 26 | //记录手指按下时的纵坐标。 27 | private float yDown; 28 | 29 | //用于计算手指滑动的速度。 30 | private VelocityTracker mVelocityTracker; 31 | 32 | @Override 33 | public boolean dispatchTouchEvent(MotionEvent event) { 34 | createVelocityTracker(event); 35 | switch (event.getAction()) { 36 | case MotionEvent.ACTION_DOWN: 37 | xDown = event.getRawX(); 38 | yDown = event.getRawY(); 39 | break; 40 | case MotionEvent.ACTION_MOVE: 41 | //记录手指移动时的横坐标。 42 | float xMove = event.getRawX(); 43 | //记录手指移动时的纵坐标。 44 | float yMove = event.getRawY(); 45 | //滑动的距离 46 | int distanceX = (int) (xMove - xDown); 47 | int distanceY = (int) (yMove - yDown); 48 | //获取顺时速度 49 | int ySpeed = getScrollVelocity(); 50 | //关闭Activity需满足以下条件: 51 | //1.x轴滑动的距离>XDISTANCE_MIN 52 | //2.y轴滑动的距离在YDISTANCE_MIN范围内 53 | //3.y轴上(即上下滑动的速度) XDISTANCE_MIN && (distanceY < YDISTANCE_MIN && distanceY > -YDISTANCE_MIN) && ySpeed < YSPEED_MIN) { 55 | finish(); 56 | overridePendingTransition(android.R.anim.slide_in_left, android.R.anim.slide_out_right); 57 | } 58 | break; 59 | case MotionEvent.ACTION_UP: 60 | recycleVelocityTracker(); 61 | break; 62 | default: 63 | break; 64 | } 65 | return super.dispatchTouchEvent(event); 66 | } 67 | 68 | @Override 69 | public void finish() { 70 | super.finish(); 71 | 72 | } 73 | 74 | /** 75 | * 创建VelocityTracker对象,并将触摸界面的滑动事件加入到VelocityTracker当中。 76 | * 77 | * @param event 事件 78 | */ 79 | private void createVelocityTracker(MotionEvent event) { 80 | if (mVelocityTracker == null) { 81 | mVelocityTracker = VelocityTracker.obtain(); 82 | } 83 | mVelocityTracker.addMovement(event); 84 | } 85 | 86 | /** 87 | * 回收VelocityTracker对象。 88 | */ 89 | private void recycleVelocityTracker() { 90 | mVelocityTracker.recycle(); 91 | mVelocityTracker = null; 92 | } 93 | 94 | /** 95 | * 获取滑动速度 96 | * 97 | * @return 滑动速度,以每秒钟移动了多少像素值为单位。 98 | */ 99 | private int getScrollVelocity() { 100 | mVelocityTracker.computeCurrentVelocity(1000); 101 | int velocity = (int) mVelocityTracker.getYVelocity(); 102 | return Math.abs(velocity); 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/UpdateActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.widget.ImageView; 7 | import android.widget.TextView; 8 | 9 | import androidx.appcompat.app.AppCompatActivity; 10 | 11 | import com.alibaba.fastjson.JSONObject; 12 | 13 | import cn.wearbbs.music.R; 14 | import cn.wearbbs.music.api.AppServiceApi; 15 | import cn.wearbbs.music.application.MainApplication; 16 | import cn.wearbbs.music.util.SharedPreferencesUtil; 17 | import cn.wearbbs.music.view.MessageView; 18 | 19 | /** 20 | * 更新 21 | */ 22 | public class UpdateActivity extends AppCompatActivity { 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_update); 28 | init(); 29 | } 30 | 31 | public void onClick(View view) { 32 | finish(); 33 | } 34 | 35 | @SuppressLint("StringFormatMatches") 36 | public void init() { 37 | findViewById(R.id.lv_loading).setVisibility(View.VISIBLE); 38 | new Thread(()->{ 39 | JSONObject data = checkUpdate(); 40 | if(data == null){ 41 | runOnUiThread(this::showErrorMessage); 42 | } 43 | else if(data.getBoolean("needUpdate")){ 44 | runOnUiThread(() -> { 45 | findViewById(R.id.lv_loading).setVisibility(View.GONE); 46 | findViewById(R.id.sv_needUpdate).setVisibility(View.VISIBLE); 47 | TextView tv_hint = findViewById(R.id.tv_hint); 48 | tv_hint.setText(String.format(getString(R.string.needUpdateHint),data.getString("currentVersion"),data.getString("latestVersion"))); 49 | TextView tv_changeLog = findViewById(R.id.tv_changeLog); 50 | tv_changeLog.setText(data.getString("changeLog")); 51 | }); 52 | } 53 | else{ 54 | runOnUiThread(() -> { 55 | findViewById(R.id.lv_loading).setVisibility(View.GONE); 56 | findViewById(R.id.sv_noUpdate).setVisibility(View.VISIBLE); 57 | TextView tv_hint_no = findViewById(R.id.tv_hint_no); 58 | tv_hint_no.setText(String.format(getString(R.string.noUpdateHint),data.getString("currentVersion"),data.getString("latestVersion"))); 59 | }); 60 | } 61 | }).start(); 62 | } 63 | 64 | /** 65 | * 检查更新(已知BUG:如果服务器返回的版本号不是数字,会导致崩溃) 66 | * @return 是否需要更新 67 | */ 68 | public static JSONObject checkUpdate() { 69 | double latestVersion; 70 | double currentVersion; 71 | try{ 72 | JSONObject info; 73 | info = AppServiceApi.getLatestVersionInfo(); 74 | latestVersion = info.getDouble("latestVersion"); 75 | currentVersion = Double.parseDouble(MainApplication.getApplicationVersion()); 76 | info.put("needUpdate",currentVersion < latestVersion); 77 | info.put("currentVersion",currentVersion); 78 | return info; 79 | } 80 | catch (Exception e){ 81 | return null; 82 | } 83 | 84 | } 85 | 86 | public void showErrorMessage(){ 87 | findViewById(R.id.lv_loading).setVisibility(View.GONE); 88 | findViewById(R.id.tv_dev_no).setVisibility(View.GONE); 89 | findViewById(R.id.tv_dev).setVisibility(View.GONE); 90 | 91 | MessageView mv_message = findViewById(R.id.mv_message); 92 | mv_message.setVisibility(View.VISIBLE); 93 | mv_message.setContent(MessageView.LOAD_FAILED, v -> { 94 | mv_message.setVisibility(View.GONE); 95 | init(); 96 | }); 97 | } 98 | 99 | @Override 100 | public void finish(){ 101 | AppServiceApi.resetServer(); 102 | super.finish(); 103 | } 104 | 105 | @Override 106 | public void onDestroy(){ 107 | AppServiceApi.resetServer(); 108 | super.onDestroy(); 109 | } 110 | 111 | 112 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/UserProfileActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.View; 7 | import android.widget.ImageView; 8 | import android.widget.TextView; 9 | 10 | import androidx.appcompat.app.AlertDialog; 11 | import androidx.appcompat.app.AppCompatActivity; 12 | 13 | import com.alibaba.fastjson.JSON; 14 | import com.alibaba.fastjson.JSONObject; 15 | import com.bumptech.glide.Glide; 16 | import com.bumptech.glide.load.resource.bitmap.CircleCrop; 17 | import com.bumptech.glide.request.RequestOptions; 18 | 19 | import cn.jackuxl.api.UserApi; 20 | import cn.wearbbs.music.R; 21 | import cn.wearbbs.music.util.SharedPreferencesUtil; 22 | 23 | public class UserProfileActivity extends AppCompatActivity { 24 | private JSONObject profile; 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_userprofile); 29 | profile = JSON.parseObject(getIntent().getStringExtra("profile")); 30 | if(profile == null){ 31 | profile = SharedPreferencesUtil.getJSONObject("profile"); 32 | } 33 | else{ 34 | findViewById(R.id.btn_logout).setVisibility(View.GONE); 35 | } 36 | init(); 37 | } 38 | 39 | public void init(){ 40 | ImageView iv_avatar = findViewById(R.id.iv_avatar); 41 | RequestOptions options = RequestOptions.bitmapTransform(new CircleCrop()).placeholder(R.drawable.ic_baseline_photo_size_select_actual_24).error(R.drawable.ic_baseline_photo_size_select_actual_24); 42 | Glide.with(this).load(profile.getString("avatarUrl").replace("http://","https://")).apply(options).into(iv_avatar); 43 | iv_avatar.setOnClickListener(v -> startActivity(new Intent(this, ViewPictureActivity.class).putExtra("url", profile.getString("avatarUrl").replace("http://", "https://")))); 44 | 45 | TextView tv_name = findViewById(R.id.tv_name); 46 | tv_name.setText(profile.getString("nickname")); 47 | 48 | TextView tv_id = findViewById(R.id.tv_id); 49 | tv_id.setText(String.format("ID:%s",profile.getString("userId"))); 50 | } 51 | 52 | @SuppressLint("NonConstantResourceId") 53 | public void onClick(View view) { 54 | switch (view.getId()) { 55 | case R.id.main_title: 56 | finish(); 57 | break; 58 | case R.id.btn_logout: 59 | // 退出登录 60 | new AlertDialog.Builder(this) 61 | .setTitle("提示") 62 | .setMessage("确定要退出登录吗") 63 | .setPositiveButton("确定", (dialogInterface, i) -> { 64 | UserApi api = new UserApi(); 65 | api.setCookie(SharedPreferencesUtil.getString("cookie", "")); 66 | new Thread(() -> { 67 | api.logout(); 68 | SharedPreferencesUtil.remove("cookie"); 69 | SharedPreferencesUtil.remove("profile"); 70 | setResult(RESULT_OK, new Intent().putExtra("logout",true)); 71 | finish(); 72 | }).start(); 73 | }) 74 | .setNegativeButton("手滑了", (dialogInterface, i) -> dialogInterface.dismiss()) 75 | .create().show(); 76 | break; 77 | case R.id.iv_avatar: 78 | break; 79 | } 80 | 81 | } 82 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/ui/ViewPictureActivity.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.ui; 2 | 3 | import android.Manifest; 4 | import android.annotation.SuppressLint; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.graphics.Bitmap; 8 | import android.net.Uri; 9 | import android.os.Build; 10 | import android.os.Bundle; 11 | import android.os.Environment; 12 | import android.os.Looper; 13 | import android.view.View; 14 | import android.widget.Toast; 15 | 16 | import androidx.annotation.NonNull; 17 | import androidx.appcompat.app.AppCompatActivity; 18 | 19 | import com.bumptech.glide.Glide; 20 | 21 | import java.io.BufferedOutputStream; 22 | import java.io.File; 23 | import java.io.FileOutputStream; 24 | import java.io.IOException; 25 | import java.util.UUID; 26 | 27 | import cn.wearbbs.music.R; 28 | import me.panpf.sketch.SketchImageView; 29 | 30 | public class ViewPictureActivity extends AppCompatActivity { 31 | private static String mSaveMessage = "保存失败"; 32 | 33 | @Override 34 | protected void onCreate(Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_viewpicture); 37 | String url = getIntent().getStringExtra("url"); 38 | SketchImageView sketchImageView = findViewById(R.id.iv_pic); 39 | sketchImageView.displayImage(url); 40 | sketchImageView.setZoomEnabled(true); 41 | } 42 | 43 | @SuppressLint("NonConstantResourceId") 44 | public void onClick(View view){ 45 | switch (view.getId()){ 46 | case R.id.ll_return: 47 | finish(); 48 | break; 49 | case R.id.ll_save: 50 | checkPermissionForSave(); 51 | break; 52 | } 53 | } 54 | 55 | public void savePicture(String photoUrl) { 56 | new Thread(()->{ 57 | Bitmap bitmap = null; 58 | try { 59 | bitmap = Glide.with(ViewPictureActivity.this) 60 | .asBitmap() 61 | .load(photoUrl) 62 | .submit(512, 512).get(); 63 | if(saveFile(bitmap)){ 64 | mSaveMessage = "保存成功"; 65 | } 66 | else{ 67 | mSaveMessage = "保存失败"; 68 | } 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | Looper.prepare(); 73 | Toast.makeText(this, mSaveMessage, Toast.LENGTH_SHORT).show(); 74 | Looper.loop(); 75 | }).start(); 76 | } 77 | 78 | public boolean saveFile(Bitmap bm) throws IOException { 79 | File dirFile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/"); 80 | if (!dirFile.exists()) { 81 | if(!dirFile.mkdir()){ 82 | return false; 83 | } 84 | } 85 | 86 | //图片命名 87 | String fileName = UUID.randomUUID().toString() + ".jpg"; 88 | File myCaptureFile = new File(Environment.getExternalStorageDirectory().getPath() + "/DCIM/Camera/" + fileName); 89 | if(!myCaptureFile.createNewFile()){ 90 | return false; 91 | } 92 | BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(myCaptureFile)); 93 | bm.compress(Bitmap.CompressFormat.JPEG, 80, bos); 94 | bos.flush(); 95 | bos.close(); 96 | 97 | //广播通知相册有图片更新 98 | Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); 99 | Uri uri = Uri.fromFile(myCaptureFile); 100 | intent.setData(uri); 101 | sendBroadcast(intent); 102 | return true; 103 | } 104 | 105 | public void checkPermissionForSave(){ 106 | // 读取权限 107 | String permission = Manifest.permission.WRITE_EXTERNAL_STORAGE; 108 | if(Build.VERSION.SDK_INT>=23){ 109 | // 检查权限是否已授权 110 | int hasPermission = checkSelfPermission(permission); 111 | // 如果没有授权 112 | if (hasPermission != PackageManager.PERMISSION_GRANTED) { 113 | // 请求权限 114 | requestPermissions(new String[]{permission}, 0); 115 | } else { 116 | // 已授权权限 117 | savePicture(getIntent().getStringExtra("url")); 118 | } 119 | } else{ 120 | savePicture(getIntent().getStringExtra("url")); 121 | } 122 | } 123 | 124 | @Override 125 | public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 126 | if (grantResults.length > 0) {//grantResults 数组中存放的是授权结果 127 | if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { 128 | // 同意授权 129 | savePicture(getIntent().getStringExtra("url")); 130 | }else { 131 | // 拒绝授权 132 | Toast.makeText(ViewPictureActivity.this, getString(R.string.permission_denied), Toast.LENGTH_SHORT).show(); 133 | } 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/util/DownloadUtil.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.util; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import java.io.File; 6 | import java.io.FileOutputStream; 7 | import java.io.IOException; 8 | import java.io.InputStream; 9 | import java.util.Objects; 10 | 11 | import okhttp3.Call; 12 | import okhttp3.Callback; 13 | import okhttp3.OkHttpClient; 14 | import okhttp3.Request; 15 | import okhttp3.Response; 16 | 17 | /** 18 | * 文件下载工具类 19 | * By Brick farmers 20 | * Url: https://blog.csdn.net/lixinxiaos/article/details/93977377 21 | */ 22 | 23 | public class DownloadUtil { 24 | 25 | private static DownloadUtil downloadUtil; 26 | private final OkHttpClient okHttpClient; 27 | 28 | public static DownloadUtil get() { 29 | if (downloadUtil == null) { 30 | downloadUtil = new DownloadUtil(); 31 | } 32 | return downloadUtil; 33 | } 34 | 35 | public DownloadUtil() { 36 | okHttpClient = new OkHttpClient(); 37 | } 38 | 39 | 40 | /** 41 | * @param url 下载连接 42 | * @param destFileDir 下载的文件储存目录 43 | * @param destFileName 下载文件名称,后面记得拼接后缀,否则手机没法识别文件类型 44 | * @param listener 下载监听 45 | */ 46 | 47 | public void download(final String url, final String destFileDir, final String destFileName, final OnDownloadListener listener) { 48 | 49 | Request request = new Request.Builder() 50 | .url(url) 51 | .build(); 52 | 53 | OkHttpClient client = new OkHttpClient(); 54 | 55 | //异步请求 56 | okHttpClient.newCall(request).enqueue(new Callback() { 57 | @Override 58 | public void onFailure(@NonNull Call call, @NonNull IOException e) { 59 | // 下载失败监听回调 60 | listener.onDownloadFailed(e); 61 | } 62 | 63 | @Override 64 | public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException { 65 | 66 | InputStream is = null; 67 | byte[] buf = new byte[2048]; 68 | int len = 0; 69 | FileOutputStream fos = null; 70 | 71 | //储存下载文件的目录 72 | File dir = new File(destFileDir); 73 | if (!dir.exists()) { 74 | dir.mkdirs(); 75 | } 76 | File file = new File(dir, destFileName); 77 | 78 | try { 79 | 80 | is = Objects.requireNonNull(response.body()).byteStream(); 81 | long total = Objects.requireNonNull(response.body()).contentLength(); 82 | fos = new FileOutputStream(file); 83 | long sum = 0; 84 | while ((len = is.read(buf)) != -1) { 85 | fos.write(buf, 0, len); 86 | sum += len; 87 | int progress = (int) (sum * 1.0f / total * 100); 88 | //下载中更新进度条 89 | listener.onDownloading(progress); 90 | } 91 | fos.flush(); 92 | //下载完成 93 | listener.onDownloadSuccess(file); 94 | } catch (Exception e) { 95 | listener.onDownloadFailed(e); 96 | }finally { 97 | 98 | try { 99 | if (is != null) { 100 | is.close(); 101 | } 102 | if (fos != null) { 103 | fos.close(); 104 | } 105 | } catch (IOException e) { 106 | 107 | } 108 | 109 | } 110 | 111 | 112 | } 113 | }); 114 | } 115 | 116 | 117 | public interface OnDownloadListener{ 118 | 119 | /** 120 | * 下载成功之后的文件 121 | */ 122 | void onDownloadSuccess(File file); 123 | 124 | /** 125 | * 下载进度 126 | */ 127 | void onDownloading(int progress); 128 | 129 | /** 130 | * 下载异常信息 131 | */ 132 | 133 | void onDownloadFailed(Exception e); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/util/QRCodeUtil.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.util; 2 | 3 | import android.graphics.Bitmap; 4 | import android.graphics.Color; 5 | import android.text.TextUtils; 6 | 7 | import androidx.annotation.ColorInt; 8 | import androidx.annotation.Nullable; 9 | 10 | import com.google.zxing.BarcodeFormat; 11 | import com.google.zxing.EncodeHintType; 12 | import com.google.zxing.WriterException; 13 | import com.google.zxing.common.BitMatrix; 14 | import com.google.zxing.qrcode.QRCodeWriter; 15 | 16 | import java.util.Hashtable; 17 | 18 | /** 19 | * 二维码工具类 20 | * 来源:CSDN 21 | */ 22 | public class QRCodeUtil { 23 | 24 | /** 25 | * 创建二维码位图 26 | * 27 | * @param content 字符串内容(支持中文) 28 | * @param width 位图宽度(单位:px) 29 | * @param height 位图高度(单位:px) 30 | * @return QRCode 31 | */ 32 | @Nullable 33 | public static Bitmap createQRCodeBitmap(String content, int width, int height){ 34 | return createQRCodeBitmap(content, width, height, "UTF-8", "H", "2", Color.BLACK, Color.WHITE); 35 | } 36 | 37 | /** 38 | * 创建二维码位图 (支持自定义配置和自定义样式) 39 | * 40 | * @param content 字符串内容 41 | * @param width 位图宽度,要求>=0(单位:px) 42 | * @param height 位图高度,要求>=0(单位:px) 43 | * @param character_set 字符集/字符转码格式 (支持格式:{@link CharacterSetECI })。传null时,zxing源码默认使用 "ISO-8859-1" 44 | * @param error_correction 容错级别 (支持级别:{@link ErrorCorrectionLevel })。传null时,zxing源码默认使用 "L" 45 | * @param margin 空白边距 (可修改,要求:整型且>=0), 传null时,zxing源码默认使用"4"。 46 | * @param color_black 黑色色块的自定义颜色值 47 | * @param color_white 白色色块的自定义颜色值 48 | * @return QRCode 49 | */ 50 | @Nullable 51 | public static Bitmap createQRCodeBitmap(String content, int width, int height, 52 | @Nullable String character_set, @Nullable String error_correction, @Nullable String margin, 53 | @ColorInt int color_black, @ColorInt int color_white){ 54 | 55 | /* 1.参数合法性判断 */ 56 | if(TextUtils.isEmpty(content)){ // 字符串内容判空 57 | return null; 58 | } 59 | 60 | if(width < 0 || height < 0){ // 宽和高都需要>=0 61 | return null; 62 | } 63 | 64 | try { 65 | /* 2.设置二维码相关配置,生成BitMatrix(位矩阵)对象 */ 66 | Hashtable hints = new Hashtable<>(); 67 | 68 | if(!TextUtils.isEmpty(character_set)) { 69 | hints.put(EncodeHintType.CHARACTER_SET, character_set); // 字符转码格式设置 70 | } 71 | 72 | if(!TextUtils.isEmpty(error_correction)){ 73 | hints.put(EncodeHintType.ERROR_CORRECTION, error_correction); // 容错级别设置 74 | } 75 | 76 | if(!TextUtils.isEmpty(margin)){ 77 | hints.put(EncodeHintType.MARGIN, margin); // 空白边距设置 78 | } 79 | BitMatrix bitMatrix = new QRCodeWriter().encode(content, BarcodeFormat.QR_CODE, width, height, hints); 80 | 81 | /* 3.创建像素数组,并根据BitMatrix(位矩阵)对象为数组元素赋颜色值 */ 82 | int[] pixels = new int[width * height]; 83 | for(int y = 0; y < height; y++){ 84 | for(int x = 0; x < width; x++){ 85 | if(bitMatrix.get(x, y)){ 86 | pixels[y * width + x] = color_black; // 黑色色块像素设置 87 | } else { 88 | pixels[y * width + x] = color_white; // 白色色块像素设置 89 | } 90 | } 91 | } 92 | 93 | /* 4.创建Bitmap对象,根据像素数组设置Bitmap每个像素点的颜色值,之后返回Bitmap对象 */ 94 | Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); 95 | bitmap.setPixels(pixels, 0, width, 0, 0, width, height); 96 | return bitmap; 97 | } catch (WriterException e) { 98 | e.printStackTrace(); 99 | } 100 | 101 | return null; 102 | } 103 | } -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/util/SharedPreferencesUtil.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.util; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import com.alibaba.fastjson.JSON; 7 | import com.alibaba.fastjson.JSONObject; 8 | 9 | import cn.wearbbs.music.application.MainApplication; 10 | 11 | public class SharedPreferencesUtil { 12 | public static void putString(String key, String value) { 13 | SharedPreferences.Editor sp = MainApplication.getContext().getSharedPreferences("cn.wearbbs.music_preferences", Context.MODE_PRIVATE).edit(); 14 | sp.putString(key, value); 15 | sp.apply(); 16 | } 17 | 18 | public static void putBoolean(String key, Boolean value) { 19 | SharedPreferences.Editor sp = MainApplication.getContext().getSharedPreferences("cn.wearbbs.music_preferences", Context.MODE_PRIVATE).edit(); 20 | sp.putBoolean(key, value); 21 | sp.apply(); 22 | } 23 | 24 | public static void putJSONObject(String key, JSONObject value) { 25 | SharedPreferences.Editor sp = MainApplication.getContext().getSharedPreferences("cn.wearbbs.music_preferences", Context.MODE_PRIVATE).edit(); 26 | sp.putString(key, value.toJSONString()); 27 | sp.apply(); 28 | } 29 | 30 | public static Boolean getBoolean(String key, Boolean defValue) { 31 | SharedPreferences sp = MainApplication.getContext().getSharedPreferences("cn.wearbbs.music_preferences", Context.MODE_PRIVATE); 32 | return sp.getBoolean(key, defValue); 33 | } 34 | 35 | public static String getString(String key, String defValue) { 36 | SharedPreferences sp = MainApplication.getContext().getSharedPreferences("cn.wearbbs.music_preferences", Context.MODE_PRIVATE); 37 | return sp.getString(key, defValue); 38 | } 39 | 40 | public static JSONObject getJSONObject(String key) { 41 | SharedPreferences sp = MainApplication.getContext().getSharedPreferences("cn.wearbbs.music_preferences", Context.MODE_PRIVATE); 42 | return JSON.parseObject(sp.getString(key, "{}")); 43 | } 44 | 45 | public static void remove(String key) { 46 | SharedPreferences.Editor sp = MainApplication.getContext().getSharedPreferences("cn.wearbbs.music_preferences", Context.MODE_PRIVATE).edit(); 47 | sp.remove(key); 48 | sp.apply(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/util/ToastUtil.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.util; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.app.Activity; 5 | import android.os.Handler; 6 | import android.os.Looper; 7 | import android.os.Message; 8 | import android.widget.Toast; 9 | 10 | import org.apache.log4j.chainsaw.Main; 11 | 12 | import cn.wearbbs.music.application.MainApplication; 13 | 14 | public class ToastUtil { 15 | public static void show(Activity activity,String text){ 16 | activity.runOnUiThread(()->{ 17 | Toast.makeText(MainApplication.getContext(), text, Toast.LENGTH_LONG).show(); 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/view/LoadingView.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.view; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.LinearLayout; 9 | 10 | import androidx.annotation.Nullable; 11 | 12 | import cn.wearbbs.music.R; 13 | 14 | public class LoadingView extends LinearLayout { 15 | View mView; 16 | 17 | @SuppressLint("InflateParams") 18 | public LoadingView(Context context) { 19 | super(context); 20 | LayoutInflater mInflater = LayoutInflater.from(context); 21 | mView = mInflater.inflate(R.layout.view_loading, null); 22 | addView(mView); 23 | } 24 | 25 | @SuppressLint("InflateParams") 26 | public LoadingView(Context context, @Nullable AttributeSet attrs) { 27 | super(context, attrs); 28 | LayoutInflater mInflater = LayoutInflater.from(context); 29 | mView = mInflater.inflate(R.layout.view_loading, null); 30 | addView(mView); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/cn/wearbbs/music/view/MessageView.java: -------------------------------------------------------------------------------- 1 | package cn.wearbbs.music.view; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.util.AttributeSet; 6 | import android.view.LayoutInflater; 7 | import android.view.View; 8 | import android.widget.ImageView; 9 | import android.widget.LinearLayout; 10 | import android.widget.TextView; 11 | 12 | import androidx.annotation.DrawableRes; 13 | import androidx.annotation.Nullable; 14 | import androidx.annotation.StringRes; 15 | 16 | import cn.wearbbs.music.R; 17 | 18 | public class MessageView extends LinearLayout { 19 | private final View mView; 20 | public final static int NO_LOGIN = 0, 21 | LOAD_FAILED = 1, 22 | NO_MUSIC = 2; 23 | @SuppressLint("InflateParams") 24 | public MessageView(Context context) { 25 | super(context); 26 | LayoutInflater mInflater = LayoutInflater.from(context); 27 | mView = mInflater.inflate(R.layout.view_message, null); 28 | addView(mView); 29 | } 30 | 31 | @SuppressLint("InflateParams") 32 | public MessageView(Context context, @Nullable AttributeSet attrs) { 33 | super(context, attrs); 34 | LayoutInflater mInflater = LayoutInflater.from(context); 35 | mView = mInflater.inflate(R.layout.view_message, null); 36 | addView(mView); 37 | } 38 | 39 | public void setImageResource(@DrawableRes int resId) { 40 | ImageView iv_img = findViewById(R.id.iv_img); 41 | iv_img.setImageResource(resId); 42 | } 43 | 44 | public void setText(@StringRes int resId) { 45 | TextView tv_img = findViewById(R.id.tv_img); 46 | tv_img.setText(resId); 47 | } 48 | 49 | public void setImageOnClickListener(OnClickListener listener){ 50 | ImageView iv_img = findViewById(R.id.iv_img); 51 | iv_img.setOnClickListener(listener); 52 | } 53 | 54 | 55 | public void setContent(int contentId,@Nullable OnClickListener listener){ 56 | switch (contentId){ 57 | case NO_LOGIN: 58 | setText(R.string.msg_noLogin); 59 | setImageResource(R.drawable.ic_baseline_login_24); 60 | setImageOnClickListener(v -> {}); 61 | break; 62 | case LOAD_FAILED: 63 | setText(R.string.load_failed); 64 | setImageResource(R.drawable.ic_baseline_refresh_24); 65 | setImageOnClickListener(listener); 66 | break; 67 | case NO_MUSIC: 68 | setText(R.string.msg_noMusic); 69 | setImageResource(R.drawable.ic_baseline_assignment_24); 70 | setImageOnClickListener(v -> {}); 71 | break; 72 | } 73 | } 74 | 75 | 76 | } 77 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/icon_play_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable-night/icon_play_order.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-night/icon_play_repeat_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable-night/icon_play_repeat_one.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/btn_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/et_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_android_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_back_ios_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_arrow_forward_ios_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_assignment_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_assignment_turned_in_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_brightness_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_brightness_24_black.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_build_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_check_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_cloud_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_cloud_download_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_desktop_windows_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_family_restroom_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_favorite_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_favorite_border_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_file_copy_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_library_music_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_lock_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_login_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_menu_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_message_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_music_video_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_pause_circle_filled_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_person_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_phone_android_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_photo_size_select_actual_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_play_circle_filled_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_playlist_play_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_radio_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_refresh_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_save_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_sd_storage_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_search_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_settings_24.xml: -------------------------------------------------------------------------------- 1 | 5 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_share_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_star_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_supervised_user_circle_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_supervised_user_circle_primary_24.xml: -------------------------------------------------------------------------------- 1 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_thumb_up_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_visibility_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_visibility_off_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_volume_down_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_volume_up_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_warning_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_outline_thumb_up_24.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_extra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable/icon_extra.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_play_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable/icon_play_order.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_play_repeat_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable/icon_play_repeat_one.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/icon_tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable/icon_tip.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/middle_ss.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/qrcode_group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable/qrcode_group.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/qrcode_update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable/qrcode_update.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/qrcode_update_dev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenSourceWear/WearMusic/21d85e7db3766f3fe2585213e9676764be6c493d/app/src/main/res/drawable/qrcode_update_dev.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ripple_item_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_progressbar_azure.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/shape_progressbar_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_comment.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 29 | 30 | 38 | 39 | 40 | 41 | 46 | 47 | 53 | 54 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_ftp.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 16 | 17 | 25 | 26 | 35 | 36 | 44 | 45 | 46 | 47 | 58 | 59 | 64 | 65 | 72 | 73 |