从GitHub移植过来
This commit is contained in:
commit
756cd6e75c
17
.gitignore
vendored
Normal file
17
.gitignore
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.idea
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
*.apk
|
20
README.md
Normal file
20
README.md
Normal file
|
@ -0,0 +1,20 @@
|
|||
## 简介
|
||||
一个对接网易云的音乐播放器
|
||||
|
||||
## 截图
|
||||
暂无
|
||||
## 联系作者
|
||||
* QQ:1966944300
|
||||
* 邮箱:1966944300@qq.com
|
||||
|
||||
## 后台
|
||||
* Github: [网易云音乐 API](https://github.com/Binaryify/NeteaseCloudMusicApi)
|
||||
|
||||
## 关于
|
||||
在兴趣的驱动下,写一个`免费`的东西,有欣喜,也还有汗水,希望你喜欢我的作品,同时也能支持一下。
|
||||
|
||||
## 修改JAR的包
|
||||
(主要修改内容MD3化)
|
||||
com.github.QuadFlask:colorpicker:0.0.15
|
||||
# (歌词做全局变量给悬浮窗歌词)
|
||||
'com.github.wangchenyan:lrcview:2.2.1'
|
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
94
app/build.gradle
Normal file
94
app/build.gradle
Normal file
|
@ -0,0 +1,94 @@
|
|||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'kotlin-android'
|
||||
}
|
||||
android {
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('../muqing.jks')
|
||||
storePassword 'muqing153'
|
||||
keyAlias 'muqing'
|
||||
keyPassword 'muqing153'
|
||||
}
|
||||
}
|
||||
namespace 'com.muqingbfq'
|
||||
compileSdk 34
|
||||
defaultConfig {
|
||||
applicationId "com.muqingbfq"
|
||||
minSdk 24
|
||||
//noinspection ExpiredTargetSdkVersion,OldTargetApi
|
||||
targetSdk 34
|
||||
versionCode 1
|
||||
versionName "2.6.0"
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
// shrinkResources true
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
android.applicationVariants.configureEach {
|
||||
variant ->
|
||||
variant.outputs.configureEach {
|
||||
//在这里修改apk文件名
|
||||
outputFileName = "Cloud_music-${variant.name}-v${variant.versionName}.apk"
|
||||
}
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
viewBinding {
|
||||
enabled = true
|
||||
}
|
||||
|
||||
}
|
||||
dependencies {
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.appcompat:appcompat:1.7.0'
|
||||
implementation 'com.google.android.material:material:1.10.0'
|
||||
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
|
||||
|
||||
implementation 'com.google.code.gson:gson:2.9.1'
|
||||
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.11.0'
|
||||
//图片处理
|
||||
implementation 'com.github.bumptech.glide:glide:4.16.0'
|
||||
implementation 'jp.wasabeef:glide-transformations:4.3.0'
|
||||
|
||||
|
||||
implementation "androidx.palette:palette-ktx:1.0.0"
|
||||
// 废弃的歌词组件
|
||||
// implementation 'com.github.wangchenyan:lrcview:2.2.1'
|
||||
implementation 'com.google.android.flexbox:flexbox:3.0.0'
|
||||
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
|
||||
//修改音乐标签库
|
||||
implementation 'com.mpatric:mp3agic:0.9.1'
|
||||
|
||||
// 沉浸式状态栏
|
||||
// release 版本
|
||||
// 将 $lastVersion 替换成上图中的版本号
|
||||
implementation 'com.gitee.zackratos:UltimateBarX:v0.8.1'
|
||||
// api project(path: ':lrcview')
|
||||
//歌词组件库
|
||||
api "com.github.cy745:EaseView:e11c3208a9"
|
||||
api "androidx.dynamicanimation:dynamicanimation-ktx:1.0.0-alpha03"
|
||||
|
||||
implementation "androidx.media3:media3-exoplayer:1.4.0"
|
||||
implementation "androidx.media3:media3-ui:1.4.0"
|
||||
implementation "androidx.media3:media3-common:1.4.0"
|
||||
implementation "androidx.media3:media3-session:1.4.0"
|
||||
implementation 'androidx.activity:activity:1.9.1'
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-process:2.6.1"
|
||||
|
||||
|
||||
|
||||
}
|
21
app/proguard-rules.pro
vendored
Normal file
21
app/proguard-rules.pro
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
20
app/release/output-metadata.json
Normal file
20
app/release/output-metadata.json
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "com.muqingbfq",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 1,
|
||||
"versionName": "2.6.0",
|
||||
"outputFile": "Cloud_music-release-v2.6.0.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File"
|
||||
}
|
95
app/src/main/AndroidManifest.xml
Normal file
95
app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,95 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 存储权限 -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 外部存储的写权限 -->
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!-- 外部存储的读权限 -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission
|
||||
android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.INTERNET" /> <!-- 网络权限 -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- 申请通知栏权限 -->
|
||||
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_NOTIFICATIONS" /> <!-- 蓝牙连接 -->
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> <!-- 悬浮窗权限 -->
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" /> <!-- 主题壁纸 -->
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/><!--你正在使用前台服务,并且目标 SDK 为 34,因此需要在 Android 13 (API 34) 上请求-->
|
||||
|
||||
<application
|
||||
android:name=".main"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:preserveLegacyExternalStorage="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:enableOnBackInvokedCallback="true"
|
||||
android:theme="@style/Theme.muqing"
|
||||
android:usesCleartextTraffic="true"
|
||||
tools:targetApi="tiramisu">
|
||||
<activity
|
||||
android:name=".home"
|
||||
android:exported="true"
|
||||
android:theme="@style/Theme.start">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity android:name=".login.user_logs" />
|
||||
<activity android:name=".clean.fragment_clean" />
|
||||
<activity android:name=".activity_search" />
|
||||
<!-- <activity-->
|
||||
<!-- android:name=".bfq"-->
|
||||
<!-- android:configChanges="keyboardHidden|screenSize"-->
|
||||
<!-- android:theme="@style/fragment_dialog" />-->
|
||||
<activity android:name=".fragment.mp3" />
|
||||
<activity android:name=".fragment.gd" />
|
||||
<activity android:name=".activity_about_software" />
|
||||
<activity android:name=".sz" />
|
||||
<activity android:name=".mq.llq" />
|
||||
<activity android:name=".yc" />
|
||||
<activity
|
||||
android:name=".activity.Music"
|
||||
android:configChanges="keyboardHidden|screenSize"
|
||||
android:theme="@style/fragment_dialog" />
|
||||
|
||||
<service
|
||||
android:name=".bfqkz"
|
||||
android:exported="true"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
<service android:name=".mq.FloatingLyricsService" android:exported="true"
|
||||
tools:ignore="ExportedService" />
|
||||
|
||||
|
||||
<service
|
||||
android:name=".PlaybackService"
|
||||
android:exported="true"
|
||||
android:foregroundServiceType="mediaPlayback"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="androidx.media3.session.MediaSessionService" />
|
||||
<action android:name="android.media.browse.MediaBrowserService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
</application>
|
||||
|
||||
</manifest>
|
56
app/src/main/assets/about.html
Normal file
56
app/src/main/assets/about.html
Normal file
|
@ -0,0 +1,56 @@
|
|||
<h2>开源在线音乐播放器项目</h2>
|
||||
<p>该项目由作者MUQING编写,采用了 JAVA 设计语言,该应用对接了网易云音乐API,为用户提供了一系列功能丰富的服务:</p>
|
||||
<ol>
|
||||
<li><strong>音乐搜索与发现</strong>:
|
||||
<ul>
|
||||
<li>用户可以通过搜索框快速查找网易云音乐曲库中的歌曲、歌单。</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>在线播放与下载</strong>:
|
||||
<ul>
|
||||
<li>支持在线流媒体播放,用户可以流畅地收听高品质音乐,并可根据网络状况切换不同音质。</li>
|
||||
<li>提供音乐下载功能,用户可将喜欢的歌曲保存到本地进行离线播放。</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>个性化歌单与收藏</strong>:
|
||||
<ul>
|
||||
<li>用户能收藏自己的歌单,以及收藏其他用户的优秀歌单。</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>歌词显示与同步</strong>:
|
||||
<ul>
|
||||
<li>在播放音乐时,同步展示歌词,并支持歌词滚动与定位。</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>账号绑定与同步</strong>:
|
||||
<ul>
|
||||
<li>用户可以使用网易云音乐账号登录,实现离线歌单和播放历史。</li>
|
||||
</ul>
|
||||
</li>
|
||||
<!-- <li><strong>界面定制与主题更换</strong>:
|
||||
<ul>
|
||||
<li>应用遵循Material 3设计规范,提供美观且符合最新设计趋势的UI体验。</li>
|
||||
<li>用户可以根据个人喜好调整界面颜色、字体大小等,以达到最佳视觉效果。</li>
|
||||
</ul>
|
||||
</li>-->
|
||||
|
||||
<!--
|
||||
<li><strong>社交互动</strong>:
|
||||
<ul>
|
||||
<li>允许用户查看并参与歌曲评论区讨论,与其他音乐爱好者交流心得。</li>
|
||||
</ul>
|
||||
</li>
|
||||
-->
|
||||
<li><strong>智能推荐系统</strong>:
|
||||
<ul>
|
||||
<li>根据用户的听歌习惯和偏好,推送个性化的音乐推荐。</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><strong>后台播放与控制</strong>:
|
||||
<ul>
|
||||
<li>支持通知栏控制和小部件操作,即使在其他应用或屏幕关闭状态下也能方便控制音乐播放。</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ol>
|
||||
<p>通过这个开源项目,开发者学习如何对接第三方API实现音乐播放功能,还能了解各种小功能和细节的实现。</p>
|
54
app/src/main/java/com/colorpicker/ColorCircle.java
Normal file
54
app/src/main/java/com/colorpicker/ColorCircle.java
Normal file
|
@ -0,0 +1,54 @@
|
|||
package com.colorpicker;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
public class ColorCircle {
|
||||
private float x, y;
|
||||
private float[] hsv = new float[3];
|
||||
private float[] hsvClone;
|
||||
private int color;
|
||||
|
||||
public ColorCircle(float x, float y, float[] hsv) {
|
||||
set(x, y, hsv);
|
||||
}
|
||||
|
||||
public double sqDist(float x, float y) {
|
||||
double dx = this.x - x;
|
||||
double dy = this.y - y;
|
||||
return dx * dx + dy * dy;
|
||||
}
|
||||
|
||||
public float getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public float getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public float[] getHsv() {
|
||||
return hsv;
|
||||
}
|
||||
|
||||
public float[] getHsvWithLightness(float lightness) {
|
||||
if (hsvClone == null)
|
||||
hsvClone = hsv.clone();
|
||||
hsvClone[0] = hsv[0];
|
||||
hsvClone[1] = hsv[1];
|
||||
hsvClone[2] = lightness;
|
||||
return hsvClone;
|
||||
}
|
||||
|
||||
public void set(float x, float y, float[] hsv) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.hsv[0] = hsv[0];
|
||||
this.hsv[1] = hsv[1];
|
||||
this.hsv[2] = hsv[2];
|
||||
this.color = Color.HSVToColor(this.hsv);
|
||||
}
|
||||
|
||||
public int getColor() {
|
||||
return color;
|
||||
}
|
||||
}
|
39
app/src/main/java/com/colorpicker/ColorCircleDrawable.java
Normal file
39
app/src/main/java/com/colorpicker/ColorCircleDrawable.java
Normal file
|
@ -0,0 +1,39 @@
|
|||
package com.colorpicker;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
|
||||
import com.colorpicker.builder.PaintBuilder;
|
||||
|
||||
public class ColorCircleDrawable extends ColorDrawable {
|
||||
private float strokeWidth;
|
||||
private Paint strokePaint = PaintBuilder.newPaint().style(Paint.Style.STROKE).stroke(strokeWidth).color(0xff9e9e9e).build();
|
||||
private Paint fillPaint = PaintBuilder.newPaint().style(Paint.Style.FILL).color(0).build();
|
||||
private Paint fillBackPaint = PaintBuilder.newPaint().shader(PaintBuilder.createAlphaPatternShader(26)).build();
|
||||
|
||||
public ColorCircleDrawable(int color) {
|
||||
super(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
canvas.drawColor(0);
|
||||
|
||||
int width = canvas.getWidth();
|
||||
float radius = width / 2f;
|
||||
strokeWidth = radius / 8f;
|
||||
|
||||
this.strokePaint.setStrokeWidth(strokeWidth);
|
||||
this.fillPaint.setColor(getColor());
|
||||
canvas.drawCircle(radius, radius, radius - strokeWidth, fillBackPaint);
|
||||
canvas.drawCircle(radius, radius, radius - strokeWidth, fillPaint);
|
||||
canvas.drawCircle(radius, radius, radius - strokeWidth, strokePaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColor(int color) {
|
||||
super.setColor(color);
|
||||
invalidateSelf();
|
||||
}
|
||||
}
|
156
app/src/main/java/com/colorpicker/ColorPickerPreference.java
Normal file
156
app/src/main/java/com/colorpicker/ColorPickerPreference.java
Normal file
|
@ -0,0 +1,156 @@
|
|||
package com.colorpicker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.preference.Preference;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.colorpicker.builder.ColorPickerClickListener;
|
||||
import com.colorpicker.builder.ColorPickerDialogBuilder;
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public class ColorPickerPreference extends Preference {
|
||||
|
||||
protected boolean alphaSlider;
|
||||
protected boolean lightSlider;
|
||||
protected boolean border;
|
||||
|
||||
protected int selectedColor = 0;
|
||||
|
||||
protected ColorPickerView.WHEEL_TYPE wheelType;
|
||||
protected int density;
|
||||
|
||||
private boolean pickerColorEdit;
|
||||
private String pickerTitle;
|
||||
private String pickerButtonCancel;
|
||||
private String pickerButtonOk;
|
||||
|
||||
protected ImageView colorIndicator;
|
||||
|
||||
public ColorPickerPreference(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public ColorPickerPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initWith(context, attrs);
|
||||
}
|
||||
|
||||
public ColorPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initWith(context, attrs);
|
||||
}
|
||||
|
||||
private void initWith(Context context, AttributeSet attrs) {
|
||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColorPickerPreference);
|
||||
|
||||
try {
|
||||
alphaSlider = typedArray.getBoolean(R.styleable.ColorPickerPreference_alphaSlider, false);
|
||||
lightSlider = typedArray.getBoolean(R.styleable.ColorPickerPreference_lightnessSlider, false);
|
||||
border = typedArray.getBoolean(R.styleable.ColorPickerPreference_border, true);
|
||||
|
||||
density = typedArray.getInt(R.styleable.ColorPickerPreference_density, 8);
|
||||
wheelType = ColorPickerView.WHEEL_TYPE.indexOf(typedArray.getInt(R.styleable.ColorPickerPreference_wheelType, 0));
|
||||
|
||||
selectedColor = typedArray.getInt(R.styleable.ColorPickerPreference_initialColor, 0xffffffff);
|
||||
|
||||
pickerColorEdit = typedArray.getBoolean(R.styleable.ColorPickerPreference_pickerColorEdit, true);
|
||||
pickerTitle = typedArray.getString(R.styleable.ColorPickerPreference_pickerTitle);
|
||||
if (pickerTitle==null)
|
||||
pickerTitle = "Choose color";
|
||||
|
||||
pickerButtonCancel = typedArray.getString(R.styleable.ColorPickerPreference_pickerButtonCancel);
|
||||
if (pickerButtonCancel==null)
|
||||
pickerButtonCancel = "cancel";
|
||||
|
||||
pickerButtonOk = typedArray.getString(R.styleable.ColorPickerPreference_pickerButtonOk);
|
||||
if (pickerButtonOk==null)
|
||||
pickerButtonOk = "ok";
|
||||
|
||||
} finally {
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
setWidgetLayoutResource(R.layout.color_widget);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onBindView(@NonNull View view) {
|
||||
super.onBindView(view);
|
||||
|
||||
int tmpColor = isEnabled()
|
||||
? selectedColor
|
||||
: darken(selectedColor, .5f);
|
||||
|
||||
colorIndicator = (ImageView) view.findViewById(R.id.color_indicator);
|
||||
|
||||
ColorCircleDrawable colorChoiceDrawable = null;
|
||||
Drawable currentDrawable = colorIndicator.getDrawable();
|
||||
if (currentDrawable != null && currentDrawable instanceof ColorCircleDrawable)
|
||||
colorChoiceDrawable = (ColorCircleDrawable) currentDrawable;
|
||||
|
||||
if (colorChoiceDrawable == null)
|
||||
colorChoiceDrawable = new ColorCircleDrawable(tmpColor);
|
||||
|
||||
colorIndicator.setImageDrawable(colorChoiceDrawable);
|
||||
}
|
||||
|
||||
public void setValue(int value) {
|
||||
if (callChangeListener(value)) {
|
||||
selectedColor = value;
|
||||
persistInt(value);
|
||||
notifyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
|
||||
setValue(restoreValue ? getPersistedInt(0) : (Integer) defaultValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
ColorPickerDialogBuilder builder = ColorPickerDialogBuilder
|
||||
.with(getContext())
|
||||
.setTitle(pickerTitle)
|
||||
.initialColor(selectedColor)
|
||||
.showBorder(border)
|
||||
.wheelType(wheelType)
|
||||
.density(density)
|
||||
.showColorEdit(pickerColorEdit)
|
||||
.setPositiveButton(pickerButtonOk, new ColorPickerClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int selectedColorFromPicker, Integer[] allColors) {
|
||||
setValue(selectedColorFromPicker);
|
||||
}
|
||||
})
|
||||
.setNegativeButton(pickerButtonCancel, null);
|
||||
|
||||
if (!alphaSlider && !lightSlider) builder.noSliders();
|
||||
else if (!alphaSlider) builder.lightnessSliderOnly();
|
||||
else if (!lightSlider) builder.alphaSliderOnly();
|
||||
|
||||
builder
|
||||
.build()
|
||||
.show();
|
||||
}
|
||||
|
||||
public static int darken(int color, float factor) {
|
||||
int a = Color.alpha(color);
|
||||
int r = Color.red(color);
|
||||
int g = Color.green(color);
|
||||
int b = Color.blue(color);
|
||||
|
||||
return Color.argb(a,
|
||||
Math.max((int)(r * factor), 0),
|
||||
Math.max((int)(g * factor), 0),
|
||||
Math.max((int)(b * factor), 0));
|
||||
}
|
||||
}
|
572
app/src/main/java/com/colorpicker/ColorPickerView.java
Normal file
572
app/src/main/java/com/colorpicker/ColorPickerView.java
Normal file
|
@ -0,0 +1,572 @@
|
|||
package com.colorpicker;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.colorpicker.builder.ColorWheelRendererBuilder;
|
||||
import com.colorpicker.builder.PaintBuilder;
|
||||
import com.colorpicker.renderer.ColorWheelRenderOption;
|
||||
import com.colorpicker.renderer.ColorWheelRenderer;
|
||||
import com.colorpicker.slider.AlphaSlider;
|
||||
import com.colorpicker.slider.LightnessSlider;
|
||||
import com.muqingbfq.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
public class ColorPickerView extends View {
|
||||
private static final float STROKE_RATIO = 1.5f;
|
||||
|
||||
private Bitmap colorWheel;
|
||||
private Canvas colorWheelCanvas;
|
||||
private Bitmap currentColor;
|
||||
private Canvas currentColorCanvas;
|
||||
private boolean showBorder;
|
||||
private int density = 8;
|
||||
|
||||
private float lightness = 1;
|
||||
private float alpha = 1;
|
||||
private int backgroundColor = 0x00000000;
|
||||
|
||||
private Integer initialColors[] = new Integer[]{null, null, null, null, null};
|
||||
private int colorSelection = 0;
|
||||
private Integer initialColor;
|
||||
private Integer pickerColorEditTextColor;
|
||||
private Paint colorWheelFill = PaintBuilder.newPaint().color(0).build();
|
||||
private Paint selectorStroke = PaintBuilder.newPaint().color(0).build();
|
||||
private Paint alphaPatternPaint = PaintBuilder.newPaint().build();
|
||||
private ColorCircle currentColorCircle;
|
||||
|
||||
private ArrayList<OnColorChangedListener> colorChangedListeners = new ArrayList<OnColorChangedListener>();
|
||||
private ArrayList<OnColorSelectedListener> listeners = new ArrayList<OnColorSelectedListener>();
|
||||
|
||||
private LightnessSlider lightnessSlider;
|
||||
private AlphaSlider alphaSlider;
|
||||
private EditText colorEdit;
|
||||
private TextWatcher colorTextChange = new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
try {
|
||||
int color = Color.parseColor(s.toString());
|
||||
|
||||
// set the color without changing the edit text preventing stack overflow
|
||||
setColor(color, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
};
|
||||
private LinearLayout colorPreview;
|
||||
|
||||
private ColorWheelRenderer renderer;
|
||||
|
||||
private int alphaSliderViewId, lightnessSliderViewId;
|
||||
|
||||
public ColorPickerView(Context context) {
|
||||
super(context);
|
||||
initWith(context, null);
|
||||
}
|
||||
|
||||
public ColorPickerView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initWith(context, attrs);
|
||||
}
|
||||
|
||||
public ColorPickerView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initWith(context, attrs);
|
||||
}
|
||||
|
||||
@TargetApi(21)
|
||||
public ColorPickerView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
initWith(context, attrs);
|
||||
}
|
||||
|
||||
private void initWith(Context context, AttributeSet attrs) {
|
||||
final TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ColorPickerPreference);
|
||||
|
||||
density = typedArray.getInt(R.styleable.ColorPickerPreference_density, 10);
|
||||
initialColor = typedArray.getInt(R.styleable.ColorPickerPreference_initialColor, 0xffffffff);
|
||||
|
||||
pickerColorEditTextColor = typedArray.getInt(R.styleable.ColorPickerPreference_pickerColorEditTextColor, 0xffffffff);
|
||||
|
||||
WHEEL_TYPE wheelType = WHEEL_TYPE.indexOf(typedArray.getInt(R.styleable.ColorPickerPreference_wheelType, 0));
|
||||
ColorWheelRenderer renderer = ColorWheelRendererBuilder.getRenderer(wheelType);
|
||||
|
||||
alphaSliderViewId = typedArray.getResourceId(R.styleable.ColorPickerPreference_alphaSliderView, 0);
|
||||
lightnessSliderViewId = typedArray.getResourceId(R.styleable.ColorPickerPreference_lightnessSliderView, 0);
|
||||
|
||||
setRenderer(renderer);
|
||||
setDensity(density);
|
||||
setInitialColor(initialColor, true);
|
||||
|
||||
typedArray.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
||||
super.onWindowFocusChanged(hasWindowFocus);
|
||||
updateColorWheel();
|
||||
currentColorCircle = findNearestByColor(initialColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
|
||||
if (alphaSliderViewId != 0)
|
||||
setAlphaSlider((AlphaSlider) getRootView().findViewById(alphaSliderViewId));
|
||||
if (lightnessSliderViewId != 0)
|
||||
setLightnessSlider((LightnessSlider) getRootView().findViewById(lightnessSliderViewId));
|
||||
|
||||
updateColorWheel();
|
||||
currentColorCircle = findNearestByColor(initialColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
updateColorWheel();
|
||||
}
|
||||
|
||||
private void updateColorWheel() {
|
||||
int width = getMeasuredWidth();
|
||||
int height = getMeasuredHeight();
|
||||
|
||||
if (height < width)
|
||||
width = height;
|
||||
if (width <= 0)
|
||||
return;
|
||||
if (colorWheel == null || colorWheel.getWidth() != width) {
|
||||
colorWheel = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
|
||||
colorWheelCanvas = new Canvas(colorWheel);
|
||||
alphaPatternPaint.setShader(PaintBuilder.createAlphaPatternShader(26));
|
||||
}
|
||||
if (currentColor == null || currentColor.getWidth() != width) {
|
||||
currentColor = Bitmap.createBitmap(width, width, Bitmap.Config.ARGB_8888);
|
||||
currentColorCanvas = new Canvas(currentColor);
|
||||
}
|
||||
drawColorWheel();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private void drawColorWheel() {
|
||||
colorWheelCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
currentColorCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
|
||||
if (renderer == null) return;
|
||||
|
||||
float half = colorWheelCanvas.getWidth() / 2f;
|
||||
float strokeWidth = STROKE_RATIO * (1f + ColorWheelRenderer.GAP_PERCENTAGE);
|
||||
float maxRadius = half - strokeWidth - half / density;
|
||||
float cSize = maxRadius / (density - 1) / 2;
|
||||
|
||||
ColorWheelRenderOption colorWheelRenderOption = renderer.getRenderOption();
|
||||
colorWheelRenderOption.density = this.density;
|
||||
colorWheelRenderOption.maxRadius = maxRadius;
|
||||
colorWheelRenderOption.cSize = cSize;
|
||||
colorWheelRenderOption.strokeWidth = strokeWidth;
|
||||
colorWheelRenderOption.alpha = alpha;
|
||||
colorWheelRenderOption.lightness = lightness;
|
||||
colorWheelRenderOption.targetCanvas = colorWheelCanvas;
|
||||
|
||||
renderer.initWith(colorWheelRenderOption);
|
||||
renderer.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int width = 0;
|
||||
if (widthMode == MeasureSpec.UNSPECIFIED)
|
||||
width = widthMeasureSpec;
|
||||
else if (widthMode == MeasureSpec.AT_MOST)
|
||||
width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
else if (widthMode == MeasureSpec.EXACTLY)
|
||||
width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int height = 0;
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED)
|
||||
height = heightMeasureSpec;
|
||||
else if (heightMode == MeasureSpec.AT_MOST)
|
||||
height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
else if (heightMode == MeasureSpec.EXACTLY)
|
||||
height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
int squareDimen = width;
|
||||
if (height < width)
|
||||
squareDimen = height;
|
||||
setMeasuredDimension(squareDimen, squareDimen);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
int lastSelectedColor = getSelectedColor();
|
||||
currentColorCircle = findNearestByPosition(event.getX(), event.getY());
|
||||
int selectedColor = getSelectedColor();
|
||||
|
||||
callOnColorChangedListeners(lastSelectedColor, selectedColor);
|
||||
|
||||
initialColor = selectedColor;
|
||||
setColorToSliders(selectedColor);
|
||||
updateColorWheel();
|
||||
invalidate();
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP: {
|
||||
int selectedColor = getSelectedColor();
|
||||
if (listeners != null) {
|
||||
for (OnColorSelectedListener listener : listeners) {
|
||||
try {
|
||||
listener.onColorSelected(selectedColor);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
setColorToSliders(selectedColor);
|
||||
setColorText(selectedColor);
|
||||
setColorPreviewColor(selectedColor);
|
||||
invalidate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void callOnColorChangedListeners(int oldColor, int newColor) {
|
||||
if (colorChangedListeners != null && oldColor != newColor) {
|
||||
for (OnColorChangedListener listener : colorChangedListeners) {
|
||||
try {
|
||||
listener.onColorChanged(newColor);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
canvas.drawColor(backgroundColor);
|
||||
|
||||
float maxRadius = canvas.getWidth() / (1f + ColorWheelRenderer.GAP_PERCENTAGE);
|
||||
float size = maxRadius / density / 2;
|
||||
if (colorWheel != null && currentColorCircle != null) {
|
||||
colorWheelFill.setColor(Color.HSVToColor(currentColorCircle.getHsvWithLightness(this.lightness)));
|
||||
colorWheelFill.setAlpha((int) (alpha * 0xff));
|
||||
|
||||
// a separate canvas is used to erase an issue with the alpha pattern around the edges
|
||||
// draw circle slightly larger than it needs to be, then erase edges to proper dimensions
|
||||
currentColorCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + 4, alphaPatternPaint);
|
||||
currentColorCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + 4, colorWheelFill);
|
||||
|
||||
selectorStroke = PaintBuilder.newPaint().color(0xffffffff).style(Paint.Style.STROKE).stroke(size * (STROKE_RATIO - 1)).xPerMode(PorterDuff.Mode.CLEAR).build();
|
||||
|
||||
if (showBorder) colorWheelCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + (selectorStroke.getStrokeWidth() / 2f), selectorStroke);
|
||||
canvas.drawBitmap(colorWheel, 0, 0, null);
|
||||
|
||||
currentColorCanvas.drawCircle(currentColorCircle.getX(), currentColorCircle.getY(), size + (selectorStroke.getStrokeWidth() / 2f), selectorStroke);
|
||||
canvas.drawBitmap(currentColor, 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
private ColorCircle findNearestByPosition(float x, float y) {
|
||||
ColorCircle near = null;
|
||||
double minDist = Double.MAX_VALUE;
|
||||
|
||||
for (ColorCircle colorCircle : renderer.getColorCircleList()) {
|
||||
double dist = colorCircle.sqDist(x, y);
|
||||
if (minDist > dist) {
|
||||
minDist = dist;
|
||||
near = colorCircle;
|
||||
}
|
||||
}
|
||||
|
||||
return near;
|
||||
}
|
||||
|
||||
private ColorCircle findNearestByColor(int color) {
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color, hsv);
|
||||
ColorCircle near = null;
|
||||
double minDiff = Double.MAX_VALUE;
|
||||
double x = hsv[1] * Math.cos(hsv[0] * Math.PI / 180);
|
||||
double y = hsv[1] * Math.sin(hsv[0] * Math.PI / 180);
|
||||
|
||||
for (ColorCircle colorCircle : renderer.getColorCircleList()) {
|
||||
float[] hsv1 = colorCircle.getHsv();
|
||||
double x1 = hsv1[1] * Math.cos(hsv1[0] * Math.PI / 180);
|
||||
double y1 = hsv1[1] * Math.sin(hsv1[0] * Math.PI / 180);
|
||||
double dx = x - x1;
|
||||
double dy = y - y1;
|
||||
double dist = dx * dx + dy * dy;
|
||||
if (dist < minDiff) {
|
||||
minDiff = dist;
|
||||
near = colorCircle;
|
||||
}
|
||||
}
|
||||
|
||||
return near;
|
||||
}
|
||||
|
||||
public int getSelectedColor() {
|
||||
int color = 0;
|
||||
if (currentColorCircle != null)
|
||||
color = Utils.colorAtLightness(currentColorCircle.getColor(), this.lightness);
|
||||
return Utils.adjustAlpha(this.alpha, color);
|
||||
}
|
||||
|
||||
public Integer[] getAllColors() {
|
||||
return initialColors;
|
||||
}
|
||||
|
||||
public void setInitialColors(Integer[] colors, int selectedColor) {
|
||||
this.initialColors = colors;
|
||||
this.colorSelection = selectedColor;
|
||||
Integer initialColor = this.initialColors[this.colorSelection];
|
||||
if (initialColor == null) initialColor = 0xffffffff;
|
||||
setInitialColor(initialColor, true);
|
||||
}
|
||||
|
||||
public void setInitialColor(int color, boolean updateText) {
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color, hsv);
|
||||
|
||||
this.alpha = Utils.getAlphaPercent(color);
|
||||
this.lightness = hsv[2];
|
||||
this.initialColors[this.colorSelection] = color;
|
||||
this.initialColor = color;
|
||||
setColorPreviewColor(color);
|
||||
setColorToSliders(color);
|
||||
if (this.colorEdit != null && updateText)
|
||||
setColorText(color);
|
||||
currentColorCircle = findNearestByColor(color);
|
||||
}
|
||||
|
||||
public void setLightness(float lightness) {
|
||||
int lastSelectedColor = getSelectedColor();
|
||||
|
||||
this.lightness = lightness;
|
||||
if (currentColorCircle != null) {
|
||||
this.initialColor = Color.HSVToColor(Utils.alphaValueAsInt(this.alpha), currentColorCircle.getHsvWithLightness(lightness));
|
||||
if (this.colorEdit != null)
|
||||
this.colorEdit.setText(Utils.getHexString(this.initialColor, this.alphaSlider != null));
|
||||
if (this.alphaSlider != null && this.initialColor != null)
|
||||
this.alphaSlider.setColor(this.initialColor);
|
||||
|
||||
callOnColorChangedListeners(lastSelectedColor, this.initialColor);
|
||||
|
||||
updateColorWheel();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
public void setColor(int color, boolean updateText) {
|
||||
setInitialColor(color, updateText);
|
||||
updateColorWheel();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setAlphaValue(float alpha) {
|
||||
int lastSelectedColor = getSelectedColor();
|
||||
|
||||
this.alpha = alpha;
|
||||
this.initialColor = Color.HSVToColor(Utils.alphaValueAsInt(this.alpha), currentColorCircle.getHsvWithLightness(this.lightness));
|
||||
if (this.colorEdit != null)
|
||||
this.colorEdit.setText(Utils.getHexString(this.initialColor, this.alphaSlider != null));
|
||||
if (this.lightnessSlider != null && this.initialColor != null)
|
||||
this.lightnessSlider.setColor(this.initialColor);
|
||||
|
||||
callOnColorChangedListeners(lastSelectedColor, this.initialColor);
|
||||
|
||||
updateColorWheel();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void addOnColorChangedListener(OnColorChangedListener listener) {
|
||||
this.colorChangedListeners.add(listener);
|
||||
}
|
||||
|
||||
public void addOnColorSelectedListener(OnColorSelectedListener listener) {
|
||||
this.listeners.add(listener);
|
||||
}
|
||||
|
||||
public void setLightnessSlider(LightnessSlider lightnessSlider) {
|
||||
this.lightnessSlider = lightnessSlider;
|
||||
if (lightnessSlider != null) {
|
||||
this.lightnessSlider.setColorPicker(this);
|
||||
this.lightnessSlider.setColor(getSelectedColor());
|
||||
}
|
||||
}
|
||||
|
||||
public void setAlphaSlider(AlphaSlider alphaSlider) {
|
||||
this.alphaSlider = alphaSlider;
|
||||
if (alphaSlider != null) {
|
||||
this.alphaSlider.setColorPicker(this);
|
||||
this.alphaSlider.setColor(getSelectedColor());
|
||||
}
|
||||
}
|
||||
|
||||
public void setColorEdit(EditText colorEdit) {
|
||||
this.colorEdit = colorEdit;
|
||||
if (this.colorEdit != null) {
|
||||
this.colorEdit.setVisibility(View.VISIBLE);
|
||||
this.colorEdit.addTextChangedListener(colorTextChange);
|
||||
setColorEditTextColor(pickerColorEditTextColor);
|
||||
}
|
||||
}
|
||||
|
||||
public void setColorEditTextColor(int argb) {
|
||||
this.pickerColorEditTextColor = argb;
|
||||
if (colorEdit != null)
|
||||
colorEdit.setTextColor(argb);
|
||||
}
|
||||
|
||||
public void setDensity(int density) {
|
||||
this.density = Math.max(2, density);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRenderer(ColorWheelRenderer renderer) {
|
||||
this.renderer = renderer;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setColorPreview(LinearLayout colorPreview, Integer selectedColor) {
|
||||
if (colorPreview == null)
|
||||
return;
|
||||
this.colorPreview = colorPreview;
|
||||
if (selectedColor == null)
|
||||
selectedColor = 0;
|
||||
int children = colorPreview.getChildCount();
|
||||
if (children == 0 || colorPreview.getVisibility() != View.VISIBLE)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < children; i++) {
|
||||
View childView = colorPreview.getChildAt(i);
|
||||
if (!(childView instanceof LinearLayout))
|
||||
continue;
|
||||
LinearLayout childLayout = (LinearLayout) childView;
|
||||
if (i == selectedColor) {
|
||||
childLayout.setBackgroundColor(Color.WHITE);
|
||||
}
|
||||
ImageView childImage = (ImageView) childLayout.findViewById(R.id.image_preview);
|
||||
childImage.setClickable(true);
|
||||
childImage.setTag(i);
|
||||
childImage.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v == null)
|
||||
return;
|
||||
Object tag = v.getTag();
|
||||
if (tag == null || !(tag instanceof Integer))
|
||||
return;
|
||||
setSelectedColor((int) tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public void setSelectedColor(int previewNumber) {
|
||||
if (initialColors == null || initialColors.length < previewNumber)
|
||||
return;
|
||||
this.colorSelection = previewNumber;
|
||||
setHighlightedColor(previewNumber);
|
||||
Integer color = initialColors[previewNumber];
|
||||
if (color == null)
|
||||
return;
|
||||
setColor(color, true);
|
||||
}
|
||||
|
||||
public void setShowBorder(boolean showBorder) {
|
||||
this.showBorder = showBorder;
|
||||
}
|
||||
|
||||
private void setHighlightedColor(int previewNumber) {
|
||||
int children = colorPreview.getChildCount();
|
||||
if (children == 0 || colorPreview.getVisibility() != View.VISIBLE)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < children; i++) {
|
||||
View childView = colorPreview.getChildAt(i);
|
||||
if (!(childView instanceof LinearLayout))
|
||||
continue;
|
||||
LinearLayout childLayout = (LinearLayout) childView;
|
||||
if (i == previewNumber) {
|
||||
childLayout.setBackgroundColor(Color.WHITE);
|
||||
} else {
|
||||
childLayout.setBackgroundColor(Color.TRANSPARENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setColorPreviewColor(int newColor) {
|
||||
if (colorPreview == null || initialColors == null || colorSelection > initialColors.length || initialColors[colorSelection] == null)
|
||||
return;
|
||||
|
||||
int children = colorPreview.getChildCount();
|
||||
if (children == 0 || colorPreview.getVisibility() != View.VISIBLE)
|
||||
return;
|
||||
|
||||
View childView = colorPreview.getChildAt(colorSelection);
|
||||
if (!(childView instanceof LinearLayout))
|
||||
return;
|
||||
LinearLayout childLayout = (LinearLayout) childView;
|
||||
ImageView childImage = (ImageView) childLayout.findViewById(R.id.image_preview);
|
||||
childImage.setImageDrawable(new ColorCircleDrawable(newColor));
|
||||
}
|
||||
|
||||
private void setColorText(int argb) {
|
||||
if (colorEdit == null)
|
||||
return;
|
||||
colorEdit.setText(Utils.getHexString(argb, this.alphaSlider != null));
|
||||
}
|
||||
|
||||
private void setColorToSliders(int selectedColor) {
|
||||
if (lightnessSlider != null)
|
||||
lightnessSlider.setColor(selectedColor);
|
||||
if (alphaSlider != null)
|
||||
alphaSlider.setColor(selectedColor);
|
||||
}
|
||||
|
||||
public enum WHEEL_TYPE {
|
||||
FLOWER, CIRCLE;
|
||||
|
||||
public static WHEEL_TYPE indexOf(int index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
return FLOWER;
|
||||
case 1:
|
||||
return CIRCLE;
|
||||
}
|
||||
return FLOWER;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.colorpicker;
|
||||
|
||||
public interface OnColorChangedListener {
|
||||
void onColorChanged(int selectedColor);
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.colorpicker;
|
||||
|
||||
public interface OnColorSelectedListener {
|
||||
void onColorSelected(int selectedColor);
|
||||
}
|
40
app/src/main/java/com/colorpicker/Utils.java
Normal file
40
app/src/main/java/com/colorpicker/Utils.java
Normal file
|
@ -0,0 +1,40 @@
|
|||
package com.colorpicker;
|
||||
|
||||
import android.graphics.Color;
|
||||
|
||||
/**
|
||||
* Created by Charles Andersons on 4/17/15.
|
||||
*/
|
||||
public class Utils {
|
||||
public static float getAlphaPercent(int argb) {
|
||||
return Color.alpha(argb) / 255f;
|
||||
}
|
||||
|
||||
public static int alphaValueAsInt(float alpha) {
|
||||
return Math.round(alpha * 255);
|
||||
}
|
||||
|
||||
public static int adjustAlpha(float alpha, int color) {
|
||||
return alphaValueAsInt(alpha) << 24 | (0x00ffffff & color);
|
||||
}
|
||||
|
||||
public static int colorAtLightness(int color, float lightness) {
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color, hsv);
|
||||
hsv[2] = lightness;
|
||||
return Color.HSVToColor(hsv);
|
||||
}
|
||||
|
||||
public static float lightnessOfColor(int color) {
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color, hsv);
|
||||
return hsv[2];
|
||||
}
|
||||
|
||||
public static String getHexString(int color, boolean showAlpha) {
|
||||
int base = showAlpha ? 0xFFFFFFFF : 0xFFFFFF;
|
||||
String format = showAlpha ? "#%08X" : "#%06X";
|
||||
return String.format(format, (base & color)).toUpperCase();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.colorpicker.builder;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
|
||||
/**
|
||||
* Created by Charles Anderson on 4/17/15.
|
||||
*/
|
||||
public interface ColorPickerClickListener {
|
||||
void onClick(DialogInterface d, int lastSelectedColor, Integer[] allColors);
|
||||
}
|
|
@ -0,0 +1,297 @@
|
|||
package com.colorpicker.builder;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.text.InputFilter;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.colorpicker.ColorPickerView;
|
||||
import com.colorpicker.OnColorChangedListener;
|
||||
import com.colorpicker.OnColorSelectedListener;
|
||||
import com.colorpicker.Utils;
|
||||
import com.colorpicker.renderer.ColorWheelRenderer;
|
||||
import com.colorpicker.slider.AlphaSlider;
|
||||
import com.colorpicker.slider.LightnessSlider;
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public class ColorPickerDialogBuilder {
|
||||
private MaterialAlertDialogBuilder builder;
|
||||
private LinearLayout pickerContainer;
|
||||
private ColorPickerView colorPickerView;
|
||||
private LightnessSlider lightnessSlider;
|
||||
private AlphaSlider alphaSlider;
|
||||
private EditText colorEdit;
|
||||
private LinearLayout colorPreview;
|
||||
|
||||
private boolean isLightnessSliderEnabled = true;
|
||||
private boolean isAlphaSliderEnabled = true;
|
||||
private boolean isBorderEnabled = true;
|
||||
private boolean isColorEditEnabled = false;
|
||||
private boolean isPreviewEnabled = false;
|
||||
private int pickerCount = 1;
|
||||
private int defaultMargin = 0;
|
||||
private int defaultMarginTop = 0;
|
||||
private Integer[] initialColor = new Integer[]{null, null, null, null, null};
|
||||
|
||||
private ColorPickerDialogBuilder(Context context) {
|
||||
this(context, 0);
|
||||
}
|
||||
|
||||
private ColorPickerDialogBuilder(Context context, int theme) {
|
||||
defaultMargin = getDimensionAsPx(context, R.dimen.default_slider_margin);
|
||||
defaultMarginTop = getDimensionAsPx(context, R.dimen.default_margin_top);
|
||||
|
||||
builder = new MaterialAlertDialogBuilder(context, theme);
|
||||
pickerContainer = new LinearLayout(context);
|
||||
pickerContainer.setOrientation(LinearLayout.VERTICAL);
|
||||
pickerContainer.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
pickerContainer.setPadding(defaultMargin, defaultMarginTop, defaultMargin, 0);
|
||||
|
||||
LinearLayout.LayoutParams layoutParamsForColorPickerView = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0);
|
||||
layoutParamsForColorPickerView.weight = 1;
|
||||
colorPickerView = new ColorPickerView(context);
|
||||
|
||||
pickerContainer.addView(colorPickerView, layoutParamsForColorPickerView);
|
||||
|
||||
builder.setView(pickerContainer);
|
||||
}
|
||||
|
||||
public static ColorPickerDialogBuilder with(Context context) {
|
||||
return new ColorPickerDialogBuilder(context);
|
||||
}
|
||||
|
||||
public static ColorPickerDialogBuilder with(Context context, int theme) {
|
||||
return new ColorPickerDialogBuilder(context, theme);
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setTitle(String title) {
|
||||
builder.setTitle(title);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setTitle(int titleId) {
|
||||
builder.setTitle(titleId);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder initialColor(int initialColor) {
|
||||
this.initialColor[0] = initialColor;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder initialColors(int[] initialColor) {
|
||||
for (int i = 0; i < initialColor.length && i < this.initialColor.length; i++) {
|
||||
this.initialColor[i] = initialColor[i];
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder wheelType(ColorPickerView.WHEEL_TYPE wheelType) {
|
||||
ColorWheelRenderer renderer = ColorWheelRendererBuilder.getRenderer(wheelType);
|
||||
colorPickerView.setRenderer(renderer);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder density(int density) {
|
||||
colorPickerView.setDensity(density);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setOnColorChangedListener(OnColorChangedListener onColorChangedListener) {
|
||||
colorPickerView.addOnColorChangedListener(onColorChangedListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setOnColorSelectedListener(OnColorSelectedListener onColorSelectedListener) {
|
||||
colorPickerView.addOnColorSelectedListener(onColorSelectedListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setPositiveButton(CharSequence text, final ColorPickerClickListener onClickListener) {
|
||||
builder.setPositiveButton(text, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
positiveButtonOnClick(dialog, onClickListener);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setPositiveButton(int textId, final ColorPickerClickListener onClickListener) {
|
||||
builder.setPositiveButton(textId, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
positiveButtonOnClick(dialog, onClickListener);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setNegativeButton(CharSequence text, DialogInterface.OnClickListener onClickListener) {
|
||||
builder.setNegativeButton(text, onClickListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setNegativeButton(int textId, DialogInterface.OnClickListener onClickListener) {
|
||||
builder.setNegativeButton(textId, onClickListener);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder noSliders() {
|
||||
isLightnessSliderEnabled = false;
|
||||
isAlphaSliderEnabled = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder alphaSliderOnly() {
|
||||
isLightnessSliderEnabled = false;
|
||||
isAlphaSliderEnabled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder lightnessSliderOnly() {
|
||||
isLightnessSliderEnabled = true;
|
||||
isAlphaSliderEnabled = false;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder showAlphaSlider(boolean showAlpha) {
|
||||
isAlphaSliderEnabled = showAlpha;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder showLightnessSlider(boolean showLightness) {
|
||||
isLightnessSliderEnabled = showLightness;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder showBorder(boolean showBorder) {
|
||||
isBorderEnabled = showBorder;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder showColorEdit(boolean showEdit) {
|
||||
isColorEditEnabled = showEdit;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setColorEditTextColor(int argb) {
|
||||
colorPickerView.setColorEditTextColor(argb);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder showColorPreview(boolean showPreview) {
|
||||
isPreviewEnabled = showPreview;
|
||||
if (!showPreview)
|
||||
pickerCount = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ColorPickerDialogBuilder setPickerCount(int pickerCount) throws IndexOutOfBoundsException {
|
||||
if (pickerCount < 1 || pickerCount > 5)
|
||||
throw new IndexOutOfBoundsException("Picker Can Only Support 1-5 Colors");
|
||||
this.pickerCount = pickerCount;
|
||||
if (this.pickerCount > 1)
|
||||
this.isPreviewEnabled = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertDialog build() {
|
||||
Context context = builder.getContext();
|
||||
colorPickerView.setInitialColors(initialColor, getStartOffset(initialColor));
|
||||
colorPickerView.setShowBorder(isBorderEnabled);
|
||||
|
||||
if (isLightnessSliderEnabled) {
|
||||
LinearLayout.LayoutParams layoutParamsForLightnessBar = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getDimensionAsPx(context, R.dimen.default_slider_height));
|
||||
lightnessSlider = new LightnessSlider(context);
|
||||
lightnessSlider.setLayoutParams(layoutParamsForLightnessBar);
|
||||
pickerContainer.addView(lightnessSlider);
|
||||
colorPickerView.setLightnessSlider(lightnessSlider);
|
||||
lightnessSlider.setColor(getStartColor(initialColor));
|
||||
lightnessSlider.setShowBorder(isBorderEnabled);
|
||||
}
|
||||
if (isAlphaSliderEnabled) {
|
||||
LinearLayout.LayoutParams layoutParamsForAlphaBar = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getDimensionAsPx(context, R.dimen.default_slider_height));
|
||||
alphaSlider = new AlphaSlider(context);
|
||||
alphaSlider.setLayoutParams(layoutParamsForAlphaBar);
|
||||
pickerContainer.addView(alphaSlider);
|
||||
colorPickerView.setAlphaSlider(alphaSlider);
|
||||
alphaSlider.setColor(getStartColor(initialColor));
|
||||
alphaSlider.setShowBorder(isBorderEnabled);
|
||||
}
|
||||
if (isColorEditEnabled) {
|
||||
LinearLayout.LayoutParams layoutParamsForColorEdit = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
colorEdit = (EditText) View.inflate(context, R.layout.color_edit, null);
|
||||
colorEdit.setFilters(new InputFilter[]{new InputFilter.AllCaps()});
|
||||
colorEdit.setSingleLine();
|
||||
colorEdit.setVisibility(View.GONE);
|
||||
|
||||
// limit number of characters to hexColors
|
||||
int maxLength = isAlphaSliderEnabled ? 9 : 7;
|
||||
colorEdit.setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
|
||||
|
||||
pickerContainer.addView(colorEdit, layoutParamsForColorEdit);
|
||||
|
||||
colorEdit.setText(Utils.getHexString(getStartColor(initialColor), isAlphaSliderEnabled));
|
||||
colorPickerView.setColorEdit(colorEdit);
|
||||
}
|
||||
if (isPreviewEnabled) {
|
||||
colorPreview = (LinearLayout) View.inflate(context, R.layout.color_preview, null);
|
||||
colorPreview.setVisibility(View.GONE);
|
||||
pickerContainer.addView(colorPreview);
|
||||
|
||||
if (initialColor.length == 0) {
|
||||
ImageView colorImage = (ImageView) View.inflate(context, R.layout.color_selector, null);
|
||||
colorImage.setImageDrawable(new ColorDrawable(Color.WHITE));
|
||||
} else {
|
||||
for (int i = 0; i < initialColor.length && i < this.pickerCount; i++) {
|
||||
if (initialColor[i] == null)
|
||||
break;
|
||||
LinearLayout colorLayout = (LinearLayout) View.inflate(context, R.layout.color_selector, null);
|
||||
ImageView colorImage = (ImageView) colorLayout.findViewById(R.id.image_preview);
|
||||
colorImage.setImageDrawable(new ColorDrawable(initialColor[i]));
|
||||
colorPreview.addView(colorLayout);
|
||||
}
|
||||
}
|
||||
colorPreview.setVisibility(View.VISIBLE);
|
||||
colorPickerView.setColorPreview(colorPreview, getStartOffset(initialColor));
|
||||
}
|
||||
|
||||
return builder.create();
|
||||
}
|
||||
|
||||
private Integer getStartOffset(Integer[] colors) {
|
||||
Integer start = 0;
|
||||
for (int i = 0; i < colors.length; i++) {
|
||||
if (colors[i] == null) {
|
||||
return start;
|
||||
}
|
||||
start = (i + 1) / 2;
|
||||
}
|
||||
return start;
|
||||
}
|
||||
|
||||
private int getStartColor(Integer[] colors) {
|
||||
Integer startColor = getStartOffset(colors);
|
||||
return startColor == null ? Color.WHITE : colors[startColor];
|
||||
}
|
||||
|
||||
private static int getDimensionAsPx(Context context, int rid) {
|
||||
return (int) (context.getResources().getDimension(rid) + .5f);
|
||||
}
|
||||
|
||||
private void positiveButtonOnClick(DialogInterface dialog, ColorPickerClickListener onClickListener) {
|
||||
int selectedColor = colorPickerView.getSelectedColor();
|
||||
Integer[] allColors = colorPickerView.getAllColors();
|
||||
onClickListener.onClick(dialog, selectedColor, allColors);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
package com.colorpicker.builder;
|
||||
|
||||
import com.colorpicker.ColorPickerView;
|
||||
import com.colorpicker.renderer.ColorWheelRenderer;
|
||||
import com.colorpicker.renderer.FlowerColorWheelRenderer;
|
||||
import com.colorpicker.renderer.SimpleColorWheelRenderer;
|
||||
|
||||
public class ColorWheelRendererBuilder {
|
||||
public static ColorWheelRenderer getRenderer(ColorPickerView.WHEEL_TYPE wheelType) {
|
||||
switch (wheelType) {
|
||||
case CIRCLE:
|
||||
return new SimpleColorWheelRenderer();
|
||||
case FLOWER:
|
||||
return new FlowerColorWheelRenderer();
|
||||
}
|
||||
throw new IllegalArgumentException("wrong WHEEL_TYPE");
|
||||
}
|
||||
}
|
82
app/src/main/java/com/colorpicker/builder/PaintBuilder.java
Normal file
82
app/src/main/java/com/colorpicker/builder/PaintBuilder.java
Normal file
|
@ -0,0 +1,82 @@
|
|||
package com.colorpicker.builder;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.graphics.Shader;
|
||||
|
||||
public class PaintBuilder {
|
||||
public static PaintHolder newPaint() {
|
||||
return new PaintHolder();
|
||||
}
|
||||
|
||||
public static class PaintHolder {
|
||||
private Paint paint;
|
||||
|
||||
private PaintHolder() {
|
||||
this.paint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||
}
|
||||
|
||||
public PaintHolder color(int color) {
|
||||
this.paint.setColor(color);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaintHolder antiAlias(boolean flag) {
|
||||
this.paint.setAntiAlias(flag);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaintHolder style(Paint.Style style) {
|
||||
this.paint.setStyle(style);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaintHolder mode(PorterDuff.Mode mode) {
|
||||
this.paint.setXfermode(new PorterDuffXfermode(mode));
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaintHolder stroke(float width) {
|
||||
this.paint.setStrokeWidth(width);
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaintHolder xPerMode(PorterDuff.Mode mode) {
|
||||
this.paint.setXfermode(new PorterDuffXfermode(mode));
|
||||
return this;
|
||||
}
|
||||
|
||||
public PaintHolder shader(Shader shader) {
|
||||
this.paint.setShader(shader);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Paint build() {
|
||||
return this.paint;
|
||||
}
|
||||
}
|
||||
|
||||
public static Shader createAlphaPatternShader(int size) {
|
||||
size /= 2;
|
||||
size = Math.max(8, size * 2);
|
||||
return new BitmapShader(createAlphaBackgroundPattern(size), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
|
||||
}
|
||||
|
||||
private static Bitmap createAlphaBackgroundPattern(int size) {
|
||||
Paint alphaPatternPaint = PaintBuilder.newPaint().build();
|
||||
Bitmap bm = Bitmap.createBitmap(size, size, Bitmap.Config.ARGB_8888);
|
||||
Canvas c = new Canvas(bm);
|
||||
int s = Math.round(size / 2f);
|
||||
for (int i = 0; i < 2; i++)
|
||||
for (int j = 0; j < 2; j++) {
|
||||
if ((i + j) % 2 == 0) alphaPatternPaint.setColor(0xffffffff);
|
||||
else alphaPatternPaint.setColor(0xffd0d0d0);
|
||||
c.drawRect(i * s, j * s, (i + 1) * s, (j + 1) * s, alphaPatternPaint);
|
||||
}
|
||||
return bm;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.colorpicker.renderer;
|
||||
|
||||
import com.colorpicker.ColorCircle;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public abstract class AbsColorWheelRenderer implements ColorWheelRenderer {
|
||||
protected ColorWheelRenderOption colorWheelRenderOption;
|
||||
protected List<ColorCircle> colorCircleList = new ArrayList<>();
|
||||
|
||||
public void initWith(ColorWheelRenderOption colorWheelRenderOption) {
|
||||
this.colorWheelRenderOption = colorWheelRenderOption;
|
||||
this.colorCircleList.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorWheelRenderOption getRenderOption() {
|
||||
if (colorWheelRenderOption == null) colorWheelRenderOption = new ColorWheelRenderOption();
|
||||
return colorWheelRenderOption;
|
||||
}
|
||||
|
||||
public List<ColorCircle> getColorCircleList() {
|
||||
return colorCircleList;
|
||||
}
|
||||
|
||||
protected int getAlphaValueAsInt() {
|
||||
return Math.round(colorWheelRenderOption.alpha * 255);
|
||||
}
|
||||
|
||||
protected int calcTotalCount(float radius, float size) {
|
||||
return Math.max(1, (int) ((1f - GAP_PERCENTAGE) * Math.PI / (Math.asin(size / radius)) + 0.5f));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.colorpicker.renderer;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
|
||||
public class ColorWheelRenderOption {
|
||||
public int density;
|
||||
public float maxRadius;
|
||||
public float cSize, strokeWidth, alpha, lightness;
|
||||
public Canvas targetCanvas;
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package com.colorpicker.renderer;
|
||||
|
||||
import com.colorpicker.ColorCircle;
|
||||
import java.util.List;
|
||||
|
||||
public interface ColorWheelRenderer {
|
||||
float GAP_PERCENTAGE = 0.025f;
|
||||
|
||||
void draw();
|
||||
|
||||
ColorWheelRenderOption getRenderOption();
|
||||
|
||||
void initWith(ColorWheelRenderOption colorWheelRenderOption);
|
||||
|
||||
List<ColorCircle> getColorCircleList();
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
package com.colorpicker.renderer;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import com.colorpicker.ColorCircle;
|
||||
import com.colorpicker.builder.PaintBuilder;
|
||||
|
||||
public class FlowerColorWheelRenderer extends AbsColorWheelRenderer {
|
||||
private Paint selectorFill = PaintBuilder.newPaint().build();
|
||||
private float[] hsv = new float[3];
|
||||
private float sizeJitter = 1.2f;
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
final int setSize = colorCircleList.size();
|
||||
int currentCount = 0;
|
||||
float half = colorWheelRenderOption.targetCanvas.getWidth() / 2f;
|
||||
int density = colorWheelRenderOption.density;
|
||||
float strokeWidth = colorWheelRenderOption.strokeWidth;
|
||||
float maxRadius = colorWheelRenderOption.maxRadius;
|
||||
float cSize = colorWheelRenderOption.cSize;
|
||||
|
||||
for (int i = 0; i < density; i++) {
|
||||
float p = (float) i / (density - 1); // 0~1
|
||||
float jitter = (i - density / 2f) / density; // -0.5 ~ 0.5
|
||||
float radius = maxRadius * p;
|
||||
float size = Math.max(1.5f + strokeWidth, cSize + (i == 0 ? 0 : cSize * sizeJitter * jitter));
|
||||
int total = Math.min(calcTotalCount(radius, size), density * 2);
|
||||
|
||||
for (int j = 0; j < total; j++) {
|
||||
double angle = Math.PI * 2 * j / total + (Math.PI / total) * ((i + 1) % 2);
|
||||
float x = half + (float) (radius * Math.cos(angle));
|
||||
float y = half + (float) (radius * Math.sin(angle));
|
||||
hsv[0] = (float) (angle * 180 / Math.PI);
|
||||
hsv[1] = radius / maxRadius;
|
||||
hsv[2] = colorWheelRenderOption.lightness;
|
||||
selectorFill.setColor(Color.HSVToColor(hsv));
|
||||
selectorFill.setAlpha(getAlphaValueAsInt());
|
||||
|
||||
colorWheelRenderOption.targetCanvas.drawCircle(x, y, size - strokeWidth, selectorFill);
|
||||
|
||||
if (currentCount >= setSize) {
|
||||
colorCircleList.add(new ColorCircle(x, y, hsv));
|
||||
} else colorCircleList.get(currentCount).set(x, y, hsv);
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.colorpicker.renderer;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import com.colorpicker.ColorCircle;
|
||||
import com.colorpicker.builder.PaintBuilder;
|
||||
public class SimpleColorWheelRenderer extends AbsColorWheelRenderer {
|
||||
private Paint selectorFill = PaintBuilder.newPaint().build();
|
||||
private float[] hsv = new float[3];
|
||||
|
||||
@Override
|
||||
public void draw() {
|
||||
final int setSize = colorCircleList.size();
|
||||
int currentCount = 0;
|
||||
float half = colorWheelRenderOption.targetCanvas.getWidth() / 2f;
|
||||
int density = colorWheelRenderOption.density;
|
||||
float maxRadius = colorWheelRenderOption.maxRadius;
|
||||
|
||||
for (int i = 0; i < density; i++) {
|
||||
float p = (float) i / (density - 1); // 0~1
|
||||
float radius = maxRadius * p;
|
||||
float size = colorWheelRenderOption.cSize;
|
||||
int total = calcTotalCount(radius, size);
|
||||
|
||||
for (int j = 0; j < total; j++) {
|
||||
double angle = Math.PI * 2 * j / total + (Math.PI / total) * ((i + 1) % 2);
|
||||
float x = half + (float) (radius * Math.cos(angle));
|
||||
float y = half + (float) (radius * Math.sin(angle));
|
||||
hsv[0] = (float) (angle * 180 / Math.PI);
|
||||
hsv[1] = radius / maxRadius;
|
||||
hsv[2] = colorWheelRenderOption.lightness;
|
||||
selectorFill.setColor(Color.HSVToColor(hsv));
|
||||
selectorFill.setAlpha(getAlphaValueAsInt());
|
||||
|
||||
colorWheelRenderOption.targetCanvas.drawCircle(x, y, size - colorWheelRenderOption.strokeWidth, selectorFill);
|
||||
|
||||
if (currentCount >= setSize)
|
||||
colorCircleList.add(new ColorCircle(x, y, hsv));
|
||||
else colorCircleList.get(currentCount).set(x, y, hsv);
|
||||
currentCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
189
app/src/main/java/com/colorpicker/slider/AbsCustomSlider.java
Normal file
189
app/src/main/java/com/colorpicker/slider/AbsCustomSlider.java
Normal file
|
@ -0,0 +1,189 @@
|
|||
package com.colorpicker.slider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.PorterDuff;
|
||||
import androidx.annotation.DimenRes;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public abstract class AbsCustomSlider extends View {
|
||||
protected Bitmap bitmap;
|
||||
protected Canvas bitmapCanvas;
|
||||
protected Bitmap bar;
|
||||
protected Canvas barCanvas;
|
||||
protected OnValueChangedListener onValueChangedListener;
|
||||
protected int barOffsetX;
|
||||
protected int handleRadius = 20;
|
||||
protected int barHeight = 5;
|
||||
protected float value = 1;
|
||||
protected boolean showBorder = false;
|
||||
|
||||
private boolean inVerticalOrientation = false;
|
||||
|
||||
public AbsCustomSlider(Context context) {
|
||||
super(context);
|
||||
init(context, null);
|
||||
}
|
||||
|
||||
public AbsCustomSlider(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
public AbsCustomSlider(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs) {
|
||||
TypedArray styledAttrs = context.getTheme().obtainStyledAttributes(
|
||||
attrs, R.styleable.AbsCustomSlider, 0, 0);
|
||||
try {
|
||||
inVerticalOrientation = styledAttrs.getBoolean(
|
||||
R.styleable.AbsCustomSlider_inVerticalOrientation, inVerticalOrientation);
|
||||
} finally {
|
||||
styledAttrs.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateBar() {
|
||||
handleRadius = getDimension(R.dimen.default_slider_handler_radius);
|
||||
barHeight = getDimension(R.dimen.default_slider_bar_height);
|
||||
barOffsetX = handleRadius;
|
||||
|
||||
if (bar == null)
|
||||
createBitmaps();
|
||||
drawBar(barCanvas);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
protected void createBitmaps() {
|
||||
int width;
|
||||
int height;
|
||||
if (inVerticalOrientation) {
|
||||
width = getHeight();
|
||||
height = getWidth();
|
||||
} else {
|
||||
width = getWidth();
|
||||
height = getHeight();
|
||||
}
|
||||
|
||||
bar = Bitmap.createBitmap(Math.max(width - barOffsetX * 2, 1), barHeight, Bitmap.Config.ARGB_8888);
|
||||
barCanvas = new Canvas(bar);
|
||||
|
||||
if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
|
||||
if (bitmap != null) bitmap.recycle();
|
||||
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
bitmapCanvas = new Canvas(bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
|
||||
int width;
|
||||
int height;
|
||||
if (inVerticalOrientation) {
|
||||
width = getHeight();
|
||||
height = getWidth();
|
||||
|
||||
canvas.rotate(-90);
|
||||
canvas.translate(-width, 0);
|
||||
} else {
|
||||
width = getWidth();
|
||||
height = getHeight();
|
||||
}
|
||||
|
||||
if (bar != null && bitmapCanvas != null) {
|
||||
bitmapCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
bitmapCanvas.drawBitmap(bar, barOffsetX, (height - bar.getHeight()) / 2, null);
|
||||
|
||||
float x = handleRadius + value * (width - handleRadius * 2);
|
||||
float y = height / 2f;
|
||||
drawHandle(bitmapCanvas, x, y);
|
||||
canvas.drawBitmap(bitmap, 0, 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void drawBar(Canvas barCanvas);
|
||||
|
||||
protected abstract void onValueChanged(float value);
|
||||
|
||||
protected abstract void drawHandle(Canvas canvas, float x, float y);
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
updateBar();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int width = 0;
|
||||
if (widthMode == MeasureSpec.UNSPECIFIED)
|
||||
width = widthMeasureSpec;
|
||||
else if (widthMode == MeasureSpec.AT_MOST)
|
||||
width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
else if (widthMode == MeasureSpec.EXACTLY)
|
||||
width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
int height = 0;
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED)
|
||||
height = heightMeasureSpec;
|
||||
else if (heightMode == MeasureSpec.AT_MOST)
|
||||
height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
else if (heightMode == MeasureSpec.EXACTLY)
|
||||
height = MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
if (bar != null) {
|
||||
if (inVerticalOrientation) {
|
||||
value = 1 - (event.getY() - barOffsetX) / bar.getWidth();
|
||||
} else {
|
||||
value = (event.getX() - barOffsetX) / bar.getWidth();
|
||||
}
|
||||
value = Math.max(0, Math.min(value, 1));
|
||||
onValueChanged(value);
|
||||
invalidate();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MotionEvent.ACTION_UP: {
|
||||
onValueChanged(value);
|
||||
if (onValueChangedListener != null)
|
||||
onValueChangedListener.onValueChanged(value);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected int getDimension(@DimenRes int id) {
|
||||
return getResources().getDimensionPixelSize(id);
|
||||
}
|
||||
|
||||
public void setShowBorder(boolean showBorder) {
|
||||
this.showBorder = showBorder;
|
||||
}
|
||||
|
||||
public void setOnValueChangedListener(OnValueChangedListener onValueChangedListener) {
|
||||
this.onValueChangedListener = onValueChangedListener;
|
||||
}
|
||||
}
|
99
app/src/main/java/com/colorpicker/slider/AlphaSlider.java
Normal file
99
app/src/main/java/com/colorpicker/slider/AlphaSlider.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
package com.colorpicker.slider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.colorpicker.ColorPickerView;
|
||||
import com.colorpicker.Utils;
|
||||
import com.colorpicker.builder.PaintBuilder;
|
||||
public class AlphaSlider extends AbsCustomSlider {
|
||||
public int color;
|
||||
private Paint alphaPatternPaint = PaintBuilder.newPaint().build();
|
||||
private Paint barPaint = PaintBuilder.newPaint().build();
|
||||
private Paint solid = PaintBuilder.newPaint().build();
|
||||
private Paint clearingStroke = PaintBuilder.newPaint().color(0xffffffff).xPerMode(PorterDuff.Mode.CLEAR).build();
|
||||
|
||||
private Paint clearStroke = PaintBuilder.newPaint().build();
|
||||
private Bitmap clearBitmap;
|
||||
private Canvas clearBitmapCanvas;
|
||||
|
||||
private ColorPickerView colorPicker;
|
||||
|
||||
public AlphaSlider(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public AlphaSlider(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public AlphaSlider(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void createBitmaps() {
|
||||
super.createBitmaps();
|
||||
alphaPatternPaint.setShader(PaintBuilder.createAlphaPatternShader(barHeight * 2));
|
||||
clearBitmap = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
|
||||
clearBitmapCanvas = new Canvas(clearBitmap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawBar(Canvas barCanvas) {
|
||||
int width = barCanvas.getWidth();
|
||||
int height = barCanvas.getHeight();
|
||||
|
||||
barCanvas.drawRect(0, 0, width, height, alphaPatternPaint);
|
||||
int l = Math.max(2, width / 256);
|
||||
for (int x = 0; x <= width; x += l) {
|
||||
float alpha = (float) x / (width - 1);
|
||||
barPaint.setColor(color);
|
||||
barPaint.setAlpha(Math.round(alpha * 255));
|
||||
barCanvas.drawRect(x, 0, x + l, height, barPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onValueChanged(float value) {
|
||||
if (colorPicker != null)
|
||||
colorPicker.setAlphaValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawHandle(Canvas canvas, float x, float y) {
|
||||
solid.setColor(color);
|
||||
solid.setAlpha(Math.round(value * 255));
|
||||
if (showBorder) canvas.drawCircle(x, y, handleRadius, clearingStroke);
|
||||
if (value < 1) {
|
||||
// this fixes the same artifact issue from ColorPickerView
|
||||
// happens when alpha pattern is drawn underneath a circle with the same size
|
||||
clearBitmapCanvas.drawColor(0, PorterDuff.Mode.CLEAR);
|
||||
clearBitmapCanvas.drawCircle(x, y, handleRadius * 0.75f + 4, alphaPatternPaint);
|
||||
clearBitmapCanvas.drawCircle(x, y, handleRadius * 0.75f + 4, solid);
|
||||
|
||||
clearStroke = PaintBuilder.newPaint().color(0xffffffff).style(Paint.Style.STROKE).stroke(6).xPerMode(PorterDuff.Mode.CLEAR).build();
|
||||
clearBitmapCanvas.drawCircle(x, y, handleRadius * 0.75f + (clearStroke.getStrokeWidth() / 2), clearStroke);
|
||||
canvas.drawBitmap(clearBitmap, 0, 0, null);
|
||||
} else {
|
||||
canvas.drawCircle(x, y, handleRadius * 0.75f, solid);
|
||||
}
|
||||
}
|
||||
|
||||
public void setColorPicker(ColorPickerView colorPicker) {
|
||||
this.colorPicker = colorPicker;
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
this.value = Utils.getAlphaPercent(color);
|
||||
if (bar != null) {
|
||||
updateBar();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
package com.colorpicker.slider;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import com.colorpicker.ColorPickerView;
|
||||
import com.colorpicker.Utils;
|
||||
import com.colorpicker.builder.PaintBuilder;
|
||||
public class LightnessSlider extends AbsCustomSlider {
|
||||
private int color;
|
||||
private Paint barPaint = PaintBuilder.newPaint().build();
|
||||
private Paint solid = PaintBuilder.newPaint().build();
|
||||
private Paint clearingStroke = PaintBuilder.newPaint().color(0xffffffff).xPerMode(PorterDuff.Mode.CLEAR).build();
|
||||
|
||||
private ColorPickerView colorPicker;
|
||||
|
||||
public LightnessSlider(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public LightnessSlider(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public LightnessSlider(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawBar(Canvas barCanvas) {
|
||||
int width = barCanvas.getWidth();
|
||||
int height = barCanvas.getHeight();
|
||||
|
||||
float[] hsv = new float[3];
|
||||
Color.colorToHSV(color, hsv);
|
||||
int l = Math.max(2, width / 256);
|
||||
for (int x = 0; x <= width; x += l) {
|
||||
hsv[2] = (float) x / (width - 1);
|
||||
barPaint.setColor(Color.HSVToColor(hsv));
|
||||
barCanvas.drawRect(x, 0, x + l, height, barPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onValueChanged(float value) {
|
||||
if (colorPicker != null)
|
||||
colorPicker.setLightness(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void drawHandle(Canvas canvas, float x, float y) {
|
||||
solid.setColor(Utils.colorAtLightness(color, value));
|
||||
if (showBorder) canvas.drawCircle(x, y, handleRadius, clearingStroke);
|
||||
canvas.drawCircle(x, y, handleRadius * 0.75f, solid);
|
||||
}
|
||||
|
||||
public void setColorPicker(ColorPickerView colorPicker) {
|
||||
this.colorPicker = colorPicker;
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
this.color = color;
|
||||
this.value = Utils.lightnessOfColor(color);
|
||||
if (bar != null) {
|
||||
updateBar();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package com.colorpicker.slider;
|
||||
|
||||
public interface OnValueChangedListener {
|
||||
void onValueChanged(float value);
|
||||
}
|
257
app/src/main/java/com/dirror/lyricviewx/ILyricViewX.kt
Normal file
257
app/src/main/java/com/dirror/lyricviewx/ILyricViewX.kt
Normal file
|
@ -0,0 +1,257 @@
|
|||
package com.dirror.lyricviewx
|
||||
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.text.Layout
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.FloatRange
|
||||
import androidx.annotation.Px
|
||||
import java.io.File
|
||||
|
||||
const val GRAVITY_CENTER = 0 // 居中
|
||||
const val GRAVITY_LEFT = 1 // 左
|
||||
const val GRAVITY_RIGHT = 2 // 右
|
||||
|
||||
fun Int.toLayoutAlign(): Layout.Alignment {
|
||||
return when (this) {
|
||||
GRAVITY_LEFT -> Layout.Alignment.ALIGN_NORMAL
|
||||
GRAVITY_CENTER -> Layout.Alignment.ALIGN_CENTER
|
||||
GRAVITY_RIGHT -> Layout.Alignment.ALIGN_OPPOSITE
|
||||
else -> Layout.Alignment.ALIGN_CENTER
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* LyricViewX 接口
|
||||
* 从 LyricViewX 提取,方便管理
|
||||
*
|
||||
* @author Moriafly
|
||||
* @since 2021年1月28日16:29:16
|
||||
*/
|
||||
interface LyricViewXInterface {
|
||||
|
||||
/**
|
||||
* 设置整句之间的间隔高度
|
||||
* @param height px
|
||||
*/
|
||||
fun setSentenceDividerHeight(@Px height: Float)
|
||||
|
||||
/**
|
||||
* 设置原句与翻译之间的间隔高度
|
||||
* @param height px
|
||||
*/
|
||||
fun setTranslateDividerHeight(@Px height: Float)
|
||||
|
||||
/**
|
||||
* 设置歌词整体的垂直偏移值,配合[setHorizontalOffsetPercent]使用
|
||||
* @param offset px
|
||||
*
|
||||
* @see [setHorizontalOffsetPercent]
|
||||
*/
|
||||
fun setHorizontalOffset(@Px offset: Float)
|
||||
|
||||
/**
|
||||
* 设置歌词整体的垂直偏移,相对于控件高度的百分比,0.5f即表示居中,配合[setHorizontalOffset]使用
|
||||
*
|
||||
* @param percent 0.0f ~ 1.0f
|
||||
*
|
||||
* @see [setHorizontalOffset]
|
||||
*/
|
||||
fun setHorizontalOffsetPercent(@FloatRange(from = 0.0, to = 1.0) percent: Float)
|
||||
|
||||
/**
|
||||
* 设置翻译相对与原词之间的缩放比例值
|
||||
* @param scaleValue 一般来说 0.8f 是个不错的值
|
||||
*/
|
||||
fun setTranslateTextScaleValue(@FloatRange(from = 0.1, to = 2.0) scaleValue: Float)
|
||||
|
||||
/**
|
||||
* 设置文字的对齐方向
|
||||
*/
|
||||
fun setTextGravity(gravity: Int)
|
||||
|
||||
/**
|
||||
* 设置非当前行歌词字体颜色 [normalColor]
|
||||
*/
|
||||
fun setNormalColor(@ColorInt normalColor: Int)
|
||||
|
||||
/**
|
||||
* 普通歌词文本字体大小 [size],单位 px
|
||||
*/
|
||||
fun setNormalTextSize(@Px size: Float)
|
||||
|
||||
/**
|
||||
* 当前歌词文本字体大小
|
||||
*/
|
||||
fun setCurrentTextSize(size: Float)
|
||||
|
||||
/**
|
||||
* 设置当前行歌词的字体颜色
|
||||
*/
|
||||
fun setCurrentColor(currentColor: Int)
|
||||
|
||||
/**
|
||||
* 设置拖动歌词时选中歌词的字体颜色
|
||||
*/
|
||||
fun setTimelineTextColor(timelineTextColor: Int)
|
||||
|
||||
/**
|
||||
* 设置拖动歌词时时间线的颜色
|
||||
*/
|
||||
fun setTimelineColor(timelineColor: Int)
|
||||
|
||||
/**
|
||||
* 设置拖动歌词时右侧时间字体颜色
|
||||
*/
|
||||
fun setTimeTextColor(timeTextColor: Int)
|
||||
|
||||
/**
|
||||
* 设置歌词为空时屏幕中央显示的文字 [label],如“暂无歌词”
|
||||
*/
|
||||
fun setLabel(label: String)
|
||||
|
||||
/**
|
||||
* 加载歌词文本
|
||||
* 两种语言的歌词时间戳需要一致
|
||||
*
|
||||
* @param mainLyricText 第一种语言歌词文本
|
||||
* @param secondLyricText 可选,第二种语言歌词文本
|
||||
*/
|
||||
fun loadLyric(mainLyricText: String?, secondLyricText: String? = null)
|
||||
|
||||
/**
|
||||
* 加载歌词 [LyricEntry] 集合
|
||||
* 如果你在 Service 等地方自行解析歌词包装成 [LyricEntry] 集合,那么可以使用此方法载入歌词
|
||||
*
|
||||
* @param lyricEntries 歌词集合
|
||||
* @since 1.3.1
|
||||
*/
|
||||
fun loadLyric(lyricEntries: List<LyricEntry>)
|
||||
|
||||
/**
|
||||
* 刷新歌词
|
||||
*
|
||||
* @param time 当前播放时间
|
||||
*/
|
||||
fun updateTime(time: Long, force: Boolean = false)
|
||||
|
||||
/**
|
||||
* 设置歌词是否允许拖动
|
||||
*
|
||||
* @param draggable 是否允许拖动
|
||||
* @param onPlayClickListener 设置歌词拖动后播放按钮点击监听器,如果允许拖动,则不能为 null
|
||||
*/
|
||||
fun setDraggable(draggable: Boolean, onPlayClickListener: OnPlayClickListener?)
|
||||
|
||||
/**
|
||||
* 设置单击
|
||||
*/
|
||||
fun setOnSingerClickListener(onSingerClickListener: OnSingleClickListener?)
|
||||
|
||||
/**
|
||||
* 获取当前歌词每句实体,可用于歌词分享
|
||||
*
|
||||
* @return LyricEntry 集合
|
||||
*/
|
||||
fun getLyricEntryList(): List<LyricEntry>
|
||||
|
||||
/**
|
||||
* 设置当前歌词每句实体
|
||||
*/
|
||||
fun setLyricEntryList(newList: List<LyricEntry>)
|
||||
|
||||
/**
|
||||
* 获取当前行歌词
|
||||
*/
|
||||
fun getCurrentLineLyricEntry(): LyricEntry?
|
||||
|
||||
/**
|
||||
* 为歌词设置自定义的字体
|
||||
*
|
||||
* @param file 字体文件
|
||||
*/
|
||||
fun setLyricTypeface(file: File)
|
||||
|
||||
/**
|
||||
* 为歌词设置自定义的字体
|
||||
*
|
||||
* @param path 字体文件路径
|
||||
*/
|
||||
fun setLyricTypeface(path: String)
|
||||
|
||||
/**
|
||||
* 为歌词设置自定义的字体,可为空,若为空则应清除字体
|
||||
*
|
||||
* @param typeface 字体对象
|
||||
*/
|
||||
fun setLyricTypeface(typeface: Typeface?)
|
||||
|
||||
/**
|
||||
* 为歌词的过渡动画设置阻尼比(数值越大,回弹次数越多)
|
||||
*
|
||||
* @param dampingRatio 阻尼比 详见[androidx.dynamicanimation.animation.SpringForce]
|
||||
*/
|
||||
fun setDampingRatioForLyric(dampingRatio: Float)
|
||||
|
||||
/**
|
||||
* 为歌词视图的滚动动画设置阻尼比(数值越大,回弹次数越多)
|
||||
*
|
||||
* @param dampingRatio 阻尼比 详见[androidx.dynamicanimation.animation.SpringForce]
|
||||
*/
|
||||
fun setDampingRatioForViewPort(dampingRatio: Float)
|
||||
|
||||
/**
|
||||
* 为歌词的过渡动画设置刚度(数值越大,动画越短)
|
||||
*
|
||||
* @param stiffness 刚度 详见[androidx.dynamicanimation.animation.SpringForce]
|
||||
*/
|
||||
fun setStiffnessForLyric(stiffness: Float)
|
||||
|
||||
/**
|
||||
* 为歌词视图的滚动动画设置刚度(数值越大,动画越短)
|
||||
*
|
||||
* @param stiffness 刚度 详见[androidx.dynamicanimation.animation.SpringForce]
|
||||
*/
|
||||
fun setStiffnessForViewPort(stiffness: Float)
|
||||
|
||||
/**
|
||||
* 设置跳转播放按钮
|
||||
*/
|
||||
fun setPlayDrawable(drawable: Drawable)
|
||||
|
||||
/**
|
||||
* 设置是否绘制歌词翻译
|
||||
*/
|
||||
fun setIsDrawTranslation(isDrawTranslation: Boolean)
|
||||
|
||||
/**
|
||||
* 是否开启特定的模糊效果
|
||||
*/
|
||||
fun setIsEnableBlurEffect(isEnableBlurEffect: Boolean)
|
||||
|
||||
/**
|
||||
* 设置元素的偏移百分比,0.5f即表示居中
|
||||
*
|
||||
* @param itemOffsetPercent 0f ~ 1f 偏移百分比
|
||||
*/
|
||||
fun setItemOffsetPercent(@FloatRange(from = 0.0, to = 1.0) itemOffsetPercent: Float)
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放按钮点击监听器,点击后应该跳转到指定播放位置
|
||||
*/
|
||||
interface OnPlayClickListener {
|
||||
/**
|
||||
* 播放按钮被点击,应该跳转到指定播放位置
|
||||
*
|
||||
* @return 是否成功消费该事件,如果成功消费,则会更新UI
|
||||
*/
|
||||
fun onPlayClick(time: Long): Boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击歌词布局
|
||||
*/
|
||||
interface OnSingleClickListener {
|
||||
fun onClick()
|
||||
}
|
82
app/src/main/java/com/dirror/lyricviewx/LyricEntry.kt
Normal file
82
app/src/main/java/com/dirror/lyricviewx/LyricEntry.kt
Normal file
|
@ -0,0 +1,82 @@
|
|||
package com.dirror.lyricviewx
|
||||
|
||||
import android.os.Build
|
||||
import android.text.Layout
|
||||
import android.text.StaticLayout
|
||||
import android.text.TextPaint
|
||||
|
||||
/**
|
||||
* 一行歌词实体
|
||||
* @since 2021年1月19日09:51:40 Moriafly 基于 LrcEntry 改造,转换为 kt ,移除部分过时方法
|
||||
* @param time 歌词时间
|
||||
* @param text 歌词文本
|
||||
*/
|
||||
class LyricEntry(@JvmField val time: Long, @JvmField val text: String) : Comparable<LyricEntry> {
|
||||
|
||||
/**
|
||||
* 第二文本
|
||||
*/
|
||||
@JvmField
|
||||
var secondText: String? = null
|
||||
|
||||
/**
|
||||
* staticLayout
|
||||
*/
|
||||
var staticLayout: StaticLayout? = null
|
||||
private set
|
||||
var secondStaticLayout: StaticLayout? = null
|
||||
private set
|
||||
|
||||
@Deprecated("存在不显示翻译的情况,会导致offset发生改变,故不再固定存储offset")
|
||||
/**
|
||||
* 歌词距离视图顶部的距离
|
||||
*/
|
||||
var offset = Float.MIN_VALUE
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
* @param textPaint 文本画笔
|
||||
* @param width 宽度
|
||||
* @param align 位置
|
||||
*/
|
||||
fun init(
|
||||
textPaint: TextPaint,
|
||||
secondTextPaint: TextPaint,
|
||||
width: Int, align: Layout.Alignment
|
||||
) {
|
||||
staticLayout = createStaticLayout(text, textPaint, width, align)
|
||||
secondStaticLayout = createStaticLayout(secondText, secondTextPaint, width, align)
|
||||
offset = Float.MIN_VALUE
|
||||
}
|
||||
|
||||
/**
|
||||
* 继承 Comparable 比较
|
||||
* @param other LyricEntry
|
||||
* @return 时间差
|
||||
*/
|
||||
override fun compareTo(other: LyricEntry): Int {
|
||||
return (time - other.time).toInt()
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun createStaticLayout(
|
||||
text: String?,
|
||||
paint: TextPaint,
|
||||
width: Number,
|
||||
align: Layout.Alignment
|
||||
): StaticLayout? {
|
||||
if (text.isNullOrEmpty()) return null
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
StaticLayout.Builder
|
||||
.obtain(text, 0, text.length, paint, width.toInt())
|
||||
.setAlignment(align)
|
||||
.setLineSpacing(0f, 1f)
|
||||
.setIncludePad(false)
|
||||
.build()
|
||||
} else {
|
||||
StaticLayout(text, paint, width.toInt(), align, 1f, 0f, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
289
app/src/main/java/com/dirror/lyricviewx/LyricUtil.kt
Normal file
289
app/src/main/java/com/dirror/lyricviewx/LyricUtil.kt
Normal file
|
@ -0,0 +1,289 @@
|
|||
package com.dirror.lyricviewx
|
||||
|
||||
import android.animation.ArgbEvaluator
|
||||
import android.animation.ValueAnimator
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Rect
|
||||
import android.text.TextUtils
|
||||
import android.text.format.DateUtils
|
||||
import android.view.MotionEvent
|
||||
import java.io.*
|
||||
import java.net.HttpURLConnection
|
||||
import java.net.URL
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.util.*
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* 工具类
|
||||
* 原 LrcUtils 转 Kotlin
|
||||
*/
|
||||
object LyricUtil {
|
||||
|
||||
private val PATTERN_LINE = Pattern.compile("((\\[\\d\\d:\\d\\d\\.\\d{2,3}])+)(.+)")
|
||||
private val PATTERN_TIME = Pattern.compile("\\[(\\d\\d):(\\d\\d)\\.(\\d{2,3})]")
|
||||
private val argbEvaluator = ArgbEvaluator()
|
||||
|
||||
/**
|
||||
* 从文件解析双语歌词
|
||||
*/
|
||||
fun parseLrc(lrcFiles: Array<out File?>?): List<LyricEntry>? {
|
||||
if (lrcFiles == null || lrcFiles.size != 2 || lrcFiles[0] == null) {
|
||||
return null
|
||||
}
|
||||
val mainLrcFile = lrcFiles[0]
|
||||
val secondLrcFile = lrcFiles[1]
|
||||
val mainEntryList = parseLrc(mainLrcFile)
|
||||
val secondEntryList = parseLrc(secondLrcFile)
|
||||
if (mainEntryList != null && secondEntryList != null) {
|
||||
for (mainEntry in mainEntryList) {
|
||||
for (secondEntry in secondEntryList) {
|
||||
if (mainEntry.time == secondEntry.time) {
|
||||
mainEntry.secondText = secondEntry.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mainEntryList
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文件解析歌词
|
||||
*/
|
||||
private fun parseLrc(lrcFile: File?): List<LyricEntry>? {
|
||||
if (lrcFile == null || !lrcFile.exists()) {
|
||||
return null
|
||||
}
|
||||
val entryList: MutableList<LyricEntry> = ArrayList()
|
||||
try {
|
||||
val br =
|
||||
BufferedReader(InputStreamReader(FileInputStream(lrcFile), StandardCharsets.UTF_8))
|
||||
var line: String
|
||||
while (br.readLine().also { line = it } != null) {
|
||||
val list = parseLine(line)
|
||||
if (list != null && list.isNotEmpty()) {
|
||||
entryList.addAll(list)
|
||||
}
|
||||
}
|
||||
br.close()
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
entryList.sort()
|
||||
return entryList
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文本解析双语歌词
|
||||
*/
|
||||
fun parseLrc(lrcTexts: Array<out String?>?): List<LyricEntry>? {
|
||||
if (lrcTexts == null || lrcTexts.size != 2 || TextUtils.isEmpty(lrcTexts[0])) {
|
||||
return null
|
||||
}
|
||||
val mainLrcText = lrcTexts[0]
|
||||
val secondLrcText = lrcTexts[1]
|
||||
val mainEntryList = mainLrcText?.let { parseLrc(it) }
|
||||
|
||||
/**
|
||||
* 当输入的secondLrcText为空时,按如下格式解析歌词
|
||||
* (音乐标签下载的第二种歌词格式)
|
||||
*
|
||||
* [00:21.11]いつも待ち合わせより15分前集合
|
||||
* [00:21.11]总会比相约时间早15分钟集合
|
||||
* [00:28.32]駅の改札ぬける
|
||||
* [00:28.32]穿过车站的检票口
|
||||
* [00:31.39]ざわめきにわくわくだね
|
||||
* [00:31.39]嘈杂声令内心兴奋不已
|
||||
* [00:35.23]どこへ向かうかなんて
|
||||
* [00:35.23]不在意接下来要去哪里
|
||||
*/
|
||||
if (TextUtils.isEmpty(secondLrcText)) {
|
||||
var lastEntry: LyricEntry? = null
|
||||
return mainEntryList?.filter { now ->
|
||||
if (lastEntry == null) {
|
||||
lastEntry = now
|
||||
return@filter true
|
||||
}
|
||||
|
||||
if (lastEntry!!.time == now.time) {
|
||||
lastEntry!!.secondText = now.text
|
||||
lastEntry = null
|
||||
return@filter false
|
||||
}
|
||||
|
||||
lastEntry = now
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
val secondEntryList = secondLrcText?.let { parseLrc(it) }
|
||||
if (mainEntryList != null && secondEntryList != null) {
|
||||
for (mainEntry in mainEntryList) {
|
||||
for (secondEntry in secondEntryList) {
|
||||
if (mainEntry.time == secondEntry.time) {
|
||||
mainEntry.secondText = secondEntry.text
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return mainEntryList
|
||||
}
|
||||
|
||||
/**
|
||||
* 从文本解析歌词
|
||||
*/
|
||||
private fun parseLrc(lrcText: String): List<LyricEntry>? {
|
||||
var lyricText = lrcText.trim()
|
||||
if (TextUtils.isEmpty(lyricText)) return null
|
||||
|
||||
if (lyricText.startsWith("\uFEFF")) {
|
||||
lyricText = lyricText.replace("\uFEFF", "")
|
||||
}
|
||||
|
||||
// 针对传入 Language="Media Monkey Format"; Lyrics="......"; 的情况
|
||||
lyricText = lyricText.substringAfter("Lyrics=\"")
|
||||
.substringBeforeLast("\";")
|
||||
|
||||
val entryList: MutableList<LyricEntry> = ArrayList()
|
||||
val array = lyricText.split("\\n".toRegex()).toTypedArray()
|
||||
for (line in array) {
|
||||
val list = parseLine(line)
|
||||
if (!list.isNullOrEmpty()) {
|
||||
entryList.addAll(list)
|
||||
}
|
||||
}
|
||||
entryList.sort()
|
||||
return entryList
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取网络文本,需要在工作线程中执行
|
||||
*/
|
||||
fun getContentFromNetwork(url: String?, charset: String?): String? {
|
||||
var lrcText: String? = null
|
||||
try {
|
||||
val url = URL(url)
|
||||
val conn = url.openConnection() as HttpURLConnection
|
||||
conn.requestMethod = "GET"
|
||||
conn.connectTimeout = 10000
|
||||
conn.readTimeout = 10000
|
||||
if (conn.responseCode == 200) {
|
||||
val `is` = conn.inputStream
|
||||
val bos = ByteArrayOutputStream()
|
||||
val buffer = ByteArray(1024)
|
||||
var len: Int
|
||||
while (`is`.read(buffer).also { len = it } != -1) {
|
||||
bos.write(buffer, 0, len)
|
||||
}
|
||||
`is`.close()
|
||||
bos.close()
|
||||
lrcText = bos.toString(charset)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return lrcText
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析一行歌词
|
||||
*/
|
||||
private fun parseLine(line: String): List<LyricEntry>? {
|
||||
var lyricLine = line
|
||||
if (TextUtils.isEmpty(lyricLine)) {
|
||||
return null
|
||||
}
|
||||
lyricLine = lyricLine.trim { it <= ' ' }
|
||||
// [00:17.65]让我掉下眼泪的
|
||||
val lineMatcher = PATTERN_LINE.matcher(lyricLine)
|
||||
if (!lineMatcher.matches()) {
|
||||
return null
|
||||
}
|
||||
val times = lineMatcher.group(1)!!
|
||||
val text = lineMatcher.group(3)!!
|
||||
val entryList: MutableList<LyricEntry> = ArrayList()
|
||||
|
||||
// [00:17.65]
|
||||
val timeMatcher = PATTERN_TIME.matcher(times)
|
||||
while (timeMatcher.find()) {
|
||||
val min = timeMatcher.group(1)!!.toLong()
|
||||
val sec = timeMatcher.group(2)!!.toLong()
|
||||
val milString = timeMatcher.group(3)!!
|
||||
var mil = milString.toLong()
|
||||
// 如果毫秒是两位数,需要乘以 10,when 新增支持 1 - 6 位毫秒,很多获取的歌词存在不同的毫秒位数
|
||||
when (milString.length) {
|
||||
1 -> mil *= 100
|
||||
2 -> mil *= 10
|
||||
4 -> mil /= 10
|
||||
5 -> mil /= 100
|
||||
6 -> mil /= 1000
|
||||
}
|
||||
val time = min * DateUtils.MINUTE_IN_MILLIS + sec * DateUtils.SECOND_IN_MILLIS + mil
|
||||
entryList.add(LyricEntry(time, text))
|
||||
}
|
||||
return entryList
|
||||
}
|
||||
|
||||
/**
|
||||
* 转为[分:秒]
|
||||
*/
|
||||
fun formatTime(milli: Long): String {
|
||||
val m = (milli / DateUtils.MINUTE_IN_MILLIS).toInt()
|
||||
val s = (milli / DateUtils.SECOND_IN_MILLIS % 60).toInt()
|
||||
val mm = String.format(Locale.getDefault(), "%02d", m)
|
||||
val ss = String.format(Locale.getDefault(), "%02d", s)
|
||||
return "$mm:$ss"
|
||||
}
|
||||
|
||||
/**
|
||||
* BUG java.lang.NoSuchFieldException: No field sDurationScale in class Landroid/animation/ValueAnimator; #3
|
||||
*/
|
||||
@SuppressLint("SoonBlockedPrivateApi")
|
||||
@Deprecated("")
|
||||
fun resetDurationScale() {
|
||||
try {
|
||||
val mField = ValueAnimator::class.java.getDeclaredField("sDurationScale")
|
||||
mField.isAccessible = true
|
||||
mField.setFloat(null, 1f)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 结合fraction,计算两个值之间的比例
|
||||
*/
|
||||
fun calcScaleValue(a: Float, b: Float, f: Float, reverse: Boolean = false): Float {
|
||||
if (b == 0f) return 1f
|
||||
return 1f + ((a - b) / b) * (if (reverse) 1f - f else f)
|
||||
}
|
||||
|
||||
/**
|
||||
* 颜色值插值函数
|
||||
*/
|
||||
fun lerpColor(a: Int, b: Int, f: Float): Int {
|
||||
return argbEvaluator.evaluate(f, a, b) as Int
|
||||
}
|
||||
|
||||
/**
|
||||
* 简单的插值函数
|
||||
*/
|
||||
fun lerp(from: Float, to: Float, fraction: Float): Float {
|
||||
return from + (to - from) * fraction
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断MotionEvent是否发生在Rect中
|
||||
*/
|
||||
fun MotionEvent.insideOf(rect: Rect?): Boolean {
|
||||
rect ?: return false
|
||||
return rect.contains(x.toInt(), y.toInt())
|
||||
}
|
||||
|
||||
fun normalize(min: Float, max: Float, value: Float, limit: Boolean = false): Float {
|
||||
if (min == max) return 1f
|
||||
return ((value - min) / (max - min)).let {
|
||||
if (limit) it.coerceIn(0f, 1f) else it
|
||||
}
|
||||
}
|
||||
}
|
1093
app/src/main/java/com/dirror/lyricviewx/LyricViewX.kt
Normal file
1093
app/src/main/java/com/dirror/lyricviewx/LyricViewX.kt
Normal file
File diff suppressed because it is too large
Load Diff
51
app/src/main/java/com/dirror/lyricviewx/ReadyHelper.kt
Normal file
51
app/src/main/java/com/dirror/lyricviewx/ReadyHelper.kt
Normal file
|
@ -0,0 +1,51 @@
|
|||
package com.dirror.lyricviewx
|
||||
|
||||
import androidx.annotation.IntDef
|
||||
|
||||
const val STATE_CREATED = 1
|
||||
const val STATE_INITIALIZING = 2
|
||||
const val STATE_INITIALIZED = 3
|
||||
const val STATE_ERROR = 4
|
||||
|
||||
@IntDef(
|
||||
STATE_CREATED,
|
||||
STATE_INITIALIZING,
|
||||
STATE_INITIALIZED,
|
||||
STATE_ERROR
|
||||
)
|
||||
@Retention(AnnotationRetention.SOURCE)
|
||||
annotation class ReadyState
|
||||
|
||||
/**
|
||||
* 简单的状态机,根据 [readyState] 的状态决定当前任务的执行或延后与否
|
||||
*/
|
||||
open class ReadyHelper {
|
||||
private var readyCallback: (Boolean) -> Unit = {}
|
||||
|
||||
@ReadyState
|
||||
var readyState: Int = STATE_CREATED
|
||||
set(value) {
|
||||
if (field == value) return
|
||||
when (value) {
|
||||
STATE_INITIALIZED,
|
||||
STATE_ERROR -> synchronized(readyCallback) {
|
||||
field = value
|
||||
readyCallback.invoke(value != STATE_ERROR)
|
||||
}
|
||||
else -> field = value
|
||||
}
|
||||
}
|
||||
|
||||
fun whenReady(performAction: (Boolean) -> Unit): Boolean {
|
||||
return when (readyState) {
|
||||
STATE_CREATED, STATE_INITIALIZING -> {
|
||||
readyCallback = performAction
|
||||
false
|
||||
}
|
||||
else -> {
|
||||
performAction(readyState != STATE_ERROR)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.dirror.lyricviewx.extension
|
||||
|
||||
import android.graphics.BlurMaskFilter
|
||||
import android.util.SparseArray
|
||||
|
||||
class BlurMaskFilterExt {
|
||||
private val maskFilterCache = SparseArray<BlurMaskFilter>()
|
||||
|
||||
fun get(radius: Int): BlurMaskFilter? {
|
||||
if (radius == 0 || radius > 25) return null
|
||||
|
||||
return maskFilterCache[radius] ?: BlurMaskFilter(radius.toFloat(), BlurMaskFilter.Blur.NORMAL)
|
||||
.also { maskFilterCache.put(radius, it) }
|
||||
}
|
||||
}
|
123
app/src/main/java/com/muqingbfq/HomeSteer.java
Normal file
123
app/src/main/java/com/muqingbfq/HomeSteer.java
Normal file
|
@ -0,0 +1,123 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.Editable;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.fragment.wode;
|
||||
import com.muqingbfq.login.user_logs;
|
||||
import com.muqingbfq.mq.EditViewDialog;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
import com.muqingbfq.view.Edit;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class HomeSteer {
|
||||
home home;
|
||||
ActivityResultLauncher<Intent> dlintent;
|
||||
|
||||
public HomeSteer(home home) {
|
||||
this.home = home;
|
||||
dlintent = home.registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
// 处理返回结果
|
||||
Intent data = result.getData();
|
||||
boolean bool = data.getBooleanExtra("bool", false);
|
||||
if (bool) {
|
||||
Yes();
|
||||
return;
|
||||
}
|
||||
}
|
||||
One();
|
||||
});
|
||||
SetIP();
|
||||
// One();
|
||||
|
||||
}
|
||||
|
||||
public void One() {
|
||||
MaterialAlertDialogBuilder materialAlertDialogBuilder = new MaterialAlertDialogBuilder(home);
|
||||
materialAlertDialogBuilder.setTitle("引导登陆");
|
||||
materialAlertDialogBuilder.setItems(new String[]{"游客", "登陆"}, (dialog, which) -> {
|
||||
if (which == 0) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
//获取游客Cookie
|
||||
String hq = wl.hq("/register/anonimous");
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
wl.setcookie(jsonObject.getString("cookie"));
|
||||
home.runOnUiThread(() -> Yes());
|
||||
} catch (Exception e) {
|
||||
home.runOnUiThread(() -> Toast.makeText(home, "游客登陆失败:" + e.getMessage(), Toast.LENGTH_SHORT).show());
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
} else if (which == 1) {
|
||||
dlintent.launch(new Intent(home, user_logs.class));
|
||||
}
|
||||
});
|
||||
materialAlertDialogBuilder.show();
|
||||
}
|
||||
|
||||
public void Yes() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置IP地址
|
||||
*/
|
||||
public void SetIP() {
|
||||
SharedPreferences nickname = home.getSharedPreferences("Set_up", Context.MODE_PRIVATE);
|
||||
if (nickname.getString("IP", "").isEmpty()) {
|
||||
EditViewDialog editViewDialog = new EditViewDialog(home, "IP");
|
||||
editViewDialog.setMessage("请输入部署了NeteaseCloudMusicApi的服务器地址,\n例如" +
|
||||
"https://api.csm.sayqz.com");
|
||||
// editViewDialog.setPositive()
|
||||
editViewDialog.buttonb.setEnabled(false);
|
||||
editViewDialog.editText.setMaxLines(1);
|
||||
editViewDialog.editText.addTextChangedListener(new Edit.TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence var1, int var2, int var3, int var4) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence var1, int var2, int var3, int var4) {
|
||||
//正则表达式检查是否为 https://api.csm.sayqz.com这样的
|
||||
editViewDialog.buttonb.setEnabled(var1.toString().matches("^(https?://).+[^/]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable var1) {
|
||||
|
||||
}
|
||||
});
|
||||
editViewDialog.setPositive(v -> {
|
||||
main.api = editViewDialog.getEditText();
|
||||
nickname.edit().putString("IP", editViewDialog.getEditText()).apply();
|
||||
One();
|
||||
editViewDialog.dismiss();
|
||||
});
|
||||
editViewDialog.show();
|
||||
} else {
|
||||
main.api = nickname.getString("IP", "");
|
||||
One();
|
||||
}
|
||||
}
|
||||
}
|
56
app/src/main/java/com/muqingbfq/MP3.java
Normal file
56
app/src/main/java/com/muqingbfq/MP3.java
Normal file
|
@ -0,0 +1,56 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Objects;
|
||||
|
||||
public class MP3 implements Serializable {
|
||||
public String id, name, zz, url;
|
||||
// 音乐的贴图
|
||||
public String picurl;
|
||||
public MP3(){}
|
||||
|
||||
|
||||
public MP3(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public MP3(String id, String name, String zz, String picurl) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.zz = zz;
|
||||
this.picurl = picurl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof MP3)) return false;
|
||||
MP3 mp3 = (MP3) o;
|
||||
if (id.equals(mp3.id)) {
|
||||
return true;
|
||||
}
|
||||
return Objects.equals(id, mp3.id) &&
|
||||
Objects.equals(name, mp3.name) &&
|
||||
Objects.equals(zz, mp3.zz) &&
|
||||
Objects.equals(picurl, mp3.picurl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(id, name, zz, picurl);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public String toString() {
|
||||
return "MP3{" +
|
||||
"id='" + id + '\'' +
|
||||
", name='" + name + '\'' +
|
||||
", zz='" + zz + '\'' +
|
||||
", picurl=" + picurl +
|
||||
",url=" + url +
|
||||
'}';
|
||||
}
|
||||
}
|
217
app/src/main/java/com/muqingbfq/PlaybackService.java
Normal file
217
app/src/main/java/com/muqingbfq/PlaybackService.java
Normal file
|
@ -0,0 +1,217 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.PlaybackException;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.Tracks;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.exoplayer.ExoPlayer;
|
||||
import androidx.media3.session.MediaSession;
|
||||
import androidx.media3.session.MediaSessionService;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.gson.Gson;
|
||||
import com.muqingbfq.api.url;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class PlaybackService extends MediaSessionService {
|
||||
public static MediaSession mediaSession = null;
|
||||
public static List<MP3> list = new ArrayList<>();
|
||||
|
||||
//历史记录
|
||||
// public static final List<MediaItem> listHistory = new ArrayList<>();
|
||||
|
||||
public static void ListSave() {
|
||||
new Thread(() -> wj.xrwb(wj.filesdri + "list.json", new Gson().toJson(list))).start();
|
||||
}
|
||||
|
||||
|
||||
public Player.Listener PlayerListener = new Player.Listener() {
|
||||
@Override
|
||||
public void onPlayWhenReadyChanged(boolean playWhenReady, int reason) {
|
||||
// 处理 playWhenReady 的变化
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlaybackStateChanged(int playbackState) {
|
||||
// 处理播放状态的变化
|
||||
if (playbackState == Player.STATE_ENDED) {
|
||||
// 检查当前播放的媒体项是否是最后一项
|
||||
Player player = mediaSession.getPlayer();
|
||||
if (player.getCurrentMediaItemIndex() == player.getMediaItemCount() - 1) {
|
||||
// 如果是最后一项,回到第一项并播放
|
||||
player.seekTo(0,0);
|
||||
player.play();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionDiscontinuity(@NonNull Player.PositionInfo oldPosition, @NonNull Player.PositionInfo newPosition, int reason) {
|
||||
// 处理位置不连续变化
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMediaItemTransition(@Nullable MediaItem mediaItem, @Player.MediaItemTransitionReason int reason) {
|
||||
if (mediaItem != null) {
|
||||
// 输出当前的 MediaItem 信息
|
||||
String title = mediaItem.mediaMetadata.title != null ? mediaItem.mediaMetadata.title.toString() : "未知标题";
|
||||
String artist = mediaItem.mediaMetadata.artist != null ? mediaItem.mediaMetadata.artist.toString() : "未知艺术家";
|
||||
|
||||
// bfqkz.lishi_list.removeIf(mp3 -> mp3.id.equals(mediaItem.mediaId));
|
||||
// bfqkz.lishi_list.add(0, new MP3(mediaItem.mediaId, title, artist, mediaItem.mediaMetadata.artworkUri.toString()));
|
||||
// new Thread(() -> wj.xrwb(wj.gd + "mp3_hc.json", new Gson().toJson(bfqkz.lishi_list))).start();
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void onTracksChanged(@Nullable Tracks tracks) {
|
||||
gj.sc(tracks);
|
||||
// Update UI using current tracks.
|
||||
}
|
||||
|
||||
int error_count = 0;//设置错误次数
|
||||
MediaItem currentMediaItem = null;
|
||||
int currentIndex = 0;
|
||||
|
||||
@Override
|
||||
public void onPlayerError(@NonNull PlaybackException error) {
|
||||
// 当播放发生错误时调用
|
||||
// 如果错误是由于资源找不到
|
||||
Player player = mediaSession.getPlayer();
|
||||
if (++error_count > 3) {
|
||||
currentIndex = player.getNextMediaItemIndex();
|
||||
if (currentIndex != C.INDEX_UNSET) {
|
||||
gj.sc("播放失败,已跳过");
|
||||
return;
|
||||
} else {
|
||||
currentMediaItem = player.getMediaItemAt(currentIndex); // 获取下一首的 MediaItem
|
||||
}
|
||||
} else {
|
||||
currentMediaItem = player.getCurrentMediaItem();
|
||||
currentIndex = player.getCurrentMediaItemIndex(); // 获取当前播放项的索引
|
||||
}
|
||||
new Thread(() -> {
|
||||
MP3 hq = url.hq(new MP3(currentMediaItem.mediaId));
|
||||
hq.picurl = url.picurl(hq.id);
|
||||
// 设置新的 MediaItem
|
||||
MediaItem newMediaItem = currentMediaItem.buildUpon()
|
||||
.setUri(hq.url) // 更新 URI
|
||||
.build(); // 构建新的 MediaItem
|
||||
main.handler.post(() -> {
|
||||
player.replaceMediaItem(currentIndex, newMediaItem);
|
||||
player.prepare();
|
||||
player.play();
|
||||
error_count = 0;
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@UnstableApi
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
ExoPlayer player = new ExoPlayer.Builder(this)
|
||||
.build();
|
||||
mediaSession = new MediaSession.Builder(this, player).build();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.setComponent(new ComponentName(this, home.class));//用ComponentName得到class对象
|
||||
intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);// 关键的一步,设置启动模式,两种情况
|
||||
PendingIntent pendingIntent = PendingIntent.getActivity(
|
||||
this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
||||
mediaSession.setSessionActivity(pendingIntent);
|
||||
// 添加 ExoPlayer 监听器
|
||||
player.addListener(PlayerListener);
|
||||
|
||||
|
||||
String nickname = wj.dqwb(wj.filesdri + "list.json");
|
||||
if (!Strings.isNullOrEmpty(nickname)) {
|
||||
try {
|
||||
list = new Gson().fromJson(nickname, new com.google.gson.reflect.TypeToken<List<MP3>>() {
|
||||
}.getType());
|
||||
for (MP3 mediaItem : list) {
|
||||
player.addMediaItem(PlaybackService.GetMp3(mediaItem));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
list = new ArrayList<>();
|
||||
wj.sc(wj.filesdri + "list.json");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public MediaSession onGetSession(@NonNull MediaSession.ControllerInfo controllerInfo) {
|
||||
return mediaSession;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
mediaSession.getPlayer().release();
|
||||
mediaSession.release();
|
||||
mediaSession = null;
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
public static MediaItem GetMp3(MP3 mp3) {
|
||||
// 创建媒体的元数据(如标题、描述、图片等)
|
||||
MediaMetadata mediaMetadata = new MediaMetadata.Builder()
|
||||
.setTitle(mp3.name)
|
||||
.setArtist(mp3.zz)
|
||||
.setAlbumTitle(mp3.zz)
|
||||
.setArtworkUri(Uri.parse(mp3.picurl)) // 图片URL
|
||||
.build();
|
||||
// 创建带有元数据的 MediaItem
|
||||
|
||||
return new MediaItem.Builder()
|
||||
.setMediaId(mp3.id) // 设置媒体的唯一ID
|
||||
.setUri(Strings.isNullOrEmpty(mp3.url) ? "" : mp3.url)
|
||||
.setMediaMetadata(mediaMetadata) // 将元数据添加到 MediaItem
|
||||
.build();
|
||||
}
|
||||
|
||||
public static void AddMediaItem(MP3 mp3) {
|
||||
if (mediaSession != null) {
|
||||
new Thread(() -> {
|
||||
MediaItem mediaItem = GetMp3(mp3);
|
||||
main.handler.post(() -> {
|
||||
Player player = mediaSession.getPlayer();
|
||||
for (int i = 0; i < player.getMediaItemCount(); i++) {
|
||||
MediaItem existingItem = player.getMediaItemAt(i);
|
||||
if (Objects.equals(existingItem.mediaId, mp3.id)) {
|
||||
player.seekTo(i, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
player.addMediaItem(0, mediaItem);
|
||||
player.seekTo(0, 0);
|
||||
player.prepare();
|
||||
player.play();
|
||||
});
|
||||
}).start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
22
app/src/main/java/com/muqingbfq/XM.java
Normal file
22
app/src/main/java/com/muqingbfq/XM.java
Normal file
|
@ -0,0 +1,22 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
public class XM {
|
||||
public String id, name, message;
|
||||
public Object picurl;
|
||||
public XM(String id, String name, String picurl) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.picurl = picurl;
|
||||
}
|
||||
public XM(String id, String name,String message, String picurl) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.picurl = picurl;
|
||||
this.message = message;
|
||||
}
|
||||
public XM(String id, String name, int picurl) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.picurl = picurl;
|
||||
}
|
||||
}
|
544
app/src/main/java/com/muqingbfq/activity/Music.java
Normal file
544
app/src/main/java/com/muqingbfq/activity/Music.java
Normal file
|
@ -0,0 +1,544 @@
|
|||
package com.muqingbfq.activity;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.util.Util;
|
||||
import androidx.palette.graphics.Palette;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.bfq_an;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.ActivityMusicBinding;
|
||||
import com.muqingbfq.fragment.Media;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.MusicViewModel;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
|
||||
public class Music extends AppCompatActivity<ActivityMusicBinding> implements GestureDetector.OnGestureListener {
|
||||
|
||||
private Player player = PlaybackService.mediaSession.getPlayer();
|
||||
private int TdtHeight = 15;
|
||||
public static Bitmap backgroundbitmap=null;
|
||||
|
||||
public static void startActivity(Context context, MP3 mp3) {
|
||||
Intent intent = new Intent(context, Music.class);
|
||||
intent.putExtra("MP3", mp3);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void startActivity(Context context) {
|
||||
Intent intent = new Intent(context, Music.class);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
|
||||
GestureDetector gestureDetector;
|
||||
float Minfloat = 1000f;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
gestureDetector = new GestureDetector(this, this);
|
||||
setContentView();
|
||||
color(backgroundbitmap);
|
||||
|
||||
// 获取屏幕的高度
|
||||
WindowManager windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
// 创建新的MarginLayoutParams
|
||||
ViewGroup.LayoutParams layoutParams = binding.toolbar.getLayoutParams();
|
||||
// 设置margin
|
||||
((ViewGroup.MarginLayoutParams) layoutParams).setMargins(0, systemBars.top, 0, 0);
|
||||
binding.toolbar.setLayoutParams(layoutParams);
|
||||
if (gj.isTablet(this)) {
|
||||
// binding.image2.setLayoutParams(layoutParams);
|
||||
ViewGroup.LayoutParams layoutParams1 = binding.cardview.getLayoutParams();
|
||||
layoutParams1.width = displayMetrics.heightPixels / 2;
|
||||
layoutParams1.height = displayMetrics.heightPixels / 2;
|
||||
|
||||
}
|
||||
return insets;
|
||||
});
|
||||
|
||||
Minfloat = displayMetrics.heightPixels - displayMetrics.heightPixels / 3f;
|
||||
binding.kg.setOnClickListener(view -> Util.handlePlayPauseButtonAction(player));
|
||||
binding.xyq.setOnClickListener(v -> {
|
||||
boolean b = player.hasNextMediaItem();
|
||||
if (b) {
|
||||
player.seekToNextMediaItem();
|
||||
} else {
|
||||
gj.ts(v.getContext(), "已经是最后一首了");
|
||||
}
|
||||
});
|
||||
binding.syq.setOnClickListener(v -> {
|
||||
boolean b = player.hasPreviousMediaItem();
|
||||
if (b) {
|
||||
player.seekToPreviousMediaItem();
|
||||
} else {
|
||||
gj.ts(v.getContext(), "已经是第一首了");
|
||||
}
|
||||
});
|
||||
player.addListener(Listener);
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
updateUI(player);
|
||||
}
|
||||
binding.tdt.post(() -> TdtHeight = binding.tdt.getHeight());
|
||||
binding.tdt.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
long actualPosition = (progress * player.getDuration()) / 100;
|
||||
String time = bfq_an.getTime(actualPosition);
|
||||
binding.timeB.setText(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// 获取 View 当前的高度;
|
||||
// 创建一个 ValueAnimator,从当前高度逐渐增加到目标高度
|
||||
ValueAnimator animator = ValueAnimator.ofInt(TdtHeight, TdtHeight + 10);
|
||||
animator.setDuration(300); // 设置动画持续时间为 300 毫秒
|
||||
animator.setInterpolator(new DecelerateInterpolator()); // 设置动画插值器
|
||||
// 在动画过程中更新 View 的高度
|
||||
animator.addUpdateListener(animation -> {
|
||||
// 获取当前动画的值(高度)
|
||||
int animatedValue = (int) animation.getAnimatedValue();
|
||||
|
||||
// 更新 View 的高度
|
||||
ViewGroup.LayoutParams layoutParams = seekBar.getLayoutParams();
|
||||
layoutParams.height = animatedValue;
|
||||
seekBar.setLayoutParams(layoutParams);
|
||||
});
|
||||
|
||||
// 开始动画
|
||||
animator.start();
|
||||
isDrag = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
isDrag = false;
|
||||
int progress = seekBar.getProgress();
|
||||
if (progress >= 100) {
|
||||
player.seekToNextMediaItem();
|
||||
} else {
|
||||
|
||||
long actualPosition = (progress * player.getDuration()) / 100;
|
||||
player.seekTo(actualPosition);
|
||||
}
|
||||
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofInt(TdtHeight, TdtHeight - 10);
|
||||
animator.setDuration(300); // 设置动画持续时间为 300 毫秒
|
||||
animator.setInterpolator(new DecelerateInterpolator()); // 设置动画插值器
|
||||
// 在动画过程中更新 View 的高度
|
||||
animator.addUpdateListener(animation -> {
|
||||
// 获取当前动画的值(高度)
|
||||
int animatedValue = (int) animation.getAnimatedValue();
|
||||
// 更新 View 的高度
|
||||
ViewGroup.LayoutParams layoutParams = seekBar.getLayoutParams();
|
||||
layoutParams.height = animatedValue;
|
||||
seekBar.setLayoutParams(layoutParams);
|
||||
});
|
||||
|
||||
// 开始动画
|
||||
animator.start();
|
||||
}
|
||||
});
|
||||
binding.back.setOnClickListener(v -> finish());
|
||||
|
||||
binding.fragmentBfq.setOnClickListener(v -> {
|
||||
if (binding.cardview.getVisibility() == View.VISIBLE) {
|
||||
binding.cardview.setVisibility(View.GONE);
|
||||
binding.lrcView.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.cardview.setVisibility(View.VISIBLE);
|
||||
binding.lrcView.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
binding.lrcView.setNormalTextSize(80f);
|
||||
binding.lrcView.setCurrentTextSize(100f);
|
||||
binding.lrcView.setTranslateTextScaleValue(0.8f);
|
||||
binding.lrcView.setHorizontalOffset(-50f);
|
||||
binding.lrcView.setHorizontalOffsetPercent(0.5f);
|
||||
binding.lrcView.setItemOffsetPercent(0.5f);
|
||||
binding.lrcView.setIsDrawTranslation(true);
|
||||
binding.lrcView.setIsEnableBlurEffect(true);
|
||||
// binding.cardview.setLayoutParams(layoutParams);
|
||||
|
||||
binding.lrcView.setDraggable(true, time -> {
|
||||
player.seekTo(time);
|
||||
return false;
|
||||
});
|
||||
|
||||
binding.lrcView.setOnSingerClickListener(() -> switchViews(binding.lrcView, binding.cardview));
|
||||
|
||||
binding.fragmentBfq.setOnTouchListener((v, event) -> {
|
||||
gestureDetector.onTouchEvent(event);
|
||||
if (event.getAction() == MotionEvent.ACTION_UP) {
|
||||
if (binding.getRoot().getRootView().getTranslationY() > (getResources().getDisplayMetrics().heightPixels / 2.0f)) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
ObjectAnimator animator = ObjectAnimator.ofFloat(binding.getRoot().getRootView()
|
||||
, "y", binding.getRoot().getRootView().getTranslationY(), 0);
|
||||
animator.setDuration(500);
|
||||
animator.start();
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
//播放列表
|
||||
binding.bfqListMp3.setOnClickListener(v -> com.muqingbfq.fragment.bflb_db.start(v.getContext()));
|
||||
|
||||
}
|
||||
|
||||
//是否拖动
|
||||
private boolean isDrag = false;
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (player != null && !isDrag) {
|
||||
// 获取当前进度和持续时间
|
||||
long currentPosition = player.getCurrentPosition();
|
||||
long duration = player.getDuration();
|
||||
|
||||
// 更新进度条(假设有一个 SeekBar 名为 seekBar)
|
||||
if (duration > 0) {
|
||||
int progress = (int) ((currentPosition * 100) / duration);
|
||||
// gj.sc(progress);
|
||||
if (progress < 1) {
|
||||
progress = 1;
|
||||
}
|
||||
binding.tdt.setProgress(progress);
|
||||
// binding.tdt.setMax(100);
|
||||
binding.lrcView.updateTime(currentPosition, true);
|
||||
binding.timeA.setText(bfq_an.getTime(duration));
|
||||
binding.timeB.setText(bfq_an.getTime(currentPosition));
|
||||
}
|
||||
}
|
||||
// 计划下一次更新
|
||||
main.handler.postDelayed(this, 1000); // 每秒更新一次
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
player.removeListener(Listener);
|
||||
}
|
||||
|
||||
private Player.Listener Listener = new Player.Listener() {
|
||||
@Override
|
||||
public void onEvents(@NonNull Player player, @NonNull Player.Events events) {
|
||||
// 监听播放状态变化
|
||||
if (events.contains(Player.EVENT_PLAY_WHEN_READY_CHANGED)) {
|
||||
boolean isPlaying = player.getPlayWhenReady();
|
||||
if (isPlaying) {
|
||||
gj.sc("正在播放");
|
||||
} else {
|
||||
gj.sc("播放暂停");
|
||||
}
|
||||
}
|
||||
|
||||
// 监听下一曲事件
|
||||
if (events.contains(Player.EVENT_MEDIA_ITEM_TRANSITION)) {
|
||||
MediaItem currentMediaItem = player.getCurrentMediaItem();
|
||||
if (currentMediaItem != null) {
|
||||
gj.sc("播放下一曲: ");
|
||||
}
|
||||
}
|
||||
|
||||
// 监听上一曲事件(通常通过手动调用控制播放器的skipToPrevious方法实现)
|
||||
if (events.contains(Player.EVENT_MEDIA_ITEM_TRANSITION)) {
|
||||
// 你的逻辑代码,处理上一曲
|
||||
gj.sc("播放上一曲");
|
||||
}
|
||||
|
||||
if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
|
||||
int playbackState = player.getPlaybackState();
|
||||
|
||||
switch (playbackState) {
|
||||
case Player.STATE_READY:
|
||||
if (player.getPlayWhenReady()) {
|
||||
if (bfqkz.lrc != null) {
|
||||
String[] strings = Media.loadLyric();
|
||||
binding.lrcView.loadLyric(strings[0], strings[1]);
|
||||
}
|
||||
|
||||
gj.sc("播放开始");
|
||||
}
|
||||
break;
|
||||
case Player.STATE_ENDED:
|
||||
gj.sc("播放结束");
|
||||
break;
|
||||
default:
|
||||
// 处理其他状态
|
||||
break;
|
||||
}
|
||||
}
|
||||
updateUI(player);
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected ActivityMusicBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityMusicBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
main.handler.post(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
// 取消所有未完成的任务
|
||||
main.handler.removeCallbacks(runnable);
|
||||
}
|
||||
|
||||
|
||||
private void updateUI(Player player) {
|
||||
boolean shouldShowPlayButton = Util.shouldShowPlayButton(player);
|
||||
binding.kg.setImageResource(shouldShowPlayButton ? R.drawable.zt : R.drawable.bf);
|
||||
// 获取当前播放的 MediaItem
|
||||
MediaItem currentMediaItem = player.getCurrentMediaItem();
|
||||
if (currentMediaItem != null && !isFinishing() && !isDestroyed()) {
|
||||
MediaMetadata metadata = currentMediaItem.mediaMetadata;
|
||||
String title = metadata.title != null ? metadata.title.toString() : "没有名字的音乐?";
|
||||
String artist = metadata.artist != null ? metadata.artist.toString() : "未知艺术家";
|
||||
binding.name.setText(title);
|
||||
binding.zz.setText(artist);
|
||||
SetBackGround(metadata.artworkUri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void SetBackGround(Uri artworkUri){
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(artworkUri)
|
||||
.apply(new RequestOptions()
|
||||
.placeholder(R.drawable.ic_launcher_foreground)
|
||||
.error(R.drawable.ic_launcher_foreground))
|
||||
.addListener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Bitmap> target, boolean isFirstResource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Bitmap resource,
|
||||
@NonNull Object model, Target<Bitmap> target,
|
||||
@NonNull DataSource dataSource,
|
||||
boolean isFirstResource) {
|
||||
color(resource);
|
||||
backgroundbitmap = resource;
|
||||
binding.cardview.imageView.setImageBitmap(resource);
|
||||
return true;
|
||||
}
|
||||
}).into(binding.cardview.imageView);
|
||||
}
|
||||
|
||||
|
||||
private void color(Bitmap bitmap) {
|
||||
if (bitmap == null) {
|
||||
return;
|
||||
}
|
||||
Palette.Builder builder = new Palette.Builder(bitmap);
|
||||
builder.generate(palette -> {
|
||||
// 获取图片中柔和的亮色
|
||||
int lightMutedColor = palette.getLightMutedColor(Color.GRAY);
|
||||
Palette.Swatch vibrantSwatch = palette.getLightVibrantSwatch();
|
||||
if (vibrantSwatch != null) {
|
||||
int bodyTextColor = vibrantSwatch.getBodyTextColor();
|
||||
binding.lrcView.setCurrentColor(bodyTextColor);
|
||||
binding.lrcView.setTimelineTextColor(bodyTextColor);
|
||||
// 计算半亮度的颜色(直接将RGB分量除以2并向下取整)
|
||||
int halfBrightnessColor = (bodyTextColor & 0x00FFFFFF) / 2;
|
||||
binding.lrcView.setNormalColor(halfBrightnessColor);
|
||||
|
||||
GradientDrawable gradientDrawable = new GradientDrawable(
|
||||
GradientDrawable.Orientation.BOTTOM_TOP, // 渐变方向:从上到下
|
||||
new int[]{vibrantSwatch.getRgb(), lightMutedColor} // 渐变颜色数组
|
||||
);
|
||||
setTint(vibrantSwatch.getTitleTextColor());
|
||||
gradientDrawable.setShape(GradientDrawable.RECTANGLE);
|
||||
binding.getRoot().setBackground(gradientDrawable);
|
||||
} else {
|
||||
int color = palette.getLightVibrantColor(Color.WHITE);
|
||||
int titleTextColor = palette.getVibrantColor(Color.GRAY);
|
||||
binding.lrcView.setCurrentColor(titleTextColor);
|
||||
binding.lrcView.setTimelineTextColor(titleTextColor);
|
||||
// 计算半亮度的颜色(直接将RGB分量除以2并向下取整)
|
||||
int halfBrightnessColor = (titleTextColor & 0x00FFFFFF) / 2;
|
||||
binding.lrcView.setNormalColor(halfBrightnessColor);
|
||||
|
||||
GradientDrawable gradientDrawable = new GradientDrawable(
|
||||
GradientDrawable.Orientation.BOTTOM_TOP, // 渐变方向:从上到下
|
||||
new int[]{color, lightMutedColor} // 渐变颜色数组
|
||||
);
|
||||
setTint(titleTextColor);
|
||||
gradientDrawable.setShape(GradientDrawable.RECTANGLE);
|
||||
binding.getRoot().setBackground(gradientDrawable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setTint(int color) {
|
||||
// this.ColorTint = color;
|
||||
ColorStateList colorStateList = ColorStateList.valueOf(color);
|
||||
binding.kg.setImageTintList(colorStateList);
|
||||
binding.syq.setImageTintList(colorStateList);
|
||||
binding.xyq.setImageTintList(colorStateList);
|
||||
binding.bfqListMp3.setImageTintList(colorStateList);
|
||||
binding.control.setImageTintList(colorStateList);
|
||||
binding.like.setImageTintList(colorStateList);
|
||||
binding.download.setImageTintList(colorStateList);
|
||||
binding.image2.setImageTintList(colorStateList);
|
||||
binding.name.setTextColor(color);
|
||||
binding.zz.setTextColor(color);
|
||||
binding.timeA.setTextColor(color);
|
||||
binding.timeB.setTextColor(color);
|
||||
|
||||
Drawable progressDrawable = binding.tdt.getProgressDrawable();
|
||||
LayerDrawable layerDrawable = (LayerDrawable) progressDrawable;
|
||||
Drawable progress = layerDrawable.findDrawableByLayerId(android.R.id.progress);
|
||||
progress.setColorFilter(color, PorterDuff.Mode.SRC_IN);
|
||||
// 设置进度条背景的颜色
|
||||
Drawable background = layerDrawable.findDrawableByLayerId(android.R.id.background);
|
||||
background.setColorFilter(Color.GRAY, PorterDuff.Mode.SRC_IN);
|
||||
}
|
||||
|
||||
//触摸
|
||||
// public boolean onTouchEvent(MotionEvent event)
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onDown(@NonNull MotionEvent e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(@NonNull MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(@NonNull MotionEvent e) {
|
||||
// 判断是哪个视图被点击了
|
||||
if (!gj.isTablet(this)) {
|
||||
switchViews(binding.cardview, binding.lrcView);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean isswitchViews = false;//是否在执行中
|
||||
|
||||
private void switchViews(final View view1, final View view2) {
|
||||
// 隐藏view1并显示view2的动画效果
|
||||
if (isswitchViews || gj.isTablet(this)) {
|
||||
return;
|
||||
}
|
||||
isswitchViews = true;
|
||||
if (view2.getId() == binding.lrcView.getId()) {
|
||||
binding.lrcView.updateTime(player.getCurrentPosition(), true);
|
||||
}
|
||||
view1.animate()
|
||||
.alpha(0.0f)
|
||||
.setDuration(500)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
view1.setVisibility(View.GONE);
|
||||
view2.setVisibility(View.VISIBLE);
|
||||
view2.setAlpha(0.0f);
|
||||
view2.animate().alpha(1.0f).setDuration(500).setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
isswitchViews = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 判断触摸点是否在视图范围内的辅助方法
|
||||
@Override
|
||||
public boolean onScroll(MotionEvent e1, @NonNull MotionEvent e2,
|
||||
float distanceX, float distanceY) {
|
||||
float y = binding.getRoot().getRootView().getTranslationY() - distanceY;
|
||||
y = Math.max(0, y);
|
||||
//移动的距离
|
||||
int heightPixels = getResources().getDisplayMetrics().heightPixels;
|
||||
if (y > heightPixels - heightPixels / 5.0) {
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
binding.getRoot().getRootView().setTranslationY(y);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(@NonNull MotionEvent e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, @NonNull MotionEvent e2,
|
||||
float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
}
|
169
app/src/main/java/com/muqingbfq/activity_about_software.java
Normal file
169
app/src/main/java/com/muqingbfq/activity_about_software.java
Normal file
|
@ -0,0 +1,169 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.text.Html;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDragHandleView;
|
||||
import com.muqingbfq.databinding.ActivityAboutSoftwareBinding;
|
||||
import com.muqingbfq.databinding.ListKaifazheBinding;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.FragmentActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class activity_about_software extends FragmentActivity<ActivityAboutSoftwareBinding> {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView();
|
||||
try {
|
||||
String versionName = getPackageManager().getPackageInfo(getPackageName(), 0).versionName;
|
||||
binding.text2.setText(String.format("%s Bate", versionName));
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
yc.start(this, e);
|
||||
}
|
||||
setSupportActionBar(findViewById(R.id.toolbar));
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
findViewById(R.id.button1).setOnClickListener(view -> {
|
||||
});
|
||||
TextView viewById = findViewById(R.id.text1);
|
||||
AssetManager assets = getAssets();
|
||||
try {
|
||||
InputStream open = assets.open("about.html");
|
||||
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(open));
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
String ling;
|
||||
while ((ling = bufferedReader.readLine()) != null) {
|
||||
stringBuilder.append(ling);
|
||||
}
|
||||
viewById.setText(Html.fromHtml(stringBuilder.toString(), Html.FROM_HTML_MODE_LEGACY));
|
||||
open.close();
|
||||
bufferedReader.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
viewById.setText(String.format("错误:%s", e));
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem itemA;
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
itemA= menu.add("特别鸣谢");
|
||||
itemA.setTitle("特别鸣谢");
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == android.R.id.home) {
|
||||
finish();
|
||||
} else if (item == itemA) {
|
||||
new botton(this);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivityAboutSoftwareBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityAboutSoftwareBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
class botton extends BottomSheetDialog {
|
||||
|
||||
List<Object[]> list = new ArrayList<>();
|
||||
public botton(@NonNull Context context) {
|
||||
super(context);
|
||||
setTitle("特别鸣谢");
|
||||
list.add(new Object[]{"2923268971","薄荷今天吃什么?", "维护开发者", "QQ"});
|
||||
list.add(new Object[]{"3301074923","威廉", "主要测试BUG", "QQ"});
|
||||
show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
LinearLayout linearLayout = new LinearLayout(getContext());
|
||||
RecyclerView recyclerView = new RecyclerView(getContext());
|
||||
recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER);
|
||||
recyclerView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
|
||||
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
linearLayout.setPadding(50,0,50,500);
|
||||
|
||||
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
recyclerView.setAdapter(new RecyclerView.Adapter<VH>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
ListKaifazheBinding binding = ListKaifazheBinding.inflate(getLayoutInflater());
|
||||
return new VH(binding.getRoot());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
Object[] objects = list.get(position);
|
||||
holder.name.setText(objects[1].toString());
|
||||
holder.zz.setText(objects[2].toString());
|
||||
// https://q1.qlogo.cn/g?b=qq&nk=1966944300&s=100
|
||||
Glide.with(getContext())
|
||||
.load("https://q1.qlogo.cn/g?b=qq&nk=" + objects[0] + "&s=100")
|
||||
.error(R.drawable.ic_launcher_foreground)
|
||||
.into(holder.imageView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
});
|
||||
linearLayout.setGravity(Gravity.CENTER_HORIZONTAL);
|
||||
linearLayout.addView(new BottomSheetDragHandleView(getContext()));
|
||||
linearLayout.addView(recyclerView);
|
||||
setContentView(linearLayout);
|
||||
}
|
||||
}
|
||||
|
||||
class VH extends RecyclerView.ViewHolder {
|
||||
public TextView name, zz;
|
||||
public ImageView imageView;
|
||||
public VH(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
name = itemView.findViewById(R.id.text1);
|
||||
zz = itemView.findViewById(R.id.text2);
|
||||
imageView = itemView.findViewById(R.id.imageView);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
345
app/src/main/java/com/muqingbfq/activity_search.java
Normal file
345
app/src/main/java/com/muqingbfq/activity_search.java
Normal file
|
@ -0,0 +1,345 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.flexbox.AlignItems;
|
||||
import com.google.android.flexbox.FlexDirection;
|
||||
import com.google.android.flexbox.FlexWrap;
|
||||
import com.google.android.flexbox.FlexboxLayoutManager;
|
||||
import com.google.android.material.search.SearchView;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.databinding.ActivitySearchBinding;
|
||||
import com.muqingbfq.databinding.ListTextBinding;
|
||||
import com.muqingbfq.databinding.ViewSearchItemBinding;
|
||||
import com.muqingbfq.fragment.search;
|
||||
import com.muqingbfq.mq.FragmentActivity;
|
||||
import com.muqingbfq.mq.VH;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.view.Edit;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class activity_search extends FragmentActivity<ActivitySearchBinding> {
|
||||
// private List<String> json_list = new ArrayList<>();
|
||||
private final List<String> list = new ArrayList<>();
|
||||
|
||||
public static void start(Activity context, View view) {
|
||||
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(context,
|
||||
view, "edit");
|
||||
context.startActivity(new Intent(context, activity_search.class), options.toBundle());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(getViewBinding().getRoot());
|
||||
setToolbar();
|
||||
FlexboxLayoutManager manager = new FlexboxLayoutManager(this) {
|
||||
@Override
|
||||
public boolean canScrollVertically() {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
//设置主轴排列方式
|
||||
manager.setFlexDirection(FlexDirection.ROW);
|
||||
//设置是否换行
|
||||
manager.setFlexWrap(FlexWrap.WRAP);
|
||||
manager.setAlignItems(AlignItems.STRETCH);//历史记录的LayoutManager
|
||||
binding.listRecycler.setLayoutManager(manager);
|
||||
// binding.listRecycler.setAdapter(new SearchRecordAdapter());
|
||||
/* binding.deleat.setOnClickListener(v -> new MaterialAlertDialogBuilder(
|
||||
activity_search.this)
|
||||
.setTitle("删除")
|
||||
.setMessage("清空历史记录?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialogInterface, ii) -> {
|
||||
int i = 0;
|
||||
Iterator<String> iterator = json_list.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
iterator.next();
|
||||
iterator.remove();
|
||||
binding.listRecycler.getAdapter().notifyItemRemoved(i++);
|
||||
}
|
||||
binding.xxbj1.setVisibility(View.GONE);
|
||||
wj.sc(wj.filesdri + wj.lishi_json);
|
||||
})
|
||||
.show());*/
|
||||
binding.searchRecycler.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.searchRecycler.setAdapter(new search_adapter(list, this::start));
|
||||
//设置项点击监听
|
||||
final Object o = new Object();
|
||||
binding.searchview.getEditText().addTextChangedListener(new Edit.TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (o) {
|
||||
list.clear();
|
||||
String hq = com.muqingbfq.mq.wl.
|
||||
hq("/search/suggest?keywords=" + s + "&type=mobile");
|
||||
try {
|
||||
JSONArray jsonArray = new JSONObject(hq).getJSONObject("result")
|
||||
.getJSONArray("allMatch");
|
||||
int length = jsonArray.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
String keyword = jsonObject.getString("keyword");
|
||||
list.add(keyword);
|
||||
}
|
||||
activity_search.this.runOnUiThread(() ->
|
||||
binding.searchRecycler.getAdapter().notifyDataSetChanged());
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
}
|
||||
});
|
||||
/* if (binding.toolbar.collapse(contextualToolbar, binding.barlayout)) {
|
||||
// Clear selection.
|
||||
return;
|
||||
}*/
|
||||
binding.searchview.inflateMenu(R.menu.search);
|
||||
binding.searchview.setOnMenuItemClickListener(
|
||||
menuItem -> {
|
||||
binding.searchview.hide();
|
||||
start(binding.searchview.getText().toString());
|
||||
return true;
|
||||
});
|
||||
|
||||
binding.searchview.addTransitionListener(
|
||||
(searchView, previousState, newState) -> {
|
||||
if (newState == SearchView.TransitionState.SHOWING ||
|
||||
newState == SearchView.TransitionState.SHOWN) {
|
||||
searchView.setText(binding.toolbar.getText());
|
||||
} else if (newState == SearchView.TransitionState.HIDING ||
|
||||
newState == SearchView.TransitionState.HIDDEN) {
|
||||
binding.toolbar.setText(searchView.getText());
|
||||
}
|
||||
});
|
||||
binding.searchview
|
||||
.getEditText()
|
||||
.setOnEditorActionListener(
|
||||
(v, actionId, event) -> {
|
||||
binding.toolbar.setText(binding.searchview.getText());
|
||||
binding.searchview.hide();
|
||||
start(binding.searchview.getText().toString());
|
||||
return false;
|
||||
});
|
||||
binding.toolbar.setOnMenuItemClickListener(item -> {
|
||||
//搜索
|
||||
start(binding.toolbar.getText().toString());
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TaskAction<T> {
|
||||
void execute(T t);
|
||||
}
|
||||
|
||||
public static class search_adapter extends RecyclerView.Adapter<VH<ViewSearchItemBinding>> {
|
||||
public List<String> list;
|
||||
TaskAction<String> taskAction;
|
||||
|
||||
public search_adapter(List<String> list, TaskAction<String> taskAction) {
|
||||
this.taskAction = taskAction;
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH<ViewSearchItemBinding> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH<>(ViewSearchItemBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
String s = list.get(position);
|
||||
((TextView) holder.itemView).setText(s);
|
||||
holder.itemView.setOnClickListener(v -> taskAction.execute(s));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
binding.searchview.hide();
|
||||
}
|
||||
|
||||
public static void addSearchRecord(String name,List<String> json_list,SearchRecordAdapter adapter) {
|
||||
try {
|
||||
int existingIndex = json_list.indexOf(name);
|
||||
if (existingIndex != -1) {
|
||||
// 交换两个元素的位置
|
||||
json_list.remove(name);
|
||||
json_list.add(0, name);
|
||||
adapter.notifyItemMoved(existingIndex, 0);
|
||||
} else {
|
||||
// json_list.remove(name);
|
||||
json_list.add(0, name);
|
||||
adapter.notifyItemInserted(0);
|
||||
}
|
||||
wj.xrwb(wj.filesdri + wj.lishi_json, new Gson().toJson(json_list));
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuItem sousuo = menu.add("搜索");
|
||||
sousuo.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivitySearchBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivitySearchBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == android.R.id.home) {
|
||||
end();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public void start(String name) {
|
||||
dismiss();
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
search sea = (search) getSupportFragmentManager().findFragmentById(R.id.search_fragment);
|
||||
binding.searchFragment.setVisibility(View.VISIBLE);
|
||||
sea.sx(name);
|
||||
// addSearchRecordd(name);
|
||||
}
|
||||
}
|
||||
|
||||
public static class SearchRecordAdapter extends RecyclerView.Adapter<VH<ListTextBinding>> {
|
||||
SearchView searchView;
|
||||
public List<String> json_list = new ArrayList<>();
|
||||
|
||||
public SearchRecordAdapter(SearchView searchView) {
|
||||
this.searchView = searchView;
|
||||
String dqwb = wj.dqwb(wj.filesdri + wj.lishi_json);
|
||||
if (dqwb != null) {
|
||||
try {
|
||||
json_list = new Gson().fromJson(dqwb, new TypeToken<List<String>>() {
|
||||
}.getType());
|
||||
} catch (Exception e) {
|
||||
wj.sc(wj.filesdri + wj.lishi_json);
|
||||
// yc.start(activity_search.this, e);
|
||||
}
|
||||
}
|
||||
gj.sc(json_list.size());
|
||||
// RecyclerView.ItemAnimator animator = new DefaultItemAnimator() {
|
||||
// @Override
|
||||
// public boolean animateRemove(RecyclerView.ViewHolder holder) {
|
||||
// ObjectAnimator fadeAnimator = ObjectAnimator.ofFloat(holder.itemView, "alpha", 1f, 0f);
|
||||
// fadeAnimator.setDuration(getRemoveDuration());
|
||||
// fadeAnimator.addListener(new AnimatorListenerAdapter() {
|
||||
// @Override
|
||||
// public void onAnimationEnd(Animator animation) {
|
||||
// dispatchRemoveFinished(holder);
|
||||
// holder.itemView.setAlpha(1f);
|
||||
// }
|
||||
// });
|
||||
// fadeAnimator.start();
|
||||
// return false;
|
||||
// }
|
||||
// };
|
||||
// binding.listRecycler.setItemAnimator(animator);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH<ListTextBinding> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH<>(ListTextBinding.inflate(LayoutInflater.from(parent.getContext()),
|
||||
parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH<ListTextBinding> holder, int position) {
|
||||
String keyword = json_list.get(position);
|
||||
holder.binding.getRoot().setText(keyword);
|
||||
holder.binding.getRoot().setOnClickListener(v -> {
|
||||
searchView.setText(keyword);
|
||||
// start(keyword);
|
||||
});
|
||||
holder.binding.getRoot().setOnCloseIconClickListener(view -> {
|
||||
json_list.remove(keyword);
|
||||
notifyItemRemoved(holder.getBindingAdapterPosition());
|
||||
wj.xrwb(wj.filesdri + wj.lishi_json, new Gson().toJson(json_list));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return json_list.size();
|
||||
}
|
||||
}
|
||||
|
||||
// @Override
|
||||
// public void onBackPressed() {
|
||||
// end();
|
||||
// }
|
||||
|
||||
private void end() {
|
||||
if (binding.searchview.isShowing()) {
|
||||
binding.searchview.hide();
|
||||
return;
|
||||
}
|
||||
if (binding.searchFragment.getVisibility() == View.VISIBLE) {
|
||||
binding.searchFragment.setVisibility(View.GONE);
|
||||
} else {
|
||||
finish();
|
||||
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
// ActivityCompat.finishAffinity(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
}
|
||||
}
|
133
app/src/main/java/com/muqingbfq/adapter/AdapterGd.java
Normal file
133
app/src/main/java/com/muqingbfq/adapter/AdapterGd.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
package com.muqingbfq.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.palette.graphics.Palette;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.api.playlist;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.ListGdBinding;
|
||||
import com.muqingbfq.fragment.gd;
|
||||
import com.muqingbfq.fragment.mp3;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.VH;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AdapterGd extends RecyclerView.Adapter<VH<ListGdBinding>> {
|
||||
|
||||
public List<XM> list = new ArrayList<>();
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH<ListGdBinding> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH<>(ListGdBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH<ListGdBinding> holder, int position) {
|
||||
XM xm = list.get(position);
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
mp3.drawable = holder.binding.image.getDrawable();
|
||||
mp3.start(v.getContext(), new String[]{xm.id, xm.name});
|
||||
});
|
||||
Glide.with(holder.itemView.getContext())
|
||||
.asBitmap()
|
||||
.load(xm.picurl)
|
||||
.addListener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Bitmap> target, boolean isFirstResource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Bitmap resource,
|
||||
@NonNull Object model, Target<Bitmap> target,
|
||||
@NonNull DataSource dataSource, boolean isFirstResource) {
|
||||
Palette.from(resource).generate(palette -> {
|
||||
int color = palette.getLightMutedColor(Color.WHITE);
|
||||
GradientDrawable gradientDrawable = new GradientDrawable(
|
||||
GradientDrawable.Orientation.BOTTOM_TOP,
|
||||
new int[]{color, color});
|
||||
gradientDrawable.setAlpha(128);
|
||||
holder.binding.text1.setBackground(gradientDrawable);
|
||||
holder.binding.getRoot().setRippleColor(ColorStateList.valueOf(color));
|
||||
});
|
||||
holder.binding.image.setImageBitmap(resource);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.into(holder.binding.image);
|
||||
holder.binding.text1.setText(xm.name);
|
||||
holder.binding.kg.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
List<MP3> an = playlist.hq(xm.id);
|
||||
if (bfqkz.ms == 2) {
|
||||
Collections.shuffle(bfqkz.list);
|
||||
}
|
||||
|
||||
main.handler.post(() -> {
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
return;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
player.clearMediaItems();
|
||||
for (MP3 mp3 : an) {
|
||||
// gj.sc(mp3.url);
|
||||
MediaItem mediaItem = PlaybackService.GetMp3(mp3);
|
||||
player.addMediaItem(mediaItem);
|
||||
}
|
||||
player.prepare();
|
||||
player.seekTo(0,0);
|
||||
player.play();
|
||||
//保存播放列表
|
||||
PlaybackService.list.clear();
|
||||
PlaybackService.list.addAll(an);
|
||||
PlaybackService.ListSave();
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
}
|
96
app/src/main/java/com/muqingbfq/adapter/AdapterGdH.java
Normal file
96
app/src/main/java/com/muqingbfq/adapter/AdapterGdH.java
Normal file
|
@ -0,0 +1,96 @@
|
|||
package com.muqingbfq.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.palette.graphics.Palette;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.api.playlist;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.ListGdBBinding;
|
||||
import com.muqingbfq.databinding.ListGdBinding;
|
||||
import com.muqingbfq.fragment.mp3;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.VH;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class AdapterGdH extends RecyclerView.Adapter<VH<ListGdBBinding>> {
|
||||
|
||||
public List<XM> list = new ArrayList<>();
|
||||
|
||||
public AdapterGdH() {}
|
||||
public AdapterGdH(List<XM> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH<ListGdBBinding> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH<>(ListGdBBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH<ListGdBBinding> holder, int position) {
|
||||
XM xm = list.get(position);
|
||||
holder.binding.text1.setText(xm.name);
|
||||
holder.binding.text2.setText(xm.message);
|
||||
gj.sc(xm.picurl);
|
||||
Glide.with(holder.itemView.getContext())
|
||||
.asBitmap()
|
||||
.load(xm.picurl)
|
||||
.addListener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Bitmap> target, boolean isFirstResource) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Bitmap resource,
|
||||
@NonNull Object model, Target<Bitmap> target,
|
||||
@NonNull DataSource dataSource, boolean isFirstResource) {
|
||||
Palette.from(resource).generate(palette -> {
|
||||
Palette.Swatch lightVibrantSwatch = palette.getVibrantSwatch();
|
||||
if (lightVibrantSwatch != null) {
|
||||
int color = lightVibrantSwatch.getRgb();
|
||||
holder.binding.text1.setTextColor(color);
|
||||
holder.binding.getRoot().setRippleColor(ColorStateList.valueOf(color));
|
||||
}
|
||||
});
|
||||
holder.binding.image.setImageBitmap(resource);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.into(holder.binding.image);
|
||||
holder.binding.getRoot().setOnClickListener(v -> mp3.start(v.getContext(), new String[]{xm.id, xm.name}));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
}
|
123
app/src/main/java/com/muqingbfq/adapter/AdapterMp3.java
Normal file
123
app/src/main/java/com/muqingbfq/adapter/AdapterMp3.java
Normal file
|
@ -0,0 +1,123 @@
|
|||
package com.muqingbfq.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.google.common.base.Strings;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.api.url;
|
||||
import com.muqingbfq.databinding.ListMp3ImageBinding;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.VH;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AdapterMp3 extends RecyclerView.Adapter<VH<ListMp3ImageBinding>> {
|
||||
public List<MP3> list = new ArrayList<>();
|
||||
|
||||
public AdapterMp3() {
|
||||
|
||||
}
|
||||
|
||||
public AdapterMp3(List<MP3> list) {
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH<ListMp3ImageBinding> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH<>(ListMp3ImageBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH<ListMp3ImageBinding> holder, int position) {
|
||||
MP3 x = list.get(position);
|
||||
holder.binding.wb1.setText(x.name);
|
||||
holder.binding.zz.setText(x.zz);
|
||||
int themeColor = gj.getThemeColor(holder.itemView.getContext(), com.google.android.material.R.attr.colorSurface);
|
||||
holder.binding.getRoot().setCardBackgroundColor(themeColor);
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
MediaItem currentMediaItem = player.getCurrentMediaItem();
|
||||
if (currentMediaItem != null) {
|
||||
String mediaId = currentMediaItem.mediaId;
|
||||
if (mediaId.equals(x.id)) {
|
||||
// holder.binding.getRoot()
|
||||
holder.binding.getRoot().setCardBackgroundColor(
|
||||
gj.getThemeColor(holder.itemView.getContext(), com.google.android.material.R.attr.colorSurfaceVariant));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Strings.isNullOrEmpty(x.picurl)) {
|
||||
holder.binding.text1.setText(String.valueOf(position + 1));
|
||||
holder.binding.imageView.setVisibility(ViewGroup.GONE);
|
||||
holder.binding.linsum.setVisibility(View.VISIBLE);
|
||||
}else{
|
||||
Glide.with(holder.itemView.getContext()).load(list.get(position).picurl)
|
||||
.apply(new RequestOptions().placeholder(R.drawable.ic_launcher_foreground))
|
||||
.error(R.drawable.ic_launcher_foreground)
|
||||
.into(holder.binding.imageView);
|
||||
}
|
||||
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
return;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
MP3 hq = url.hq(x);
|
||||
// gj.sc(String.format("链接:%s,图片:%s", hq.url,hq.picurl));
|
||||
main.handler.post(() -> {
|
||||
for (int i = 0; i < player.getMediaItemCount(); i++) {
|
||||
MediaItem currentItem = player.getMediaItemAt(i);
|
||||
if (currentItem.mediaId.equals(hq.id)) {
|
||||
gj.sc("存在播放:" + currentItem.mediaId + "==" + hq.id + " i=" + i);
|
||||
player.seekTo(i,0);
|
||||
player.prepare();
|
||||
player.play();
|
||||
notifyDataSetChanged();
|
||||
return;
|
||||
}
|
||||
}
|
||||
gj.sc("不存在添加播放");
|
||||
PlaybackService.list.add(hq);
|
||||
PlaybackService.ListSave();
|
||||
MediaItem mediaItem = PlaybackService.GetMp3(hq);
|
||||
player.addMediaItem(0,mediaItem);
|
||||
player.seekTo(0,0);
|
||||
player.prepare();
|
||||
player.play();
|
||||
notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
}
|
181
app/src/main/java/com/muqingbfq/api/FileDownloader.java
Normal file
181
app/src/main/java/com/muqingbfq/api/FileDownloader.java
Normal file
|
@ -0,0 +1,181 @@
|
|||
package com.muqingbfq.api;
|
||||
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.mpatric.mp3agic.ID3v2;
|
||||
import com.mpatric.mp3agic.Mp3File;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class FileDownloader {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
AlertDialog dialog;
|
||||
TextView textView;
|
||||
Context context;
|
||||
public FileDownloader(Context context) {
|
||||
this.context = context;
|
||||
main.handler.post(() -> {
|
||||
textView = new TextView(context);
|
||||
dialog = new MaterialAlertDialogBuilder(context)
|
||||
.setTitle("下载中...")
|
||||
.setView(textView)
|
||||
.show();
|
||||
});
|
||||
}
|
||||
public void downloadFile(MP3 x) {
|
||||
Request request = new Request.Builder()
|
||||
.url(main.api + url.api + "?id=" + x.id + "&level=" +
|
||||
"standard" + "&cookie=" + wl.Cookie)
|
||||
.build();
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
||||
e.printStackTrace();
|
||||
// 下载失败处理
|
||||
}
|
||||
@Override
|
||||
public void onResponse(@NonNull Call call, @NonNull Response response) {
|
||||
if (!response.isSuccessful()) {
|
||||
// 下载失败处理
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JSONObject json = new JSONObject(response.body().string());
|
||||
JSONArray data = json.getJSONArray("data");
|
||||
JSONObject jsonObject = data.getJSONObject(0);
|
||||
String url = jsonObject.getString("url");
|
||||
downloadFile(url, x);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
long fileSizeDownloaded = 0;
|
||||
public void downloadFile(String url, MP3 x) {
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
// 创建通知渠道(仅适用于Android 8.0及以上版本)
|
||||
// 发起请求
|
||||
client.newCall(request).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
||||
e.printStackTrace();
|
||||
// 下载失败处理
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
@Override
|
||||
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
|
||||
if (!response.isSuccessful()) {
|
||||
// 下载失败处理
|
||||
return;
|
||||
}
|
||||
File outputFile = new File(wj.mp3, x.id + ".mp3");
|
||||
File parentFile = outputFile.getParentFile();
|
||||
if (!parentFile.isDirectory()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
InputStream inputStream = null;
|
||||
FileOutputStream outputStream = null;
|
||||
try {
|
||||
byte[] buffer = new byte[4096];
|
||||
long fileSize = response.body().contentLength();
|
||||
inputStream = response.body().byteStream();
|
||||
outputStream = new FileOutputStream(outputFile);
|
||||
|
||||
int read;
|
||||
fileSizeDownloaded = 0;
|
||||
while ((read = inputStream.read(buffer)) != -1) {
|
||||
outputStream.write(buffer, 0, read);
|
||||
fileSizeDownloaded += read;
|
||||
// 更新通知栏进度
|
||||
// updateNotificationProgress(context, fileSize, fileSizeDownloaded);
|
||||
if (textView != null) {
|
||||
main.handler.post(() ->
|
||||
textView.setText(x.name + ":" +
|
||||
(int) ((fileSizeDownloaded * 100) / fileSize)));
|
||||
}
|
||||
}
|
||||
try {
|
||||
Mp3File mp3file = new Mp3File(outputFile);
|
||||
if (mp3file.hasId3v2Tag()) {
|
||||
ID3v2 id3v2Tag = mp3file.getId3v2Tag();
|
||||
// 设置新的ID值
|
||||
gj.sc(x.name);
|
||||
id3v2Tag.setTitle(x.name);
|
||||
id3v2Tag.setArtist(x.zz);
|
||||
id3v2Tag.setAlbum(x.zz);
|
||||
id3v2Tag.setLyrics(com.muqingbfq.api.url.Lrc(x.id));
|
||||
ByteArrayOutputStream o = new ByteArrayOutputStream();
|
||||
if (x.picurl instanceof String) {
|
||||
Request build = new Request.Builder().url(x.picurl)
|
||||
.build();
|
||||
Response execute = client.newCall(build).execute();
|
||||
if (execute.isSuccessful()) {
|
||||
id3v2Tag.setAlbumImage(execute.body().bytes()
|
||||
, "image/jpeg");
|
||||
}
|
||||
}
|
||||
o.close();
|
||||
mp3file.save(wj.mp3 + x.id);
|
||||
outputFile.delete();
|
||||
}
|
||||
// 保存修改后的音乐文件,删除原来的文件
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
outputFile.delete();
|
||||
}
|
||||
dismiss();
|
||||
// 下载完成处理
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
// 下载失败处理
|
||||
} finally {
|
||||
if (inputStream != null) {
|
||||
inputStream.close();
|
||||
}
|
||||
if (outputStream != null) {
|
||||
outputStream.close();
|
||||
}
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
if (dialog == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
main.handler.post(() -> dialog.dismiss());
|
||||
}
|
||||
}
|
224
app/src/main/java/com/muqingbfq/api/playlist.java
Normal file
224
app/src/main/java/com/muqingbfq/api/playlist.java
Normal file
|
@ -0,0 +1,224 @@
|
|||
package com.muqingbfq.api;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Environment;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.mpatric.mp3agic.ID3v2;
|
||||
import com.mpatric.mp3agic.Mp3File;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class playlist extends Thread {
|
||||
public static final String api = "/playlist/track/all?id=";
|
||||
|
||||
public static String gethq(String uid) {
|
||||
if (wj.cz(wj.filesdri + "user.mq")) {
|
||||
return wl.hq(api + uid + "&limit=100" + "&cookie=" + wl.Cookie);
|
||||
// gj.sc(hq);
|
||||
} else {
|
||||
return wl.hq(api + uid + "&limit=100");
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hq(List<MP3> list, String uid) {
|
||||
switch (uid) {
|
||||
case "mp3_xz.json":
|
||||
return playlist.hq_xz(list);
|
||||
case "mp3_like.json":
|
||||
return playlist.hq_like(list);
|
||||
case "mp3_hc.json":
|
||||
return hq_hc(list);
|
||||
}
|
||||
list.clear();
|
||||
try {
|
||||
String hq = wj.dqwb(wj.gd + uid);
|
||||
if (hq == null || hq.isEmpty()) {
|
||||
hq = gethq(uid);
|
||||
}
|
||||
list.clear();
|
||||
JSONObject json = new JSONObject(hq);
|
||||
JSONArray songs = json.getJSONArray("songs");
|
||||
int length = songs.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = songs.getJSONObject(i);
|
||||
String id = jsonObject.getString("id");
|
||||
String name = jsonObject.getString("name");
|
||||
try {
|
||||
String tns = jsonObject.getString("tns");
|
||||
tns = tns.replace("[\"", "(");
|
||||
tns = tns.replace("\"]", ")");
|
||||
name += tns;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
JSONObject al = jsonObject.getJSONObject("al");
|
||||
JSONArray ar = jsonObject.getJSONArray("ar");
|
||||
StringBuilder zz = new StringBuilder();
|
||||
int length_a = ar.length();
|
||||
for (int j = 0; j < length_a; j++) {
|
||||
zz.append(ar.getJSONObject(j).getString("name"))
|
||||
.append("/");
|
||||
}
|
||||
zz.append("-").append(al.getString("name"));
|
||||
String picUrl = al.getString("picUrl");
|
||||
list.add(new MP3(id, name, zz.toString(), picUrl));
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
gj.sc("失败的错误 " + e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static List<MP3> hq(String uid) {
|
||||
List<MP3> list = new ArrayList<>();
|
||||
try {
|
||||
String hq = wj.dqwb(wj.gd + uid);
|
||||
if (hq == null || hq.isEmpty()) {
|
||||
hq = gethq(uid);
|
||||
}
|
||||
JSONObject json = new JSONObject(hq);
|
||||
JSONArray songs = json.getJSONArray("songs");
|
||||
int length = songs.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = songs.getJSONObject(i);
|
||||
String id = jsonObject.getString("id");
|
||||
String name = jsonObject.getString("name");
|
||||
try {
|
||||
String tns = jsonObject.getString("tns");
|
||||
tns = tns.replace("[\"", "(");
|
||||
tns = tns.replace("\"]", ")");
|
||||
name += tns;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
JSONObject al = jsonObject.getJSONObject("al");
|
||||
JSONArray ar = jsonObject.getJSONArray("ar");
|
||||
StringBuilder zz = new StringBuilder();
|
||||
int length_a = ar.length();
|
||||
for (int j = 0; j < length_a; j++) {
|
||||
zz.append(ar.getJSONObject(j).getString("name"))
|
||||
.append("/");
|
||||
}
|
||||
zz.append("-").append(al.getString("name"));
|
||||
String picUrl = al.getString("picUrl");
|
||||
list.add(new MP3(id, name, zz.toString(), picUrl));
|
||||
}
|
||||
return list;
|
||||
} catch (Exception e) {
|
||||
gj.sc("失败的错误 " + e);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static boolean hq_like(List<MP3> list) {
|
||||
try {
|
||||
String dqwb = wj.dqwb(wj.gd + "mp3_like.json");
|
||||
if (dqwb == null) {
|
||||
return false;
|
||||
}
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
Gson gson = new Gson();
|
||||
list.clear();
|
||||
list.addAll(gson.fromJson(dqwb, type));
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
gj.sc("失败的错误 " + e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hq_xz(List<MP3> list) {
|
||||
try {
|
||||
File file = new File(wj.filesdri + "mp3");
|
||||
File[] files = file.listFiles();
|
||||
list.clear();
|
||||
for (File value : files) {
|
||||
ID3v2 mp3File = new Mp3File(value).getId3v2Tag();
|
||||
String id = value.getName();
|
||||
String name = mp3File.getTitle();
|
||||
String zz = mp3File.getArtist();
|
||||
list.add(new MP3(id, name, zz, value.toString()));
|
||||
}
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
gj.sc("失败的错误 " + e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean hq_hc(List<MP3> list) {
|
||||
try {
|
||||
String dqwb = wj.dqwb(wj.gd + "mp3_hc.json");
|
||||
if (dqwb == null) {
|
||||
return false;
|
||||
}
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
Gson gson = new Gson();
|
||||
list.clear();
|
||||
list.addAll(gson.fromJson(dqwb, type));
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
gj.sc("失败的错误 " + e);
|
||||
wj.sc(wj.gd + "mp3_hc.json");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static void hq_cd(Activity context, List<MP3> list) {
|
||||
boolean cd = wj.isCD(context);
|
||||
if (!cd) {
|
||||
return;
|
||||
}
|
||||
list.clear();
|
||||
try {
|
||||
String absolutePath = Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
CD(new File(absolutePath), list);
|
||||
} catch (Exception e) {
|
||||
gj.sc("失败的错误 " + e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CD(File file, List<MP3> list) {
|
||||
for (File a : file.listFiles()) {
|
||||
if (a.isFile()) {
|
||||
try {
|
||||
// 创建一个 Mp3File 对象,用于读取 MP3 文件
|
||||
Mp3File mp3file = new Mp3File(a);
|
||||
// 检查是否存在 ID3v2 标签
|
||||
if (mp3file.hasId3v2Tag()) {
|
||||
// 获取 ID3v2 标签实例
|
||||
ID3v2 id3v2tag = mp3file.getId3v2Tag();
|
||||
MP3 mp3 = new MP3(a.toString(), id3v2tag.getTitle(),
|
||||
id3v2tag.getArtist()
|
||||
, a.toString());
|
||||
list.add(mp3);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else if (a.isDirectory()) {
|
||||
String string = a.getName();
|
||||
if (string.startsWith(".") || string.equals("Android") || string.equals("data")) {
|
||||
continue;
|
||||
}
|
||||
CD(a, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
117
app/src/main/java/com/muqingbfq/api/resource.java
Normal file
117
app/src/main/java/com/muqingbfq/api/resource.java
Normal file
|
@ -0,0 +1,117 @@
|
|||
package com.muqingbfq.api;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
import com.muqingbfq.XM;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.List;
|
||||
|
||||
public class resource {
|
||||
|
||||
public static void recommend(List<XM> list) {
|
||||
try {
|
||||
list.clear();
|
||||
JSONObject json;
|
||||
String hq = wl.hq("/recommend/resource?cookie=" + wl.Cookie);
|
||||
if (hq == null) {
|
||||
hq = wj.dqwb(wj.gd_json);
|
||||
if (hq != null) {
|
||||
json = new JSONObject(hq);
|
||||
if (json.getInt("code") == 200) {
|
||||
wj.xrwb(wj.gd_json, hq);
|
||||
JSONArray recommend = json.getJSONArray("recommend");
|
||||
int length = recommend.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = recommend.getJSONObject(i);
|
||||
add(jsonObject, list);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
json = new JSONObject(hq);
|
||||
if (json.getInt("code") == 200) {
|
||||
wj.xrwb(wj.gd_json, hq);
|
||||
JSONArray recommend = json.getJSONArray("recommend");
|
||||
int length = recommend.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = recommend.getJSONObject(i);
|
||||
add(jsonObject, list);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc("resource tuijian" + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static XM Playlist_content(String UID) throws JSONException {
|
||||
String hq = wl.get(main.api + "/playlist/detail?id=" + UID);
|
||||
JSONObject js = new JSONObject(hq).getJSONObject("playlist");
|
||||
String id = js.getString("id");
|
||||
String name = js.getString("name");
|
||||
String coverImgUrl = js.getString("coverImgUrl");
|
||||
|
||||
long playCount = js.getLong("playCount");
|
||||
String formattedNumber = String.valueOf(playCount);
|
||||
if (playCount > 9999) {
|
||||
DecimalFormat df = new DecimalFormat("#,###.0万");
|
||||
formattedNumber = df.format(playCount / 10000);
|
||||
}
|
||||
String s = js.getInt("trackCount") + "首,"
|
||||
+ "by " + js.getJSONObject("creator").getString("nickname")
|
||||
+ ",播放"
|
||||
+ formattedNumber + "次";
|
||||
return new XM(id, name, s, coverImgUrl);
|
||||
}
|
||||
|
||||
// 排行榜
|
||||
public static void leaderboard(List<XM> list) {
|
||||
String hq;
|
||||
try {
|
||||
if (wj.cz(wj.gd_phb)) {
|
||||
hq = wj.dqwb(wj.gd_phb);
|
||||
} else {
|
||||
hq = wl.hq("/toplist");
|
||||
if (hq == null) {
|
||||
return;
|
||||
}
|
||||
wj.xrwb(wj.gd_phb, hq);
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
if (jsonObject.getInt("code") == 200) {
|
||||
JSONArray list_array = jsonObject.getJSONArray("list");
|
||||
int length = list_array.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject get = list_array.getJSONObject(i);
|
||||
String id = get.getString("id");
|
||||
String name = get.getString("name") + "\n";
|
||||
String description = get.getString("description");
|
||||
if (!TextUtils.isEmpty(description) && !description.equals("null")) {
|
||||
name += description;
|
||||
}
|
||||
String coverImgUrl = get.getString("coverImgUrl");
|
||||
list.add(new XM(id, name, coverImgUrl));
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void add(JSONObject jsonObject, List<XM> list) throws Exception {
|
||||
String id = jsonObject.getString("id");
|
||||
String name = jsonObject.getString("name");
|
||||
String picUrl = jsonObject.getString("picUrl");
|
||||
list.add(new XM(id, name, picUrl));
|
||||
}
|
||||
}
|
114
app/src/main/java/com/muqingbfq/api/url.java
Normal file
114
app/src/main/java/com/muqingbfq/api/url.java
Normal file
|
@ -0,0 +1,114 @@
|
|||
package com.muqingbfq.api;
|
||||
|
||||
import com.mpatric.mp3agic.ID3v2;
|
||||
import com.mpatric.mp3agic.Mp3File;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.fragment.Media;
|
||||
import com.muqingbfq.home;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class url extends Thread {
|
||||
public static String api = "/song/url/v1";
|
||||
MP3 x;
|
||||
|
||||
public url(MP3 x) {
|
||||
this.x = x;
|
||||
start();
|
||||
}
|
||||
|
||||
public static MP3 hq(MP3 x) {
|
||||
// gj.sc(x.id);
|
||||
MP3 mp3 = new MP3();
|
||||
mp3.name = x.name;
|
||||
mp3.id = x.id;
|
||||
mp3.picurl = x.picurl;
|
||||
mp3.zz = x.zz;
|
||||
mp3.url = x.url;
|
||||
getLrc(x.id);
|
||||
Media.loadLyric();
|
||||
try {
|
||||
if (wj.cz(mp3.id)) {
|
||||
mp3.url = mp3.id;
|
||||
return mp3;
|
||||
} else if (wj.cz(wj.mp3 + mp3.id)) {
|
||||
mp3.url = wj.mp3 + mp3.id;
|
||||
return mp3;
|
||||
} else if (wj.cz(wj.filesdri + "hc/" + mp3.id)) {
|
||||
mp3.url = wj.filesdri + "hc/" + mp3.id;
|
||||
return mp3;
|
||||
}
|
||||
String level = "standard";
|
||||
boolean wiFiConnected = gj.isWiFiConnected();
|
||||
if (wiFiConnected) {
|
||||
level = "exhigh";
|
||||
}
|
||||
String hq = wl.hq(api + "?id=" + mp3.id + "&level=" +
|
||||
level + "&cookie=" + wl.Cookie);
|
||||
if (hq == null) {
|
||||
return null;
|
||||
}
|
||||
JSONObject json = new JSONObject(hq);
|
||||
// gj.sc(json);
|
||||
if (json.getInt("code") == -460) {
|
||||
String message = json.getString("message");
|
||||
gj.sc(message);
|
||||
return null;
|
||||
}
|
||||
JSONArray data = json.getJSONArray("data");
|
||||
JSONObject jsonObject = data.getJSONObject(0);
|
||||
// gj.sc(jsonObject.getString("url"));
|
||||
mp3.url = jsonObject.getString("url");
|
||||
mp3.picurl = picurl(mp3.id);
|
||||
return mp3;
|
||||
} catch (JSONException e) {
|
||||
gj.sc("url hq :" + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void getLrc(String id) {
|
||||
String file = wj.mp3 + id;
|
||||
boolean cz = wj.cz(id);
|
||||
if (cz) {
|
||||
file = id;
|
||||
}
|
||||
if (cz || wj.cz(file)) {
|
||||
try {
|
||||
Mp3File mp3file = new Mp3File(file);
|
||||
if (mp3file.hasId3v2Tag()) {
|
||||
ID3v2 id3v2Tag = mp3file.getId3v2Tag();
|
||||
com.muqingbfq.bfqkz.lrc = id3v2Tag.getLyrics();
|
||||
}
|
||||
if (com.muqingbfq.bfqkz.lrc == null) {
|
||||
com.muqingbfq.bfqkz.lrc = wl.hq("/lyric?id=" + id);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc("url getlrc:" + e);
|
||||
}
|
||||
} else {
|
||||
com.muqingbfq.bfqkz.lrc = wl.hq("/lyric?id=" + id);
|
||||
}
|
||||
}
|
||||
|
||||
public static String Lrc(String id) {
|
||||
return wl.hq("/lyric?id=" + id);
|
||||
}
|
||||
|
||||
public static String picurl(String id) {
|
||||
String hq = wl.hq("/song/detail?ids=" + id);
|
||||
try {
|
||||
return new JSONObject(hq).getJSONArray("songs").getJSONObject(0)
|
||||
.getJSONObject("al").getString("picUrl");
|
||||
} catch (Exception e) {
|
||||
gj.sc("url picurl:" + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
60
app/src/main/java/com/muqingbfq/bfq_an.java
Normal file
60
app/src/main/java/com/muqingbfq/bfq_an.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.api.url;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class bfq_an {
|
||||
static SimpleDateFormat simpleDateFormat = new SimpleDateFormat("mm:ss", Locale.CHINA);
|
||||
public static String getTime(long time) {
|
||||
return simpleDateFormat.format(new Date(time));
|
||||
}
|
||||
|
||||
public static boolean islike() {
|
||||
boolean contains = false;
|
||||
String dqwb = wj.dqwb(wj.gd + "mp3_like.json");
|
||||
if (dqwb != null) {
|
||||
try {
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
List<MP3> o = new Gson().fromJson(dqwb, type);
|
||||
if (o != null) {
|
||||
contains = o.contains(bfqkz.xm);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
wj.sc(wj.gd + "mp3_like.json");
|
||||
}
|
||||
}
|
||||
return bfqkz.like_bool = contains;
|
||||
}
|
||||
|
||||
public static boolean getlike(MP3 xm) {
|
||||
boolean contains = false;
|
||||
String dqwb = wj.dqwb(wj.gd + "mp3_like.json");
|
||||
if (dqwb != null) {
|
||||
try {
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
List<MP3> o = new Gson().fromJson(dqwb, type);
|
||||
if (o != null) {
|
||||
contains = o.contains(xm);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
wj.sc(wj.gd + "mp3_like.json");
|
||||
}
|
||||
}
|
||||
return contains;
|
||||
}
|
||||
}
|
93
app/src/main/java/com/muqingbfq/bfqkz.java
Normal file
93
app/src/main/java/com/muqingbfq/bfqkz.java
Normal file
|
@ -0,0 +1,93 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
import android.support.v4.media.MediaBrowserCompat;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media.MediaBrowserServiceCompat;
|
||||
|
||||
import com.muqingbfq.api.url;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class bfqkz extends MediaBrowserServiceCompat {
|
||||
|
||||
public static List<MP3> list = new ArrayList<>();
|
||||
//保存原始list顺序
|
||||
public static List<MP3> list_baocun = new ArrayList<>();
|
||||
public static List<MP3> lishi_list = new ArrayList<>();
|
||||
public static int ms;
|
||||
// 0 循环 1 顺序 2 随机
|
||||
public static MP3 xm;
|
||||
public static boolean like_bool;
|
||||
public static String lrc;
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public static int getmti() {
|
||||
if (xm == null) {
|
||||
return 0;
|
||||
}
|
||||
int i = bfqkz.list.indexOf(xm) + 1;
|
||||
if (i >= bfqkz.list.size()) {
|
||||
i = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
public PendingIntent pendingIntent;
|
||||
|
||||
public MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
// super.onCreate();
|
||||
// Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
// intent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
// intent.setComponent(new ComponentName(this, home.class));//用ComponentName得到class对象
|
||||
// intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
// | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);// 关键的一步,设置启动模式,两种情况
|
||||
// pendingIntent = com.muqingbfq.mq.NotificationManagerCompat.getActivity(this, intent);
|
||||
// com.muqingbfq.api.playlist.hq_hc(bfqkz.lishi_list);
|
||||
// new BluetoothMusicController(this);
|
||||
// playback = new PlaybackStateCompat.Builder();
|
||||
// playback.setState(PlaybackStateCompat.STATE_NONE, 0, 1.0f)
|
||||
// .build();
|
||||
// mSession.setCallback(new callback());
|
||||
// mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||
//
|
||||
// playback.setActions(PlaybackStateCompat.ACTION_PLAY);
|
||||
// playback.setActions(PlaybackStateCompat.ACTION_STOP);
|
||||
//
|
||||
// mSession.setPlaybackState(playback.build());
|
||||
// setSessionToken(mSession.getSessionToken());
|
||||
// mSession.setActive(true);
|
||||
// notify = new com.muqingbfq.mq.NotificationManagerCompat(this);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public BrowserRoot onGetRoot(@NonNull String clientPackageName, int clientUid, @Nullable Bundle rootHints) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaBrowserCompat.MediaItem>> result) {
|
||||
|
||||
}
|
||||
}
|
133
app/src/main/java/com/muqingbfq/clean/fragment_clean.java
Normal file
133
app/src/main/java/com/muqingbfq/clean/fragment_clean.java
Normal file
|
@ -0,0 +1,133 @@
|
|||
package com.muqingbfq.clean;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.text.format.Formatter;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.databinding.ActivityCleanBinding;
|
||||
import com.muqingbfq.mq.FragmentActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class fragment_clean extends FragmentActivity<ActivityCleanBinding> {
|
||||
List<String[]> list = new ArrayList<>();
|
||||
List<String> list_box = new ArrayList<>();
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView();
|
||||
// setToolbar();
|
||||
UI();
|
||||
}
|
||||
|
||||
private void UI() {
|
||||
list.clear();
|
||||
list.add(new String[]{"下载的音乐", wj.mp3});
|
||||
list.add(new String[]{"下载的歌单",wj.gd});
|
||||
list.add(new String[]{"缓存的音乐",wj.filesdri+"hc"});
|
||||
list.add(new String[]{"内部缓存", getCacheDir().toString()});
|
||||
String s = Glide.getPhotoCacheDir(this).toString();
|
||||
list.add(new String[]{"Glide缓存", s});
|
||||
binding.toolbar.setTitle("储存清理");
|
||||
binding.recyclerview.setAdapter(adapter);
|
||||
}
|
||||
|
||||
private final RecyclerView.Adapter<VH> adapter = new RecyclerView.Adapter<VH>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View inflate = LayoutInflater.from(fragment_clean.this).
|
||||
inflate(R.layout.list_clean, parent, false);
|
||||
return new VH(inflate);
|
||||
}
|
||||
|
||||
@SuppressLint({"ClickableViewAccessibility", "SetTextI18n"})
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
String[] s = list.get(position);
|
||||
File file = new File(s[1]);
|
||||
long leng = 0;
|
||||
int size = 0;
|
||||
if (file.isDirectory()) {
|
||||
gj.sc(file.toString());
|
||||
for (File a : file.listFiles()) {
|
||||
leng += a.length();
|
||||
size++;
|
||||
}
|
||||
} else {
|
||||
holder.checkBox.setEnabled(false);
|
||||
}
|
||||
String s1 = Formatter.formatFileSize(fragment_clean.this, leng);
|
||||
holder.checkBox.setText(s[0] + ":" + s1 + " 共计:" + size+" 个文件");
|
||||
holder.checkBox.setOnCheckedChangeListener((compoundButton, b) -> {
|
||||
if (b) {
|
||||
list_box.add(file.toString());
|
||||
}else {
|
||||
list_box.remove(file.toString());
|
||||
}
|
||||
menu_deleat.setVisible(list_box.size() > 0);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
};
|
||||
|
||||
class VH extends RecyclerView.ViewHolder {
|
||||
public CheckBox checkBox;
|
||||
public VH(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
checkBox = itemView.findViewById(R.id.box);
|
||||
}
|
||||
}
|
||||
|
||||
MenuItem menu_deleat;
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
menu_deleat = menu.add("删除");
|
||||
menu_deleat.setIcon(R.drawable.delete);
|
||||
menu_deleat.setTitle("删除");
|
||||
menu_deleat.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
menu_deleat.setVisible(false);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivityCleanBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityCleanBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
} else if (item == menu_deleat) {
|
||||
for (int i = 0; i < list_box.size(); i++) {
|
||||
File s= new File(list_box.get(i));
|
||||
wj.sc(s);
|
||||
}
|
||||
list_box.clear();
|
||||
menu_deleat.setVisible(false);
|
||||
UI();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
29
app/src/main/java/com/muqingbfq/fragment/Media.java
Normal file
29
app/src/main/java/com/muqingbfq/fragment/Media.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import com.dirror.lyricviewx.LyricViewX;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class Media {
|
||||
|
||||
public static String[] loadLyric() {
|
||||
if (com.muqingbfq.bfqkz.lrc == null) {
|
||||
return null;
|
||||
}
|
||||
JSONObject jsonObject;
|
||||
String a = null, b = null;
|
||||
try {
|
||||
jsonObject = new JSONObject(com.muqingbfq.bfqkz.lrc);
|
||||
a = jsonObject.getJSONObject("lrc").getString("lyric");
|
||||
b = jsonObject.getJSONObject("tlyric").getString("lyric");
|
||||
} catch (Exception e) {
|
||||
gj.sc("Media loadLyric " + e);
|
||||
}
|
||||
LyricViewX.lrc(a, b);
|
||||
// gj.sc(LyricViewX.lyricEntryList.get(0).text);
|
||||
|
||||
return new String[]{a, b};
|
||||
}
|
||||
|
||||
}
|
230
app/src/main/java/com/muqingbfq/fragment/bflb_db.java
Normal file
230
app/src/main/java/com/muqingbfq/fragment/bflb_db.java
Normal file
|
@ -0,0 +1,230 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import static android.content.Context.WINDOW_SERVICE;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.FragmentBflbDbBinding;
|
||||
import com.muqingbfq.databinding.ListMp3ABinding;
|
||||
import com.muqingbfq.list.MyViewHoder;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.yc;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class bflb_db extends BottomSheetDialog {
|
||||
public static RecyclerView.Adapter<MyViewHoder> adapter;
|
||||
FragmentBflbDbBinding binding;
|
||||
|
||||
private void ingList() {
|
||||
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
return;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
List<MediaItem> list = new ArrayList<>();
|
||||
int mediaItemCount = player.getMediaItemCount();
|
||||
for (int i = 0; i < mediaItemCount; i++) {
|
||||
list.add(player.getMediaItemAt(i));
|
||||
}
|
||||
binding.lb.setAdapter(new spq(list));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
// 获取底部弹窗的根视图
|
||||
View view = findViewById(com.google.android.material.R.id.design_bottom_sheet);
|
||||
if (view != null) {
|
||||
// 设置宽度
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
layoutParams.width = (int) (getContext().getResources().getDisplayMetrics().widthPixels * 0.6); // 占屏幕宽度的 60%
|
||||
view.setLayoutParams(layoutParams);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = FragmentBflbDbBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
// 设置高度
|
||||
// DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
// WindowManager windowManager = (WindowManager) getContext().getSystemService(WINDOW_SERVICE);
|
||||
// windowManager.getDefaultDisplay().getMetrics(displayMetrics);
|
||||
// binding.getRoot().getLayoutParams().height = displayMetrics.heightPixels - displayMetrics.heightPixels / 3;
|
||||
// binding.getRoot().requestLayout();
|
||||
|
||||
|
||||
// 设置固定的高度(例如 500dp)
|
||||
try {
|
||||
ingList();
|
||||
if (bfqkz.xm != null) {
|
||||
binding.lb.smoothScrollToPosition(getI());
|
||||
}
|
||||
binding.textView.setOnClickListener(v -> {
|
||||
if (bfqkz.xm != null) {
|
||||
binding.lb.smoothScrollToPosition(getI());
|
||||
}
|
||||
});
|
||||
binding.sc.setOnClickListener(view -> new MaterialAlertDialogBuilder(getContext())
|
||||
.setTitle("清空播放列表")
|
||||
.setPositiveButton("确定", (dialogInterface, i) -> {
|
||||
bfqkz.list.clear();
|
||||
dismiss();
|
||||
})
|
||||
.setNegativeButton("取消", null)
|
||||
.show());
|
||||
|
||||
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
|
||||
ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0) {
|
||||
@Override
|
||||
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder target) {
|
||||
int fromPosition = viewHolder.getAbsoluteAdapterPosition();
|
||||
int toPosition = target.getAbsoluteAdapterPosition();
|
||||
// 在这里处理数据集的移动
|
||||
// Collections.swap(bfqkz.list, fromPosition, toPosition);
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
player.moveMediaItem(fromPosition, toPosition);
|
||||
adapter.notifyItemMoved(fromPosition, toPosition);
|
||||
return true; // 返回true表示已经处理了拖动
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int direction) {
|
||||
// 不处理滑动操作
|
||||
}
|
||||
});
|
||||
itemTouchHelper.attachToRecyclerView(binding.lb);
|
||||
|
||||
} catch (Exception e) {
|
||||
yc.start(getContext(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private int getI() {
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
return 0;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
int i = player.getCurrentMediaItemIndex();
|
||||
if (i == -1) {
|
||||
i = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
public bflb_db(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public static void start(Context context) {
|
||||
bflb_db dialog = new bflb_db(context);
|
||||
dialog.setOnShowListener(dialogInterface -> {
|
||||
BottomSheetDialog d = (BottomSheetDialog) dialogInterface;
|
||||
FrameLayout bottomSheet = d.findViewById(com.google.android.material.R.id.design_bottom_sheet);
|
||||
if (bottomSheet != null) {
|
||||
BottomSheetBehavior<View> behavior = BottomSheetBehavior.from(bottomSheet);
|
||||
behavior.setState(BottomSheetBehavior.STATE_EXPANDED); // 默认展开
|
||||
behavior.setSkipCollapsed(true); // 禁止折叠
|
||||
}
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
private class spq extends RecyclerView.Adapter<MyViewHoder> {
|
||||
List<MediaItem> list;
|
||||
|
||||
public spq(List<MediaItem> list) {
|
||||
this.list = list;
|
||||
adapter = this;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MyViewHoder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new MyViewHoder(ListMp3ABinding.
|
||||
inflate(getLayoutInflater(), parent, false));
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MyViewHoder holder, int position) {
|
||||
MediaItem mediaItem = list.get(position);
|
||||
holder.bindingA.name.setText(mediaItem.mediaMetadata.title);
|
||||
holder.bindingA.zz.setText(String.format(" · %s", mediaItem.mediaMetadata.artist));
|
||||
int color = ContextCompat.getColor(holder.getContext(), R.color.text);
|
||||
//获取当前播放的项目
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
if (mediaItem.mediaId.equals(PlaybackService.mediaSession.getPlayer().getCurrentMediaItem().mediaId)) {
|
||||
color = ContextCompat.getColor(holder.getContext(), R.color.text_cz);
|
||||
}
|
||||
}
|
||||
holder.bindingA.name.setTextColor(color);
|
||||
holder.bindingA.zz.setTextColor(color);
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
// gj.sc(String.format("id=%s",mediaItem.mediaId));
|
||||
// MP3 hq = url.hq(new MP3(mediaItem.mediaId));
|
||||
// gj.sc(hq);
|
||||
main.handler.post(() -> {
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
int absoluteAdapterPosition = holder.getAbsoluteAdapterPosition();
|
||||
player.seekTo(absoluteAdapterPosition, 0);
|
||||
// player.replaceMediaItem(absoluteAdapterPosition, PlaybackService.GetMp3(hq));
|
||||
player.prepare();
|
||||
player.play();
|
||||
notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
});
|
||||
holder.bindingA.delete.setOnClickListener(v -> {
|
||||
list.remove(holder.getAbsoluteAdapterPosition());
|
||||
PlaybackService.ListSave();
|
||||
PlaybackService.list.removeIf(mp3 -> mp3.id.equals(mediaItem.mediaId));
|
||||
notifyItemRemoved(holder.getAbsoluteAdapterPosition());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
// binding.textView.setText(String.valueOf(bfqkz.list.size()));
|
||||
return list.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
super.dismiss();
|
||||
adapter = null;
|
||||
}
|
||||
}
|
190
app/src/main/java/com/muqingbfq/fragment/bfq_db.java
Normal file
190
app/src/main/java/com/muqingbfq/fragment/bfq_db.java
Normal file
|
@ -0,0 +1,190 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.MediaMetadata;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.common.util.Util;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.activity.Music;
|
||||
import com.muqingbfq.bfq_an;
|
||||
import com.muqingbfq.databinding.FragmentBfqDbBinding;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.Fragment;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
public class bfq_db extends Fragment<FragmentBfqDbBinding> implements GestureDetector.OnGestureListener {
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
@Override
|
||||
protected FragmentBfqDbBinding inflateViewBinding(LayoutInflater inflater, ViewGroup container) {
|
||||
return FragmentBfqDbBinding.inflate(inflater, container, false);
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public void setUI(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
// 获取当前活动的主题
|
||||
binding.txb.setOnClickListener(view -> bflb_db.start(getContext()));
|
||||
gestureDetector = new GestureDetector(getContext(), this);
|
||||
binding.kg.setOnClickListener(view -> {
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
Util.handlePlayPauseButtonAction(PlaybackService.mediaSession.getPlayer());
|
||||
}
|
||||
});
|
||||
binding.getRoot().setOnTouchListener((view, motionEvent) -> {
|
||||
gestureDetector.onTouchEvent(motionEvent);
|
||||
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
||||
binding.linearLayout.setTranslationX(0);
|
||||
// binding.getRoot().setAlpha(1.0f);
|
||||
}
|
||||
return false;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
main.handler.post(runnable); // 恢复监听或更新UI
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
main.handler.removeCallbacks(runnable);
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
PlaybackService.mediaSession.getPlayer().removeListener(playerListener); // 移除监听器
|
||||
}
|
||||
}
|
||||
|
||||
private void setUI(Player player) {
|
||||
|
||||
binding.kg.setImageResource(player.isPlaying() ? R.drawable.bf : R.drawable.zt);
|
||||
|
||||
MediaItem currentMediaItem = player.getCurrentMediaItem();
|
||||
if (currentMediaItem != null) {
|
||||
MediaMetadata metadata = currentMediaItem.mediaMetadata;
|
||||
String title = metadata.title != null ? metadata.title.toString() : "没有名字的音乐?";
|
||||
String artist = metadata.artist != null ? metadata.artist.toString() : "未知艺术家";
|
||||
binding.textview1.setText(title);
|
||||
binding.textview2.setText(artist);
|
||||
if (player.getMediaItemCount() > 0) {
|
||||
binding.getRoot().setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.getRoot().setVisibility(View.GONE);
|
||||
}
|
||||
} else {
|
||||
binding.getRoot().setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private final Player.Listener playerListener = new Player.Listener() {
|
||||
@Override
|
||||
public void onEvents(@NonNull Player player, @NonNull Player.Events events) {
|
||||
// boolean shouldShowPlayButton = Util.shouldShowPlayButton(player);
|
||||
// gj.sc("播放状态" + shouldShowPlayButton);
|
||||
setUI(player);
|
||||
}
|
||||
};
|
||||
|
||||
private void setPlay() {
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
PlaybackService.mediaSession.getPlayer().addListener(playerListener);
|
||||
}
|
||||
}
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (PlaybackService.mediaSession != null) {
|
||||
setPlay();
|
||||
setUI(PlaybackService.mediaSession.getPlayer());
|
||||
main.handler.removeCallbacks(this);
|
||||
} else {
|
||||
main.handler.postDelayed(this, 1000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public boolean onDown(@NonNull MotionEvent motionEvent) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onShowPress(@NonNull MotionEvent motionEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSingleTapUp(@NonNull MotionEvent motionEvent) {
|
||||
// bfq.startactivity(getContext(), bfqkz.xm);
|
||||
Music.startActivity(getContext());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(@Nullable MotionEvent motionEvent, @NonNull MotionEvent motionEvent1,
|
||||
float v, float v1) {
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
return false;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
if (player.getMediaItemCount() == 0) {
|
||||
//如果只有一首曲子
|
||||
return false;
|
||||
}
|
||||
if (player.getCurrentMediaItemIndex() == 0) {
|
||||
//如果没有上一曲
|
||||
if (v < 0) {
|
||||
v = 0;
|
||||
}
|
||||
}else if (player.getCurrentMediaItemIndex() == player.getMediaItemCount() - 1) {
|
||||
//如果没有下一曲
|
||||
if (v > 0) {
|
||||
v = 0;
|
||||
}
|
||||
}
|
||||
binding.linearLayout.setTranslationX(binding.linearLayout.getTranslationX() - v);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLongPress(@NonNull MotionEvent motionEvent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onFling(@Nullable MotionEvent e1,
|
||||
@NonNull MotionEvent e2, float v, float v1) {
|
||||
float distance = e1.getX() - e2.getX();
|
||||
float threshold = getResources().getDisplayMetrics().widthPixels / 2.0f;
|
||||
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
return false;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
// 判断手势方向并限制滑动距离
|
||||
if (distance > threshold) {
|
||||
// 向左滑动 下一曲
|
||||
player.seekToNextMediaItem();
|
||||
} else if (distance < -threshold) {
|
||||
// 向右滑动 上一曲
|
||||
player.seekToPreviousMediaItem();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
282
app/src/main/java/com/muqingbfq/fragment/gd.java
Normal file
282
app/src/main/java/com/muqingbfq/fragment/gd.java
Normal file
|
@ -0,0 +1,282 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.adapter.AdapterGdH;
|
||||
import com.muqingbfq.api.resource;
|
||||
import com.muqingbfq.databinding.ActivityGdBinding;
|
||||
import com.muqingbfq.databinding.ListGdBBinding;
|
||||
import com.muqingbfq.mq.FragmentActivity;
|
||||
import com.muqingbfq.mq.VH;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class gd extends FragmentActivity<ActivityGdBinding> {
|
||||
|
||||
public Adapter adapter = new Adapter();
|
||||
int k;
|
||||
|
||||
public static void start(Activity context, String[] str, View view) {
|
||||
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(context,
|
||||
view, "text");
|
||||
Intent intent = new Intent(context, gd.class);
|
||||
intent.putExtra("id", str[0]);
|
||||
intent.putExtra("name", str[1]);
|
||||
context.startActivity(intent, options.toBundle());
|
||||
}
|
||||
|
||||
public static void start(Context context, String[] str) {
|
||||
Intent intent = new Intent(context, gd.class);
|
||||
intent.putExtra("id", str[0]);
|
||||
intent.putExtra("name", str[1]);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView();
|
||||
Intent intent = getIntent();
|
||||
binding.title.setText(intent.getStringExtra("name"));
|
||||
// k = (int) (getResources().getDisplayMetrics().widthPixels / getResources().getDisplayMetrics().density + 0.5f);
|
||||
// GridLayoutManager gridLayoutManager = new GridLayoutManager(this, k / 120);
|
||||
binding.lb.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.lb.setAdapter(adapter);
|
||||
|
||||
binding.edittext.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
if (binding.edittext.getVisibility() == View.VISIBLE) {
|
||||
adapter.getFilter().filter(charSequence);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
});
|
||||
String id = intent.getStringExtra("id");
|
||||
|
||||
binding.fragmentDb.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int height = binding.fragmentDb.getHeight();
|
||||
binding.lb.setPadding(0,0,0,height);
|
||||
}
|
||||
});
|
||||
new start(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivityGdBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityGdBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
class start extends Thread {
|
||||
String id;
|
||||
|
||||
public start(String id) {
|
||||
binding.recyclerview1Bar.setVisibility(View.VISIBLE);
|
||||
this.id = id;
|
||||
adapter.list.clear();
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
if (id.equals("排行榜")) {
|
||||
resource.leaderboard(adapter.list);
|
||||
} else {
|
||||
String hq = wl.hq("/search?keywords=" + id + "&limit=" + (k * 3) + "&type=1000");
|
||||
try {
|
||||
JSONArray jsonArray = new JSONObject(hq).getJSONObject("result")
|
||||
.getJSONArray("playlists");
|
||||
int length = jsonArray.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
String id = jsonObject.getString("id");
|
||||
String name = jsonObject.getString("name");
|
||||
String coverImgUrl = jsonObject.getString("coverImgUrl");
|
||||
adapter.list.add(new XM(id, name, coverImgUrl));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
binding.lb.setAdapter(adapter);
|
||||
binding.recyclerview1Bar.setVisibility(View.GONE);
|
||||
if (adapter.list.isEmpty()) {
|
||||
binding.recyclerview1Text.setVisibility(View.VISIBLE);
|
||||
binding.recyclerview1Text.setOnClickListener(v -> new start(id));
|
||||
} else {
|
||||
binding.recyclerview1Text.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void setonlong(int position) {
|
||||
XM xm = list.get(position);
|
||||
gj.sc(xm.name);
|
||||
String[] stringArray = getResources()
|
||||
.getStringArray(R.array.gd_list);
|
||||
if (!wj.cz(wj.gd + xm.id)) {
|
||||
stringArray = new String[]{"下载歌单"};
|
||||
}
|
||||
String[] finalStringArray = stringArray;
|
||||
new MaterialAlertDialogBuilder(this).
|
||||
setItems(stringArray, (dialog, id) -> {
|
||||
switch (finalStringArray[id]) {
|
||||
case "下载歌单":
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
String hq = playlist.gethq(xm.id);
|
||||
if (hq != null) {
|
||||
try {
|
||||
XM fh = resource.Playlist_content(xm.id);
|
||||
JSONObject json = new JSONObject(hq);
|
||||
json.put("name", fh.name);
|
||||
json.put("picUrl", fh.picurl);
|
||||
json.put("message", fh.message);
|
||||
// json.put(fh.id, json);
|
||||
wj.xrwb(wj.gd + xm.id, json.toString());
|
||||
// wode.addlist(fh);
|
||||
// main.handler.post(() -> notifyItemChanged(position));
|
||||
} catch (JSONException e) {
|
||||
gj.sc("list gd onclick thear " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
break;
|
||||
case "删除歌单":
|
||||
// 删除项目
|
||||
try {
|
||||
wj.sc(wj.gd + xm.id);
|
||||
// wode.removelist(xm);
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 在这里处理菜单项的点击事件
|
||||
}).show();
|
||||
}
|
||||
*/
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuItem itemA = menu.add("搜索");
|
||||
itemA.setTitle("搜索");
|
||||
itemA.setIcon(R.drawable.sousuo);
|
||||
itemA.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == android.R.id.home) {
|
||||
if (binding.edittext.getVisibility() == View.VISIBLE) {
|
||||
binding.title.setVisibility(View.VISIBLE);
|
||||
binding.edittext.setVisibility(View.GONE);
|
||||
gj.ycjp(binding.edittext);
|
||||
adapter.getFilter().filter("");
|
||||
} else {
|
||||
ActivityCompat.finishAfterTransition(this);
|
||||
}
|
||||
} else if (itemId == 0) {
|
||||
binding.title.setVisibility(View.GONE);
|
||||
binding.edittext.setVisibility(View.VISIBLE);
|
||||
// gj.tcjp(binding.edittext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public class Adapter extends AdapterGdH implements Filterable {
|
||||
private List<XM> list_ys;
|
||||
|
||||
public Adapter() {
|
||||
list_ys = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH<ListGdBBinding> holder, int position) {
|
||||
super.onBindViewHolder(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence charSequence) {
|
||||
String charString = charSequence.toString();
|
||||
if (charString.isEmpty()) {
|
||||
//没有过滤的内容,则使用源数据
|
||||
list = list_ys;
|
||||
} else {
|
||||
List<XM> filteredList = new ArrayList<>();
|
||||
for (int i = 0; i < list_ys.size(); i++) {
|
||||
XM xm = list_ys.get(i);
|
||||
if (xm.name.contains(charString)) {
|
||||
filteredList.add(list_ys.get(i));
|
||||
}
|
||||
}
|
||||
list = filteredList;
|
||||
}
|
||||
|
||||
FilterResults filterResults = new FilterResults();
|
||||
filterResults.values = list;
|
||||
return filterResults;
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
|
||||
list = (List<XM>) filterResults.values;
|
||||
binding.lb.setAdapter(adapter);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
130
app/src/main/java/com/muqingbfq/fragment/gd_adapter.java
Normal file
130
app/src/main/java/com/muqingbfq/fragment/gd_adapter.java
Normal file
|
@ -0,0 +1,130 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.activity_search;
|
||||
import com.muqingbfq.adapter.AdapterGd;
|
||||
import com.muqingbfq.adapter.AdapterMp3;
|
||||
import com.muqingbfq.api.resource;
|
||||
import com.muqingbfq.databinding.FragmentGdBinding;
|
||||
import com.muqingbfq.databinding.ListMp3ImageBinding;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.Fragment;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class gd_adapter extends Fragment<FragmentGdBinding> {
|
||||
List<XM> list = new ArrayList<>();
|
||||
AdapterMp3 adapterMp3 = new AdapterMp3();
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected FragmentGdBinding inflateViewBinding(LayoutInflater inflater, ViewGroup container) {
|
||||
return FragmentGdBinding.inflate(inflater, container, false);
|
||||
}
|
||||
|
||||
AdapterGd adapterGd = new AdapterGd();
|
||||
@Override
|
||||
public void setUI(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(),LinearLayoutManager.HORIZONTAL,false);
|
||||
binding.recyclerview1.setHasFixedSize(true);
|
||||
binding.recyclerview1.setNestedScrollingEnabled(false);
|
||||
binding.recyclerview1.setLayoutManager(linearLayoutManager);
|
||||
adapterGd.list = list;
|
||||
binding.recyclerview1.setAdapter(adapterGd);
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
resource.recommend(list);
|
||||
main.handler.post(new sx());
|
||||
}
|
||||
}.start();
|
||||
|
||||
mp3list();
|
||||
binding.recyclerview2.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recyclerview2.setNestedScrollingEnabled(false);
|
||||
binding.recyclerview2.setAdapter(adapterMp3);
|
||||
// requireActivity().findViewById(R.id.linearLayout4).post(new Runnable() {
|
||||
// @Override
|
||||
// public void run() {
|
||||
// binding.recyclerview2.setPadding(0, 0, 0,
|
||||
// requireActivity().findViewById(R.id.linearLayout4).getHeight());
|
||||
// }
|
||||
// });
|
||||
}
|
||||
private class sx implements Runnable {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
binding.recyclerview1.getAdapter().notifyDataSetChanged();
|
||||
binding.recyclerview1Bar.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public void mp3list() {
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
String hq = wl.hq("/recommend/songs" + "?cookie=" + wl.Cookie);
|
||||
if (hq == null) {
|
||||
hq = wj.dqwb(wj.filesdri + "songs.json");
|
||||
}
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
JSONObject data = jsonObject.getJSONObject("data");
|
||||
JSONArray dailySongs = data.getJSONArray("dailySongs");
|
||||
for (int i = 0; i < dailySongs.length(); i++) {
|
||||
JSONObject jsonObject1 = dailySongs.getJSONObject(i);
|
||||
String id = jsonObject1.getString("id");
|
||||
String name = jsonObject1.getString("name");
|
||||
JSONArray ar = jsonObject1.getJSONArray("ar");
|
||||
StringBuilder zz = new StringBuilder();
|
||||
for (int j = 0; j < ar.length(); j++) {
|
||||
zz.append(ar.getJSONObject(j).getString("name")).append(' ');
|
||||
}
|
||||
JSONObject al = jsonObject1.getJSONObject("al");
|
||||
String picUrl = al.getString("picUrl");
|
||||
adapterMp3.list.add(new MP3(id, name, zz.toString(), picUrl));
|
||||
}
|
||||
wj.xrwb(wj.filesdri + "songs.json", hq);
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
adapterMp3.notifyDataSetChanged();
|
||||
binding.recyclerview2Bar.setVisibility(View.GONE);
|
||||
});
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public void Gdlist() {
|
||||
|
||||
}
|
||||
}
|
364
app/src/main/java/com/muqingbfq/fragment/mp3.java
Normal file
364
app/src/main/java/com/muqingbfq/fragment/mp3.java
Normal file
|
@ -0,0 +1,364 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.GradientDrawable;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Filter;
|
||||
import android.widget.Filterable;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.load.DataSource;
|
||||
import com.bumptech.glide.load.engine.GlideException;
|
||||
import com.bumptech.glide.request.RequestListener;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.bumptech.glide.request.target.Target;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.adapter.AdapterMp3;
|
||||
import com.muqingbfq.api.playlist;
|
||||
import com.muqingbfq.api.url;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.ActivityMp3Binding;
|
||||
import com.muqingbfq.databinding.ListMp3ImageBinding;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.FragmentActivity;
|
||||
import com.muqingbfq.mq.VH;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import jp.wasabeef.glide.transformations.BlurTransformation;
|
||||
|
||||
public class mp3 extends FragmentActivity<ActivityMp3Binding> {
|
||||
private List<MP3> list = new ArrayList<>();
|
||||
private List<MP3> list_ys = new ArrayList<>();
|
||||
public Adapter adapter;
|
||||
|
||||
public static void start(Activity context, String[] str, View view) {
|
||||
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(context,
|
||||
view, "text");
|
||||
Intent intent = new Intent(context, mp3.class);
|
||||
intent.putExtra("id", str[0]);
|
||||
intent.putExtra("name", str[1]);
|
||||
context.startActivity(intent, options.toBundle());
|
||||
}
|
||||
|
||||
public static void start(Context context, String[] strings) {
|
||||
Intent intent = new Intent(context, mp3.class);
|
||||
intent.putExtra("id", strings[0]);
|
||||
intent.putExtra("name", strings[1]);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static Drawable drawable = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView();
|
||||
// drawable=null;
|
||||
TypedValue typedValue = new TypedValue();
|
||||
getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true);
|
||||
; // 获取当前主题的背景颜色
|
||||
if (drawable != null) {
|
||||
|
||||
// 4. 设置到 ImageView 上
|
||||
ImageView imageView = findViewById(R.id.toolbarimage);
|
||||
Glide.with(this)
|
||||
.load(drawable)
|
||||
.apply(RequestOptions.bitmapTransform(new BlurTransformation(26,3)))
|
||||
.addListener(new RequestListener<Drawable>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, @Nullable Object model, @NonNull Target<Drawable> target, boolean isFirstResource) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Drawable resource, @NonNull Object model, Target<Drawable> target, @NonNull DataSource dataSource, boolean isFirstResource) {
|
||||
|
||||
// 使用 Glide 加载图片并应用高斯模糊效果
|
||||
// 1. 创建渐变遮罩层(从底部白色渐变到顶部透明)
|
||||
GradientDrawable gradient = new GradientDrawable(GradientDrawable.Orientation.BOTTOM_TOP,
|
||||
new int[] {typedValue.data, Color.TRANSPARENT});
|
||||
gradient.setShape(GradientDrawable.RECTANGLE);
|
||||
|
||||
// 3. 使用 LayerDrawable 来组合模糊图像和渐变效果
|
||||
LayerDrawable layerDrawable = new LayerDrawable(new Drawable[]{resource, gradient});
|
||||
imageView.setImageDrawable(layerDrawable);
|
||||
return true;
|
||||
}
|
||||
})
|
||||
.into(imageView);
|
||||
}
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, 0, systemBars.right, systemBars.bottom);
|
||||
// binding.toolbar.setPadding(0, systemBars.top, 0, 0);
|
||||
FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) binding.toolbar.getLayoutParams();
|
||||
params.setMargins(0, systemBars.top, 0, 0); // 参数分别是 left, top, right, bottom
|
||||
binding.toolbar.setLayoutParams(params);
|
||||
|
||||
return insets;
|
||||
});
|
||||
Intent intent = getIntent();
|
||||
binding.title.setText(intent.getStringExtra("name"));
|
||||
String id = intent.getStringExtra("id");
|
||||
adapter = new Adapter(list);
|
||||
binding.lb.setLayoutManager(new LinearLayoutManager(this));
|
||||
binding.lb.setAdapter(adapter);
|
||||
new start(id);
|
||||
binding.edittext.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
if (binding.edittext.getVisibility() == View.VISIBLE) {
|
||||
adapter.getFilter().filter(charSequence);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
if (binding.edittext.getVisibility() == View.VISIBLE) {
|
||||
binding.title.setVisibility(View.VISIBLE);
|
||||
binding.edittext.setVisibility(View.GONE);
|
||||
gj.ycjp(binding.edittext);
|
||||
adapter.getFilter().filter("");
|
||||
} else {
|
||||
finish();
|
||||
// ActivityCompat.finishAfterTransition(mp3.this);
|
||||
}
|
||||
}
|
||||
});
|
||||
binding.fragmentDb.post(() -> {
|
||||
int height = binding.fragmentDb.getHeight();
|
||||
binding.lb.setPadding(0, 0, 0, height);
|
||||
});
|
||||
binding.playButton.setOnClickListener(v -> {
|
||||
v.setEnabled(false);
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
return;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
player.clearMediaItems();
|
||||
List<MP3> aalist = new ArrayList<>(list);
|
||||
if (bfqkz.ms == 2) {
|
||||
Collections.shuffle(aalist);
|
||||
}
|
||||
for (int i = 0; i < aalist.size(); i++) {
|
||||
MP3 mp3 = aalist.get(i);
|
||||
// mp3 = url.hq(mp3);
|
||||
MediaItem mediaItem = PlaybackService.GetMp3(mp3);
|
||||
player.addMediaItem(mediaItem);
|
||||
}
|
||||
player.prepare();
|
||||
player.seekTo(0, 0);
|
||||
player.play();
|
||||
//保存播放列表
|
||||
PlaybackService.list.clear();
|
||||
PlaybackService.list.addAll(aalist);
|
||||
PlaybackService.ListSave();
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
while (true){
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
//检测音乐是否播放
|
||||
runOnUiThread(() -> {
|
||||
if (!player.isPlaying()) {
|
||||
return;
|
||||
}
|
||||
v.setEnabled(true);
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
MenuItem itemA = menu.add("搜索");
|
||||
itemA.setTitle("搜索");
|
||||
itemA.setIcon(R.drawable.sousuo);
|
||||
itemA.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivityMp3Binding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityMp3Binding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == android.R.id.home) {
|
||||
if (binding.edittext.getVisibility() == View.VISIBLE) {
|
||||
binding.title.setVisibility(View.VISIBLE);
|
||||
binding.edittext.setVisibility(View.GONE);
|
||||
gj.ycjp(binding.edittext);
|
||||
adapter.getFilter().filter("");
|
||||
} else {
|
||||
ActivityCompat.finishAfterTransition(this);
|
||||
}
|
||||
} else if (itemId == 0) {
|
||||
binding.title.setVisibility(View.GONE);
|
||||
binding.edittext.setVisibility(View.VISIBLE);
|
||||
gj.tcjp(binding.edittext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class start extends Thread {
|
||||
String id;
|
||||
|
||||
public start(String id) {
|
||||
binding.recyclerview1Bar.setVisibility(View.VISIBLE);
|
||||
this.id = id;
|
||||
list.clear();
|
||||
list_ys.clear();
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
switch (id) {
|
||||
case "mp3_xz.json":
|
||||
playlist.hq_xz(list);
|
||||
break;
|
||||
case "mp3_like.json":
|
||||
playlist.hq_like(list);
|
||||
break;
|
||||
case "cd.json":
|
||||
playlist.hq_cd(mp3.this, list);
|
||||
break;
|
||||
default:
|
||||
playlist.hq(list, id);
|
||||
break;
|
||||
}
|
||||
list_ys = list;
|
||||
main.handler.post(() -> {
|
||||
binding.lb.getAdapter().notifyDataSetChanged();
|
||||
binding.recyclerview1Bar.setVisibility(View.GONE);
|
||||
if (list.isEmpty()) {
|
||||
binding.recyclerview1Text.setVisibility(View.VISIBLE);
|
||||
binding.recyclerview1Text.setOnClickListener(v -> new start(id));
|
||||
} else {
|
||||
binding.recyclerview1Text.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class Adapter extends AdapterMp3 implements Filterable {
|
||||
|
||||
private List<MP3> list_ys;
|
||||
|
||||
public Adapter(List<MP3> list) {
|
||||
this.list = list;
|
||||
list_ys = list;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH<ListMp3ImageBinding> holder, int position) {
|
||||
super.onBindViewHolder(holder, position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Filter getFilter() {
|
||||
return new Filter() {
|
||||
@Override
|
||||
protected FilterResults performFiltering(CharSequence charSequence) {
|
||||
String charString = charSequence.toString();
|
||||
if (charString.isEmpty()) {
|
||||
//没有过滤的内容,则使用源数据
|
||||
list = list_ys;
|
||||
} else {
|
||||
List<MP3> filteredList = new ArrayList<>();
|
||||
for (int i = 0; i < list_ys.size(); i++) {
|
||||
MP3 mp3 = list_ys.get(i);
|
||||
if (mp3.name.contains(charString)
|
||||
|| mp3.zz.contains(charString)) {
|
||||
filteredList.add(list_ys.get(i));
|
||||
}
|
||||
}
|
||||
list = filteredList;
|
||||
}
|
||||
|
||||
FilterResults filterResults = new FilterResults();
|
||||
filterResults.values = list;
|
||||
return filterResults;
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
protected void publishResults(CharSequence charSequence, FilterResults filterResults) {
|
||||
list = (List<MP3>) filterResults.values;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void startactivity(Context context, String id) {
|
||||
context.startActivity(new Intent(context, mp3.class).putExtra("id", id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
adapter = null;
|
||||
}
|
||||
}
|
231
app/src/main/java/com/muqingbfq/fragment/search.java
Normal file
231
app/src/main/java/com/muqingbfq/fragment/search.java
Normal file
|
@ -0,0 +1,231 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
|
||||
import com.google.android.material.tabs.TabLayoutMediator;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.adapter.AdapterGdH;
|
||||
import com.muqingbfq.adapter.AdapterMp3;
|
||||
import com.muqingbfq.databinding.FragmentSearchBinding;
|
||||
import com.muqingbfq.databinding.RecyclerVBinding;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class search extends Fragment {
|
||||
public FragmentSearchBinding binding;
|
||||
public String string;
|
||||
List<Fragment> fragments=new ArrayList<>();
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
binding = FragmentSearchBinding.inflate(inflater, container, false);
|
||||
binding.viewPager.setSaveEnabled(false);
|
||||
adapter = new FragmentStateAdapter(this) {
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
return fragments.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return fragments.size();
|
||||
}
|
||||
};
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
public static class mp3 extends Fragment {
|
||||
public static mp3 newInstance(String string) {
|
||||
mp3 fragment = new mp3();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("string", string);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
List<MP3> list = new ArrayList<>();
|
||||
String string = getArguments().getString("string");
|
||||
RecyclerVBinding binding = RecyclerVBinding.inflate(inflater, container, false);
|
||||
binding.recycleview.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recycleview.setAdapter(new AdapterMp3(list));
|
||||
list.clear();
|
||||
binding.recyclerviewBar.setVisibility(View.VISIBLE);
|
||||
binding.recyclerviewText.setVisibility(View.GONE);
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
mp3(list, string);
|
||||
main.handler.post(() -> {
|
||||
binding.recyclerviewBar.setVisibility(View.GONE);
|
||||
if (list.isEmpty()) {
|
||||
binding.recyclerviewText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.recyclerviewText.setVisibility(View.GONE);
|
||||
}
|
||||
binding.recycleview.getAdapter().notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
return binding.getRoot();
|
||||
}
|
||||
}
|
||||
public static class gd extends Fragment {
|
||||
public static gd newInstance(String string) {
|
||||
gd fragment = new gd();
|
||||
Bundle args = new Bundle();
|
||||
args.putString("string", string);
|
||||
fragment.setArguments(args);
|
||||
return fragment;
|
||||
}
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
List<XM> list = new ArrayList<>();
|
||||
String string = getArguments().getString("string");
|
||||
RecyclerVBinding binding = RecyclerVBinding.inflate(inflater, container, false);
|
||||
binding.recycleview.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
binding.recycleview.setAdapter(new AdapterGdH(list));
|
||||
list.clear();
|
||||
binding.recyclerviewBar.setVisibility(View.VISIBLE);
|
||||
binding.recyclerviewText.setVisibility(View.GONE);
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
gd(list, string);
|
||||
main.handler.post(() -> {
|
||||
binding.recyclerviewBar.setVisibility(View.GONE);
|
||||
if (list.isEmpty()) {
|
||||
binding.recyclerviewText.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.recyclerviewText.setVisibility(View.GONE);
|
||||
}
|
||||
binding.recycleview.getAdapter().notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
return binding.getRoot();
|
||||
}
|
||||
}
|
||||
private FragmentStateAdapter adapter;
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void sx(String string) {
|
||||
this.string = string;
|
||||
fragments.clear();
|
||||
fragments.add(mp3.newInstance(string));
|
||||
fragments.add(gd.newInstance(string));
|
||||
binding.viewPager.setAdapter(adapter);
|
||||
adapter.notifyDataSetChanged();
|
||||
String[] strtab = new String[]{"歌曲", "歌单"};
|
||||
//将tabbView绑定到tab
|
||||
new TabLayoutMediator(binding.tablayout, binding.viewPager, (tab, position) ->
|
||||
tab.setText(strtab[position])).attach();
|
||||
}
|
||||
|
||||
private static void mp3(List<MP3> list, String str) {
|
||||
try {
|
||||
Long.parseLong(str);
|
||||
com.muqingbfq.api.playlist.hq(list, str);
|
||||
return;
|
||||
} catch (NumberFormatException e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
String hq = wl.hq("/search?keywords=" + str + "&type=1");
|
||||
try {
|
||||
JSONArray jsonArray = new JSONObject(hq).getJSONObject("result")
|
||||
.getJSONArray("songs");
|
||||
int length = jsonArray.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
String id = jsonObject.getString("id");
|
||||
String name = jsonObject.getString("name");
|
||||
JSONArray artists = jsonObject.getJSONArray("artists");
|
||||
int length1 = artists.length();
|
||||
StringBuilder zz = null;
|
||||
for (int j = 0; j < length1; j++) {
|
||||
JSONObject josn = artists.getJSONObject(j);
|
||||
String name_zz = josn.getString("name");
|
||||
if (zz == null) {
|
||||
zz = new StringBuilder(name_zz);
|
||||
} else {
|
||||
zz.append("/").append(name_zz);
|
||||
}
|
||||
}
|
||||
list.add(new MP3(id, name, zz.toString(), ""));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
|
||||
private static void gd(List<XM> list, String str) {
|
||||
try {
|
||||
Long.parseLong(str);
|
||||
String hq = wl.hq("/playlist/detail?id=" + str);
|
||||
JSONObject js = new JSONObject(hq).getJSONObject("playlist");
|
||||
String id = js.getString("id");
|
||||
String name = js.getString("name");
|
||||
String coverImgUrl = js.getString("coverImgUrl");
|
||||
// gj.sc(name);
|
||||
list.add(new XM(id, name, coverImgUrl));
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
try {
|
||||
String hq = wl.hq("/search?keywords=" + str + "&type=1000");
|
||||
JSONArray jsonArray = new JSONObject(hq).getJSONObject("result")
|
||||
.getJSONArray("playlists");
|
||||
int length = jsonArray.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
String id = jsonObject.getString("id");
|
||||
|
||||
String name = jsonObject.getString("name");
|
||||
String coverImgUrl = jsonObject.getString("coverImgUrl");
|
||||
|
||||
long playCount = jsonObject.getLong("playCount");
|
||||
String formattedNumber = String.valueOf(playCount);
|
||||
if (playCount > 9999) {
|
||||
DecimalFormat df = new DecimalFormat("#,###.0万");
|
||||
formattedNumber = df.format(playCount / 10000);
|
||||
}
|
||||
String s = jsonObject.getInt("trackCount") + "首,"
|
||||
+ "by " + jsonObject.getJSONObject("creator").getString("nickname")
|
||||
+ ",播放"
|
||||
+ formattedNumber + "次";
|
||||
list.add(new XM(id, name, s, coverImgUrl));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}
|
69
app/src/main/java/com/muqingbfq/fragment/sz.java
Normal file
69
app/src/main/java/com/muqingbfq/fragment/sz.java
Normal file
|
@ -0,0 +1,69 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.activity_about_software;
|
||||
import com.muqingbfq.clean.fragment_clean;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class sz {
|
||||
@SuppressLint("NonConstantResourceId")
|
||||
public static void switch_sz(Context context, int id) {
|
||||
if (id == R.id.a) {
|
||||
gj.llq(context, "https://github.com/muqing153/CloudMusic");
|
||||
} else if (id == R.id.b) {
|
||||
context.startActivity(new Intent(context, com.muqingbfq.sz.class));
|
||||
// 设置中心
|
||||
} else if (id == R.id.c) {
|
||||
context.startActivity(new Intent(context, fragment_clean.class));
|
||||
// 储存清理
|
||||
} else if (id == R.id.d) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse("mqqapi://card/show_pslcard?card_type=group&uin="
|
||||
+ 674891685));
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
Toast.makeText(context, "无法打开 QQ", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
// 如果没有安装 QQ 客户端或无法打开 QQ,您可以在此处理异常
|
||||
// 官方聊群
|
||||
} else if (id == R.id.e) {
|
||||
try {
|
||||
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||
"mqqwpa://im/chat?chat_type=wpa&uin=" + "1966944300"));
|
||||
context.startActivity(intent);
|
||||
} catch (Exception e) {
|
||||
// 如果没有安装 QQ 或无法跳转,则会抛出异常
|
||||
Toast.makeText(context, "无法打开 QQ", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
// 联系作者
|
||||
} else if (id == R.id.f) {
|
||||
context.startActivity(new Intent(context, activity_about_software.class));
|
||||
// 关于软件
|
||||
} else if (id == R.id.g) {
|
||||
// 关闭软件
|
||||
ActivityManager mActivityManager = (ActivityManager)
|
||||
main.application.getSystemService(Context.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> mList = mActivityManager.getRunningAppProcesses();
|
||||
for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : mList)
|
||||
{
|
||||
if (runningAppProcessInfo.pid != android.os.Process.myPid())
|
||||
{
|
||||
android.os.Process.killProcess(runningAppProcessInfo.pid);
|
||||
}
|
||||
}
|
||||
android.os.Process.killProcess(android.os.Process.myPid());
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
273
app/src/main/java/com/muqingbfq/fragment/wode.java
Normal file
273
app/src/main/java/com/muqingbfq/fragment/wode.java
Normal file
|
@ -0,0 +1,273 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.gson.Gson;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.api.playlist;
|
||||
import com.muqingbfq.api.resource;
|
||||
import com.muqingbfq.databinding.FragmentWdBinding;
|
||||
import com.muqingbfq.login.user_logs;
|
||||
import com.muqingbfq.login.visitor;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.EditViewDialog;
|
||||
import com.muqingbfq.mq.Fragment;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class wode extends Fragment<FragmentWdBinding> {
|
||||
public TextView name, jieshao;
|
||||
public ImageView imageView;
|
||||
private final Object[][] lista = {
|
||||
{R.drawable.mdimusicbox, "最近播放", "mp3_hc.json"},
|
||||
{R.drawable.download, "下载音乐", "mp3_xz.json"},
|
||||
{R.drawable.mdialbum, "喜欢音乐", "mp3_like.json"},
|
||||
{R.drawable.filesearc, "本地搜索", "cd.json"},
|
||||
{R.drawable.api, "更换接口", "API"},
|
||||
{R.drawable.gd, "导入歌单", "gd"},
|
||||
{R.drawable.paihangbang, "排行榜", "排行榜"},
|
||||
{R.drawable.ic_launcher_foreground, "开发中", ""}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected FragmentWdBinding inflateViewBinding(LayoutInflater inflater, ViewGroup container) {
|
||||
return FragmentWdBinding.inflate(inflater, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUI(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
name = binding.text1;
|
||||
jieshao = binding.text2;
|
||||
imageView = binding.imageView;
|
||||
binding.cardview.setOnClickListener(new dl());
|
||||
|
||||
binding.recyclerview1.setNestedScrollingEnabled(false);
|
||||
binding.recyclerview1.setLayoutManager(new GridLayoutManager(getContext(), 4));
|
||||
binding.recyclerview1.setFocusable(false);
|
||||
binding.recyclerview1.setAdapter(new RecyclerView.Adapter<VH>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View inflate = View.inflate(getContext(), R.layout.view_button, null);
|
||||
return new VH(inflate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
String s = lista[position][1].toString();
|
||||
holder.textView.setText(s);
|
||||
holder.imageView.setImageResource((Integer) lista[position][0]);
|
||||
String data = lista[position][2].toString();
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
switch (data) {
|
||||
case "cd.json":
|
||||
case "mp3_hc.json":
|
||||
case "mp3_xz.json":
|
||||
case "mp3_like.json":
|
||||
Intent a = new Intent(getContext(), com.muqingbfq.fragment.mp3.class);
|
||||
a.putExtra("id", data);
|
||||
a.putExtra("name", s);
|
||||
getContext().startActivity(a);
|
||||
break;
|
||||
case "排行榜":
|
||||
gd.start(getActivity(), new String[]{data, s});
|
||||
break;
|
||||
case "API":
|
||||
EditViewDialog editViewDialog = new EditViewDialog(getContext(), "更换接口API")
|
||||
.setMessage("当前接口:\n" + main.api);
|
||||
editViewDialog.setPositive(view1 -> {
|
||||
String str = editViewDialog.getEditText();
|
||||
boolean http = str.startsWith("http");
|
||||
if (str.isEmpty() || !http) {
|
||||
gj.ts(getContext(), "请输入正确的api");
|
||||
} else {
|
||||
gj.ts(getContext(), "更换成功");
|
||||
main.api = str;
|
||||
wj.xrwb(wj.filesdri + "API.mq", main.api);
|
||||
editViewDialog.dismiss();
|
||||
}
|
||||
}).show();
|
||||
break;
|
||||
case "gd":
|
||||
EditViewDialog editViewDialog1 = new EditViewDialog(getContext(),
|
||||
"导入歌单")
|
||||
.setMessage("请用网易云https链接来进行导入或者歌单id");
|
||||
editViewDialog1.setPositive(view1 -> {
|
||||
String str = editViewDialog1.getEditText();
|
||||
// 使用正则表达式提取链接
|
||||
Pattern pattern = Pattern.compile("https?://[\\w./?=&]+");
|
||||
Matcher matcher = pattern.matcher(str);
|
||||
if (matcher.find())
|
||||
str = matcher.group();
|
||||
if (!str.isEmpty()) {
|
||||
// 使用截取方法获取歌单 ID
|
||||
str = str.substring(str.indexOf("id=") + 3, str.indexOf("&"));
|
||||
}
|
||||
String finalStr = str;
|
||||
gj.ts(getContext(), "导入中");
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
String hq = playlist.gethq(finalStr);
|
||||
if (hq != null) {
|
||||
try {
|
||||
XM fh = resource.Playlist_content(finalStr);
|
||||
JSONObject json = new JSONObject(hq);
|
||||
json.put("name", fh.name);
|
||||
json.put("picUrl", fh.picurl);
|
||||
json.put("message", fh.message);
|
||||
// json.put(fh.id, json);
|
||||
wj.xrwb(wj.gd + finalStr, json.toString());
|
||||
// addlist(fh);
|
||||
} catch (JSONException e) {
|
||||
gj.sc("list gd onclick thear " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
editViewDialog1.dismiss();
|
||||
}).show();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return lista.length;
|
||||
}
|
||||
});
|
||||
|
||||
binding.recyclerview2.setNestedScrollingEnabled(false);
|
||||
binding.recyclerview2.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
// adaper = new baseadapter();
|
||||
// binding.recyclerview2.setAdapter(adaper);
|
||||
// sx();
|
||||
new threadLogin().start();
|
||||
}
|
||||
|
||||
ActivityResultLauncher<Intent> dlintent = registerForActivityResult(
|
||||
new ActivityResultContracts.StartActivityForResult(),
|
||||
result -> {
|
||||
if (result.getResultCode() == Activity.RESULT_OK) {
|
||||
// 处理返回结果
|
||||
Intent data = result.getData();
|
||||
boolean bool = data.getBooleanExtra("bool", false);
|
||||
if (bool) {
|
||||
// gj.sc("dl");
|
||||
new threadLogin().start();
|
||||
}
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
class dl implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
File file = new File(wj.filesdri, "user.mq");
|
||||
if (file.exists()) {
|
||||
String[] a = new String[]{"退出登录"};
|
||||
new MaterialAlertDialogBuilder(getContext())
|
||||
.setItems(a, (dialogInterface, i) -> {
|
||||
boolean delete = file.delete();
|
||||
if (delete) {
|
||||
wj.sc(wj.filesdri + "user.mq");
|
||||
binding.text1.setText(getString(R.string.app_name));
|
||||
binding.text2.setText(getString(R.string.app_name));
|
||||
imageView.setImageResource(R.drawable.ic_launcher_foreground);
|
||||
new visitor();//游客模式
|
||||
wj.sc(wj.filesdri + "user.mq");
|
||||
// new com.muqingbfq.login.user_message();
|
||||
}
|
||||
}).show();
|
||||
} else {
|
||||
dlintent.launch(new Intent(getContext(), user_logs.class));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class VH extends RecyclerView.ViewHolder {
|
||||
public ImageView imageView;
|
||||
public TextView textView;
|
||||
|
||||
public VH(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
imageView = itemView.findViewById(R.id.image);
|
||||
textView = itemView.findViewById(R.id.text1);
|
||||
}
|
||||
}
|
||||
|
||||
//登陆 获取用户信息 头像 昵称 签名
|
||||
class threadLogin extends Thread {
|
||||
|
||||
public void run() {
|
||||
String hq = wl.hq("/user/account?cookie=" + wl.Cookie);
|
||||
if (hq != null) {
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
int code = jsonObject.getInt("code");
|
||||
if (code == 200) {
|
||||
JSONObject profile = jsonObject.getJSONObject("profile");
|
||||
String nickname = profile.getString("nickname");
|
||||
String avatarUrl = profile.getString("avatarUrl");
|
||||
String signature = profile.getString("signature");
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
binding.text1.setText(nickname);
|
||||
binding.text2.setText(signature);
|
||||
Glide.with(getContext())
|
||||
.load(avatarUrl)
|
||||
.error(R.drawable.ic_launcher_foreground)
|
||||
.into(binding.imageView);
|
||||
});
|
||||
wj.xrwb(wj.filesdri + "user.mq", new Gson().toJson(new user_logs.USER(nickname, signature, avatarUrl)));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
} else {
|
||||
if (!wj.cz(wj.filesdri + "user.mq")) {
|
||||
return;
|
||||
}
|
||||
String dqwb = wj.dqwb(wj.filesdri + "user.mq");
|
||||
user_logs.USER user = new Gson().fromJson(dqwb, user_logs.USER.class);
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
binding.text1.setText(user.name);
|
||||
binding.text2.setText(user.qianming);
|
||||
Glide.with(getContext())
|
||||
.load(user.picUrl)
|
||||
.error(R.drawable.ic_launcher_foreground)
|
||||
.into(binding.imageView);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
348
app/src/main/java/com/muqingbfq/home.java
Normal file
348
app/src/main/java/com/muqingbfq/home.java
Normal file
|
@ -0,0 +1,348 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ComponentName;
|
||||
import android.os.Bundle;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.GravityCompat;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.media3.session.MediaController;
|
||||
import androidx.media3.session.SessionToken;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.google.android.flexbox.AlignItems;
|
||||
import com.google.android.flexbox.FlexDirection;
|
||||
import com.google.android.flexbox.FlexWrap;
|
||||
import com.google.android.flexbox.FlexboxLayoutManager;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.android.material.search.SearchView;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.MoreExecutors;
|
||||
import com.muqingbfq.databinding.ActivityHomeBinding;
|
||||
import com.muqingbfq.fragment.gd_adapter;
|
||||
import com.muqingbfq.fragment.search;
|
||||
import com.muqingbfq.fragment.sz;
|
||||
import com.muqingbfq.fragment.wode;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
import com.muqingbfq.view.Edit;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class home extends AppCompatActivity<ActivityHomeBinding> {
|
||||
|
||||
@Override
|
||||
protected ActivityHomeBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityHomeBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(R.style.Theme_muqing);
|
||||
EdgeToEdge.enable(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
SessionToken sessionToken =
|
||||
new SessionToken(this, new ComponentName(this, PlaybackService.class));
|
||||
ListenableFuture<MediaController> controllerFuture =
|
||||
new MediaController.Builder(this, sessionToken).buildAsync();
|
||||
controllerFuture.addListener(() -> {
|
||||
|
||||
}, MoreExecutors.directExecutor());
|
||||
if (Strings.isNullOrEmpty(wl.Cookie)) {
|
||||
new HomeSteer(this) {
|
||||
@Override
|
||||
public void Yes() {
|
||||
UI();
|
||||
}
|
||||
};
|
||||
} else UI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
|
||||
public void toolbar() {
|
||||
setSupportActionBar(binding.toolbar);
|
||||
}
|
||||
|
||||
public void UI() {
|
||||
setContentView();
|
||||
// 禁止ViewPager2滑动
|
||||
binding.viewPager.setUserInputEnabled(false);
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
// v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
v.setPadding(systemBars.left, 0, systemBars.right, 0);
|
||||
binding.chb.setPadding(0, systemBars.top, 0, 0);
|
||||
return insets;
|
||||
});
|
||||
// viewTop=binding.
|
||||
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
if (binding.searchview.isShowing()) {
|
||||
binding.searchview.hide();
|
||||
return;
|
||||
}
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
});
|
||||
|
||||
//初始化侧滑
|
||||
binding.chb.setNavigationItemSelectedListener(item -> {
|
||||
sz.switch_sz(home.this, item.getItemId());
|
||||
return false;
|
||||
});
|
||||
List<Fragment> list = new ArrayList<>();
|
||||
list.add(new gd_adapter());
|
||||
list.add(new wode());
|
||||
binding.viewPager.setAdapter(new FragmentStateAdapter(this) {
|
||||
@NonNull
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
return list.get(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
});
|
||||
binding.viewPager.setCurrentItem(0,false);
|
||||
if (!gj.isTablet(this)) {
|
||||
binding.linearLayout4.post(() -> {
|
||||
int height = binding.linearLayout4.getHeight();
|
||||
binding.viewPager.setPadding(0, 0, 0, height);
|
||||
});
|
||||
}
|
||||
binding.fragmentDb.post(() -> {
|
||||
int height = binding.fragmentDb.getHeight();
|
||||
binding.searchview.setPadding(0, 0, 0, height);
|
||||
});
|
||||
binding.tablayout.addView("推荐", R.drawable.zhuye).setOnClickListener(v -> {
|
||||
for (int i = 0; i < binding.tablayout.sizeView; i++) {
|
||||
MaterialCardView childAt = (MaterialCardView) binding.tablayout.linearLayout.getChildAt(i);
|
||||
if (i == 0) {
|
||||
//背景高亮
|
||||
childAt.setCardBackgroundColor(gj.getThemeColor(v.getContext(), com.google.android.material.R.attr.colorPrimaryContainer));
|
||||
} else {
|
||||
//背景恢复
|
||||
childAt.setCardBackgroundColor(gj.getThemeColor(v.getContext(), com.google.android.material.R.attr.colorSurface));
|
||||
}
|
||||
}
|
||||
binding.viewPager.setCurrentItem(0,false);
|
||||
});
|
||||
binding.tablayout.addView("我的", R.drawable.user).setOnClickListener(v -> {
|
||||
for (int i = 0; i < binding.tablayout.sizeView; i++) {
|
||||
MaterialCardView childAt = (MaterialCardView) binding.tablayout.linearLayout.getChildAt(i);
|
||||
if (i == 1) {
|
||||
//背景高亮
|
||||
childAt.setCardBackgroundColor(gj.getThemeColor(v.getContext(), com.google.android.material.R.attr.colorPrimaryContainer));
|
||||
} else {
|
||||
//背景恢复
|
||||
childAt.setCardBackgroundColor(gj.getThemeColor(v.getContext(), com.google.android.material.R.attr.colorSurface));
|
||||
}
|
||||
}
|
||||
binding.viewPager.setCurrentItem(1,false);
|
||||
});
|
||||
toolbar();
|
||||
SearchUI();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
//在销毁 Activity 之前,系统会先调用 onDestroy()。系统调用此回调的原因如下:
|
||||
// 保存列表数据
|
||||
|
||||
}
|
||||
|
||||
public boolean issearchclicklist = false;//是否点击了列表项目
|
||||
//搜索建议列表
|
||||
private List<String> searchList = new ArrayList<>();
|
||||
|
||||
private activity_search.SearchRecordAdapter searchRecordAdapter;
|
||||
|
||||
public void SearchUI() {
|
||||
binding.searchview
|
||||
.getEditText()
|
||||
.setOnEditorActionListener(
|
||||
(v, actionId, event) -> {
|
||||
// binding.searchview.hide();
|
||||
searchStart(binding.toolbar.getText().toString());
|
||||
return false;
|
||||
});
|
||||
binding.searchview.setOnMenuItemClickListener(
|
||||
menuItem -> {
|
||||
// Handle menuItem click.
|
||||
return true;
|
||||
});
|
||||
FlexboxLayoutManager manager = new FlexboxLayoutManager(this);
|
||||
//设置主轴排列方式
|
||||
manager.setFlexDirection(FlexDirection.ROW);
|
||||
//设置是否换行
|
||||
manager.setFlexWrap(FlexWrap.WRAP);
|
||||
manager.setAlignItems(AlignItems.STRETCH);//历史记录的LayoutManager
|
||||
binding.listRecycler.setNestedScrollingEnabled(false);
|
||||
binding.listRecycler.setLayoutManager(manager);
|
||||
binding.searchview.addTransitionListener(
|
||||
(searchView, previousState, newState) -> {
|
||||
if (newState == SearchView.TransitionState.SHOWING) {
|
||||
// Handle search view opened.
|
||||
gj.sc("SHOWING");
|
||||
binding.tablayout.setVisibility(View.GONE);
|
||||
// 添加 Fragment
|
||||
getSupportFragmentManager()
|
||||
.beginTransaction()
|
||||
.replace(binding.searchFragment.getId(), new search())
|
||||
.commit();
|
||||
searchRecordAdapter = new activity_search.SearchRecordAdapter(binding.searchview);
|
||||
binding.listRecycler.setAdapter(searchRecordAdapter);
|
||||
} else if (newState == SearchView.TransitionState.SHOWN) {
|
||||
gj.sc("SHOWN");
|
||||
} else if (newState == SearchView.TransitionState.HIDING) {
|
||||
if (!gj.isTablet(this)) {
|
||||
binding.tablayout.setVisibility(View.VISIBLE);
|
||||
}
|
||||
searchRecordAdapter = null;
|
||||
binding.listRecycler.setAdapter(null);
|
||||
binding.searchFragment.setVisibility(View.GONE);
|
||||
binding.searchRecycler.setVisibility(View.GONE);
|
||||
binding.xxbj1.setVisibility(View.VISIBLE);
|
||||
// 移除当前显示的 Fragment
|
||||
getSupportFragmentManager().beginTransaction()
|
||||
.remove(getSupportFragmentManager()
|
||||
.findFragmentById(binding.searchFragment.getId()))
|
||||
.commit();
|
||||
}
|
||||
});
|
||||
final Object o = new Object();
|
||||
binding.searchview.getEditText().addTextChangedListener(new Edit.TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence var1, int var2, int var3, int var4) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence var1, int var2, int var3, int var4) {
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (o) {
|
||||
searchList.clear();
|
||||
String hq = com.muqingbfq.mq.wl.
|
||||
hq("/search/suggest?keywords=" + var1.toString() + "&type=mobile");
|
||||
try {
|
||||
JSONArray jsonArray = new JSONObject(hq).getJSONObject("result")
|
||||
.getJSONArray("allMatch");
|
||||
int length = jsonArray.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(i);
|
||||
String keyword = jsonObject.getString("keyword");
|
||||
searchList.add(keyword);
|
||||
}
|
||||
runOnUiThread(() -> binding.searchRecycler.setAdapter(new activity_search.search_adapter(searchList, string -> {
|
||||
gj.sc(string);
|
||||
issearchclicklist = true;
|
||||
binding.searchRecycler.setAdapter(null);
|
||||
binding.searchRecycler.setVisibility(View.GONE);
|
||||
binding.xxbj1.setVisibility(View.GONE);
|
||||
binding.searchFragment.setVisibility(View.VISIBLE);
|
||||
binding.searchview.setText(string);
|
||||
searchStart(string);
|
||||
// binding.searchRecycler.set
|
||||
})));
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable var1) {
|
||||
if (Strings.isNullOrEmpty(var1.toString())) {
|
||||
binding.searchRecycler.setAdapter(null);
|
||||
binding.searchRecycler.setVisibility(View.GONE);
|
||||
binding.xxbj1.setVisibility(View.VISIBLE);
|
||||
binding.searchFragment.setVisibility(View.GONE);
|
||||
} else if (!issearchclicklist) {
|
||||
binding.searchRecycler.setVisibility(View.VISIBLE);
|
||||
binding.xxbj1.setVisibility(View.GONE);
|
||||
binding.searchFragment.setVisibility(View.GONE);
|
||||
} else issearchclicklist = false;
|
||||
|
||||
}
|
||||
});
|
||||
binding.toolbar.setNavigationIcon(R.drawable.menu);
|
||||
binding.deleat.setOnClickListener(v -> new MaterialAlertDialogBuilder(
|
||||
v.getContext())
|
||||
.setTitle("删除")
|
||||
.setMessage("清空历史记录?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialogInterface, ii) -> {
|
||||
searchRecordAdapter.json_list.clear();
|
||||
binding.listRecycler.setAdapter(searchRecordAdapter);
|
||||
com.muqingbfq.mq.wj.sc(com.muqingbfq.mq.wj.filesdri +
|
||||
com.muqingbfq.mq.wj.lishi_json);
|
||||
})
|
||||
.show());
|
||||
|
||||
}
|
||||
|
||||
public void searchStart(String name) {
|
||||
issearchclicklist = true;
|
||||
binding.toolbar.setText(binding.searchview.getText());
|
||||
if (!TextUtils.isEmpty(name)) {
|
||||
search sea = (search) getSupportFragmentManager().findFragmentById(binding.searchFragment.getId());
|
||||
binding.searchFragment.setVisibility(View.VISIBLE);
|
||||
binding.searchRecycler.setVisibility(View.GONE);
|
||||
sea.sx(name);
|
||||
activity_search.addSearchRecord(name, searchRecordAdapter.json_list, searchRecordAdapter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.home, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
if (item.getItemId() == R.id.menu_search) {
|
||||
binding.searchview.show();
|
||||
// startActivity(new Intent(this, activity_search.class));
|
||||
} else if (item.getItemId() == android.R.id.home) {
|
||||
//展开侧滑
|
||||
binding.chct.openDrawer(GravityCompat.START);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
28
app/src/main/java/com/muqingbfq/list/MyViewHoder.java
Normal file
28
app/src/main/java/com/muqingbfq/list/MyViewHoder.java
Normal file
|
@ -0,0 +1,28 @@
|
|||
package com.muqingbfq.list;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.databinding.ListMp3ABinding;
|
||||
import com.muqingbfq.databinding.ListMp3Binding;
|
||||
|
||||
public class MyViewHoder extends RecyclerView.ViewHolder {
|
||||
public ListMp3Binding binding;
|
||||
public ListMp3ABinding bindingA;
|
||||
public MyViewHoder(@NonNull ListMp3Binding itemView) {
|
||||
super(itemView.getRoot());
|
||||
binding = itemView;
|
||||
}
|
||||
public MyViewHoder(@NonNull ListMp3ABinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
bindingA = itemView;
|
||||
}
|
||||
public Context getContext() {
|
||||
return itemView.getContext();
|
||||
}
|
||||
}
|
5
app/src/main/java/com/muqingbfq/list/file_list.java
Normal file
5
app/src/main/java/com/muqingbfq/list/file_list.java
Normal file
|
@ -0,0 +1,5 @@
|
|||
package com.muqingbfq.list;
|
||||
|
||||
public class file_list {
|
||||
|
||||
}
|
4
app/src/main/java/com/muqingbfq/list/yylb.java
Normal file
4
app/src/main/java/com/muqingbfq/list/yylb.java
Normal file
|
@ -0,0 +1,4 @@
|
|||
package com.muqingbfq.list;
|
||||
|
||||
public class yylb {
|
||||
}
|
250
app/src/main/java/com/muqingbfq/login/user_logs.java
Normal file
250
app/src/main/java/com/muqingbfq/login/user_logs.java
Normal file
|
@ -0,0 +1,250 @@
|
|||
package com.muqingbfq.login;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.activity.OnBackPressedCallback;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.WindowCompat;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.muqingbfq.databinding.ActivityUserLogsBinding;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
||||
public class user_logs extends AppCompatActivity<ActivityUserLogsBinding> {
|
||||
@Override
|
||||
protected ActivityUserLogsBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityUserLogsBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
public static class USER {
|
||||
public String name, qianming;
|
||||
public Object picUrl;
|
||||
|
||||
public USER(String user, String qianming, Object picUrl) {
|
||||
this.name = user;
|
||||
this.qianming = qianming;
|
||||
this.picUrl = picUrl;
|
||||
}
|
||||
}
|
||||
|
||||
erweima thread;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView();
|
||||
setSupportActionBar(binding.toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
binding.login.setOnClickListener(view1 -> {
|
||||
if (!TextUtils.isEmpty(binding.editUser.getText())) {
|
||||
wl.setcookie(binding.editUser.getText().toString());
|
||||
}
|
||||
new Thread(() -> {
|
||||
gj.sc(wl.Cookie);
|
||||
String hq = wl.hq("/login/status?cookie=" + wl.Cookie);
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
JSONObject data = jsonObject.getJSONObject("data");
|
||||
JSONObject account1 = data.getJSONObject("account");
|
||||
finish(true);
|
||||
|
||||
} catch (JSONException e) {
|
||||
gj.sc(e);
|
||||
runOnUiThread(() -> {
|
||||
gj.ycjp(binding.editUser);
|
||||
Toast.makeText(user_logs.this, "不成功的Cookie登陆", Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
});
|
||||
binding.button1.setOnClickListener(view -> {
|
||||
if (binding.layout1.getVisibility() == View.VISIBLE) {
|
||||
binding.layout1.setVisibility(View.GONE);
|
||||
binding.layout2.setVisibility(View.VISIBLE);
|
||||
binding.button1.setText("账号");
|
||||
thread = new erweima();
|
||||
thread.start();
|
||||
} else {
|
||||
thread.interrupt();
|
||||
binding.button1.setText("二维码");
|
||||
binding.layout1.setVisibility(View.VISIBLE);
|
||||
binding.layout2.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
@Override
|
||||
public void handleOnBackPressed() {
|
||||
finish(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
//some statement
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish(false);
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void finish(boolean aBoolean) {
|
||||
Intent resultIntent = new Intent();
|
||||
resultIntent.putExtra("bool", aBoolean);
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
super.finish();
|
||||
}
|
||||
|
||||
|
||||
public static Bitmap stringToBitmap(String string) {
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
byte[] bitmapArray = Base64.decode(string.split(",")[1], Base64.DEFAULT);
|
||||
bitmap = BitmapFactory.decodeByteArray(bitmapArray, 0, bitmapArray.length);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
public String account, password;
|
||||
|
||||
class CloudUser extends Thread {
|
||||
public CloudUser() {
|
||||
InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
|
||||
View v = getWindow().peekDecorView();
|
||||
if (null != v) {
|
||||
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
|
||||
}
|
||||
user_logs.this.account = binding.editUser.getText().toString();
|
||||
gj.xcts(user_logs.this, "设置成功");
|
||||
wl.setcookie(account);
|
||||
finish();
|
||||
// start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
try {
|
||||
String hq = wl.hq("/login/cellphone?phone=" + account + "&password=" + password);
|
||||
if (TextUtils.isEmpty(hq)) {
|
||||
return;
|
||||
}
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
int code = jsonObject.getInt("code");
|
||||
if (code == 200) {
|
||||
JSONObject data = jsonObject.getJSONObject("profile");
|
||||
String nickname = data.getString("nickname");//用户名
|
||||
String avatarUrl = data.getString("avatarUrl");//用户头像
|
||||
String signature = data.getString("signature");//用户签名
|
||||
String cookie = jsonObject.getString("cookie");
|
||||
|
||||
String s = new Gson().toJson(new user_logs.USER(nickname, signature, avatarUrl));
|
||||
wj.xrwb(wj.filesdri + "user.mq", s);
|
||||
user_logs.this.finish(true);
|
||||
} else if (code == 502) {
|
||||
gj.xcts(user_logs.this, jsonObject.getString("message"));
|
||||
} else {
|
||||
gj.xcts(user_logs.this, "找不到此账号");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class erweima extends Thread {
|
||||
int code = 800;
|
||||
String unikey, qrimg, hq;
|
||||
private long time = 0;
|
||||
|
||||
public erweima() {
|
||||
binding.text1.setText("请使用网易云音乐扫码");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
while (code != 0 && !Thread.currentThread().isInterrupted()) {
|
||||
gj.sc(code);
|
||||
try {
|
||||
hq = wl.hq("/login/qr/check?key=" + unikey + Time());
|
||||
if (hq != null) {
|
||||
JSONObject json = new JSONObject(hq);
|
||||
code = json.getInt("code");
|
||||
switch (code) {
|
||||
case 800:
|
||||
case 400:
|
||||
setwb("二维码过期");
|
||||
hqkey();
|
||||
break;
|
||||
case 801:
|
||||
setwb("等待扫码");
|
||||
break;
|
||||
case 802:
|
||||
setwb("等待确认");
|
||||
break;
|
||||
case 803:
|
||||
setwb("登录成功");
|
||||
wl.setcookie(json.getString("cookie"));
|
||||
code = 0;
|
||||
user_logs.this.finish(true);
|
||||
break;
|
||||
default:
|
||||
code = 0;
|
||||
// 默认情况下的操作
|
||||
break;
|
||||
}
|
||||
}
|
||||
sleep(1000);
|
||||
} catch (Exception e) {
|
||||
Thread.currentThread().interrupt();
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void hqkey() throws Exception {
|
||||
unikey = new JSONObject(Objects.requireNonNull(wl.hq("/login/qr/key"))).
|
||||
getJSONObject("data").getString("unikey");
|
||||
JSONObject jsonObject = new JSONObject(Objects.requireNonNull(wl.hq("/login/qr/create?key=" +
|
||||
unikey +
|
||||
"&qrimg=base64")));
|
||||
qrimg = jsonObject.getJSONObject("data").getString("qrimg");
|
||||
main.handler.post(() -> binding.image.setImageBitmap(user_logs.stringToBitmap(qrimg)));
|
||||
}
|
||||
|
||||
private String Time() {
|
||||
if (time < System.currentTimeMillis() - 1000) {
|
||||
time = System.currentTimeMillis();
|
||||
}
|
||||
return "×tamp" + time;
|
||||
}
|
||||
|
||||
private void setwb(String wb) {
|
||||
main.handler.post(() -> binding.text1.setText(wb));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
29
app/src/main/java/com/muqingbfq/login/visitor.java
Normal file
29
app/src/main/java/com/muqingbfq/login/visitor.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package com.muqingbfq.login;
|
||||
|
||||
import android.content.Intent;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
import com.muqingbfq.yc;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class visitor extends Thread {
|
||||
public visitor() {
|
||||
start();
|
||||
}
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
String hq = wl.hq("/register/anonimous");
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
wl.setcookie(jsonObject.getString("cookie"));
|
||||
} catch (Exception e) {
|
||||
com.muqingbfq.mq.gj.sc(e);
|
||||
}
|
||||
}
|
||||
}
|
117
app/src/main/java/com/muqingbfq/main.java
Normal file
117
app/src/main/java/com/muqingbfq/main.java
Normal file
|
@ -0,0 +1,117 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.lifecycle.DefaultLifecycleObserver;
|
||||
import androidx.lifecycle.LifecycleOwner;
|
||||
import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.login.visitor;
|
||||
import com.muqingbfq.mq.FloatingLyricsService;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
public class main extends Application {
|
||||
public static Application application;
|
||||
public static Handler handler = new Handler(Looper.getMainLooper());
|
||||
public static String api = "https://api.csm.sayqz.com";
|
||||
// public static String http = "https://www.muqingkaifazhe.top/muqingbfq.php"; 过时的更新检测
|
||||
public static SharedPreferences sp;
|
||||
public static SharedPreferences.Editor edit;
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
if (wj.filesdri == null) {
|
||||
new wj(this);
|
||||
}
|
||||
File file = new File(wj.filesdri + "API.mq");
|
||||
if (file.exists() && file.isFile()) {
|
||||
String dqwb = wj.dqwb(file.toString());
|
||||
if (!TextUtils.isEmpty(dqwb) && dqwb.startsWith("http")) {
|
||||
api = dqwb;
|
||||
} else {
|
||||
file.delete();
|
||||
}
|
||||
} else {
|
||||
wj.xrwb(file.toString(), main.api);
|
||||
}
|
||||
application = this;
|
||||
sp = getSharedPreferences("Set_up", MODE_PRIVATE);
|
||||
edit = sp.edit();
|
||||
boolean bj = false;
|
||||
try {
|
||||
com.muqingbfq.bfqkz.ms = sp.getInt("ms", 1);
|
||||
} catch (Exception e) {
|
||||
edit.putInt("ms", 1);
|
||||
bj = true;
|
||||
com.muqingbfq.bfqkz.ms = 1;
|
||||
}
|
||||
try {
|
||||
wl.Cookie = sp.getString("Cookie", "");
|
||||
} catch (Exception e) {
|
||||
edit.putString("Cookie", "");
|
||||
wl.Cookie = "";
|
||||
bj = true;
|
||||
}
|
||||
if (bj) {
|
||||
edit.commit();
|
||||
}
|
||||
|
||||
wl.Cookie = main.sp.getString("Cookie", "");
|
||||
if (wl.Cookie.isEmpty()) {
|
||||
new visitor();
|
||||
}
|
||||
SharedPreferences theme = getSharedPreferences("theme", MODE_PRIVATE);
|
||||
@SuppressLint("CommitPrefEdits") SharedPreferences.Editor edit = theme.edit();
|
||||
int i = theme.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
if (i == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
|
||||
edit.putInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
edit.apply();
|
||||
}
|
||||
AppCompatDelegate.setDefaultNightMode(i);
|
||||
String jsonList = this.getSharedPreferences("list", Context.MODE_PRIVATE)
|
||||
.getString("listData", null); // 获取保存的 JSON 字符串
|
||||
if (jsonList != null) {
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
bfqkz.list = new com.google.gson.Gson().fromJson(jsonList, type);
|
||||
// 将 JSON 字符串转换回列表数据
|
||||
}
|
||||
// bfqkz.xm = wj.getMP3FromFile();
|
||||
// 注册 ProcessLifecycleOwner 以监听应用生命周期事件
|
||||
ProcessLifecycleOwner.get().getLifecycle().addObserver(new DefaultLifecycleObserver() {
|
||||
@Override
|
||||
public void onStart(@Nullable LifecycleOwner owner) {
|
||||
// 应用进入前台
|
||||
gj.sc("onStart");
|
||||
if (FloatingLyricsService.lei != null) {
|
||||
stopService(new Intent(main.this, FloatingLyricsService.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop(@Nullable LifecycleOwner owner) {
|
||||
// 应用进入后台
|
||||
gj.sc("onStop");
|
||||
if (Settings.canDrawOverlays(main.this) && FloatingLyricsService.lei == null ) {
|
||||
startService(new Intent(main.this, FloatingLyricsService.class));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
65
app/src/main/java/com/muqingbfq/mq/AppCompatActivity.java
Normal file
65
app/src/main/java/com/muqingbfq/mq/AppCompatActivity.java
Normal file
|
@ -0,0 +1,65 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public abstract class AppCompatActivity<ViewBindingType extends ViewBinding> extends androidx.appcompat.app.AppCompatActivity {
|
||||
|
||||
protected abstract ViewBindingType getViewBindingObject(LayoutInflater layoutInflater);
|
||||
|
||||
protected ViewBindingType getViewBinding() {
|
||||
binding = getViewBindingObject(getLayoutInflater());
|
||||
return binding;
|
||||
}
|
||||
|
||||
public ViewBindingType binding;
|
||||
|
||||
public void setContentView() {
|
||||
EdgeToEdge.enable(this);
|
||||
super.setContentView(getViewBinding().getRoot());
|
||||
// Window window = getWindow();
|
||||
//// 请求进行全屏布局+更改状态栏字体颜色
|
||||
// // 获取程序是不是夜间模式
|
||||
// int uiMode = getApplicationContext().getResources().getConfiguration().uiMode;
|
||||
// if ((uiMode & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES) {
|
||||
//// SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION and SYSTEM_UI_FLAG_LAYOUT_STABLE请求进行全屏布局
|
||||
//// SYSTEM_UI_FLAG_VISIBLE进行更改状态栏字体颜色
|
||||
// window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_VISIBLE);//白色
|
||||
// } else {
|
||||
// window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);//黑色
|
||||
//
|
||||
// }
|
||||
//// 让内容显示在系统栏的后面,也就是显示在状态栏和导航栏的后面
|
||||
// WindowCompat.setDecorFitsSystemWindows(window, true);
|
||||
//// 沉浸状态栏(给任务栏上透明的色)(Android 10 上,只需要将系统栏颜色设为完全透明即可:)
|
||||
// window.setStatusBarColor(Color.TRANSPARENT);
|
||||
// // 沉浸导航栏(设置透明色)
|
||||
// window.setNavigationBarColor(Color.TRANSPARENT);
|
||||
//
|
||||
//// 在安卓10以上禁用系统栏视觉保护。
|
||||
//// 当设置了 导航栏 栏背景为透明时,NavigationBarContrastEnforced 如果为true,则系统会自动绘制一个半透明背景
|
||||
//// 状态栏的StatusBarContrast 效果同理,但是值默认为false,因此不用设置
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
// window.setNavigationBarContrastEnforced(false);
|
||||
// }
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
// v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right,0);
|
||||
return insets;
|
||||
});
|
||||
}
|
||||
}
|
176
app/src/main/java/com/muqingbfq/mq/EditViewDialog.java
Normal file
176
app/src/main/java/com/muqingbfq/mq/EditViewDialog.java
Normal file
|
@ -0,0 +1,176 @@
|
|||
package com.muqingbfq.mq;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.text.InputType;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.main;
|
||||
|
||||
public class EditViewDialog {
|
||||
|
||||
public EditText editText;
|
||||
public Button buttona, buttonb;
|
||||
public MaterialAlertDialogBuilder AlertDialogBuilder;
|
||||
private AlertDialog alertDialog;
|
||||
|
||||
public EditViewDialog(@NonNull Context context, String str) {
|
||||
AlertDialogBuilder = new MaterialAlertDialogBuilder(context);
|
||||
setTitle(str);
|
||||
@SuppressLint("InflateParams") View inflate = LayoutInflater.from(context).inflate(R.layout.view_edit, null);
|
||||
editText = inflate.findViewById(R.id.editview);
|
||||
buttona = inflate.findViewById(R.id.button1);
|
||||
buttonb = inflate.findViewById(R.id.button2);
|
||||
AlertDialogBuilder.setView(inflate);
|
||||
buttona.setOnClickListener(view -> dismiss());
|
||||
buttonb.setOnClickListener(view -> dismiss());
|
||||
}
|
||||
public EditViewDialog setTitle(String str) {
|
||||
AlertDialogBuilder.setTitle(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public EditViewDialog setMessage(String str) {
|
||||
AlertDialogBuilder.setMessage(str);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AlertDialog show() {
|
||||
alertDialog = AlertDialogBuilder.show();
|
||||
alertDialog.setCancelable(true);
|
||||
alertDialog.setCanceledOnTouchOutside(false);
|
||||
return alertDialog;
|
||||
}
|
||||
|
||||
public String getEditText() {
|
||||
return editText.getText().toString();
|
||||
}
|
||||
|
||||
public EditViewDialog setEditinputType(String str) {
|
||||
int inputType;
|
||||
switch (str) {
|
||||
case "textCapCharacters":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS;
|
||||
break;
|
||||
case "textCapWords":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_CAP_WORDS;
|
||||
break;
|
||||
case "textCapSentences":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES;
|
||||
break;
|
||||
case "textAutoCorrect":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_AUTO_CORRECT;
|
||||
break;
|
||||
case "textAutoComplete":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE;
|
||||
break;
|
||||
case "textMultiLine":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_MULTI_LINE;
|
||||
break;
|
||||
case "textImeMultiLine":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_IME_MULTI_LINE;
|
||||
break;
|
||||
case "textNoSuggestions":
|
||||
inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
|
||||
break;
|
||||
case "textUri":
|
||||
inputType = InputType.TYPE_TEXT_VARIATION_URI;
|
||||
break;
|
||||
case "textEmailAddress":
|
||||
inputType = InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS;
|
||||
break;
|
||||
case "textEmailSubject":
|
||||
inputType = InputType.TYPE_TEXT_VARIATION_EMAIL_SUBJECT;
|
||||
break;
|
||||
case "textShortMessage":
|
||||
inputType = InputType.TYPE_TEXT_VARIATION_SHORT_MESSAGE;
|
||||
break;
|
||||
case "textLongMessage":
|
||||
inputType = InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE;
|
||||
break;
|
||||
case "textPersonName":
|
||||
inputType = InputType.TYPE_TEXT_VARIATION_PERSON_NAME;
|
||||
break;
|
||||
case "textPostalAddress":
|
||||
inputType = InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS;
|
||||
break;
|
||||
case "textPassword":
|
||||
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
|
||||
break;
|
||||
case "textVisiblePassword":
|
||||
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
|
||||
break;
|
||||
case "textWebEditText":
|
||||
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT;
|
||||
break;
|
||||
case "textFilter":
|
||||
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_FILTER;
|
||||
break;
|
||||
case "textPhonetic":
|
||||
inputType = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PHONETIC;
|
||||
break;
|
||||
case "number":
|
||||
inputType = InputType.TYPE_CLASS_NUMBER;
|
||||
break;
|
||||
case "numberSigned":
|
||||
inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED;
|
||||
break;
|
||||
case "numberDecimal":
|
||||
inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_DECIMAL;
|
||||
break;
|
||||
case "phone":
|
||||
inputType = InputType.TYPE_CLASS_PHONE;
|
||||
break;
|
||||
case "datetime":
|
||||
inputType = InputType.TYPE_CLASS_DATETIME;
|
||||
break;
|
||||
case "date":
|
||||
inputType = InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE;
|
||||
break;
|
||||
case "time":
|
||||
inputType = InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME;
|
||||
break;
|
||||
case "none":
|
||||
case "text":
|
||||
default:
|
||||
inputType = InputType.TYPE_CLASS_TEXT;
|
||||
break;
|
||||
}
|
||||
editText.setInputType(inputType);
|
||||
return this;
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
main.handler.post(() -> alertDialog.dismiss());
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消按钮
|
||||
* @param a
|
||||
*/
|
||||
public void setNegative(View.OnClickListener a) {
|
||||
buttona.setOnClickListener(a);
|
||||
}
|
||||
|
||||
/**
|
||||
* 确定按钮
|
||||
* @param a
|
||||
* @return
|
||||
*/
|
||||
public EditViewDialog setPositive(View.OnClickListener a) {
|
||||
buttonb.setOnClickListener(a);
|
||||
return this;
|
||||
}
|
||||
|
||||
public interface OnClickListener extends View.OnClickListener {
|
||||
@Override
|
||||
void onClick(View view);
|
||||
}
|
||||
}
|
242
app/src/main/java/com/muqingbfq/mq/FloatingLyricsService.java
Normal file
242
app/src/main/java/com/muqingbfq/mq/FloatingLyricsService.java
Normal file
|
@ -0,0 +1,242 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Service;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.dirror.lyricviewx.LyricEntry;
|
||||
import com.dirror.lyricviewx.LyricViewX;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.PlaybackService;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.FloatLrcviewBinding;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class FloatingLyricsService extends Service {
|
||||
private WindowManager windowManager;
|
||||
FloatLrcviewBinding binding;
|
||||
|
||||
public Runnable updateSeekBar = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (PlaybackService.mediaSession == null || LyricViewX.lyricEntryList.isEmpty()) {
|
||||
handler.postDelayed(this, 1000);
|
||||
return;
|
||||
}
|
||||
int index = 0;
|
||||
for (int i = 0; i < LyricViewX.lyricEntryList.size(); i++) {
|
||||
LyricEntry lineLrc = LyricViewX.lyricEntryList.get(i);
|
||||
if (lineLrc.time <= PlaybackService.mediaSession.getPlayer().getCurrentPosition()) {
|
||||
index = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < LyricViewX.lyricEntryList.size()) {
|
||||
LyricEntry currentLrc = LyricViewX.lyricEntryList.get(index);
|
||||
binding.lrcView.setText(currentLrc.text);
|
||||
if (currentLrc.secondText != null) {
|
||||
binding.lrcViewMessage.setText(currentLrc.secondText);
|
||||
} else {
|
||||
binding.lrcViewMessage.setText("");
|
||||
}
|
||||
}
|
||||
handler.postDelayed(this, 1000); // 每秒更新一次进度
|
||||
}
|
||||
};
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public static FloatingLyricsService lei;
|
||||
|
||||
public static boolean get() {
|
||||
File file = new File(wj.filesdri + "FloatingLyricsService.json");
|
||||
if (file.exists() && file.isFile()) {
|
||||
String dqwb = wj.dqwb(file.toString());
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<SETUP>() {
|
||||
}.getType();
|
||||
SETUP setup = gson.fromJson(dqwb, type);
|
||||
return setup.i != 0;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Handler handler = new Handler();
|
||||
WindowManager.LayoutParams params;
|
||||
|
||||
public static class SETUP {
|
||||
//0是关闭 1是打开 2是锁定
|
||||
public int i = 1, size = 20;
|
||||
public float Alpha = 0.9f;
|
||||
public String Color = "#0088FF";
|
||||
public int Y = 0;
|
||||
}
|
||||
|
||||
public SETUP setup = new SETUP();
|
||||
|
||||
public int lock() {
|
||||
if (setup != null && setup.i == 2) {
|
||||
return WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
}
|
||||
return WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
}
|
||||
|
||||
private int initialY;
|
||||
private float initialTouchY;
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
lei = this;
|
||||
try {
|
||||
File file = new File(wj.filesdri + "FloatingLyricsService.json");
|
||||
if (file.exists() && file.isFile()) {
|
||||
String dqwb = wj.dqwb(file.toString());
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<SETUP>() {
|
||||
}.getType();
|
||||
setup = gson.fromJson(dqwb, type);
|
||||
} else {
|
||||
setup = new SETUP();
|
||||
}
|
||||
if (setup.i == 0) {
|
||||
//在Service中关闭自己
|
||||
stopSelf();
|
||||
return;
|
||||
}
|
||||
// 创建悬浮窗歌词的 View
|
||||
// FloatLrcviewBinding
|
||||
binding = FloatLrcviewBinding.inflate(LayoutInflater.from(this));
|
||||
// binding.getRoot().setOnTouchListener(this);
|
||||
// int i = WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;FLAG_NOT_TOUCH_MODAL
|
||||
params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.MATCH_PARENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
|
||||
WindowManager.LayoutParams.TYPE_PHONE,
|
||||
lock(),
|
||||
PixelFormat.TRANSLUCENT
|
||||
);
|
||||
|
||||
params.y = setup.Y;
|
||||
binding.getRoot().setAlpha(setup.Alpha);
|
||||
float v = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX,
|
||||
setup.size,
|
||||
getResources().getDisplayMetrics());
|
||||
binding.lrcView.setTextSize(v);
|
||||
binding.lrcView.setTextColor(Color.parseColor(setup.Color));
|
||||
binding.lrcViewMessage.setTextSize(v - 1.0f);
|
||||
binding.lrcViewMessage.setTextColor(Color.parseColor(setup.Color));
|
||||
binding.lock.setColorFilter(Color.BLACK);
|
||||
binding.lock.setOnClickListener(v1 -> setyc());
|
||||
// params.gravity = Gravity.CENTER;
|
||||
// 获取 WindowManager 并将悬浮窗歌词添加到窗口中
|
||||
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
windowManager.addView(binding.getRoot(), params);
|
||||
if (setup.i == 2) {
|
||||
setyc();
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
handler.post(updateSeekBar); // 在播放开始时启动更新进度
|
||||
|
||||
} catch (Exception e) {
|
||||
wj.sc(wj.filesdri + "FloatingLyricsService.json");
|
||||
gj.sc(getClass() + ":" + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// Service.stopSelf();
|
||||
// 在 Service 销毁时移除悬浮窗歌词
|
||||
if (windowManager != null && binding != null) {
|
||||
windowManager.removeView(binding.getRoot());
|
||||
handler.removeCallbacks(updateSeekBar); // 在播放开始时启动更新进度
|
||||
}
|
||||
lei = null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void baocun() {
|
||||
wj.xrwb(new File(wj.filesdri + "FloatingLyricsService.json").toString(),
|
||||
new Gson().toJson(setup));
|
||||
}
|
||||
|
||||
public static void baocun(SETUP setup) {
|
||||
if (setup == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
wj.xrwb(new File(wj.filesdri + "FloatingLyricsService.json").toString(),
|
||||
new Gson().toJson(setup));
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public void setyc() {
|
||||
setup.i = 2;
|
||||
params.flags = lock();
|
||||
binding.lock.setVisibility(View.GONE);
|
||||
binding.getRoot().setCardBackgroundColor(Color.parseColor("#00FFFFFF"));
|
||||
binding.getRoot().setOnTouchListener(null);
|
||||
params.type = Build.VERSION.SDK_INT < Build.VERSION_CODES.O ?
|
||||
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT : WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
|
||||
//将alpha设置为最大遮挡不透明度
|
||||
params.alpha = 0.8f;
|
||||
windowManager.updateViewLayout(binding.getRoot(), params);
|
||||
baocun();
|
||||
}
|
||||
|
||||
@SuppressLint({"CheckResult", "ClickableViewAccessibility"})
|
||||
public void show() {
|
||||
setup.i = 1;
|
||||
params.flags = lock();
|
||||
binding.lock.setVisibility(View.VISIBLE);
|
||||
binding.getRoot().setCardBackgroundColor(Color.parseColor("#FFFFFF"));
|
||||
binding.getRoot().setOnTouchListener((v12, event) -> {
|
||||
switch (event.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// 获取初始Y位置和初始触摸Y坐标
|
||||
initialY = params.y;
|
||||
initialTouchY = event.getRawY();
|
||||
return true;
|
||||
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// 手指在Y轴移动时,计算新的Y位置
|
||||
params.y = initialY + (int) (event.getRawY() - initialTouchY);
|
||||
// 更新悬浮窗的Y轴位置
|
||||
setup.Y = params.y;
|
||||
windowManager.updateViewLayout(binding.getRoot(), params);
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
windowManager.updateViewLayout(binding.getRoot(), params);
|
||||
baocun();
|
||||
}
|
||||
}
|
27
app/src/main/java/com/muqingbfq/mq/Fragment.java
Normal file
27
app/src/main/java/com/muqingbfq/mq/Fragment.java
Normal file
|
@ -0,0 +1,27 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
public abstract class Fragment<Binding extends ViewBinding> extends androidx.fragment.app.Fragment {
|
||||
|
||||
protected abstract Binding inflateViewBinding(LayoutInflater inflater,ViewGroup container);
|
||||
|
||||
public Binding binding;
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
binding=inflateViewBinding(inflater,container);
|
||||
setUI(inflater, container, savedInstanceState);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
public abstract void setUI(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState);
|
||||
}
|
60
app/src/main/java/com/muqingbfq/mq/FragmentActivity.java
Normal file
60
app/src/main/java/com/muqingbfq/mq/FragmentActivity.java
Normal file
|
@ -0,0 +1,60 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.activity.EdgeToEdge;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public abstract class FragmentActivity<ViewBindingType extends ViewBinding>
|
||||
extends AppCompatActivity<ViewBindingType> {
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void setToolbar() {
|
||||
View viewById = findViewById(R.id.toolbar);
|
||||
if (viewById != null) {
|
||||
setSupportActionBar((Toolbar) viewById);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView() {
|
||||
EdgeToEdge.enable(this);
|
||||
super.setContentView(getViewBinding().getRoot());
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
v.setPadding(systemBars.left, 0, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
setToolbar();
|
||||
|
||||
}
|
||||
|
||||
public void onBack() {
|
||||
|
||||
// getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
|
||||
// @Override
|
||||
// public void handleOnBackPressed() {
|
||||
// if (binding.searchview.isShowing()) {
|
||||
// binding.searchview.hide();
|
||||
// return;
|
||||
// }
|
||||
// moveTaskToBack(true);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
13
app/src/main/java/com/muqingbfq/mq/MusicViewModel.java
Normal file
13
app/src/main/java/com/muqingbfq/mq/MusicViewModel.java
Normal file
|
@ -0,0 +1,13 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
public class MusicViewModel extends ViewModel {
|
||||
private MutableLiveData<String> imageUrl = new MutableLiveData<>();
|
||||
|
||||
public MutableLiveData<String> getImageUrl() {
|
||||
return imageUrl;
|
||||
}
|
||||
}
|
13
app/src/main/java/com/muqingbfq/mq/RecyclerAdapter.java
Normal file
13
app/src/main/java/com/muqingbfq/mq/RecyclerAdapter.java
Normal file
|
@ -0,0 +1,13 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
public class RecyclerAdapter {
|
||||
|
||||
}
|
16
app/src/main/java/com/muqingbfq/mq/VH.java
Normal file
16
app/src/main/java/com/muqingbfq/mq/VH.java
Normal file
|
@ -0,0 +1,16 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
public class VH<bind extends ViewBinding> extends RecyclerView.ViewHolder {
|
||||
|
||||
public bind binding;
|
||||
public VH(bind itemView) {
|
||||
super(itemView.getRoot());
|
||||
binding = itemView;
|
||||
}
|
||||
}
|
170
app/src/main/java/com/muqingbfq/mq/gj.java
Normal file
170
app/src/main/java/com/muqingbfq/mq/gj.java
Normal file
|
@ -0,0 +1,170 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Service;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.TypedArray;
|
||||
import android.net.Uri;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.Log;
|
||||
import android.util.TypedValue;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.yc;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.net.NetworkInterface;
|
||||
import java.net.SocketException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class gj {
|
||||
public static void ts(Context a, Object b) {
|
||||
Toast.makeText(a, b.toString(), Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public static void xcts(Context context, Object b) {
|
||||
main.handler.post(() -> Toast.makeText(context, b.toString(), Toast.LENGTH_SHORT).show());
|
||||
|
||||
}
|
||||
|
||||
public static boolean isAppInForeground(Context context) {
|
||||
ActivityManager activityManager = (ActivityManager) context.getSystemService(Service.ACTIVITY_SERVICE);
|
||||
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfoList = activityManager.getRunningAppProcesses();
|
||||
|
||||
if (runningAppProcessInfoList == null) {
|
||||
Log.d("runningAppProcess:", "runningAppProcessInfoList is null!");
|
||||
return false;
|
||||
}
|
||||
for (ActivityManager.RunningAppProcessInfo processInfo : runningAppProcessInfoList) {
|
||||
if (processInfo.processName.equals(context.getPackageName()) && (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否是平板
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static boolean isTablet(Context context) {
|
||||
DisplayMetrics dm = context.getResources().getDisplayMetrics();
|
||||
return dm.widthPixels > dm.heightPixels;
|
||||
}
|
||||
|
||||
public static int getThemeColor(Context context, int id) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
if (context.getTheme().resolveAttribute(id, typedValue, true)) {
|
||||
return typedValue.data;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
public static void sc(Object a) {
|
||||
if (a == null) {
|
||||
a = "null";
|
||||
}
|
||||
Log.d("打印", a.toString());
|
||||
}
|
||||
|
||||
public static void llq(Context context, String str) {
|
||||
context.startActivity(new Intent(context, llq.class).putExtra("url", str));
|
||||
}
|
||||
|
||||
public static void fx(Context context, String str) {
|
||||
Intent shareIntent = new Intent(Intent.ACTION_SEND);
|
||||
shareIntent.setType("text/plain");
|
||||
shareIntent.putExtra(Intent.EXTRA_TEXT, str);
|
||||
context.startActivity(shareIntent);
|
||||
}
|
||||
|
||||
/**
|
||||
* 复制文字到剪切板
|
||||
*/
|
||||
public static void fz(Context context, String text) {
|
||||
ClipboardManager systemService =
|
||||
(ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
// 创建能够存入剪贴板的ClipData对象
|
||||
//‘Label’这是任意文字标签
|
||||
ClipData mClipData = ClipData.newPlainText("Label", text);
|
||||
//将ClipData数据复制到剪贴板:
|
||||
systemService.setPrimaryClip(mClipData);
|
||||
gj.ts(context, "复制成功");
|
||||
}
|
||||
|
||||
public static boolean isWiFiConnected() {
|
||||
try {
|
||||
for (NetworkInterface networkInterface : Collections.list(NetworkInterface.getNetworkInterfaces())) {
|
||||
if (networkInterface.isUp() && !networkInterface.isLoopback()) {
|
||||
if (networkInterface.getDisplayName().contains("wlan")) {
|
||||
return true; // Wi-Fi网络
|
||||
} else if (networkInterface.getDisplayName().contains("rmnet")) {
|
||||
return false; // 流量网络
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SocketException e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
return false; // 默认为流量网络
|
||||
}
|
||||
|
||||
public static void tcjp(EditText editText) {
|
||||
editText.requestFocus();//获取焦点
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
// gj.sc(imm.isActive());
|
||||
//没有显示键盘,弹出
|
||||
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
|
||||
}
|
||||
|
||||
public static void ycjp(EditText editText) {
|
||||
InputMethodManager imm = (InputMethodManager)
|
||||
editText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm.isActive()) //有显示键盘,隐藏
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(),
|
||||
InputMethodManager.HIDE_NOT_ALWAYS);
|
||||
}
|
||||
|
||||
public static int getztl(Context context) {
|
||||
// 获得状态栏高度
|
||||
@SuppressLint({"InternalInsetResource", "DiscouragedApi"}) int resourceId =
|
||||
context.getResources().
|
||||
getIdentifier("status_bar_height", "dimen", "android");
|
||||
return context.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
|
||||
public static int getbackgroundColor(AppCompatActivity appCompatActivity) {
|
||||
TypedArray array = appCompatActivity.getTheme().obtainStyledAttributes(new int[]{
|
||||
android.R.attr.colorBackground
|
||||
// android.R.attr.textColorPrimary,
|
||||
});
|
||||
int backgroundColor = array.getColor(0, 0xFF00FF);
|
||||
// int textColor = array.getColor(1, 0xFF00FF);
|
||||
array.recycle();
|
||||
return backgroundColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* dp转px
|
||||
* @param context
|
||||
* @param dpValue
|
||||
*/
|
||||
public static int dp2px(Context context, float dpValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
}
|
217
app/src/main/java/com/muqingbfq/mq/llq.java
Normal file
217
app/src/main/java/com/muqingbfq/mq/llq.java
Normal file
|
@ -0,0 +1,217 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.webkit.WebChromeClient;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.databinding.ActivityLlqBinding;
|
||||
import com.muqingbfq.databinding.ViewDownloadBinding;
|
||||
import com.muqingbfq.main;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Locale;
|
||||
|
||||
import okhttp3.Call;
|
||||
import okhttp3.Callback;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class llq extends AppCompatActivity<ActivityLlqBinding> {
|
||||
WebView web;
|
||||
|
||||
@Override
|
||||
protected ActivityLlqBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityLlqBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView();
|
||||
Intent intent = getIntent();
|
||||
setSupportActionBar(binding.toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
web = binding.webview;
|
||||
web.getSettings().setJavaScriptEnabled(true);
|
||||
web.getSettings().setDomStorageEnabled(true);
|
||||
// 禁用缓存
|
||||
web.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
|
||||
web.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public void onPageFinished(WebView view, String url) {
|
||||
super.onPageFinished(view, url);
|
||||
setTitle(web.getTitle());
|
||||
// 在这里获取到了网页的标题
|
||||
}
|
||||
});
|
||||
web.setDownloadListener((url1, userAgent, contentDisposition, mimetype, contentLength) -> {
|
||||
String size = "0B";
|
||||
if (contentLength > 0) {
|
||||
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
|
||||
int digitGroups = (int) (Math.log10(contentLength) / Math.log10(1024));
|
||||
size = String.format(Locale.getDefault(), "%.1f %s", contentLength
|
||||
/ Math.pow(1024, digitGroups), units[digitGroups]);
|
||||
}
|
||||
final String filename = url1.substring(url1.lastIndexOf('/') + 1);
|
||||
new MaterialAlertDialogBuilder(llq.this)
|
||||
.setTitle(filename)
|
||||
.setMessage("文件链接:" + url1 +
|
||||
"\n文件大小:" + size)
|
||||
.setNegativeButton("取消", null)
|
||||
.setNegativeButton("下载", (dialogInterface, i) -> {
|
||||
// 检查权限
|
||||
if (ContextCompat.checkSelfPermission(llq.this,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
// 如果没有写入存储的权限,则请求权限
|
||||
ActivityCompat.requestPermissions(llq.this,
|
||||
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
|
||||
1);
|
||||
} else {
|
||||
ViewDownloadBinding inflate = ViewDownloadBinding.inflate(getLayoutInflater());
|
||||
AlertDialog show = new MaterialAlertDialogBuilder(llq.this)
|
||||
.setTitle(String.format(Locale.getDefault()
|
||||
, "文件名称:%s", filename))
|
||||
.setMessage(String.format(Locale.getDefault()
|
||||
, "下载路径:%s", Environment.
|
||||
getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()))
|
||||
.setView(inflate.getRoot())
|
||||
.show();
|
||||
OkHttpClient okHttpClient = new OkHttpClient();
|
||||
Request build = new Request.Builder()
|
||||
.url(url1)
|
||||
.build();
|
||||
okHttpClient.newCall(build).enqueue(new Callback() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Call call, @NonNull IOException e) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResponse(@NonNull Call call, @NonNull Response response) throws IOException {
|
||||
InputStream inputStream = response.body().byteStream();
|
||||
long l = response.body().contentLength();
|
||||
File file = new File(Environment.getExternalStoragePublicDirectory(
|
||||
Environment.DIRECTORY_DOWNLOADS).getAbsolutePath(),
|
||||
filename);
|
||||
FileOutputStream fileOutputStream = null;
|
||||
try {
|
||||
int read;
|
||||
fileOutputStream = new FileOutputStream(file);
|
||||
byte[] bytes = new byte[2048];
|
||||
long downloadedSize = 0;
|
||||
while ((read = inputStream.read(bytes)) != -1) {
|
||||
fileOutputStream.write(bytes, 0, read);
|
||||
downloadedSize += read;
|
||||
int progress = (int) ((100 * downloadedSize) / l);
|
||||
main.handler.post(() -> inflate.textview.setText(
|
||||
String.format(Locale.getDefault(),
|
||||
"%d%%", progress)));
|
||||
}
|
||||
fileOutputStream.close();
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
} finally {
|
||||
if (fileOutputStream != null) {
|
||||
fileOutputStream.close();
|
||||
}
|
||||
}
|
||||
main.handler.post(() -> {
|
||||
gj.ts(llq.this, "下载完成");
|
||||
show.dismiss();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}).show();
|
||||
});
|
||||
web.setWebChromeClient(new WebChromeClient() {
|
||||
@Override
|
||||
public void onProgressChanged(WebView view, int newProgress) {
|
||||
super.onProgressChanged(view, newProgress);
|
||||
if (newProgress == 100) {
|
||||
binding.webViewProgressBar.setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.webViewProgressBar.setProgress(newProgress);
|
||||
if (!binding.webViewProgressBar.isShown()) {
|
||||
binding.webViewProgressBar.setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
loadUrl(intent.getStringExtra("url"));
|
||||
}
|
||||
|
||||
private void loadUrl(String url) {
|
||||
web.loadUrl(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||
if (requestCode == 1 && grantResults.length > 0 &&
|
||||
grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
// 权限已授予,执行文件下载操作
|
||||
gj.ts(this, "权限已授予,请重新执行文件下载操作");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.llq, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (web.canGoBack()) {
|
||||
web.goBack();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == android.R.id.home) {
|
||||
finish();
|
||||
} else if (itemId == R.id.fx) {
|
||||
gj.fx(this, web.getUrl());
|
||||
// 服务中心
|
||||
} else if (itemId == R.id.sx) {
|
||||
web.reload();
|
||||
} else if (itemId == R.id.menu_web) {
|
||||
startActivity(new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse(web.getUrl())));
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
}
|
237
app/src/main/java/com/muqingbfq/mq/wj.java
Normal file
237
app/src/main/java/com/muqingbfq/mq/wj.java
Normal file
|
@ -0,0 +1,237 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import static androidx.core.content.ContextCompat.startActivity;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.home;
|
||||
import com.muqingbfq.yc;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class wj {
|
||||
public static String filesdri;
|
||||
public static String mp3 = "mp3/";
|
||||
public static String lishi_json = "lishi.json";
|
||||
public static String gd = "gd/";
|
||||
public static String tx = "image/";
|
||||
public static String gd_json = "gd.json", gd_xz = "gd_xz.json",
|
||||
gd_phb = "gd_phb.json", mp3_like = "mp3_like.json";
|
||||
|
||||
public wj(Context context) {
|
||||
try {
|
||||
wj.filesdri = context.getExternalFilesDir("").getAbsolutePath() + "/";
|
||||
// context.getFilesDir().toString() + "/";
|
||||
gd_json = filesdri + gd_json;
|
||||
mp3 = filesdri + mp3;
|
||||
gd = filesdri + gd;
|
||||
gd_xz = filesdri + gd_xz;
|
||||
gd_phb = filesdri + gd_phb;
|
||||
mp3_like = gd + mp3_like;
|
||||
tx = filesdri + tx;
|
||||
} catch (Exception e) {
|
||||
yc.start(context, e);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 这里定义的是一个文件保存的方法,写入到文件中,所以是输出流
|
||||
* */
|
||||
public static boolean xrwb(String url, String text) {
|
||||
if (text == null) {
|
||||
text = "";
|
||||
}
|
||||
File file = new File(url);
|
||||
//如果文件不存在,创建文件
|
||||
try {
|
||||
File parentFile = file.getParentFile();
|
||||
if (!parentFile.isDirectory()) {
|
||||
parentFile.mkdirs();
|
||||
}
|
||||
if (!file.exists())
|
||||
file.createNewFile();
|
||||
//创建FileOutputStream对象,写入内容
|
||||
FileOutputStream fos = new FileOutputStream(file);
|
||||
//向文件中写入内容
|
||||
fos.write(text.getBytes());
|
||||
fos.close();
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String dqwb(String url) {
|
||||
try {
|
||||
File file = new File(url);
|
||||
if (!file.exists()) {
|
||||
return null;
|
||||
}
|
||||
FileInputStream fis = new FileInputStream(file);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
|
||||
StringBuilder str = new StringBuilder();
|
||||
String line;
|
||||
while ((line = br.readLine()) != null) {
|
||||
str.append(line);
|
||||
}
|
||||
br.close();
|
||||
fis.close();
|
||||
return str.toString();
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean cz(String url) {
|
||||
return new File(url).exists();
|
||||
}
|
||||
|
||||
|
||||
public static boolean sc(String url) {
|
||||
File file = new File(url);
|
||||
return file.delete();
|
||||
}
|
||||
|
||||
public static void sc(File url) {
|
||||
if (url.exists()) {
|
||||
File[] files = url.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
// 递归调用,删除子文件夹及其内容
|
||||
sc(file);
|
||||
} else {
|
||||
file.delete(); // 删除文件
|
||||
}
|
||||
}
|
||||
}
|
||||
url.delete(); // 删除当前文件夹
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String convertToMd5(String url) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||
byte[] messageDigest = md.digest(url.getBytes());
|
||||
StringBuilder hexString = new StringBuilder();
|
||||
for (byte value : messageDigest) {
|
||||
String hex = Integer.toHexString(0xFF & value);
|
||||
if (hex.length() == 1) {
|
||||
hexString.append('0');
|
||||
}
|
||||
hexString.append(hex);
|
||||
}
|
||||
return hexString.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static void fz(String sourceFilePath, String targetFilePath) {
|
||||
File sourceFile = new File(sourceFilePath);
|
||||
File targetFile = new File(targetFilePath);
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
try (InputStream in = Files.newInputStream(sourceFile.toPath());
|
||||
OutputStream out = Files.newOutputStream(targetFile.toPath())) {
|
||||
byte[] buf = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = in.read(buf)) > 0) {
|
||||
out.write(buf, 0, bytesRead);
|
||||
}
|
||||
// 文件复制完成
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 保存MP3对象到文件
|
||||
public static void setMP3ToFile(MP3 mp3) {
|
||||
if (mp3 == null) {
|
||||
return;
|
||||
}
|
||||
Gson gson = new Gson();
|
||||
String json = gson.toJson(mp3);
|
||||
xrwb(filesdri + "mp3.dat", json);
|
||||
}
|
||||
|
||||
// 从文件中加载MP3对象
|
||||
public static MP3 getMP3FromFile() {
|
||||
Gson gson = new Gson();
|
||||
MP3 mp3 = null;
|
||||
try {
|
||||
File file = new File(filesdri + "mp3.dat");
|
||||
if (file.exists() && file.length() > 0) {
|
||||
FileReader reader = new FileReader(file);
|
||||
mp3 = gson.fromJson(reader, MP3.class);
|
||||
reader.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return mp3;
|
||||
}
|
||||
|
||||
|
||||
private static final int REQUEST_EXTERNAL_STORAGE = 1;
|
||||
private static String[] PERMISSIONS_STORAGE = {
|
||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||
Manifest.permission.WRITE_EXTERNAL_STORAGE
|
||||
};
|
||||
|
||||
public static boolean isCD(Activity context) {
|
||||
//检查权限(NEED_PERMISSION)是否被授权 PackageManager.PERMISSION_GRANTED表示同意授权
|
||||
if (Build.VERSION.SDK_INT >= 30) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
|
||||
context.startActivity(intent);
|
||||
} else {
|
||||
Log.i("swyLog", "Android 11以上,当前已有权限");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
|
||||
PackageManager.PERMISSION_GRANTED) {
|
||||
//申请权限
|
||||
ActivityCompat.requestPermissions(context
|
||||
, PERMISSIONS_STORAGE, REQUEST_EXTERNAL_STORAGE);
|
||||
} else {
|
||||
Log.i("swyLog", "Android 6.0以上,11以下,当前已有权限");
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
Log.i("swyLog", "Android 6.0以下,已获取权限");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
118
app/src/main/java/com/muqingbfq/mq/wl.java
Normal file
118
app/src/main/java/com/muqingbfq/mq/wl.java
Normal file
|
@ -0,0 +1,118 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.OptIn;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.util.UnstableApi;
|
||||
import androidx.media3.database.DatabaseProvider;
|
||||
import androidx.media3.database.StandaloneDatabaseProvider;
|
||||
import androidx.media3.datasource.DataSource;
|
||||
import androidx.media3.datasource.cache.Cache;
|
||||
import androidx.media3.datasource.cache.CacheDataSource;
|
||||
import androidx.media3.datasource.cache.LeastRecentlyUsedCacheEvictor;
|
||||
import androidx.media3.datasource.cache.SimpleCache;
|
||||
|
||||
import com.muqingbfq.main;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import okhttp3.MultipartBody;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class wl {
|
||||
public static String Cookie;
|
||||
|
||||
public static void setcookie(String cookie) {
|
||||
wl.Cookie = cookie;
|
||||
main.edit.putString("Cookie", cookie);
|
||||
main.edit.commit();
|
||||
}
|
||||
|
||||
public static String hq(String url) {
|
||||
try {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder()
|
||||
.url(main.api + url)
|
||||
.build();
|
||||
Response response = client.newCall(request).execute();
|
||||
if (response.body() != null) {
|
||||
return response.body().string();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc("wl hq(Strnig) " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String post(String str, String[][] a) {
|
||||
OkHttpClient client = new OkHttpClient().newBuilder()
|
||||
.build();
|
||||
// MediaType mediaType = MediaType.parse("text/plain");
|
||||
MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM);
|
||||
for (String[] b : a) {
|
||||
builder.addFormDataPart(b[0], b[1]);
|
||||
}
|
||||
builder.addFormDataPart("cookie", Cookie);
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.url(main.api + str)
|
||||
.method("POST", builder.build())
|
||||
.addHeader("User-Agent", "Apifox/1.0.0 (https://apifox.com)")
|
||||
.addHeader("Accept", "*/*")
|
||||
.addHeader("Host", "139.196.224.229:3000")
|
||||
.addHeader("Connection", "keep-alive")
|
||||
.build();
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
if (response.body() != null) {
|
||||
return response.body().string();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String get(String url) {
|
||||
OkHttpClient client = new OkHttpClient();
|
||||
Request request = new Request.Builder()
|
||||
.url(url)
|
||||
.build();
|
||||
try {
|
||||
Response response = client.newCall(request).execute();
|
||||
if (response.body() != null) {
|
||||
return response.body().string();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc("wl get(Strnig) " + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@OptIn(markerClass = UnstableApi.class)
|
||||
public static DataSource.Factory DownMp3() {
|
||||
Context context = main.application;
|
||||
|
||||
DatabaseProvider databaseProvider = new StandaloneDatabaseProvider(context);
|
||||
// 创建一个 File 对象来指定缓存目录
|
||||
File downloadDirectory = new File(wj.mp3);
|
||||
// 如果缓存目录不存在,则创建它
|
||||
if (!downloadDirectory.exists()) {
|
||||
downloadDirectory.mkdirs();
|
||||
}
|
||||
Cache cache =
|
||||
new SimpleCache(
|
||||
downloadDirectory, new LeastRecentlyUsedCacheEvictor(100 * 1024 * 1024), databaseProvider);
|
||||
|
||||
CacheDataSource.Factory httpDataSourceFactory = new CacheDataSource.Factory();
|
||||
return new CacheDataSource.Factory()
|
||||
.setCache(cache)
|
||||
.setUpstreamDataSourceFactory(httpDataSourceFactory);
|
||||
}
|
||||
|
||||
}
|
274
app/src/main/java/com/muqingbfq/sz.java
Normal file
274
app/src/main/java/com/muqingbfq/sz.java
Normal file
|
@ -0,0 +1,274 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.provider.Settings;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
import androidx.activity.result.contract.ActivityResultContracts;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.media3.common.Player;
|
||||
|
||||
import com.colorpicker.ColorPickerView;
|
||||
import com.colorpicker.builder.ColorPickerDialogBuilder;
|
||||
import com.dirror.lyricviewx.LyricEntry;
|
||||
import com.dirror.lyricviewx.LyricViewX;
|
||||
import com.google.android.material.slider.Slider;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.databinding.ActivitySzBinding;
|
||||
import com.muqingbfq.databinding.ActivitySzSetlrcBinding;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.FloatingLyricsService;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class sz extends AppCompatActivity<ActivitySzBinding> {
|
||||
@Override
|
||||
protected ActivitySzBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivitySzBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle bundle) {
|
||||
super.onCreate(bundle);
|
||||
setContentView();
|
||||
ViewCompat.setOnApplyWindowInsetsListener(binding.getRoot(), (v, insets) -> {
|
||||
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
|
||||
// v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
|
||||
return insets;
|
||||
});
|
||||
setSupportActionBar(binding.toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
setTitle(getString(R.string.sz));
|
||||
SharedPreferences theme = getSharedPreferences("theme", MODE_PRIVATE);
|
||||
@SuppressLint("CommitPrefEdits") SharedPreferences.Editor edit = theme.edit();
|
||||
int i = theme.getInt("theme", AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
|
||||
if (i == AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) {
|
||||
binding.switchA1.setChecked(true);
|
||||
binding.switchA2.setEnabled(false);
|
||||
} else {
|
||||
binding.switchA1.setChecked(false);
|
||||
binding.switchA2.setEnabled(true);
|
||||
binding.switchA2.setChecked(i == AppCompatDelegate.MODE_NIGHT_YES);
|
||||
}
|
||||
binding.switchA1.setOnCheckedChangeListener((compoundButton, b) -> {
|
||||
if (b) {
|
||||
// 跟随系统设置切换颜色模式
|
||||
int ms = AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM;
|
||||
AppCompatDelegate.setDefaultNightMode(ms);
|
||||
edit.putInt("theme", ms);
|
||||
edit.apply();
|
||||
}
|
||||
binding.switchA2.setEnabled(!b);
|
||||
});
|
||||
binding.switchA2.setOnCheckedChangeListener((compoundButton, b) -> {
|
||||
if (compoundButton.isEnabled()) {
|
||||
int ms;
|
||||
if (b) {
|
||||
ms = AppCompatDelegate.MODE_NIGHT_YES;
|
||||
} else {
|
||||
ms = AppCompatDelegate.MODE_NIGHT_NO;
|
||||
}
|
||||
AppCompatDelegate.setDefaultNightMode(ms);
|
||||
edit.putInt("theme", ms);
|
||||
edit.commit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public static class setlrc extends Fragment implements Slider.OnSliderTouchListener, Slider.OnChangeListener {
|
||||
ActivitySzSetlrcBinding binding;
|
||||
ActivityResultLauncher<Intent> LyricsService = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
if (Settings.canDrawOverlays(getContext())) {
|
||||
// getContext().startService(new Intent(getContext(), FloatingLyricsService.class));
|
||||
binding.switchA3.setChecked(true);
|
||||
binding.slide1.setEnabled(true);
|
||||
} else {
|
||||
binding.switchA3.setChecked(false);
|
||||
binding.slide1.setEnabled(false);
|
||||
}
|
||||
UI();
|
||||
});
|
||||
FloatingLyricsService.SETUP setup;
|
||||
|
||||
Handler handler = new Handler();
|
||||
|
||||
private Runnable ThreadLrc = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (PlaybackService.mediaSession == null) {
|
||||
handler.postDelayed(this, 1000);
|
||||
return;
|
||||
}
|
||||
Player player = PlaybackService.mediaSession.getPlayer();
|
||||
int index = 0;
|
||||
for (int i = 0; i < LyricViewX.lyricEntryList.size(); i++) {
|
||||
LyricEntry lineLrc = LyricViewX.lyricEntryList.get(i);
|
||||
gj.sc(player.getCurrentPosition());
|
||||
if (lineLrc.time <= player.getCurrentPosition()) {
|
||||
index = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < LyricViewX.lyricEntryList.size()) {
|
||||
LyricEntry currentLrc = LyricViewX.lyricEntryList.get(index);
|
||||
if (currentLrc.secondText != null) {
|
||||
binding.lrcViewMessage.setText(currentLrc.secondText);
|
||||
} else {
|
||||
binding.lrcViewMessage.setText("");
|
||||
}
|
||||
binding.lrcView.setText(currentLrc.text);
|
||||
}
|
||||
handler.postDelayed(this, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
binding = ActivitySzSetlrcBinding.inflate(inflater, container, false);
|
||||
UI();
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
private void UI() {
|
||||
File file = new File(wj.filesdri + "FloatingLyricsService.json");
|
||||
if (file.exists() && file.isFile()) {
|
||||
String dqwb = wj.dqwb(file.toString());
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<FloatingLyricsService.SETUP>() {
|
||||
}.getType();
|
||||
|
||||
binding.slide1.setEnabled(true);
|
||||
setup = gson.fromJson(dqwb, type);
|
||||
binding.slide1.setValue(setup.size);
|
||||
|
||||
binding.lrcView.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, setup.size, getResources().getDisplayMetrics()));
|
||||
binding.lrcView.setTextColor(Color.parseColor(setup.Color));
|
||||
|
||||
binding.lrcViewMessage.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, setup.size, getResources().getDisplayMetrics()) - 1.0f);
|
||||
binding.lrcViewMessage.setTextColor(Color.parseColor(setup.Color));
|
||||
binding.lrclin.setOnClickListener(view -> ColorPickerDialogBuilder.with(view.getContext()).setTitle("调色盘").initialColor(Color.parseColor(setup.Color)).wheelType(ColorPickerView.WHEEL_TYPE.FLOWER).density(6).setOnColorSelectedListener(selectedColor -> {
|
||||
}).setPositiveButton("确定", (dialog, selectedColor, allColors) -> {
|
||||
setup.Color = String.format("#%08X", selectedColor);
|
||||
binding.lrcView.setTextColor(selectedColor);
|
||||
binding.lrcViewMessage.setTextColor(selectedColor);
|
||||
FloatingLyricsService.baocun(setup);
|
||||
}).setNegativeButton("取消", null).build().show());
|
||||
binding.textSlide1.setText(String.valueOf(setup.size));
|
||||
if (setup.i != 0) {
|
||||
binding.switchA3.setChecked(true);
|
||||
}
|
||||
binding.lock.setVisibility(View.VISIBLE);
|
||||
binding.lock.setImageResource(setup.i == 2 ? R.drawable.lock : R.drawable.lock_open);
|
||||
handler.post(ThreadLrc);
|
||||
} else {
|
||||
binding.lock.setVisibility(View.GONE);
|
||||
}
|
||||
binding.switchA3.setOnCheckedChangeListener((compoundButton, b) -> {
|
||||
if (b) {
|
||||
if (setup != null) {
|
||||
setup.i = 1;
|
||||
}
|
||||
if (!Settings.canDrawOverlays(getContext())) {
|
||||
// 无权限,需要申请权限
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName()));
|
||||
LyricsService.launch(intent);
|
||||
} else {
|
||||
getContext().startService(new Intent(getContext(), FloatingLyricsService.class));
|
||||
}
|
||||
} else {
|
||||
if (setup != null) {
|
||||
setup.i = 0;
|
||||
}
|
||||
main.application.stopService(new Intent(main.application, FloatingLyricsService.class));
|
||||
}
|
||||
FloatingLyricsService.baocun(setup);
|
||||
});
|
||||
//强制跳转到权限界面
|
||||
binding.switchA3.setOnLongClickListener(v -> {
|
||||
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getContext().getPackageName()));
|
||||
LyricsService.launch(intent);
|
||||
return false;
|
||||
});
|
||||
binding.slide1.setLabelFormatter(value -> String.valueOf((int) value));
|
||||
binding.slide1.addOnChangeListener(this);
|
||||
binding.slide1.addOnChangeListener(this);
|
||||
binding.slide1.addOnSliderTouchListener(this);
|
||||
binding.lock.setOnClickListener(view -> {
|
||||
if (setup.i == 1) {
|
||||
setup.i = 2;
|
||||
binding.lock.setImageResource(R.drawable.lock);
|
||||
} else if (setup.i == 2) {
|
||||
setup.i = 1;
|
||||
binding.lock.setImageResource(R.drawable.lock_open);
|
||||
}
|
||||
FloatingLyricsService.baocun(setup);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
handler.removeCallbacks(ThreadLrc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(@NonNull Slider slider) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(@NonNull Slider slider) {
|
||||
if (setup == null) {
|
||||
return;
|
||||
}
|
||||
FloatingLyricsService.baocun(setup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onValueChange(@NonNull Slider slider, float value, boolean fromUser) {
|
||||
if (setup == null) {
|
||||
return;
|
||||
}
|
||||
if (slider == binding.slide1) {
|
||||
setup.size = (int) value;
|
||||
float v = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, setup.size, getResources().getDisplayMetrics());
|
||||
binding.lrcView.setTextSize(v);
|
||||
binding.lrcViewMessage.setTextSize(v - 1.0f);
|
||||
binding.textSlide1.setText(String.valueOf(setup.size));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
55
app/src/main/java/com/muqingbfq/view/CardImage.java
Normal file
55
app/src/main/java/com/muqingbfq/view/CardImage.java
Normal file
|
@ -0,0 +1,55 @@
|
|||
package com.muqingbfq.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public class CardImage extends MaterialCardView {
|
||||
public ImageView imageView;
|
||||
|
||||
public CardImage(Context context) {
|
||||
super(context);
|
||||
start();
|
||||
}
|
||||
public CardImage(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
start();
|
||||
}
|
||||
|
||||
public CardImage(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
start();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
imageView = new ImageView(getContext());
|
||||
imageView.setScaleType(ImageView.ScaleType.FIT_XY);
|
||||
addView(imageView);
|
||||
imageView.setImageResource(R.drawable.ic_launcher_foreground);
|
||||
}
|
||||
|
||||
|
||||
public void setImage(Object bitmap) {
|
||||
try {
|
||||
Glide.with(this)
|
||||
.load(bitmap)
|
||||
.error(R.drawable.ic_launcher_foreground)
|
||||
.into(imageView);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setImageapply(Object bitmap) {
|
||||
Glide.with(getContext())
|
||||
.load(bitmap)
|
||||
.apply(new RequestOptions().placeholder(R.drawable.ic_launcher_foreground))
|
||||
// .error(R.drawable.app_warning)
|
||||
.into(imageView);
|
||||
}
|
||||
}
|
168
app/src/main/java/com/muqingbfq/view/Edit.java
Normal file
168
app/src/main/java/com/muqingbfq/view/Edit.java
Normal file
|
@ -0,0 +1,168 @@
|
|||
package com.muqingbfq.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.RippleDrawable;
|
||||
import android.text.Editable;
|
||||
import android.text.NoCopySpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.google.android.material.textfield.TextInputLayout;
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public class Edit extends LinearLayout {
|
||||
public Edit(@NonNull Context context) {
|
||||
super(context);
|
||||
initView();
|
||||
}
|
||||
|
||||
AttributeSet attrs;
|
||||
public Edit(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this.attrs=attrs;
|
||||
initView();
|
||||
}
|
||||
|
||||
public Edit(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
this.attrs=attrs;
|
||||
initView();
|
||||
}
|
||||
|
||||
ImageView chahao;
|
||||
public EditText editText;
|
||||
|
||||
private void initView() {
|
||||
TextInputLayout layout = new TextInputLayout(getContext());
|
||||
// layout.clearIconDrawable
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
setBackgroundResource(R.drawable.ui_editview);
|
||||
setPadding(30, 0, 30, 0);
|
||||
//构建编辑框
|
||||
editText = new EditText(getContext());
|
||||
editText.setHint("搜索");
|
||||
editText.setSingleLine(true);
|
||||
editText.setBackground(null);
|
||||
editText.setTransitionName("edit");
|
||||
Drawable startIcon = ContextCompat.getDrawable(getContext(), R.drawable.sousuo);
|
||||
startIcon.setTint(ContextCompat.getColor(getContext(),
|
||||
R.color.text_tm));
|
||||
Drawable endIcon = ContextCompat.getDrawable(getContext(), R.drawable.chahao);
|
||||
editText.setCompoundDrawablesRelativeWithIntrinsicBounds(startIcon, null, endIcon, null);
|
||||
editText.setImeOptions(EditorInfo.IME_ACTION_SEARCH);
|
||||
// editText.passwordToggleEnabled
|
||||
addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence var1, int var2, int var3, int var4) {
|
||||
}
|
||||
@Override
|
||||
public void onTextChanged(CharSequence var1, int var2, int var3, int var4) {
|
||||
iskong();
|
||||
}
|
||||
@Override
|
||||
public void afterTextChanged(Editable var1) {
|
||||
}
|
||||
});
|
||||
|
||||
ImageView imageView = new ImageView(getContext());
|
||||
imageView.setImageResource(R.drawable.sousuo);
|
||||
addView(imageView, (int) TypedValue.applyDimension(TypedValue.
|
||||
COMPLEX_UNIT_DIP, 26, getResources().getDisplayMetrics())
|
||||
, (int) TypedValue.applyDimension(TypedValue.
|
||||
COMPLEX_UNIT_DIP, 26, getResources().getDisplayMetrics()));
|
||||
LayoutParams layoutParams = new LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
layoutParams.weight = 1;
|
||||
layoutParams.gravity = Gravity.CENTER;
|
||||
addView(editText, layoutParams);
|
||||
|
||||
chahao = new ImageView(getContext());
|
||||
chahao.setImageResource(R.drawable.chahao);
|
||||
Drawable rippleDrawable = new RippleDrawable(
|
||||
ContextCompat.getColorStateList(getContext(),R.color.bj),
|
||||
chahao.getBackground(),
|
||||
null
|
||||
);
|
||||
chahao.setBackground(rippleDrawable);
|
||||
chahao.setOnClickListener(view -> {
|
||||
editText.setText("");
|
||||
view.setVisibility(GONE);
|
||||
});
|
||||
addView(chahao, (int) TypedValue.applyDimension(TypedValue.
|
||||
COMPLEX_UNIT_DIP, 26, getResources().getDisplayMetrics())
|
||||
, (int) TypedValue.applyDimension(TypedValue.
|
||||
COMPLEX_UNIT_DIP, 26, getResources().getDisplayMetrics()));
|
||||
iskong();
|
||||
if (attrs != null) {
|
||||
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Edit);
|
||||
boolean isEnabled = a.getBoolean(R.styleable.Edit_Enabled, true);
|
||||
if (!isEnabled) {
|
||||
removeView(imageView);
|
||||
removeView(chahao);
|
||||
|
||||
// editText.setGravity(Gravity.CENTER);
|
||||
editText.setFocusable(false);
|
||||
editText.setFocusableInTouchMode(false);
|
||||
// editText.setEnabled(false);
|
||||
editText.setClickable(false);
|
||||
|
||||
}
|
||||
a.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
public void addTextChangedListener(TextWatcher textWatcher) {
|
||||
editText.addTextChangedListener(textWatcher);
|
||||
}
|
||||
|
||||
public void iskong() {
|
||||
if (editText.getText().toString().isEmpty()) {
|
||||
chahao.setVisibility(GONE);
|
||||
} else {
|
||||
chahao.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLayoutParams(ViewGroup.LayoutParams params) {
|
||||
// params.width = ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
params.height = ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
super.setLayoutParams(params);
|
||||
// setGravity(Gravity.CENTER_VERTICAL);
|
||||
}
|
||||
|
||||
public void setOnEditorActionListener(TextView.OnEditorActionListener a) {
|
||||
editText.setOnEditorActionListener(a);
|
||||
}
|
||||
|
||||
public void setText(String s) {
|
||||
editText.setText(s);
|
||||
}
|
||||
|
||||
public Editable getText() {
|
||||
return editText.getText();
|
||||
}
|
||||
|
||||
public interface TextWatcher extends NoCopySpan, android.text.TextWatcher {
|
||||
void beforeTextChanged(CharSequence var1, int var2, int var3, int var4);
|
||||
|
||||
void onTextChanged(CharSequence var1, int var2, int var3, int var4);
|
||||
|
||||
void afterTextChanged(Editable var1);
|
||||
}
|
||||
}
|
73
app/src/main/java/com/muqingbfq/view/RecyclerViewH.java
Normal file
73
app/src/main/java/com/muqingbfq/view/RecyclerViewH.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
package com.muqingbfq.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class RecyclerViewH extends RecyclerView {
|
||||
public RecyclerViewH(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public RecyclerViewH(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public RecyclerViewH(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
// private boolean disallowIntercept = false;
|
||||
|
||||
private int startX = 0;
|
||||
private int startY = 0;
|
||||
boolean isDispatch = true;
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
if (isDispatch) {
|
||||
switch (ev.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
startX = (int) ev.getX();
|
||||
startY = (int) ev.getY();
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int endX = (int) ev.getX();
|
||||
int endY = (int) ev.getY();
|
||||
int disX = Math.abs(endX - startX);
|
||||
int disY = Math.abs(endY - startY);
|
||||
if (Math.abs(disY) > ViewConfiguration.get(getContext()).getScaledTouchSlop()) {
|
||||
// 当前手指移动距离大于系统认定的最小滚动距离时,不允许父容器拦截触摸事件
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
/*
|
||||
if (disX > disY) {
|
||||
//为了解决RecyclerView嵌套RecyclerView时横向滑动的问题
|
||||
if (disallowIntercept) {
|
||||
getParent().requestDisallowInterceptTouchEvent(disallowIntercept);
|
||||
} else {
|
||||
getParent().requestDisallowInterceptTouchEvent(canScrollHorizontally(startX - endX));
|
||||
}
|
||||
} else {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
}*/
|
||||
case MotionEvent.ACTION_UP:
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
getParent().requestDisallowInterceptTouchEvent(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
@Override
|
||||
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
|
||||
super.requestDisallowInterceptTouchEvent(disallowIntercept);
|
||||
}
|
||||
|
||||
}
|
47
app/src/main/java/com/muqingbfq/view/StrokeTextView.java
Normal file
47
app/src/main/java/com/muqingbfq/view/StrokeTextView.java
Normal file
|
@ -0,0 +1,47 @@
|
|||
package com.muqingbfq.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.text.TextPaint;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatTextView;
|
||||
|
||||
public class StrokeTextView extends AppCompatTextView {
|
||||
|
||||
public StrokeTextView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public StrokeTextView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public StrokeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
// 获取当前TextView的Paint对象
|
||||
TextPaint textPaint = getPaint();
|
||||
|
||||
// 保存原来的文本颜色
|
||||
int currentTextColor = getCurrentTextColor();
|
||||
|
||||
// 设置描边效果
|
||||
textPaint.setStyle(Paint.Style.STROKE); // 设置为描边
|
||||
textPaint.setStrokeWidth(1); // 设置描边的宽度
|
||||
setTextColor(Color.BLACK); // 描边的颜色
|
||||
|
||||
// 绘制描边
|
||||
super.onDraw(canvas);
|
||||
|
||||
// 恢复原来的文本颜色并绘制文本内容
|
||||
textPaint.setStyle(Paint.Style.FILL); // 恢复为填充
|
||||
setTextColor(currentTextColor); // 恢复原来的颜色
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
}
|
115
app/src/main/java/com/muqingbfq/view/TabLayout.java
Normal file
115
app/src/main/java/com/muqingbfq/view/TabLayout.java
Normal file
|
@ -0,0 +1,115 @@
|
|||
package com.muqingbfq.view;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Color;
|
||||
import android.media.Image;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.view.ContextThemeWrapper;
|
||||
import androidx.appcompat.widget.LinearLayoutCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
public class TabLayout extends MaterialCardView {
|
||||
public TabLayout(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public TabLayout(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs,0);
|
||||
}
|
||||
|
||||
public TabLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs,defStyleAttr);
|
||||
Init(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public LinearLayout linearLayout;
|
||||
private void Init(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
linearLayout = new LinearLayout(context, attrs, defStyleAttr);
|
||||
int a = gj.dp2px(context, 9);
|
||||
LinearLayout.LayoutParams layoutParams ;
|
||||
if (gj.isTablet(context)) {
|
||||
// 这是平板模式
|
||||
layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT);
|
||||
linearLayout.setOrientation(LinearLayout.VERTICAL);
|
||||
linearLayout.setGravity(Gravity.CENTER);
|
||||
setContentPadding(a, 0, a, 0);
|
||||
} else {
|
||||
// 这是手机模式
|
||||
layoutParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
linearLayout.setOrientation(LinearLayout.HORIZONTAL);
|
||||
linearLayout.setGravity(Gravity.CENTER);
|
||||
setContentPadding(0, a, 0, a);
|
||||
}
|
||||
// linearLayout.setBackgroundColor(Color.BLACK);
|
||||
addView(linearLayout,layoutParams);
|
||||
// addView(new Button(context));
|
||||
}
|
||||
|
||||
public int sizeView = 0;
|
||||
|
||||
@SuppressLint("ResourceType")
|
||||
public View addView(String title, int icon) {
|
||||
Context context = getContext();
|
||||
Item itemB = new Item(context);
|
||||
|
||||
itemB.imageView.setImageResource(icon);
|
||||
itemB.textView.setText(title);
|
||||
LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
|
||||
int a = gj.dp2px(context, 26);
|
||||
if (!gj.isTablet(context)) {
|
||||
layoutParams.setMargins(a, 0, a, 0);
|
||||
} else {
|
||||
layoutParams.setMargins(0, a, 0, a);
|
||||
}
|
||||
linearLayout.addView(itemB, layoutParams);
|
||||
if (sizeView == 0) {
|
||||
itemB.setCardBackgroundColor(gj.getThemeColor(context, com.google.android.material.R.attr.colorPrimaryContainer));
|
||||
}
|
||||
sizeView++;
|
||||
|
||||
return itemB;
|
||||
}
|
||||
|
||||
public static class Item extends MaterialCardView {
|
||||
ImageView imageView;
|
||||
TextView textView;
|
||||
public Item(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public Item(Context context, @Nullable AttributeSet attrs) {
|
||||
super(new ContextThemeWrapper(context, com.google.android.material.R.style.Widget_Material3_CardView_Elevated), attrs);
|
||||
setUseCompatPadding(true);
|
||||
setFocusable(true);
|
||||
setClickable(true);
|
||||
// 没有边缘线
|
||||
setStrokeWidth(0);
|
||||
int padding = gj.dp2px(context, 6);
|
||||
setContentPadding(padding,padding,padding,padding);
|
||||
inflate(context, R.layout.view_tab, this);
|
||||
imageView = findViewById(R.id.image);
|
||||
textView = findViewById(R.id.text);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
36
app/src/main/java/com/muqingbfq/view/Text.java
Normal file
36
app/src/main/java/com/muqingbfq/view/Text.java
Normal file
|
@ -0,0 +1,36 @@
|
|||
package com.muqingbfq.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
public class Text extends androidx.appcompat.widget.AppCompatTextView {
|
||||
public Text(@NonNull Context context) {
|
||||
super(context);
|
||||
initView();
|
||||
}
|
||||
|
||||
|
||||
public Text(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initView();
|
||||
}
|
||||
|
||||
public Text(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
initView();
|
||||
}
|
||||
|
||||
private void initView() {
|
||||
this.setEllipsize(TextUtils.TruncateAt.MARQUEE);
|
||||
this.setSingleLine(true);
|
||||
this.setMarqueeRepeatLimit(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFocused() {
|
||||
return true;
|
||||
}
|
||||
}
|
29
app/src/main/java/com/muqingbfq/view/Toolbar.java
Normal file
29
app/src/main/java/com/muqingbfq/view/Toolbar.java
Normal file
|
@ -0,0 +1,29 @@
|
|||
package com.muqingbfq.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.appbar.MaterialToolbar;
|
||||
|
||||
public class Toolbar extends MaterialToolbar {
|
||||
public Toolbar(@NonNull Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public Toolbar(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public Toolbar(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// 处理触摸事件逻辑
|
||||
return false;
|
||||
}
|
||||
}
|
72
app/src/main/java/com/muqingbfq/yc.java
Normal file
72
app/src/main/java/com/muqingbfq/yc.java
Normal file
|
@ -0,0 +1,72 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.provider.Settings;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.databinding.ActivityYcBinding;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
public class yc extends AppCompatActivity<ActivityYcBinding> {
|
||||
public Object exception;
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView();
|
||||
Intent intent = getIntent();
|
||||
exception = intent.getStringExtra("e");
|
||||
|
||||
String deviceModel = Build.MODEL;
|
||||
String deviceManufacturer = Build.MANUFACTURER;
|
||||
String osVersion = Build.VERSION.RELEASE;
|
||||
int sdkVersion = Build.VERSION.SDK_INT;
|
||||
@SuppressLint("HardwareIds") String deviceId = Settings.Secure.getString(getContentResolver(),
|
||||
Settings.Secure.ANDROID_ID);
|
||||
DisplayMetrics metrics = getResources().getDisplayMetrics();
|
||||
int widthPixels = metrics.widthPixels;
|
||||
int heightPixels = metrics.heightPixels;
|
||||
float density = metrics.density;
|
||||
int densityDpi = metrics.densityDpi;
|
||||
// 假设你已经获取到了手机信息,保存在相应的变量中
|
||||
String wb = "设备型号:" + deviceModel + "\n"
|
||||
+ "制造商:" + deviceManufacturer + "\n"
|
||||
+ "设备ID:" + deviceId + "\n"
|
||||
+ "操作系统版本:" + osVersion + "\n"
|
||||
+ "SDK版本:" + sdkVersion + "\n"
|
||||
+ "屏幕尺寸:" + widthPixels + "x" + heightPixels + "\n"
|
||||
+ "屏幕密度:" + density + "\n"
|
||||
+ "密度DPI:" + densityDpi + "\n" +
|
||||
"异常信息: " + exception.toString();
|
||||
binding.text.setText(wb);
|
||||
binding.button2.setOnClickListener(view -> finish());
|
||||
}
|
||||
public static void start(Context context, Object e) {
|
||||
gj.sc(e);
|
||||
Intent intent = new Intent(context, yc.class);
|
||||
intent.putExtra("e",e.toString());
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void tc(Context context, Object exception) {
|
||||
new MaterialAlertDialogBuilder(context)
|
||||
.setTitle("不是特别重要的警告")
|
||||
.setMessage(exception.toString())
|
||||
.setNegativeButton("无视", null)
|
||||
.setPositiveButton("分享", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivityYcBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityYcBinding.inflate(layoutInflater);
|
||||
}
|
||||
}
|
10
app/src/main/res/drawable/abc_ic_menu_share_mtrl_alpha.xml
Normal file
10
app/src/main/res/drawable/abc_ic_menu_share_mtrl_alpha.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:width="128dp"
|
||||
android:height="128dp">
|
||||
<path
|
||||
android:pathData="M18 16.08c-0.76 0 -1.44 0.3 -1.96 0.77L8.91 12.7c0.05 -0.23 0.09 -0.46 0.09 -0.7s-0.04 -0.47 -0.09 -0.7l7.05 -4.11c0.54 0.5 1.25 0.81 2.04 0.81c1.66 0 3 -1.34 3 -3s-1.34 -3 -3 -3s-3 1.34 -3 3c0 0.24 0.04 0.47 0.09 0.7L8.04 9.81C7.5 9.31 6.79 9 6 9c-1.66 0 -3 1.34 -3 3s1.34 3 3 3c0.79 0 1.5 -0.31 2.04 -0.81l7.12 4.15c-0.05 0.21 -0.08 0.43 -0.08 0.66c0 1.61 1.31 2.91 2.92 2.91s2.92 -1.3 2.92 -2.91s-1.31 -2.92 -2.92 -2.92M18 4c0.55 0 1 0.45 1 1s-0.45 1 -1 1s-1 -0.45 -1 -1s0.45 -1 1 -1M6 13c-0.55 0 -1 -0.45 -1 -1s0.45 -1 1 -1s1 0.45 1 1s-0.45 1 -1 1m12 7c-0.55 0 -1 -0.45 -1 -1s0.45 -1 1 -1s1 0.45 1 1s-0.45 1 -1 1"
|
||||
android:fillColor="#000000" />
|
||||
</vector>
|
34
app/src/main/res/drawable/api.xml
Normal file
34
app/src/main/res/drawable/api.xml
Normal file
|
@ -0,0 +1,34 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="48"
|
||||
android:viewportHeight="48">
|
||||
<path
|
||||
android:pathData="m37,22l-3,3l-11,-11l3,-3c1.5,-1.5 7,-4 11,0s1.5,9.5 0,11"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#666666"
|
||||
android:strokeColor="#666666"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="m42,6l-5,5"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#666666"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="m11,26l3,-3l11,11l-3,3c-1.5,1.5 -7,4 -11,0s-1.5,-9.5 0,-11"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#666666"
|
||||
android:strokeColor="#666666"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="m23,32l4,-4M6,42l5,-5m5,-12l4,-4"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="4"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="#666666"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
16
app/src/main/res/drawable/app_warning.xml
Normal file
16
app/src/main/res/drawable/app_warning.xml
Normal file
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:tint="@color/text"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M512,427.7c-33.1,0 -60,26.9 -60,60v226.7c0,33.1 26.9,60 60,60s60,-26.9 60,-60V487.7c0,-33.1 -26.9,-60 -60,-60z"
|
||||
android:fillColor="#515151"/>
|
||||
<path
|
||||
android:pathData="M512,309.7m-60,0a60,60 0,1 0,120 0,60 60,0 1,0 -120,0Z"
|
||||
android:fillColor="#515151"/>
|
||||
<path
|
||||
android:pathData="M512,14C237,14 14,237 14,512s223,498 498,498 498,-223 498,-498S787,14 512,14zM512,910c-219.8,0 -398,-178.2 -398,-398S292.2,114 512,114 910,292.2 910,512 731.8,910 512,910z"
|
||||
android:fillColor="#515151"/>
|
||||
</vector>
|
11
app/src/main/res/drawable/background.xml
Normal file
11
app/src/main/res/drawable/background.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:state_pressed="true">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#80F2ECF6" />
|
||||
</shape>
|
||||
</item>
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
</shape>
|
||||
</item>
|
||||
</selector>
|
9
app/src/main/res/drawable/bf.xml
Normal file
9
app/src/main/res/drawable/bf.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="46dp"
|
||||
android:height="46dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:fillColor="@color/text"
|
||||
android:pathData="M244.8,226h203.3c17.8,0 31.8,14 31.8,31.6v508.6c0,17.6 -14,31.8 -31.8,31.8L244.8,798c-17.6,0 -31.6,-14.2 -31.6,-31.8L213.2,257.6c0,-17.6 14,-31.6 31.6,-31.6zM575.7,226h203.3c17.8,0 31.8,14 31.8,31.6v508.6c0,17.6 -14,31.8 -31.8,31.8L575.7,798c-17.6,0 -32,-14.2 -32,-31.8L543.6,257.6c0,-17.6 14.4,-31.6 32,-31.6zM747.1,289.2L607.2,289.2v445.4h139.9L747.1,289.2zM416.3,289.2L276.4,289.2v445.4h139.9L416.3,289.2z" />
|
||||
</vector>
|
10
app/src/main/res/drawable/chahao.xml
Normal file
10
app/src/main/res/drawable/chahao.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:width="26dp"
|
||||
android:height="26dp">
|
||||
<path
|
||||
android:fillColor="#CCCCCC"
|
||||
android:pathData="M19 6.41L17.59 5L12 10.59L6.41 5L5 6.41L10.59 12L5 17.59L6.41 19L12 13.41L17.59 19L19 17.59L13.41 12z" />
|
||||
</vector>
|
11
app/src/main/res/drawable/delete.xml
Normal file
11
app/src/main/res/drawable/delete.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:tint="?attr/colorControlNormal"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2 -2V7H6zM8 9h8v10H8zm7.5 -5l-1 -1h-5l-1 1H5v2h14V4z" />
|
||||
</vector>
|
9
app/src/main/res/drawable/download.xml
Normal file
9
app/src/main/res/drawable/download.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:width="128dp"
|
||||
android:height="128dp">
|
||||
<path
|
||||
android:pathData="M8 17v-2h8v2zm8 -7l-4 4l-4 -4h2.5V6h3v4zm-4 -8c5.5 0 10 4.5 10 10s-4.5 10 -10 10S2 17.5 2 12S6.5 2 12 2m0 2c-4.42 0 -8 3.58 -8 8s3.58 8 8 8s8 -3.58 8 -8s-3.58 -8 -8 -8"
|
||||
android:fillColor="#000000" />
|
||||
</vector>
|
9
app/src/main/res/drawable/end.xml
Normal file
9
app/src/main/res/drawable/end.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:width="26dp"
|
||||
android:height="26dp">
|
||||
<path
|
||||
android:pathData="M7.41 8.58L12 13.17l4.59 -4.59L18 10l-6 6l-6 -6z"
|
||||
android:fillColor="#000000" />
|
||||
</vector>
|
9
app/src/main/res/drawable/filesearc.xml
Normal file
9
app/src/main/res/drawable/filesearc.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:width="128dp"
|
||||
android:height="128dp">
|
||||
<path
|
||||
android:pathData="M16.5 12c2.5 0 4.5 2 4.5 4.5c0 0.88 -0.25 1.71 -0.69 2.4l3.08 3.1L22 23.39l-3.12 -3.07c-0.69 0.43 -1.51 0.68 -2.38 0.68c-2.5 0 -4.5 -2 -4.5 -4.5s2 -4.5 4.5 -4.5m0 2a2.5 2.5 0 0 0 -2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 2.5 -2.5a2.5 2.5 0 0 0 -2.5 -2.5M9 4l2 2h8a2 2 0 0 1 2 2v3.81A6.48 6.48 0 0 0 16.5 10a6.5 6.5 0 0 0 -6.5 6.5c0 1.29 0.37 2.5 1 3.5H3a2 2 0 0 1 -2 -2V6c0 -1.11 0.89 -2 2 -2z"
|
||||
android:fillColor="#000000" />
|
||||
</vector>
|
12
app/src/main/res/drawable/foreground.xml
Normal file
12
app/src/main/res/drawable/foreground.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M995,538.7c-13.8,-8.9 -30.3,-12 -46.4,-8.5l-182.9,39.3c-28.2,6.1 -48.6,31.4 -48.6,60.2v224.6c0,21.8 -17.7,39.5 -39.6,39.5 -21.8,0 -39.5,-17.7 -39.5,-39.5s17.7,-39.6 39.5,-39.6c15.4,0 27.8,-12.4 27.8,-27.8s-12.4,-27.8 -27.8,-27.8c-52.4,0 -95.1,42.7 -95.1,95.2 0,52.4 42.7,95.1 95.1,95.1 52.5,0 95.1,-42.7 95.1,-95.1V629.8c0,-2.9 2,-5.3 4.8,-5.9l182.9,-39.3c1.9,-0.5 3.6,0.2 4.5,0.8 1,0.6 2.2,1.8 2.7,5V814c0,21.8 -17.7,39.6 -39.6,39.6 -10.5,0 -20.5,-4.1 -27.9,-11.6 -7.5,-7.5 -11.6,-17.4 -11.6,-28 0,-21.8 17.7,-39.6 39.6,-39.6 15.4,0 27.8,-12.4 27.8,-27.8s-12.4,-27.8 -27.8,-27.8c-52.5,0 -95.1,42.7 -95.1,95.1 0,25.4 9.9,49.3 27.9,67.3s41.8,27.9 67.2,27.9c52.5,0 95.1,-42.7 95.1,-95.1V590.5c0,-4.4 -0.5,-8.7 -1.4,-12.9 -3.4,-16.1 -12.9,-29.9 -26.7,-38.9z"
|
||||
android:fillColor="#000000"/>
|
||||
<path
|
||||
android:pathData="M338.3,315.1c35.3,-63.9 90.4,-114.1 158.6,-143.6 81.4,-35.2 171.7,-36.6 254.1,-3.9 106.7,42.3 183,134.9 204.1,247.8 3.1,16.7 17.7,28.4 34.1,28.4 2.1,0 4.3,-0.2 6.4,-0.6 18.9,-3.5 31.3,-21.7 27.8,-40.5C997.9,266.2 905.7,154.1 776.6,103c-99.7,-39.5 -208.8,-37.8 -307.3,4.7 -92.5,40 -165.1,111.6 -206.4,202.8 -48.9,3.6 -96.2,19.8 -137.8,48C62.2,400.9 19.7,465.3 5.3,539.7c-14.4,74.4 1,150 43.5,212.9 52.9,78.3 140.8,125 235.2,125h153.3c19.2,0 34.7,-15.6 34.7,-34.7s-15.6,-34.7 -34.7,-34.7H284c-71.3,0 -137.7,-35.3 -177.7,-94.4C74.2,666.3 62.6,609.3 73.5,553c10.9,-56.2 43,-104.8 90.5,-136.9 47.5,-32.1 104.5,-43.7 160.8,-32.8 56.2,10.9 104.8,43 136.9,90.5 10.7,15.9 32.3,20.1 48.2,9.3 15.9,-10.7 20.1,-32.3 9.3,-48.2 -42.4,-62.8 -106.6,-105.3 -180.9,-119.8z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/fuwuzhongxing.xml
Normal file
9
app/src/main/res/drawable/fuwuzhongxing.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="64dp"
|
||||
android:height="64dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<path
|
||||
android:pathData="M512,0a512,512 0,1 0,512 512A512.4,512.4 0,0 0,512 0zM512,938.1A554.5,554.5 0,0 1,390.5 745.4a775.4,775.4 0,0 1,121.5 -9.5,725.5 725.5,0 0,1 117.2,9.5 544.1,544.1 0,0 1,-117.2 192.7zM512,666.5a834,834 0,0 0,-143.6 12.6,661.3 661.3,0 0,1 -20.4,-133.2h321.5a681.2,681.2 0,0 1,-19.5 130.2,790.6 790.6,0 0,0 -138,-9.5zM71.2,545.8h208.3a728.5,728.5 0,0 0,22.6 149.3,450 450,0 0,0 -151.9,71.6 439.5,439.5 0,0 1,-79 -220.9zM507.7,85.9a554.1,554.1 0,0 1,120.2 190.5,683 683,0 0,1 -115.9,10 722.9,722.9 0,0 1,-121.9 -10.4,542.8 542.8,0 0,1 117.6,-190zM512,355.8a749.3,749.3 0,0 0,137.1 -13,661.3 661.3,0 0,1 21.3,134.5L348.9,477.3a680.8,680.8 0,0 1,20.4 -134.5,787.5 787.5,0 0,0 142.8,13zM279.4,475.6h-208.3a439.5,439.5 0,0 1,81.1 -221.3,462.1 462.1,0 0,0 149.7,72.5 748.5,748.5 0,0 0,-22.6 148.8zM739.8,547.6l213,3a439.5,439.5 0,0 1,-79.8 216.9A466.4,466.4 0,0 0,718.1 694.2a748,748 0,0 0,21.7 -146.7zM739.8,478.2a728.5,728.5 0,0 0,-23.4 -151.4,477.3 477.3,0 0,0 153.6,-75.1 439.5,439.5 0,0 1,83.3 228.7zM826.6,199.6a417.8,417.8 0,0 1,-130.2 61.6,650.8 650.8,0 0,0 -103.7,-183.1 441.7,441.7 0,0 1,232.1 121.5zM422.2,78.1A642.6,642.6 0,0 0,322.4 260.3a397.5,397.5 0,0 1,-125.4 -58.6A442.1,442.1 0,0 1,422.2 78.1zM194,819.2a384,384 0,0 1,130.2 -58.6A650.8,650.8 0,0 0,425.2 945.9a442.1,442.1 0,0 1,-231.3 -126.7zM596.2,945.9a643.5,643.5 0,0 0,101.5 -185.7,401.8 401.8,0 0,1 130.2,60.3 441.7,441.7 0,0 1,-231.7 125.4z"
|
||||
android:fillColor="@color/text"/>
|
||||
</vector>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user