git fetch origin
This commit is contained in:
commit
04c1a63caa
16
.gitignore
vendored
Normal file
16
.gitignore
vendored
Normal file
|
@ -0,0 +1,16 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
||||
*.apk
|
6
.idea/compiler.xml
Normal file
6
.idea/compiler.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
</component>
|
||||
</project>
|
10
.idea/deploymentTargetSelector.xml
Normal file
10
.idea/deploymentTargetSelector.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
3
.idea/dictionaries/19669.xml
Normal file
3
.idea/dictionaries/19669.xml
Normal file
|
@ -0,0 +1,3 @@
|
|||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="19669" />
|
||||
</component>
|
21
.idea/gradle.xml
Normal file
21
.idea/gradle.xml
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="testRunner" value="GRADLE" />
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="jbr-17" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
<option value="$PROJECT_DIR$/StatusBarUtil" />
|
||||
<option value="$PROJECT_DIR$/app" />
|
||||
</set>
|
||||
</option>
|
||||
<option name="resolveExternalAnnotations" value="false" />
|
||||
</GradleProjectSettings>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
27
.idea/inspectionProfiles/Project_Default.xml
Normal file
27
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AndroidLintHardcodedText" enabled="true" level="WARNING" enabled_by_default="true" editorAttributes="WARNING_ATTRIBUTES" />
|
||||
<inspection_tool class="AutoCloseableResource" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="METHOD_MATCHER_CONFIG" value="java.util.Formatter,format,java.io.Writer,append,com.google.common.base.Preconditions,checkNotNull,org.hibernate.Session,close,java.io.PrintWriter,printf,java.io.PrintStream,printf,android.content.res.Resources.Theme,obtainStyledAttributes,android.content.Context,obtainStyledAttributes,okhttp3.Call,execute" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="DataFlowIssue" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="SUGGEST_NULLABLE_ANNOTATIONS" value="false" />
|
||||
<option name="DONT_REPORT_TRUE_ASSERT_STATEMENTS" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="FieldMayBeFinal" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||
<inspection_tool class="IgnoreResultOfCall" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="m_reportAllNonLibraryCalls" value="false" />
|
||||
<option name="callCheckString" value="java.io.InputStream,read|skip|available|markSupported,java.io.Reader,read|skip|ready|markSupported,java.lang.AbstractStringBuilder,capacity|codePointAt|codePointBefore|codePointCount|indexOf|lastIndexOf|offsetByCodePoints|substring|subSequence,java.lang.Boolean,.*,java.lang.Byte,.*,java.lang.Character,.*,java.lang.Double,.*,java.lang.Float,.*,java.lang.Integer,.*,java.lang.Long,.*,java.lang.Math,.*,java.lang.Object,equals|hashCode|toString,java.lang.Short,.*,java.lang.StrictMath,.*,java.lang.String,.*,java.lang.Thread,interrupted,java.math.BigDecimal,.*,java.math.BigInteger,.*,java.net.InetAddress,.*,java.net.URI,.*,java.nio.channels.AsynchronousChannelGroup,.*,java.nio.channels.Channel,isOpen,java.nio.channels.FileChannel,open|map|lock|tryLock|write,java.nio.channels.ScatteringByteChannel,read,java.nio.channels.SocketChannel,open|socket|isConnected|isConnectionPending,java.util.Arrays,.*,java.util.Collections,(?!addAll).*,java.util.List,of,java.util.Map,of|ofEntries|entry,java.util.Set,of,java.util.UUID,.*,java.util.concurrent.BlockingQueue,offer|remove,java.util.concurrent.CountDownLatch,await|getCount,java.util.concurrent.ExecutorService,awaitTermination|isShutdown|isTerminated,java.util.concurrent.ForkJoinPool,awaitQuiescence,java.util.concurrent.Semaphore,tryAcquire|availablePermits|isFair|hasQueuedThreads|getQueueLength|getQueuedThreads,java.util.concurrent.locks.Condition,await|awaitNanos|awaitUntil,java.util.concurrent.locks.Lock,tryLock|newCondition,java.util.regex.Matcher,pattern|toMatchResult|start|end|group|groupCount|matches|find|lookingAt|quoteReplacement|replaceAll|replaceFirst|regionStart|regionEnd|hasTransparentBounds|hasAnchoringBounds|hitEnd|requireEnd,java.util.regex.Pattern,.*,java.util.stream.BaseStream,.*,java.util.stream.DoubleStream,.*,java.util.stream.IntStream,.*,java.util.stream.LongStream,.*,java.util.stream.Stream,.*" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="InnerClassMayBeStatic" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PrivatePropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PropertyName" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||
<option name="processCode" value="true" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnusedSymbol" enabled="true" level="INFORMATION" enabled_by_default="true" />
|
||||
</profile>
|
||||
</component>
|
14
.idea/inspectionProfiles/profiles_settings.xml
Normal file
14
.idea/inspectionProfiles/profiles_settings.xml
Normal file
|
@ -0,0 +1,14 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<list size="8">
|
||||
<item index="0" class="java.lang.String" itemvalue="INFORMATION" />
|
||||
<item index="1" class="java.lang.String" itemvalue="TEXT ATTRIBUTES" />
|
||||
<item index="2" class="java.lang.String" itemvalue="TYPO" />
|
||||
<item index="3" class="java.lang.String" itemvalue="SERVER PROBLEM" />
|
||||
<item index="4" class="java.lang.String" itemvalue="WEAK WARNING" />
|
||||
<item index="5" class="java.lang.String" itemvalue="INFO" />
|
||||
<item index="6" class="java.lang.String" itemvalue="WARNING" />
|
||||
<item index="7" class="java.lang.String" itemvalue="ERROR" />
|
||||
</list>
|
||||
</settings>
|
||||
</component>
|
6
.idea/kotlinc.xml
Normal file
6
.idea/kotlinc.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="KotlinJpsPluginSettings">
|
||||
<option name="version" value="1.8.0" />
|
||||
</component>
|
||||
</project>
|
10
.idea/migrations.xml
Normal file
10
.idea/migrations.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectMigrations">
|
||||
<option name="MigrateToGradleLocalJavaHome">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
</set>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
52
.idea/misc.xml
Normal file
52
.idea/misc.xml
Normal file
|
@ -0,0 +1,52 @@
|
|||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="NullableNotNullManager">
|
||||
<option name="myDefaultNullable" value="androidx.annotation.Nullable" />
|
||||
<option name="myDefaultNotNull" value="android.annotation.NonNull" />
|
||||
<option name="myNullables">
|
||||
<value>
|
||||
<list size="15">
|
||||
<item index="0" class="java.lang.String" itemvalue="com.android.annotations.Nullable" />
|
||||
<item index="1" class="java.lang.String" itemvalue="org.jspecify.nullness.Nullable" />
|
||||
<item index="2" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNullable" />
|
||||
<item index="3" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableDecl" />
|
||||
<item index="4" class="java.lang.String" itemvalue="org.jetbrains.annotations.Nullable" />
|
||||
<item index="5" class="java.lang.String" itemvalue="androidx.annotation.Nullable" />
|
||||
<item index="6" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.Nullable" />
|
||||
<item index="7" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.Nullable" />
|
||||
<item index="8" class="java.lang.String" itemvalue="android.support.annotation.Nullable" />
|
||||
<item index="9" class="java.lang.String" itemvalue="jakarta.annotation.Nullable" />
|
||||
<item index="10" class="java.lang.String" itemvalue="javax.annotation.CheckForNull" />
|
||||
<item index="11" class="java.lang.String" itemvalue="javax.annotation.Nullable" />
|
||||
<item index="12" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.Nullable" />
|
||||
<item index="13" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NullableType" />
|
||||
<item index="14" class="java.lang.String" itemvalue="android.annotation.Nullable" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
<option name="myNotNulls">
|
||||
<value>
|
||||
<list size="14">
|
||||
<item index="0" class="java.lang.String" itemvalue="androidx.annotation.RecentlyNonNull" />
|
||||
<item index="1" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.qual.NonNull" />
|
||||
<item index="2" class="java.lang.String" itemvalue="jakarta.annotation.Nonnull" />
|
||||
<item index="3" class="java.lang.String" itemvalue="androidx.annotation.NonNull" />
|
||||
<item index="4" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullType" />
|
||||
<item index="5" class="java.lang.String" itemvalue="android.support.annotation.NonNull" />
|
||||
<item index="6" class="java.lang.String" itemvalue="org.jspecify.nullness.NonNull" />
|
||||
<item index="7" class="java.lang.String" itemvalue="com.android.annotations.NonNull" />
|
||||
<item index="8" class="java.lang.String" itemvalue="edu.umd.cs.findbugs.annotations.NonNull" />
|
||||
<item index="9" class="java.lang.String" itemvalue="org.checkerframework.checker.nullness.compatqual.NonNullDecl" />
|
||||
<item index="10" class="java.lang.String" itemvalue="org.jetbrains.annotations.NotNull" />
|
||||
<item index="11" class="java.lang.String" itemvalue="javax.annotation.Nonnull" />
|
||||
<item index="12" class="java.lang.String" itemvalue="org.eclipse.jdt.annotation.NonNull" />
|
||||
<item index="13" class="java.lang.String" itemvalue="android.annotation.NonNull" />
|
||||
</list>
|
||||
</value>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK" />
|
||||
<component name="SuppressKotlinCodeStyleNotification">
|
||||
<option name="disableForAll" value="true" />
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
47
README.md
Normal file
47
README.md
Normal file
|
@ -0,0 +1,47 @@
|
|||
## 云音乐是什么?
|
||||
一个对接网易云的音乐播放器
|
||||
* 内置集成作者自己写的歌词Lrc组件支持单行歌词和多行歌词支持悬浮窗歌词。
|
||||
* 内置适配Android13的通知栏 (不完善请大佬请教)
|
||||
* 对接了网易云的歌单,歌曲,搜索,二维码登录等其余功能。
|
||||
* main.java-内api变量是网易云SDK后台服务器地址(可变更)
|
||||
* http 是我服务器php接口的地址用于软件更新用或者其他功能(个根据需要做出必须的更改或者删除)
|
||||
|
||||
## 进度
|
||||
* UI
|
||||
* 歌单
|
||||
* 音乐播放选择列表
|
||||
* 音乐播放器控制界面
|
||||
* 登陆
|
||||
* 设置
|
||||
* 侧滑栏内小功能
|
||||
* 代码
|
||||
* 适配主题和UI美化
|
||||
* 维护接口(不完善)
|
||||
* 蓝牙功能
|
||||
* 通知栏控制
|
||||
* 悬浮歌词功能
|
||||
* 基本播放器功能(控制暂停 上下曲 播放歌单操作)
|
||||
|
||||
|
||||
## 截图
|
||||
<img alt="Screenshot_20240224_154259_com.muqingbfq.jpg" height="700" src="image%2FScreenshot_20240224_154259_com.muqingbfq.jpg" width="300"/>
|
||||
<img alt="Screenshot_20240224_154302_com.muqingbfq.jpg" height="700" src="image%2FScreenshot_20240224_154302_com.muqingbfq.jpg" width="300"/>
|
||||
<img alt="Screenshot_20240224_154306_com.muqingbfq.jpg" height="700" src="image%2FScreenshot_20240224_154306_com.muqingbfq.jpg" width="300"/>
|
||||
|
||||
## 在使用中有任何问题,欢迎反馈给我,可以用以下联系方式跟我交流
|
||||
* QQ:1966944300
|
||||
|
||||
## 后台
|
||||
* Github: [网易云音乐 API](https://github.com/Binaryify/NeteaseCloudMusicApi)
|
||||
|
||||
## 关于
|
||||
在兴趣的驱动下,写一个`免费`的东西,有欣喜,也还有汗水,希望你喜欢我的作品,同时也能支持一下。
|
||||
|
||||
|
||||
## 修改JAR的包
|
||||
```javascript
|
||||
[//]: # (主要修改内容MD3化)
|
||||
com.github.QuadFlask:colorpicker:0.0.15
|
||||
[//]: # (歌词做全局变量给悬浮窗歌词)
|
||||
'com.github.wangchenyan:lrcview:2.2.1'
|
||||
...
|
1
StatusBarUtil/.gitignore
vendored
Normal file
1
StatusBarUtil/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
25
StatusBarUtil/build.gradle
Normal file
25
StatusBarUtil/build.gradle
Normal file
|
@ -0,0 +1,25 @@
|
|||
apply plugin: 'com.android.library'
|
||||
version = "1.5.1"
|
||||
android {
|
||||
namespace 'com.jaeger.library'
|
||||
compileSdk 34
|
||||
resourcePrefix "statusbarutil_"
|
||||
defaultConfig {
|
||||
minSdkVersion 23
|
||||
//noinspection ExpiredTargetSdkVersion
|
||||
targetSdk 31
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.appcompat:appcompat:1.6.1'
|
||||
implementation 'com.google.android.material:material:1.9.0'
|
||||
}
|
6
StatusBarUtil/src/main/AndroidManifest.xml
Normal file
6
StatusBarUtil/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,6 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.jaeger.library">
|
||||
|
||||
<application />
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,726 @@
|
|||
package com.jaeger.library;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.os.Build;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.IntRange;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/**
|
||||
* Created by Jaeger on 16/2/14.
|
||||
* <p>
|
||||
* Email: chjie.jaeger@gmail.com
|
||||
* GitHub: <a href="https://github.com/laobie">...</a>
|
||||
*/
|
||||
public class StatusBarUtil {
|
||||
|
||||
public static final int DEFAULT_STATUS_BAR_ALPHA = 112;
|
||||
private static final int FAKE_STATUS_BAR_VIEW_ID = R.id.statusbarutil_fake_status_bar_view;
|
||||
private static final int FAKE_TRANSLUCENT_VIEW_ID = R.id.statusbarutil_translucent_view;
|
||||
private static final int TAG_KEY_HAVE_SET_OFFSET = -123;
|
||||
|
||||
/**
|
||||
* 设置状态栏颜色
|
||||
*
|
||||
* @param activity 需要设置的 activity
|
||||
* @param color 状态栏颜色值
|
||||
*/
|
||||
public static void setColor(Activity activity, @ColorInt int color) {
|
||||
setColor(activity, color, DEFAULT_STATUS_BAR_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态栏颜色
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param color 状态栏颜色值
|
||||
* @param statusBarAlpha 状态栏透明度
|
||||
*/
|
||||
|
||||
public static void setColor(Activity activity, @ColorInt int color, @IntRange(from = 0, to = 255) int statusBarAlpha) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
activity.getWindow().setStatusBarColor(calculateStatusColor(color, statusBarAlpha));
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
|
||||
View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
|
||||
if (fakeStatusBarView != null) {
|
||||
if (fakeStatusBarView.getVisibility() == View.GONE) {
|
||||
fakeStatusBarView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
|
||||
} else {
|
||||
decorView.addView(createStatusBarView(activity, color, statusBarAlpha));
|
||||
}
|
||||
setRootView(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为滑动返回界面设置状态栏颜色
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param color 状态栏颜色值
|
||||
*/
|
||||
public static void setColorForSwipeBack(Activity activity, int color) {
|
||||
setColorForSwipeBack(activity, color, DEFAULT_STATUS_BAR_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为滑动返回界面设置状态栏颜色
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param color 状态栏颜色值
|
||||
* @param statusBarAlpha 状态栏透明度
|
||||
*/
|
||||
public static void setColorForSwipeBack(Activity activity, @ColorInt int color,
|
||||
@IntRange(from = 0, to = 255) int statusBarAlpha) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
|
||||
ViewGroup contentView = ((ViewGroup) activity.findViewById(android.R.id.content));
|
||||
View rootView = contentView.getChildAt(0);
|
||||
int statusBarHeight = getStatusBarHeight(activity);
|
||||
if (rootView instanceof CoordinatorLayout) {
|
||||
final CoordinatorLayout coordinatorLayout = (CoordinatorLayout) rootView;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
coordinatorLayout.setFitsSystemWindows(false);
|
||||
contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
|
||||
boolean isNeedRequestLayout = contentView.getPaddingTop() < statusBarHeight;
|
||||
if (isNeedRequestLayout) {
|
||||
contentView.setPadding(0, statusBarHeight, 0, 0);
|
||||
coordinatorLayout.post(coordinatorLayout::requestLayout);
|
||||
}
|
||||
} else {
|
||||
coordinatorLayout.setStatusBarBackgroundColor(calculateStatusColor(color, statusBarAlpha));
|
||||
}
|
||||
} else {
|
||||
contentView.setPadding(0, statusBarHeight, 0, 0);
|
||||
contentView.setBackgroundColor(calculateStatusColor(color, statusBarAlpha));
|
||||
}
|
||||
setTransparentForWindow(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态栏纯色 不加半透明效果
|
||||
*
|
||||
* @param activity 需要设置的 activity
|
||||
* @param color 状态栏颜色值
|
||||
*/
|
||||
public static void setColorNoTranslucent(Activity activity, @ColorInt int color) {
|
||||
setColor(activity, color, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态栏颜色(5.0以下无半透明效果,不建议使用)
|
||||
*
|
||||
* @param activity 需要设置的 activity
|
||||
* @param color 状态栏颜色值
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setColorDiff(Activity activity, @ColorInt int color) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
transparentStatusBar(activity);
|
||||
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
|
||||
// 移除半透明矩形,以免叠加
|
||||
View fakeStatusBarView = contentView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
|
||||
if (fakeStatusBarView != null) {
|
||||
if (fakeStatusBarView.getVisibility() == View.GONE) {
|
||||
fakeStatusBarView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
fakeStatusBarView.setBackgroundColor(color);
|
||||
} else {
|
||||
contentView.addView(createStatusBarView(activity, color));
|
||||
}
|
||||
setRootView(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使状态栏半透明
|
||||
* <p>
|
||||
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
*/
|
||||
public static void setTranslucent(Activity activity) {
|
||||
setTranslucent(activity, DEFAULT_STATUS_BAR_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使状态栏半透明
|
||||
* <p>
|
||||
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param statusBarAlpha 状态栏透明度
|
||||
*/
|
||||
public static void setTranslucent(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
setTransparent(activity);
|
||||
addTranslucentView(activity, statusBarAlpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* 针对根布局是 CoordinatorLayout, 使状态栏半透明
|
||||
* <p>
|
||||
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param statusBarAlpha 状态栏透明度
|
||||
*/
|
||||
public static void setTranslucentForCoordinatorLayout(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
transparentStatusBar(activity);
|
||||
addTranslucentView(activity, statusBarAlpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置状态栏全透明
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
*/
|
||||
public static void setTransparent(Activity activity) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
transparentStatusBar(activity);
|
||||
setRootView(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 使状态栏透明(5.0以上半透明效果,不建议使用)
|
||||
* <p>
|
||||
* 适用于图片作为背景的界面,此时需要图片填充到状态栏
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setTranslucentDiff(Activity activity) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// 设置状态栏透明
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
setRootView(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为DrawerLayout 布局设置状态栏变色
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
* @param color 状态栏颜色值
|
||||
*/
|
||||
public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {
|
||||
setColorForDrawerLayout(activity, drawerLayout, color, DEFAULT_STATUS_BAR_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为DrawerLayout 布局设置状态栏颜色,纯色
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
* @param color 状态栏颜色值
|
||||
*/
|
||||
public static void setColorNoTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {
|
||||
setColorForDrawerLayout(activity, drawerLayout, color, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为DrawerLayout 布局设置状态栏变色
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
* @param color 状态栏颜色值
|
||||
* @param statusBarAlpha 状态栏透明度
|
||||
*/
|
||||
public static void setColorForDrawerLayout(Activity activity, DrawerLayout drawerLayout, @ColorInt int color,
|
||||
@IntRange(from = 0, to = 255) int statusBarAlpha) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
} else {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
// 生成一个状态栏大小的矩形
|
||||
// 添加 statusBarView 到布局中
|
||||
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
|
||||
View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID);
|
||||
if (fakeStatusBarView != null) {
|
||||
if (fakeStatusBarView.getVisibility() == View.GONE) {
|
||||
fakeStatusBarView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
fakeStatusBarView.setBackgroundColor(color);
|
||||
} else {
|
||||
contentLayout.addView(createStatusBarView(activity, color), 0);
|
||||
}
|
||||
// 内容布局不是 LinearLayout 时,设置padding top
|
||||
if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {
|
||||
contentLayout.getChildAt(1)
|
||||
.setPadding(contentLayout.getPaddingLeft(), getStatusBarHeight(activity) + contentLayout.getPaddingTop(),
|
||||
contentLayout.getPaddingRight(), contentLayout.getPaddingBottom());
|
||||
}
|
||||
// 设置属性
|
||||
setDrawerLayoutProperty(drawerLayout, contentLayout);
|
||||
addTranslucentView(activity, statusBarAlpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 DrawerLayout 属性
|
||||
*
|
||||
* @param drawerLayout DrawerLayout
|
||||
* @param drawerLayoutContentLayout DrawerLayout 的内容布局
|
||||
*/
|
||||
private static void setDrawerLayoutProperty(DrawerLayout drawerLayout, ViewGroup drawerLayoutContentLayout) {
|
||||
ViewGroup drawer = (ViewGroup) drawerLayout.getChildAt(1);
|
||||
drawerLayout.setFitsSystemWindows(false);
|
||||
drawerLayoutContentLayout.setFitsSystemWindows(false);
|
||||
drawerLayoutContentLayout.setClipToPadding(true);
|
||||
drawer.setFitsSystemWindows(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为DrawerLayout 布局设置状态栏变色(5.0以下无半透明效果,不建议使用)
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
* @param color 状态栏颜色值
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setColorForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout, @ColorInt int color) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
// 生成一个状态栏大小的矩形
|
||||
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
|
||||
View fakeStatusBarView = contentLayout.findViewById(FAKE_STATUS_BAR_VIEW_ID);
|
||||
if (fakeStatusBarView != null) {
|
||||
if (fakeStatusBarView.getVisibility() == View.GONE) {
|
||||
fakeStatusBarView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
fakeStatusBarView.setBackgroundColor(calculateStatusColor(color, DEFAULT_STATUS_BAR_ALPHA));
|
||||
} else {
|
||||
// 添加 statusBarView 到布局中
|
||||
contentLayout.addView(createStatusBarView(activity, color), 0);
|
||||
}
|
||||
// 内容布局不是 LinearLayout 时,设置padding top
|
||||
if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {
|
||||
contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0);
|
||||
}
|
||||
// 设置属性
|
||||
setDrawerLayoutProperty(drawerLayout, contentLayout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 DrawerLayout 布局设置状态栏透明
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
*/
|
||||
public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {
|
||||
setTranslucentForDrawerLayout(activity, drawerLayout, DEFAULT_STATUS_BAR_ALPHA);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 DrawerLayout 布局设置状态栏透明
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
*/
|
||||
public static void setTranslucentForDrawerLayout(Activity activity, DrawerLayout drawerLayout,
|
||||
@IntRange(from = 0, to = 255) int statusBarAlpha) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
setTransparentForDrawerLayout(activity, drawerLayout);
|
||||
addTranslucentView(activity, statusBarAlpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 DrawerLayout 布局设置状态栏透明
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
*/
|
||||
public static void setTransparentForDrawerLayout(Activity activity, DrawerLayout drawerLayout) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
} else {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
|
||||
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
|
||||
// 内容布局不是 LinearLayout 时,设置padding top
|
||||
if (!(contentLayout instanceof LinearLayout) && contentLayout.getChildAt(1) != null) {
|
||||
contentLayout.getChildAt(1).setPadding(0, getStatusBarHeight(activity), 0, 0);
|
||||
}
|
||||
|
||||
// 设置属性
|
||||
setDrawerLayoutProperty(drawerLayout, contentLayout);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 DrawerLayout 布局设置状态栏透明(5.0以上半透明效果,不建议使用)
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param drawerLayout DrawerLayout
|
||||
*/
|
||||
@Deprecated
|
||||
public static void setTranslucentForDrawerLayoutDiff(Activity activity, DrawerLayout drawerLayout) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
// 设置状态栏透明
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
// 设置内容布局属性
|
||||
ViewGroup contentLayout = (ViewGroup) drawerLayout.getChildAt(0);
|
||||
contentLayout.setFitsSystemWindows(true);
|
||||
contentLayout.setClipToPadding(true);
|
||||
// 设置抽屉布局属性
|
||||
ViewGroup vg = (ViewGroup) drawerLayout.getChildAt(1);
|
||||
vg.setFitsSystemWindows(false);
|
||||
// 设置 DrawerLayout 属性
|
||||
drawerLayout.setFitsSystemWindows(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为头部是 ImageView 的界面设置状态栏全透明
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param needOffsetView 需要向下偏移的 View
|
||||
*/
|
||||
public static void setTransparentForImageView(Activity activity, View needOffsetView) {
|
||||
setTranslucentForImageView(activity, 0, needOffsetView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为头部是 ImageView 的界面设置状态栏透明(使用默认透明度)
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param needOffsetView 需要向下偏移的 View
|
||||
*/
|
||||
public static void setTranslucentForImageView(Activity activity, View needOffsetView) {
|
||||
setTranslucentForImageView(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为头部是 ImageView 的界面设置状态栏透明
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param statusBarAlpha 状态栏透明度
|
||||
* @param needOffsetView 需要向下偏移的 View
|
||||
*/
|
||||
public static void setTranslucentForImageView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha,
|
||||
View needOffsetView) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
|
||||
return;
|
||||
}
|
||||
setTransparentForWindow(activity);
|
||||
addTranslucentView(activity, statusBarAlpha);
|
||||
if (needOffsetView != null) {
|
||||
Object haveSetOffset = needOffsetView.getTag(TAG_KEY_HAVE_SET_OFFSET);
|
||||
if (haveSetOffset != null && (Boolean) haveSetOffset) {
|
||||
return;
|
||||
}
|
||||
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) needOffsetView.getLayoutParams();
|
||||
layoutParams.setMargins(layoutParams.leftMargin, layoutParams.topMargin + getStatusBarHeight(activity),
|
||||
layoutParams.rightMargin, layoutParams.bottomMargin);
|
||||
needOffsetView.setTag(TAG_KEY_HAVE_SET_OFFSET, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 fragment 头部是 ImageView 的设置状态栏透明
|
||||
*
|
||||
* @param activity fragment 对应的 activity
|
||||
* @param needOffsetView 需要向下偏移的 View
|
||||
*/
|
||||
public static void setTranslucentForImageViewInFragment(Activity activity, View needOffsetView) {
|
||||
setTranslucentForImageViewInFragment(activity, DEFAULT_STATUS_BAR_ALPHA, needOffsetView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 fragment 头部是 ImageView 的设置状态栏透明
|
||||
*
|
||||
* @param activity fragment 对应的 activity
|
||||
* @param needOffsetView 需要向下偏移的 View
|
||||
*/
|
||||
public static void setTransparentForImageViewInFragment(Activity activity, View needOffsetView) {
|
||||
setTranslucentForImageViewInFragment(activity, 0, needOffsetView);
|
||||
}
|
||||
|
||||
/**
|
||||
* 为 fragment 头部是 ImageView 的设置状态栏透明
|
||||
*
|
||||
* @param activity fragment 对应的 activity
|
||||
* @param statusBarAlpha 状态栏透明度
|
||||
* @param needOffsetView 需要向下偏移的 View
|
||||
*/
|
||||
public static void setTranslucentForImageViewInFragment(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha,
|
||||
View needOffsetView) {
|
||||
setTranslucentForImageView(activity, statusBarAlpha, needOffsetView);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
clearPreviousSetting(activity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 隐藏伪状态栏 View
|
||||
*
|
||||
* @param activity 调用的 Activity
|
||||
*/
|
||||
public static void hideFakeStatusBarView(Activity activity) {
|
||||
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
|
||||
View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
|
||||
if (fakeStatusBarView != null) {
|
||||
fakeStatusBarView.setVisibility(View.GONE);
|
||||
}
|
||||
View fakeTranslucentView = decorView.findViewById(FAKE_TRANSLUCENT_VIEW_ID);
|
||||
if (fakeTranslucentView != null) {
|
||||
fakeTranslucentView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public static void setLightMode(Activity activity) {
|
||||
setMIUIStatusBarDarkIcon(activity, true);
|
||||
setMeizuStatusBarDarkIcon(activity, true);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.M)
|
||||
public static void setDarkMode(Activity activity) {
|
||||
setMIUIStatusBarDarkIcon(activity, false);
|
||||
setMeizuStatusBarDarkIcon(activity, false);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改 MIUI V6 以上状态栏颜色
|
||||
*/
|
||||
private static void setMIUIStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) {
|
||||
Class<? extends Window> clazz = activity.getWindow().getClass();
|
||||
try {
|
||||
Class<?> layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
|
||||
Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
|
||||
int darkModeFlag = field.getInt(layoutParams);
|
||||
Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
|
||||
extraFlagField.invoke(activity.getWindow(), darkIcon ? darkModeFlag : 0, darkModeFlag);
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改魅族状态栏字体颜色 Flyme 4.0
|
||||
*/
|
||||
private static void setMeizuStatusBarDarkIcon(@NonNull Activity activity, boolean darkIcon) {
|
||||
try {
|
||||
WindowManager.LayoutParams lp = activity.getWindow().getAttributes();
|
||||
Field darkFlag = WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
|
||||
Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
|
||||
darkFlag.setAccessible(true);
|
||||
meizuFlags.setAccessible(true);
|
||||
int bit = darkFlag.getInt(null);
|
||||
int value = meizuFlags.getInt(lp);
|
||||
if (darkIcon) {
|
||||
value |= bit;
|
||||
} else {
|
||||
value &= ~bit;
|
||||
}
|
||||
meizuFlags.setInt(lp, value);
|
||||
activity.getWindow().setAttributes(lp);
|
||||
} catch (Exception e) {
|
||||
//e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static void clearPreviousSetting(Activity activity) {
|
||||
ViewGroup decorView = (ViewGroup) activity.getWindow().getDecorView();
|
||||
View fakeStatusBarView = decorView.findViewById(FAKE_STATUS_BAR_VIEW_ID);
|
||||
if (fakeStatusBarView != null) {
|
||||
decorView.removeView(fakeStatusBarView);
|
||||
ViewGroup rootView = (ViewGroup) ((ViewGroup) activity.findViewById(android.R.id.content)).getChildAt(0);
|
||||
rootView.setPadding(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加半透明矩形条
|
||||
*
|
||||
* @param activity 需要设置的 activity
|
||||
* @param statusBarAlpha 透明值
|
||||
*/
|
||||
private static void addTranslucentView(Activity activity, @IntRange(from = 0, to = 255) int statusBarAlpha) {
|
||||
ViewGroup contentView = (ViewGroup) activity.findViewById(android.R.id.content);
|
||||
View fakeTranslucentView = contentView.findViewById(FAKE_TRANSLUCENT_VIEW_ID);
|
||||
if (fakeTranslucentView != null) {
|
||||
if (fakeTranslucentView.getVisibility() == View.GONE) {
|
||||
fakeTranslucentView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
fakeTranslucentView.setBackgroundColor(Color.argb(statusBarAlpha, 0, 0, 0));
|
||||
} else {
|
||||
contentView.addView(createTranslucentStatusBarView(activity, statusBarAlpha));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个和状态栏大小相同的彩色矩形条
|
||||
*
|
||||
* @param activity 需要设置的 activity
|
||||
* @param color 状态栏颜色值
|
||||
* @return 状态栏矩形条
|
||||
*/
|
||||
private static View createStatusBarView(Activity activity, @ColorInt int color) {
|
||||
return createStatusBarView(activity, color, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成一个和状态栏大小相同的半透明矩形条
|
||||
*
|
||||
* @param activity 需要设置的activity
|
||||
* @param color 状态栏颜色值
|
||||
* @param alpha 透明值
|
||||
* @return 状态栏矩形条
|
||||
*/
|
||||
private static View createStatusBarView(Activity activity, @ColorInt int color, int alpha) {
|
||||
// 绘制一个和状态栏一样高的矩形
|
||||
View statusBarView = new View(activity);
|
||||
LinearLayout.LayoutParams params =
|
||||
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
|
||||
statusBarView.setLayoutParams(params);
|
||||
statusBarView.setBackgroundColor(calculateStatusColor(color, alpha));
|
||||
statusBarView.setId(FAKE_STATUS_BAR_VIEW_ID);
|
||||
return statusBarView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置根布局参数
|
||||
*/
|
||||
private static void setRootView(Activity activity) {
|
||||
ViewGroup parent = (ViewGroup) activity.findViewById(android.R.id.content);
|
||||
for (int i = 0, count = parent.getChildCount(); i < count; i++) {
|
||||
View childView = parent.getChildAt(i);
|
||||
if (childView instanceof ViewGroup) {
|
||||
childView.setFitsSystemWindows(true);
|
||||
((ViewGroup) childView).setClipToPadding(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置透明
|
||||
*/
|
||||
private static void setTransparentForWindow(Activity activity) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
activity.getWindow()
|
||||
.getDecorView()
|
||||
.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
activity.getWindow()
|
||||
.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS, WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使状态栏透明
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.KITKAT)
|
||||
private static void transparentStatusBar(Activity activity) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
|
||||
activity.getWindow().setStatusBarColor(Color.TRANSPARENT);
|
||||
} else {
|
||||
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建半透明矩形 View
|
||||
*
|
||||
* @param alpha 透明值
|
||||
* @return 半透明 View
|
||||
*/
|
||||
private static View createTranslucentStatusBarView(Activity activity, int alpha) {
|
||||
// 绘制一个和状态栏一样高的矩形
|
||||
View statusBarView = new View(activity);
|
||||
LinearLayout.LayoutParams params =
|
||||
new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(activity));
|
||||
statusBarView.setLayoutParams(params);
|
||||
statusBarView.setBackgroundColor(Color.argb(alpha, 0, 0, 0));
|
||||
statusBarView.setId(FAKE_TRANSLUCENT_VIEW_ID);
|
||||
return statusBarView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取状态栏高度
|
||||
*
|
||||
* @param context context
|
||||
* @return 状态栏高度
|
||||
*/
|
||||
private static int getStatusBarHeight(Context context) {
|
||||
// 获得状态栏高度
|
||||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
||||
return context.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算状态栏颜色
|
||||
*
|
||||
* @param color color值
|
||||
* @param alpha alpha值
|
||||
* @return 最终的状态栏颜色
|
||||
*/
|
||||
private static int calculateStatusColor(@ColorInt int color, int alpha) {
|
||||
if (alpha == 0) {
|
||||
return color;
|
||||
}
|
||||
float a = 1 - alpha / 255f;
|
||||
int red = color >> 16 & 0xff;
|
||||
int green = color >> 8 & 0xff;
|
||||
int blue = color & 0xff;
|
||||
red = (int) (red * a + 0.5);
|
||||
green = (int) (green * a + 0.5);
|
||||
blue = (int) (blue * a + 0.5);
|
||||
return 0xff << 24 | red << 16 | green << 8 | blue;
|
||||
}
|
||||
}
|
5
StatusBarUtil/src/main/res/values/ids.xml
Normal file
5
StatusBarUtil/src/main/res/values/ids.xml
Normal file
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<item type="id" name="statusbarutil_fake_status_bar_view" />
|
||||
<item type="id" name="statusbarutil_translucent_view" />
|
||||
</resources>
|
1
app/.gitignore
vendored
Normal file
1
app/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/build
|
78
app/build.gradle
Normal file
78
app/build.gradle
Normal file
|
@ -0,0 +1,78 @@
|
|||
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 33
|
||||
defaultConfig {
|
||||
applicationId "com.muqingbfq"
|
||||
minSdk 23
|
||||
//noinspection ExpiredTargetSdkVersion,OldTargetApi
|
||||
targetSdk 33
|
||||
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.6.1'
|
||||
implementation 'com.google.android.material:material:1.9.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 "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'
|
||||
|
||||
// 沉浸式状态栏
|
||||
implementation project(path: ':StatusBarUtil')
|
||||
// api project(path: ':lrcview')
|
||||
//歌词组件库
|
||||
api "com.github.cy745:EaseView:e11c3208a9"
|
||||
api "androidx.dynamicanimation:dynamicanimation-ktx:1.0.0-alpha03"
|
||||
}
|
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
|
89
app/src/main/AndroidManifest.xml
Normal file
89
app/src/main/AndroidManifest.xml
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?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"/>
|
||||
<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:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.muqing"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:preserveLegacyExternalStorage="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
tools:targetApi="s">
|
||||
<activity
|
||||
android:name=".home"
|
||||
android:exported="true">
|
||||
<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" />
|
||||
|
||||
<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.floating" />
|
||||
<service android:name=".mq.FloatingLyricsService" />
|
||||
|
||||
<receiver
|
||||
android:name=".MyButtonClickReceiver"
|
||||
android:enabled="true"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MEDIA_BUTTON" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
</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
|
||||
}
|
||||
}
|
||||
}
|
1098
app/src/main/java/com/dirror/lyricviewx/LyricViewX.kt
Normal file
1098
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) }
|
||||
}
|
||||
}
|
50
app/src/main/java/com/muqingbfq/MP3.java
Normal file
50
app/src/main/java/com/muqingbfq/MP3.java
Normal file
|
@ -0,0 +1,50 @@
|
|||
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(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 +
|
||||
'}';
|
||||
}
|
||||
}
|
170
app/src/main/java/com/muqingbfq/MediaPlayer.java
Normal file
170
app/src/main/java/com/muqingbfq/MediaPlayer.java
Normal file
|
@ -0,0 +1,170 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.media.AudioAttributes;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
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.mpatric.mp3agic.ID3v2;
|
||||
import com.mpatric.mp3agic.Mp3File;
|
||||
import com.muqingbfq.fragment.bflb_db;
|
||||
import com.muqingbfq.fragment.mp3;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class MediaPlayer extends android.media.MediaPlayer {
|
||||
// 每秒更新一次进度
|
||||
@SuppressLint("UnsafeOptInUsageError")
|
||||
public MediaPlayer() {
|
||||
setOnErrorListener((mediaPlayer, i, i1) -> {
|
||||
if (bfqkz.list.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
//针对错误进行相应的处理
|
||||
bfqkz.list.remove(bfqkz.xm);
|
||||
bfqkz.xm = bfqkz.list.get(bfqkz.getmti());
|
||||
new bfqkz.mp3(com.muqingbfq.api.
|
||||
url.hq(bfqkz.xm));
|
||||
return false;
|
||||
});
|
||||
setOnCompletionListener(mediaPlayer -> {
|
||||
if (bfqkz.list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
bfq_an.xyq();
|
||||
});
|
||||
setAudioAttributes(new AudioAttributes
|
||||
.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
|
||||
.build());
|
||||
// main.handler.post(updateSeekBar); // 在播放开始时启动更新进度
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pause() throws IllegalStateException {
|
||||
if (isPlaying()) {
|
||||
super.pause();
|
||||
// bfq.isPlaying = false;
|
||||
}
|
||||
|
||||
if (bfqkz.notify != null) {
|
||||
bfqkz.notify.tzl_button();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() throws IllegalStateException {
|
||||
if (bfqkz.xm == null) {
|
||||
if (bfqkz.list != null && !bfqkz.list.isEmpty()) {
|
||||
bfq_an.xyq();
|
||||
}
|
||||
return;
|
||||
}
|
||||
super.start();
|
||||
|
||||
if (bfqkz.notify != null) {
|
||||
bfqkz.notify.tzl_button();
|
||||
}
|
||||
// bfq.isPlaying = true;
|
||||
}
|
||||
|
||||
|
||||
public void setDataSource(MP3 mp3) throws IOException {
|
||||
reset();
|
||||
super.setDataSource(mp3.url);
|
||||
prepare();
|
||||
start();
|
||||
bfqkz.xm = mp3;
|
||||
main.handler.post(() -> {
|
||||
bfui();
|
||||
if (bfqkz.notify != null) {
|
||||
bfqkz.notify.tzl();
|
||||
}
|
||||
});
|
||||
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
if (bfqkz.lishi_list.size() >= 100) {
|
||||
bfqkz.lishi_list.remove(0);
|
||||
}
|
||||
bfqkz.lishi_list.remove(bfqkz.xm);
|
||||
if (!bfqkz.lishi_list.contains(bfqkz.xm)) {
|
||||
bfqkz.lishi_list.add(0, bfqkz.xm);
|
||||
wj.xrwb(wj.gd + "mp3_hc.json", new com.google.gson.Gson().toJson(bfqkz.lishi_list));
|
||||
}
|
||||
wj.setMP3ToFile(bfqkz.xm);
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public void DataSource(MP3 path) throws Exception {
|
||||
reset();
|
||||
super.setDataSource(path.url);
|
||||
prepare();
|
||||
setTX();
|
||||
}
|
||||
|
||||
public void setTX() {
|
||||
Glide.with(main.application)
|
||||
.asBitmap()
|
||||
.load(bfqkz.xm.picurl)
|
||||
.listener(new RequestListener<Bitmap>() {
|
||||
@Override
|
||||
public boolean onLoadFailed(@Nullable GlideException e, Object model,
|
||||
@NonNull Target<Bitmap> target,
|
||||
boolean isFirstResource) {
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
Mp3File mp3file = new Mp3File(bfqkz.xm.picurl);
|
||||
if (mp3file.hasId3v2Tag()) {
|
||||
ID3v2 id3v2Tag = mp3file.getId3v2Tag();
|
||||
byte[] albumImage = id3v2Tag.getAlbumImage();
|
||||
bitmap =
|
||||
BitmapFactory.decodeByteArray(albumImage, 0, albumImage.length);
|
||||
|
||||
}
|
||||
} catch (Exception a) {
|
||||
gj.sc(getClass() + " yc:" + a);
|
||||
}
|
||||
if (bfqkz.notify != null) {
|
||||
bfqkz.notify.setbitmap(bitmap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Bitmap bitmap, @NonNull Object model, Target<Bitmap> target,
|
||||
@NonNull DataSource dataSource,
|
||||
boolean isFirstResource) {
|
||||
if (bfqkz.notify != null) {
|
||||
bfqkz.notify.setbitmap(bitmap);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.submit();
|
||||
}
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void bfui() {
|
||||
setTX();
|
||||
if (bflb_db.adapter != null) {
|
||||
// bflb_db.adapter.
|
||||
bflb_db.adapter.notifyDataSetChanged();
|
||||
}
|
||||
if (mp3.adapter != null) {
|
||||
mp3.adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
213
app/src/main/java/com/muqingbfq/MyButtonClickReceiver.java
Normal file
213
app/src/main/java/com/muqingbfq/MyButtonClickReceiver.java
Normal file
|
@ -0,0 +1,213 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.provider.Settings;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.mq.FloatingLyricsService;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class MyButtonClickReceiver extends BroadcastReceiver {
|
||||
private final Timer timer = new Timer();
|
||||
private static int clickCount;
|
||||
|
||||
public MyButtonClickReceiver() {
|
||||
super();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String action = intent.getAction();
|
||||
if (action.equals(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED)) {
|
||||
int bluetoothState = intent.getIntExtra(BluetoothAdapter.EXTRA_CONNECTION_STATE, 0);
|
||||
//蓝牙断开
|
||||
if (bluetoothState == BluetoothAdapter.STATE_DISCONNECTED) {
|
||||
receiverPause();
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (action.equals("android.intent.action.HEADSET_PLUG")) {
|
||||
if (intent.hasExtra("state")) {
|
||||
if (intent.getIntExtra("state", 2) == 0) {
|
||||
//拔出
|
||||
if (bfqkz.mt.isPlaying()) {
|
||||
receiverPause();
|
||||
}
|
||||
} else if (intent.getIntExtra("state", 2) == 1) {
|
||||
receiverPlay();
|
||||
//插入
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
|
||||
KeyEvent keyEvent = intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
|
||||
if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_HEADSETHOOK && keyEvent.getAction() == KeyEvent.ACTION_UP) {
|
||||
clickCount = clickCount + 1;
|
||||
if (clickCount == 1) {
|
||||
HeadsetTimerTask headsetTimerTask = new HeadsetTimerTask();
|
||||
timer.schedule(headsetTimerTask, 500);
|
||||
}
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_NEXT && keyEvent.getAction() == KeyEvent.ACTION_UP) {
|
||||
handler(2);
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PREVIOUS && keyEvent.getAction() == KeyEvent.ACTION_UP) {
|
||||
handler(3);
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PAUSE && keyEvent.getAction() == KeyEvent.ACTION_UP) {
|
||||
handler(4);
|
||||
} else if (keyEvent.getKeyCode() == KeyEvent.KEYCODE_MEDIA_PLAY && keyEvent.getAction() == KeyEvent.ACTION_UP) {
|
||||
handler(5);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case "kg":
|
||||
playOrPause();
|
||||
break;
|
||||
case "syq":
|
||||
bfq_an.syq();
|
||||
break;
|
||||
case "xyq":
|
||||
bfq_an.xyq();
|
||||
break;
|
||||
case "lrc":
|
||||
if (FloatingLyricsService.lei == null) {
|
||||
if (!Settings.canDrawOverlays(main.application)) {
|
||||
// 无权限,需要申请权限
|
||||
main.application.startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
|
||||
Uri.parse("package:" + main.application.getPackageName())));
|
||||
} else {
|
||||
main.application.startService(
|
||||
new Intent(main.application, FloatingLyricsService.class));
|
||||
}
|
||||
return;
|
||||
}
|
||||
FloatingLyricsService lei = FloatingLyricsService.lei;
|
||||
if (lei.setup.i == 1) {
|
||||
lei.setyc();
|
||||
} else {
|
||||
lei.setup.i = 0;
|
||||
lei.baocun();
|
||||
main.application.stopService(
|
||||
new Intent(main.application, FloatingLyricsService.class));
|
||||
}
|
||||
break;
|
||||
case "like":
|
||||
try {
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
List<MP3> list = gson.fromJson(wj.dqwb(wj.gd + "mp3_like.json"), type);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
if (bfqkz.like_bool) {
|
||||
list.remove(bfqkz.xm);
|
||||
bfqkz.like_bool = false;
|
||||
} else {
|
||||
if (!list.contains(bfqkz.xm)) {
|
||||
list.add(bfqkz.xm);
|
||||
bfqkz.like_bool = true;
|
||||
}
|
||||
}
|
||||
wj.xrwb(wj.gd + "mp3_like.json", gson.toJson(list));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 处理按钮点击事件的逻辑
|
||||
}
|
||||
|
||||
class HeadsetTimerTask extends TimerTask {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
if (clickCount == 1) {
|
||||
handler(1);
|
||||
} else if (clickCount == 2) {
|
||||
handler(2);
|
||||
} else if (clickCount >= 3) {
|
||||
handler(3);
|
||||
}
|
||||
clickCount = 0;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
private void handler(int a) {
|
||||
switch (a) {
|
||||
case 1:
|
||||
playOrPause();
|
||||
break;
|
||||
case 2:
|
||||
playNext();
|
||||
break;
|
||||
case 3:
|
||||
playPrevious();
|
||||
break;
|
||||
case 4:
|
||||
receiverPause();
|
||||
break;
|
||||
case 5:
|
||||
receiverPlay();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// * 对蓝牙 播放
|
||||
public void receiverPlay() {
|
||||
bfqkz.mt.start();
|
||||
}
|
||||
|
||||
// * 对蓝牙 暂停
|
||||
public void receiverPause() {
|
||||
bfqkz.mt.pause();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对蓝牙 播放-暂停
|
||||
*/
|
||||
public static void playOrPause() {
|
||||
if (bfqkz.mt == null) {
|
||||
return;
|
||||
}
|
||||
// gj.sc(isMusicServiceBound);播放/暂停按钮点击事件 if (isMusicServiceBound)
|
||||
if (bfqkz.mt.isPlaying()) {
|
||||
bfqkz.mt.pause();
|
||||
} else {
|
||||
bfqkz.mt.start();
|
||||
}
|
||||
bfqkz.notify.tzl();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对蓝牙 下一首
|
||||
*/
|
||||
public void playNext() {
|
||||
bfq_an.xyq();
|
||||
}
|
||||
|
||||
/**
|
||||
* 对蓝牙 上一首
|
||||
*/
|
||||
public void playPrevious() {
|
||||
bfq_an.syq();
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
185
app/src/main/java/com/muqingbfq/activity_about_software.java
Normal file
185
app/src/main/java/com/muqingbfq/activity_about_software.java
Normal file
|
@ -0,0 +1,185 @@
|
|||
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.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 AppCompatActivity<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 -> {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
int jianchagengxin = gj.jianchagengxin(activity_about_software.this);
|
||||
if (jianchagengxin == 400) {
|
||||
gj.xcts(activity_about_software.this, "无网络");
|
||||
} else if (jianchagengxin == 0) {
|
||||
gj.xcts(activity_about_software.this, "已经是最新的客户端了");
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
});
|
||||
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);
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
viewById.setText(Html.fromHtml(stringBuilder.toString(), Html.FROM_HTML_MODE_LEGACY));
|
||||
} else {
|
||||
viewById.setText(Html.fromHtml(stringBuilder.toString()));
|
||||
}
|
||||
open.close();
|
||||
bufferedReader.close();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
352
app/src/main/java/com/muqingbfq/activity_search.java
Normal file
352
app/src/main/java/com/muqingbfq/activity_search.java
Normal file
|
@ -0,0 +1,352 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
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.DefaultItemAnimator;
|
||||
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.dialog.MaterialAlertDialogBuilder;
|
||||
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.fragment.search;
|
||||
import com.muqingbfq.mq.FragmentActivity;
|
||||
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.Iterator;
|
||||
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());
|
||||
}
|
||||
|
||||
class VH extends RecyclerView.ViewHolder {
|
||||
public VH(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
}
|
||||
}
|
||||
|
||||
@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 RecyclerView.Adapter<VH>() {
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH(LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.view_search_item, 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 -> {
|
||||
binding.searchview.setText(s);
|
||||
start(s);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
});
|
||||
//设置项点击监听
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
public void dismiss() {
|
||||
binding.searchview.hide();
|
||||
}
|
||||
|
||||
private void addSearchRecord(String name) {
|
||||
try {
|
||||
if (!binding.xxbj1.isShown()) {
|
||||
binding.xxbj1.setVisibility(View.VISIBLE);
|
||||
}
|
||||
int existingIndex = json_list.indexOf(name);
|
||||
if (existingIndex != -1) {
|
||||
// 交换两个元素的位置
|
||||
json_list.remove(name);
|
||||
json_list.add(0, name);
|
||||
binding.listRecycler.getAdapter().notifyItemMoved(existingIndex, 0);
|
||||
} else {
|
||||
// json_list.remove(name);
|
||||
json_list.add(0, name);
|
||||
binding.listRecycler.getAdapter().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);
|
||||
addSearchRecord(name);
|
||||
}
|
||||
}
|
||||
|
||||
class SearchRecordAdapter extends RecyclerView.Adapter<SearchRecordAdapter.ViewHolder> {
|
||||
public SearchRecordAdapter() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (json_list.isEmpty()) {
|
||||
binding.xxbj1.setVisibility(View.INVISIBLE);
|
||||
}
|
||||
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 ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(ListTextBinding.inflate(LayoutInflater.from(parent.getContext()),
|
||||
parent, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
String keyword = json_list.get(position);
|
||||
holder.binding.getRoot().setText(keyword);
|
||||
holder.binding.getRoot().setOnClickListener(v -> {
|
||||
binding.toolbar.setText(keyword);
|
||||
start(keyword);
|
||||
});
|
||||
holder.binding.getRoot().setOnCloseIconClickListener(view -> {
|
||||
json_list.remove(keyword);
|
||||
notifyItemRemoved(holder.getAdapterPosition());
|
||||
wj.xrwb(wj.filesdri + wj.lishi_json, new Gson().toJson(json_list));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return json_list.size();
|
||||
}
|
||||
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ListTextBinding binding;
|
||||
|
||||
|
||||
public ViewHolder(ListTextBinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
binding = itemView;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
76
app/src/main/java/com/muqingbfq/adapter/AdapterMp3.java
Normal file
76
app/src/main/java/com/muqingbfq/adapter/AdapterMp3.java
Normal file
|
@ -0,0 +1,76 @@
|
|||
package com.muqingbfq.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.api.url;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.ListMp3ImageBinding;
|
||||
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<>();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH<ListMp3ImageBinding> onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new VH<>(ListMp3ImageBinding.inflate(
|
||||
LayoutInflater.from(parent.getContext()), parent, false));
|
||||
}
|
||||
|
||||
int position_wei;
|
||||
@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);
|
||||
if (bfqkz.xm != null && x.id.equals(bfqkz.xm.id)) {
|
||||
holder.binding.getRoot().setCardBackgroundColor(
|
||||
gj.getThemeColor(holder.itemView.getContext(), com.google.android.material.R.attr.colorSurfaceVariant));
|
||||
} else {
|
||||
holder.binding.getRoot().setCardBackgroundColor(ContextCompat
|
||||
.getColor(holder.itemView.getContext(), android.R.color.transparent));
|
||||
}
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (bfqkz.xm == null || !bfqkz.xm.id.equals(x.id)) {
|
||||
bfqkz.xm = x;
|
||||
new url(x);
|
||||
notifyDataSetChanged();
|
||||
|
||||
} else if (!bfqkz.mt.isPlaying()) {
|
||||
bfqkz.mt.start();
|
||||
}
|
||||
if (!bfqkz.list.contains(x)) {
|
||||
bfqkz.list.add(0, x);
|
||||
}
|
||||
// bfqkz.list.addAll(list);
|
||||
// bfq.start(getContext());
|
||||
});
|
||||
Glide.with(holder.itemView.getContext()).load(x.picurl)
|
||||
.apply(new RequestOptions().placeholder(R.drawable.ic_launcher_foreground))
|
||||
.error(R.drawable.ic_launcher_foreground)
|
||||
.into(holder.binding.imageView);
|
||||
}
|
||||
|
||||
@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());
|
||||
}
|
||||
}
|
182
app/src/main/java/com/muqingbfq/api/playlist.java
Normal file
182
app/src/main/java/com/muqingbfq/api/playlist.java
Normal file
|
@ -0,0 +1,182 @@
|
|||
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.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 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);
|
||||
getLrc(x.id);
|
||||
Media.loadLyric();
|
||||
try {
|
||||
if (wj.cz(x.id)) {
|
||||
x.url = x.id;
|
||||
return x;
|
||||
} else if (wj.cz(wj.mp3 + x.id)) {
|
||||
x.url = wj.mp3 + x.id;
|
||||
return x;
|
||||
} else if (wj.cz(wj.filesdri + "hc/" + x.id)) {
|
||||
x.url = wj.filesdri + "hc/" + x.id;
|
||||
return x;
|
||||
}
|
||||
String level = "standard";
|
||||
boolean wiFiConnected = gj.isWiFiConnected();
|
||||
if (wiFiConnected) {
|
||||
level = "exhigh";
|
||||
}
|
||||
String hq = wl.hq(api + "?id=" + x.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"));
|
||||
x.url = jsonObject.getString("url");
|
||||
return x;
|
||||
} catch (JSONException e) {
|
||||
gj.sc("url hq :" + e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
com.muqingbfq.bfqkz.mp3(hq(x));
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
603
app/src/main/java/com/muqingbfq/bfq.java
Normal file
603
app/src/main/java/com/muqingbfq/bfq.java
Normal file
|
@ -0,0 +1,603 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
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.BitmapFactory;
|
||||
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.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.animation.LinearInterpolator;
|
||||
import android.widget.SeekBar;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
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.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.jaeger.library.StatusBarUtil;
|
||||
import com.mpatric.mp3agic.ID3v2;
|
||||
import com.mpatric.mp3agic.Mp3File;
|
||||
import com.muqingbfq.api.FileDownloader;
|
||||
import com.muqingbfq.databinding.ActivityBfqBinding;
|
||||
import com.muqingbfq.fragment.Media;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public class bfq extends AppCompatActivity<ActivityBfqBinding>
|
||||
implements GestureDetector.OnGestureListener {
|
||||
public String lrc;
|
||||
public MP3 mp3;
|
||||
public boolean isplay = true;
|
||||
GestureDetector gestureDetector;
|
||||
int seekbarH = 0;
|
||||
|
||||
private void lrc(View v) {
|
||||
// 隐藏view2并显示view1的动画效果
|
||||
v.animate()
|
||||
.alpha(0.0f)
|
||||
.setDuration(500)
|
||||
.setListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
v.setVisibility(View.GONE);
|
||||
binding.cardview.setVisibility(View.VISIBLE);
|
||||
binding.cardview.setAlpha(0.0f);
|
||||
binding.cardview.animate().alpha(1.0f).setDuration(500).setListener(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
private void setLrc() {
|
||||
DisplayMetrics dm = getResources().getDisplayMetrics();
|
||||
ViewGroup.LayoutParams layoutParams = binding.cardview.getLayoutParams();
|
||||
if (!gj.isTablet(this)) {
|
||||
layoutParams.height = (int) (dm.widthPixels / 1.3f);
|
||||
layoutParams.width = (int) (dm.widthPixels / 1.3f);
|
||||
binding.lrcView.setOnClickListener(this::lrc);
|
||||
|
||||
binding.lrcView.setOnSingerClickListener(() -> lrc(binding.lrcView));
|
||||
// binding.lrcView.setTextGravity(GRAVITY_LEFT)
|
||||
} else {
|
||||
layoutParams.height = (int) (dm.heightPixels / 2.0f);
|
||||
layoutParams.width = (int) (dm.heightPixels / 2.0f);
|
||||
}
|
||||
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 -> {
|
||||
bfqkz.mt.seekTo((int) time);
|
||||
return false;
|
||||
});
|
||||
binding.tdt.setThumb(null);
|
||||
binding.tdt.post(() -> seekbarH = binding.tdt.getHeight());
|
||||
binding.tdt.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
|
||||
// 当进度发生变化时执行操作
|
||||
setTime_b(bfq_an.getTime(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartTrackingTouch(SeekBar seekBar) {
|
||||
// 当开始拖动滑块时执行操作
|
||||
// 创建 ValueAnimator 对象,实现进度条高度的平滑过渡
|
||||
ValueAnimator animator = ValueAnimator.ofInt(seekbarH,
|
||||
seekbarH + 30);
|
||||
animator.addUpdateListener(animation -> {
|
||||
seekBar.getLayoutParams().height = (int) animation.getAnimatedValue();
|
||||
seekBar.requestLayout();
|
||||
});
|
||||
animator.setDuration(200); // 设置动画持续时间为 200 毫秒
|
||||
animator.start(); // 开始执行动画
|
||||
isplay = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStopTrackingTouch(SeekBar seekBar) {
|
||||
// 当停止拖动滑块时执行操作
|
||||
isplay = true;
|
||||
// 创建 ValueAnimator 对象,实现恢复进度条高度的平滑过渡
|
||||
ValueAnimator animator = ValueAnimator.ofInt(seekbarH + 30,
|
||||
seekbarH);
|
||||
animator.addUpdateListener(animation -> {
|
||||
seekBar.getLayoutParams().height = (int) animation.getAnimatedValue();
|
||||
seekBar.requestLayout();
|
||||
});
|
||||
animator.setDuration(200); // 设置动画持续时间为 200 毫秒
|
||||
animator.start(); // 开始执行动画
|
||||
bfqkz.mt.seekTo(seekBar.getProgress());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static void startactivity(Context context, MP3 mp3) {
|
||||
gj.sc(mp3.toString());
|
||||
Intent intent = new Intent(context, bfq.class);
|
||||
intent.putExtra("MP3", mp3);
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
context.startActivity(intent);
|
||||
}
|
||||
|
||||
ObjectAnimator rotateAnimation;
|
||||
|
||||
private void Animation() {
|
||||
if (bfqkz.mt.isPlaying()) {
|
||||
if (rotateAnimation.isPaused()) {
|
||||
rotateAnimation.resume();
|
||||
} else {
|
||||
rotateAnimation.start();
|
||||
}
|
||||
} else {
|
||||
rotateAnimation.pause();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
StatusBarUtil.setTransparent(this);
|
||||
|
||||
setContentView();
|
||||
gestureDetector = new GestureDetector(this, this);
|
||||
setLrc();
|
||||
rotateAnimation = ObjectAnimator.ofFloat(binding.cardview
|
||||
, "rotation", 0f, 360f);
|
||||
rotateAnimation.setDuration(30000); // 设置动画持续时间,单位为毫秒
|
||||
rotateAnimation.setRepeatCount(ObjectAnimator.INFINITE); // 设置重复次数为无限
|
||||
rotateAnimation.setInterpolator(new LinearInterpolator()); // 设置插值器,这里使用线性插值器
|
||||
rotateAnimation.start();
|
||||
binding.kg.setOnClickListener(v -> {
|
||||
if (bfqkz.mt.isPlaying()) {
|
||||
bfqkz.mt.pause();
|
||||
setbf(false);
|
||||
} else {
|
||||
bfqkz.mt.start();
|
||||
setbf(true);
|
||||
}
|
||||
Animation();
|
||||
});
|
||||
binding.xyq.setOnClickListener(v -> bfq_an.xyq());
|
||||
binding.syq.setOnClickListener(v -> bfq_an.syq());
|
||||
|
||||
binding.image1.setOnClickListener(new toolbar());
|
||||
binding.image2.setOnClickListener(new toolbar());
|
||||
|
||||
binding.bfqListMp3.
|
||||
setOnClickListener(view1 -> com.muqingbfq.fragment.bflb_db.start(this));
|
||||
binding.control.setOnClickListener(new bfq_an.control(binding.control));
|
||||
|
||||
binding.like.setOnClickListener(view1 -> {
|
||||
try {
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
List<MP3> list = gson.fromJson(wj.dqwb(wj.gd + "mp3_like.json"), type);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
if (bfqkz.like_bool) {
|
||||
list.remove(bfqkz.xm);
|
||||
setlike(false);
|
||||
} else {
|
||||
if (!list.contains(bfqkz.xm)) {
|
||||
list.add(bfqkz.xm);
|
||||
setlike(true);
|
||||
}
|
||||
}
|
||||
bfqkz.like_bool = !bfqkz.like_bool;
|
||||
wj.xrwb(wj.gd + "mp3_like.json", gson.toJson(list));
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
});
|
||||
binding.download.setOnClickListener(view -> {
|
||||
if (wj.cz(wj.mp3 + bfqkz.xm.id)) {
|
||||
gj.ts(this, "你已经下载过这首歌曲了");
|
||||
return;
|
||||
}
|
||||
if (bfqkz.xm != null) {
|
||||
new FileDownloader(bfq.this).downloadFile(bfqkz.xm);
|
||||
}
|
||||
});
|
||||
Intent intent = getIntent();
|
||||
mp3 = (MP3) intent.getSerializableExtra("MP3");
|
||||
new thread().start();
|
||||
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;
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private class toolbar implements View.OnClickListener {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (v.getId() == R.id.image1) {
|
||||
finish();
|
||||
} else if (v.getId() == R.id.image2) {
|
||||
com.muqingbfq.mq.gj.fx(v.getContext(),
|
||||
"音乐名称:" + mp3.name +
|
||||
"\n 作者:" + mp3.zz +
|
||||
"\n 链接:https://music.163.com/#/song?id=" + mp3.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class thread extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
if (mp3 != null) {
|
||||
if (bfqkz.xm == null || !bfqkz.xm.equals(mp3)) {
|
||||
bfqkz.xm = mp3;
|
||||
bfqkz.mp3(com.muqingbfq.api.url.hq(mp3));
|
||||
}
|
||||
}
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
main.handler.post(() -> {
|
||||
if (mp3 != null) {
|
||||
sx();
|
||||
}
|
||||
setbf(bfqkz.mt.isPlaying());
|
||||
Animation();
|
||||
});
|
||||
// main.handler.post(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
public void sx() {
|
||||
setname(mp3.name);
|
||||
setzz(mp3.zz);
|
||||
bfq_an.islike();
|
||||
int duration = bfqkz.mt.getDuration();
|
||||
setMax(duration);
|
||||
gj.sc(duration);
|
||||
setTime_a(bfq_an.getTime(duration));
|
||||
int position = bfqkz.mt.getCurrentPosition();
|
||||
Progress(position);
|
||||
setImageBitmap();
|
||||
}
|
||||
|
||||
public void setname(String str) {
|
||||
binding.name.setText(str);
|
||||
}
|
||||
|
||||
public void setzz(String str) {
|
||||
binding.zz.setText(str);
|
||||
}
|
||||
|
||||
public void kgsetImageResource(int a) {
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
binding.kg.setImageResource(a);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivityBfqBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityBfqBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
public void setlike(boolean bool) {
|
||||
if (bool) {
|
||||
binding.like.setImageTintList(ContextCompat.
|
||||
getColorStateList(binding.getRoot().getContext(), android.R.color.holo_red_dark));
|
||||
} else {
|
||||
binding.like.setImageTintList(ColorStateList.valueOf(ColorTint));
|
||||
}
|
||||
islike = bool;
|
||||
}
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isplay) {
|
||||
int position = bfqkz.mt.getCurrentPosition();
|
||||
Progress(position);
|
||||
}
|
||||
if (mp3 != null && !mp3.equals(bfqkz.xm) && binding != null) {
|
||||
mp3 = bfqkz.xm;
|
||||
setname(mp3.name);
|
||||
setzz(mp3.zz);
|
||||
bfq_an.islike();
|
||||
int duration = bfqkz.mt.getDuration();
|
||||
setMax(duration);
|
||||
gj.sc(duration);
|
||||
setTime_a(bfq_an.getTime(duration));
|
||||
int position = bfqkz.mt.getCurrentPosition();
|
||||
Progress(position);
|
||||
setImageBitmap();
|
||||
}
|
||||
if (bfqkz.mt.isPlaying() != isPlaying) {
|
||||
|
||||
setbf(bfqkz.mt.isPlaying());
|
||||
}
|
||||
if (bfqkz.like_bool != islike) {
|
||||
setlike(bfqkz.like_bool);
|
||||
}
|
||||
if (!Objects.equals(bfqkz.lrc, lrc)) {
|
||||
lrc = bfqkz.lrc;
|
||||
String[] strings = Media.loadLyric();
|
||||
binding.lrcView.loadLyric(strings[0], strings[1]);
|
||||
}
|
||||
main.handler.postDelayed(this, 1000); // 每秒更新一次进度
|
||||
}
|
||||
};
|
||||
|
||||
public boolean islike = false;
|
||||
public boolean isPlaying = false;
|
||||
|
||||
public void setbf(boolean bool) {
|
||||
if (bool) {
|
||||
//开始
|
||||
kgsetImageResource(R.drawable.bf);
|
||||
} else {
|
||||
//暂停
|
||||
kgsetImageResource(R.drawable.zt);
|
||||
}
|
||||
isPlaying = bool;
|
||||
}
|
||||
|
||||
public void setImageBitmap() {
|
||||
if (binding == null) {
|
||||
return;
|
||||
}
|
||||
if (wj.cz(bfqkz.xm.picurl)) {
|
||||
try {
|
||||
Mp3File mp3file = new Mp3File(bfqkz.xm.picurl);
|
||||
if (mp3file.hasId3v2Tag()) {
|
||||
ID3v2 id3v2Tag = mp3file.getId3v2Tag();
|
||||
byte[] albumImage = id3v2Tag.getAlbumImage();
|
||||
Bitmap bitmap = BitmapFactory.
|
||||
decodeByteArray(albumImage, 0, albumImage.length);
|
||||
binding.cardview.imageView.setImageBitmap(bitmap);
|
||||
color(bitmap);
|
||||
}
|
||||
return;
|
||||
} catch (Exception a) {
|
||||
gj.sc(getClass() + " yc:" + a);
|
||||
}
|
||||
}
|
||||
if (!isFinishing()) {
|
||||
Glide.with(this)
|
||||
.asBitmap()
|
||||
.load(mp3.picurl)
|
||||
.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 false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onResourceReady(@NonNull Bitmap resource,
|
||||
@NonNull Object model, Target<Bitmap> target,
|
||||
@NonNull DataSource dataSource,
|
||||
boolean isFirstResource) {
|
||||
color(resource);
|
||||
binding.cardview.imageView.setImageBitmap(resource);
|
||||
return true;
|
||||
}
|
||||
}).into(binding.cardview.imageView);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void color(Bitmap bitmap) {
|
||||
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 int ColorTint = Color.WHITE;
|
||||
|
||||
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);*/
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStart() {
|
||||
super.onStart();
|
||||
main.handler.post(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
main.handler.removeCallbacks(runnable);
|
||||
}
|
||||
|
||||
public void setTime_a(String str) {
|
||||
binding.timeA.setText(str);
|
||||
}
|
||||
|
||||
public void setTime_b(String str) {
|
||||
binding.timeB.setText(str);
|
||||
}
|
||||
|
||||
public void setMax(int max) {
|
||||
binding.tdt.setMax(Math.max(0, max));
|
||||
}
|
||||
|
||||
public void Progress(int progress) {
|
||||
int min = Math.min(progress, binding.tdt.getMax());
|
||||
binding.tdt.setProgress(min);
|
||||
binding.lrcView.updateTime(min, true);
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
private void switchViews(final View view1, final View view2) {
|
||||
// 隐藏view1并显示view2的动画效果
|
||||
if (binding.cardview.getVisibility() == View.VISIBLE) {
|
||||
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(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 判断触摸点是否在视图范围内的辅助方法
|
||||
@Override
|
||||
public boolean onScroll(@NonNull 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(@NonNull MotionEvent e1, @NonNull MotionEvent e2,
|
||||
float velocityX, float velocityY) {
|
||||
return false;
|
||||
}
|
||||
}
|
141
app/src/main/java/com/muqingbfq/bfq_an.java
Normal file
141
app/src/main/java/com/muqingbfq/bfq_an.java
Normal file
|
@ -0,0 +1,141 @@
|
|||
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 {
|
||||
|
||||
public static void syq() {
|
||||
if (bfqkz.list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
bfqkz.mt.pause();
|
||||
int i = bfqkz.list.indexOf(bfqkz.xm) - 1;
|
||||
if (i < 0) {
|
||||
i = 0;
|
||||
}
|
||||
bfqkz.xm = bfqkz.list.get(i);
|
||||
new url(bfqkz.xm);
|
||||
}
|
||||
|
||||
public static void xyq() {
|
||||
if (bfqkz.list.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
bfqkz.mt.pause();
|
||||
// gj.sc(bfqkz.list.get(bfqkz.getmti()));
|
||||
new url(bfqkz.list.get(bfqkz.getmti()));
|
||||
}
|
||||
|
||||
public static class control implements View.OnClickListener {
|
||||
ImageView imageView;
|
||||
|
||||
public control(ImageView imageView) {
|
||||
this.imageView = imageView;
|
||||
setImage(-1);
|
||||
}
|
||||
|
||||
private void setImage(int i) {
|
||||
gj.sc(bfqkz.ms);
|
||||
|
||||
bfqkz.mt.setLooping(bfqkz.ms == 0);
|
||||
switch (bfqkz.ms) {
|
||||
case 0:
|
||||
imageView.setImageResource(R.drawable.mt_xh);
|
||||
if (i == -1) {
|
||||
break;
|
||||
}
|
||||
if (!bfqkz.list_baocun.isEmpty()) {
|
||||
bfqkz.list.clear();
|
||||
bfqkz.list.addAll(bfqkz.list_baocun);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
imageView.setImageResource(R.drawable.mt_sx);
|
||||
if (i == -1) {
|
||||
break;
|
||||
}
|
||||
if (!bfqkz.list_baocun.isEmpty()) {
|
||||
bfqkz.list.clear();
|
||||
bfqkz.list.addAll(bfqkz.list_baocun);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
imageView.setImageResource(R.drawable.mt_sj);
|
||||
if (i == -1) {
|
||||
break;
|
||||
}
|
||||
bfqkz.list_baocun.clear();
|
||||
bfqkz.list_baocun.addAll(bfqkz.list);
|
||||
Collections.shuffle(bfqkz.list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (bfqkz.ms == 2) {
|
||||
bfqkz.ms = 0;
|
||||
} else {
|
||||
bfqkz.ms++;
|
||||
}
|
||||
setImage(bfqkz.ms);
|
||||
main.edit.putInt("ms", bfqkz.ms);
|
||||
main.edit.commit();
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
190
app/src/main/java/com/muqingbfq/bfqkz.java
Normal file
190
app/src/main/java/com/muqingbfq/bfqkz.java
Normal file
|
@ -0,0 +1,190 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
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.BluetoothMusicController;
|
||||
import com.muqingbfq.mq.gj;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class bfqkz extends MediaBrowserServiceCompat {
|
||||
public final static MediaPlayer mt = new MediaPlayer();
|
||||
|
||||
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 com.muqingbfq.mq.NotificationManagerCompat notify;
|
||||
|
||||
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 static void mp3(MP3 mp3) {
|
||||
try {
|
||||
if (mp3 == null) {
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(mp3.url)) {
|
||||
//针对错误进行相应的处理
|
||||
bfqkz.list.remove(bfqkz.xm);
|
||||
bfq_an.xyq();
|
||||
return;
|
||||
}
|
||||
if (TextUtils.isEmpty(mp3.picurl)) {
|
||||
mp3.picurl = url.picurl(mp3.id);
|
||||
}
|
||||
mt.setDataSource(mp3);
|
||||
} catch (Exception e) {
|
||||
gj.sc("bfqkz mp3(" + mp3 + ") :" + e);
|
||||
}
|
||||
}
|
||||
|
||||
public static class mp3 extends Thread {
|
||||
MP3 mp3;
|
||||
|
||||
public mp3(MP3 mp3) {
|
||||
this.mp3 = mp3;
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
mp3(mp3);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public MediaSessionCompat mSession;
|
||||
public PlaybackStateCompat.Builder playback;
|
||||
|
||||
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);
|
||||
mSession = new MediaSessionCompat(this, "MediaSessionCompat",
|
||||
home.componentName, pendingIntent);
|
||||
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);
|
||||
}
|
||||
|
||||
class callback extends MediaSessionCompat.Callback {
|
||||
@Override
|
||||
public void onPlay() {
|
||||
super.onPlay();
|
||||
if (playback.build().getState() == PlaybackStateCompat.STATE_PAUSED) {
|
||||
mt.start();
|
||||
playback.setState(PlaybackStateCompat.STATE_PLAYING, 0, 1.0f)
|
||||
.build();
|
||||
mSession.setPlaybackState(playback.build());
|
||||
}
|
||||
gj.sc(this.getClass());
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
if (playback.build().getState() == PlaybackStateCompat.STATE_PLAYING) {
|
||||
mt.pause();
|
||||
playback.setState(PlaybackStateCompat.STATE_PAUSED, 0, 1.0f)
|
||||
.build();
|
||||
mSession.setPlaybackState(playback.build());
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SwitchIntDef")
|
||||
@Override
|
||||
public void onPlayFromUri(Uri uri, Bundle extras) {
|
||||
try {
|
||||
switch (playback.build().getState()) {
|
||||
case PlaybackStateCompat.STATE_PLAYING:
|
||||
case PlaybackStateCompat.STATE_PAUSED:
|
||||
case PlaybackStateCompat.STATE_NONE:
|
||||
// mp3(uri);/
|
||||
playback.setState(PlaybackStateCompat.STATE_CONNECTING, 0, 1.0f)
|
||||
.build();
|
||||
mSession.setPlaybackState(playback.build());
|
||||
//我们可以保存当前播放音乐的信息,以便客户端刷新UI
|
||||
mSession.setMetadata(new MediaMetadataCompat.Builder()
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, extras.getString("title"))
|
||||
.build()
|
||||
);
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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};
|
||||
}
|
||||
|
||||
}
|
139
app/src/main/java/com/muqingbfq/fragment/bflb_db.java
Normal file
139
app/src/main/java/com/muqingbfq/fragment/bflb_db.java
Normal file
|
@ -0,0 +1,139 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Bundle;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.ItemTouchHelper;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.api.url;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.FragmentBflbDbBinding;
|
||||
import com.muqingbfq.databinding.ListMp3ABinding;
|
||||
import com.muqingbfq.list.MyViewHoder;
|
||||
import com.muqingbfq.yc;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
public class bflb_db extends BottomSheetDialog {
|
||||
public static RecyclerView.Adapter<MyViewHoder> adapter;
|
||||
FragmentBflbDbBinding binding;
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding = FragmentBflbDbBinding.inflate(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
try {
|
||||
binding.lb.setAdapter(new spq());
|
||||
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.getAdapterPosition();
|
||||
int toPosition = target.getAdapterPosition();
|
||||
// 在这里处理数据集的移动
|
||||
Collections.swap(bfqkz.list,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() {
|
||||
int i = bfqkz.list.indexOf(bfqkz.xm);
|
||||
if (i == -1) {
|
||||
i = 0;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
public bflb_db(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public static void start(Context context) {
|
||||
new bflb_db(context).show();
|
||||
}
|
||||
|
||||
private class spq extends RecyclerView.Adapter<MyViewHoder> {
|
||||
public spq() {
|
||||
adapter = this;
|
||||
}
|
||||
@NonNull
|
||||
@Override
|
||||
public MyViewHoder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new MyViewHoder(ListMp3ABinding.
|
||||
inflate(getLayoutInflater(),parent,false));
|
||||
}
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MyViewHoder holder, int position) {
|
||||
MP3 x = bfqkz.list.get(position);
|
||||
holder.bindingA.name.setText(x.name);
|
||||
holder.bindingA.zz.setText(String.format(" · %s", x.zz));
|
||||
int color = ContextCompat.getColor(holder.getContext(), R.color.text);
|
||||
if (bfqkz.xm != null && x.id.equals(bfqkz.xm.id)) {
|
||||
color = ContextCompat.getColor(holder.getContext(), R.color.text_cz);
|
||||
}
|
||||
holder.bindingA.name.setTextColor(color);
|
||||
holder.bindingA.zz.setTextColor(color);
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (bfqkz.xm != x) {
|
||||
bfqkz.xm = x;
|
||||
new url(x);
|
||||
}
|
||||
});
|
||||
holder.bindingA.delete.setOnClickListener(v -> {
|
||||
bfqkz.list.remove(holder.getAdapterPosition());
|
||||
notifyItemRemoved(holder.getAdapterPosition());
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
// binding.textView.setText(String.valueOf(bfqkz.list.size()));
|
||||
return bfqkz.list.size();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dismiss() {
|
||||
super.dismiss();
|
||||
adapter = null;
|
||||
}
|
||||
}
|
152
app/src/main/java/com/muqingbfq/fragment/bfq_db.java
Normal file
152
app/src/main/java/com/muqingbfq/fragment/bfq_db.java
Normal file
|
@ -0,0 +1,152 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.bfq;
|
||||
import com.muqingbfq.bfq_an;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.FragmentBfqDbBinding;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class bfq_db extends Fragment implements GestureDetector.OnGestureListener {
|
||||
FragmentBfqDbBinding binding;
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container,
|
||||
Bundle savedInstanceState) {
|
||||
binding = FragmentBfqDbBinding.inflate(inflater, container, false);
|
||||
// 获取当前活动的主题
|
||||
binding.kg.setOnClickListener(v -> {
|
||||
if (bfqkz.mt.isPlaying()) {
|
||||
bfqkz.mt.pause();
|
||||
} else {
|
||||
bfqkz.mt.start();
|
||||
}
|
||||
setkg(bfqkz.mt.isPlaying());
|
||||
});
|
||||
binding.txb.setOnClickListener(view -> bflb_db.start(getContext()));
|
||||
gestureDetector = new GestureDetector(getContext(), this);
|
||||
binding.getRoot().setOnTouchListener((view, motionEvent) -> {
|
||||
gestureDetector.onTouchEvent(motionEvent);
|
||||
if (motionEvent.getAction() == MotionEvent.ACTION_UP) {
|
||||
binding.linearLayout.setTranslationX(0);
|
||||
// binding.getRoot().setAlpha(1.0f);
|
||||
} else if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
// binding.getRoot().setAlpha(0.2f);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
handler.post(runnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
handler.removeCallbacks(runnable);
|
||||
}
|
||||
|
||||
MP3 mp3;
|
||||
boolean isPlaying = false, isvisible = false;
|
||||
Handler handler = new Handler();
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (!Objects.equals(mp3, bfqkz.xm)) {
|
||||
mp3 = bfqkz.xm;
|
||||
setname(mp3.name, " - " + mp3.zz);
|
||||
}
|
||||
if (bfqkz.mt.isPlaying() != isPlaying) {
|
||||
setkg(bfqkz.mt.isPlaying());
|
||||
}
|
||||
if (isvisible != bfqkz.list.isEmpty()) {
|
||||
isvisible = bfqkz.list.isEmpty();
|
||||
if (isvisible) {
|
||||
binding.getRoot().setVisibility(View.GONE);
|
||||
} else {
|
||||
binding.getRoot().setVisibility(View.VISIBLE);
|
||||
}
|
||||
}
|
||||
handler.postDelayed(this, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
public void setkg(boolean bool) {
|
||||
if (bool) {
|
||||
binding.kg.setImageResource(R.drawable.bf);
|
||||
} else {
|
||||
binding.kg.setImageResource(R.drawable.zt);
|
||||
}
|
||||
isPlaying = bool;
|
||||
}
|
||||
|
||||
public void setname(String a,String b) {
|
||||
binding.textview1.setText(a);
|
||||
binding.textview2.setText(b);
|
||||
}
|
||||
|
||||
@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);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onScroll(@Nullable MotionEvent motionEvent, @NonNull MotionEvent motionEvent1,
|
||||
float v, float v1) {
|
||||
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 (distance > threshold) {
|
||||
// 向左滑动
|
||||
// 在这里添加你的逻辑代码
|
||||
bfq_an.xyq();
|
||||
} else if (distance < -threshold) {
|
||||
// 向右滑动
|
||||
// 在这里添加你的逻辑代码
|
||||
bfq_an.syq();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
352
app/src/main/java/com/muqingbfq/fragment/gd.java
Normal file
352
app/src/main/java/com/muqingbfq/fragment/gd.java
Normal file
|
@ -0,0 +1,352 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityOptions;
|
||||
import android.content.Intent;
|
||||
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.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.palette.graphics.Palette;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
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.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.api.playlist;
|
||||
import com.muqingbfq.api.resource;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.ActivityGdBinding;
|
||||
import com.muqingbfq.databinding.ListGdBBinding;
|
||||
import com.muqingbfq.databinding.ListGdBinding;
|
||||
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;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class gd extends com.muqingbfq.mq.FragmentActivity<ActivityGdBinding> {
|
||||
public static String gdid;
|
||||
private final List<XM> list = new ArrayList<>();
|
||||
public baseadapter 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());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getViewBinding().getRoot());
|
||||
setToolbar();
|
||||
|
||||
|
||||
Intent intent = getIntent();
|
||||
binding.title.setText(intent.getStringExtra("name"));
|
||||
adapter = new baseadapter(this, list);
|
||||
k = (int) (getResources().getDisplayMetrics().widthPixels / getResources().getDisplayMetrics().density + 0.5f);
|
||||
|
||||
GridLayoutManager gridLayoutManager = new GridLayoutManager(this, k / 120);
|
||||
binding.lb.setLayoutManager(gridLayoutManager);
|
||||
binding.lb.setAdapter(adapter);
|
||||
String id = intent.getStringExtra("id");
|
||||
new start(id);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
if (item.getItemId() == android.R.id.home) {
|
||||
ActivityCompat.finishAfterTransition(this);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ActivityGdBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityGdBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
class start extends Thread {
|
||||
String id;
|
||||
|
||||
public start(String id) {
|
||||
binding.recyclerview1Bar.setVisibility(View.VISIBLE);
|
||||
this.id = id;
|
||||
list.clear();
|
||||
start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
if (id.equals("排行榜")) {
|
||||
resource.leaderboard(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");
|
||||
list.add(new XM(id, name, coverImgUrl));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
main.handler.post(() -> {
|
||||
adapter.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 static class baseadapter extends RecyclerView.Adapter<VH> {
|
||||
Activity context;
|
||||
public List<XM> list;
|
||||
|
||||
public baseadapter(Activity context, List<XM> list) {
|
||||
this.context = context;
|
||||
this.list = list;
|
||||
}
|
||||
|
||||
boolean bool = false;
|
||||
|
||||
public baseadapter(Activity context, List<XM> list, boolean bool) {
|
||||
this.context = context;
|
||||
this.list = list;
|
||||
this.bool = bool;
|
||||
}
|
||||
|
||||
public void setonlong(int position) {
|
||||
XM xm = list.get(position);
|
||||
gj.sc(xm.name);
|
||||
String[] stringArray = context.getResources()
|
||||
.getStringArray(R.array.gd_list);
|
||||
if (!wj.cz(wj.gd + xm.id)) {
|
||||
stringArray = new String[]{"下载歌单"};
|
||||
}
|
||||
String[] finalStringArray = stringArray;
|
||||
new MaterialAlertDialogBuilder(context).
|
||||
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();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
if (bool) {
|
||||
return new VH(ListGdBBinding.bind(LayoutInflater.from(context)
|
||||
.inflate(R.layout.list_gd_b, parent, false)));
|
||||
}
|
||||
return new VH(ListGdBinding.bind(LayoutInflater.from(context)
|
||||
.inflate(R.layout.list_gd, parent, false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull VH holder, int position) {
|
||||
XM xm = list.get(position);
|
||||
holder.itemView.setOnClickListener(new CARD(position));
|
||||
Drawable color_kg = ContextCompat.getDrawable(context, R.drawable.zt);
|
||||
if (xm.id.equals(gdid)) {
|
||||
color_kg = ContextCompat.getDrawable(context, R.drawable.bf);
|
||||
}
|
||||
if (bool) {
|
||||
holder.bindingB.text1.setText(xm.name);
|
||||
holder.bindingB.text2.setText(xm.message);
|
||||
Glide.with(holder.itemView.getContext())
|
||||
.load(xm.picurl)
|
||||
.apply(new RequestOptions()
|
||||
.placeholder(R.drawable.ic_launcher_foreground)
|
||||
.error(R.drawable.ic_launcher_foreground))
|
||||
.into(holder.bindingB.image);
|
||||
holder.bindingB.kg.setOnClickListener(new KG(this, xm.id));
|
||||
} else {
|
||||
holder.binding.text1.setText(xm.name);
|
||||
holder.binding.kg.setImageDrawable(color_kg);
|
||||
holder.binding.kg.setOnClickListener(new KG(this, xm.id));
|
||||
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 false;
|
||||
}
|
||||
|
||||
@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.itemView.setOnLongClickListener(v -> {
|
||||
setonlong(position);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
class KG implements View.OnClickListener {
|
||||
RecyclerView.Adapter adapter;
|
||||
String id;
|
||||
|
||||
public KG(RecyclerView.Adapter adapter, String id) {
|
||||
this.adapter = adapter;
|
||||
this.id = id;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
boolean an = playlist.hq(bfqkz.list, id);
|
||||
if (bfqkz.ms == 2) {
|
||||
Collections.shuffle(bfqkz.list);
|
||||
}
|
||||
main.handler.post(() -> {
|
||||
if (an) {
|
||||
com.muqingbfq.bfq_an.xyq();
|
||||
((ImageView) v).setImageResource(R.drawable.bf);
|
||||
com.muqingbfq.fragment.gd.gdid = id;
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
class CARD implements View.OnClickListener {
|
||||
int position;
|
||||
|
||||
public CARD(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
XM xm = list.get(position);
|
||||
mp3.start(context, new String[]{xm.id, xm.name}, view);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class VH extends RecyclerView.ViewHolder {
|
||||
public ListGdBinding binding;
|
||||
|
||||
public VH(@NonNull ListGdBinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
binding = itemView;
|
||||
}
|
||||
|
||||
ListGdBBinding bindingB;
|
||||
|
||||
public VH(@NonNull ListGdBBinding itemView) {
|
||||
super(itemView.getRoot());
|
||||
bindingB = itemView;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
117
app/src/main/java/com/muqingbfq/fragment/gd_adapter.java
Normal file
117
app/src/main/java/com/muqingbfq/fragment/gd_adapter.java
Normal file
|
@ -0,0 +1,117 @@
|
|||
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.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);
|
||||
}
|
||||
|
||||
|
||||
@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);
|
||||
binding.recyclerview1.setAdapter(new gd.baseadapter(getActivity(), list));
|
||||
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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
336
app/src/main/java/com/muqingbfq/fragment/mp3.java
Normal file
336
app/src/main/java/com/muqingbfq/fragment/mp3.java
Normal file
|
@ -0,0 +1,336 @@
|
|||
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.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 androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.DividerItemDecoration;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.MP3;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.api.FileDownloader;
|
||||
import com.muqingbfq.api.playlist;
|
||||
import com.muqingbfq.bfq;
|
||||
import com.muqingbfq.bfq_an;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.ActivityMp3Binding;
|
||||
import com.muqingbfq.databinding.ListMp3Binding;
|
||||
import com.muqingbfq.list.MyViewHoder;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.FragmentActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class mp3 extends FragmentActivity<ActivityMp3Binding> {
|
||||
private List<MP3> list = new ArrayList<>();
|
||||
private List<MP3> list_ys = new ArrayList<>();
|
||||
public static 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());
|
||||
}
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getViewBinding().getRoot());
|
||||
setToolbar();
|
||||
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) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@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 static class Adapter extends RecyclerView.Adapter<MyViewHoder> implements Filterable {
|
||||
|
||||
private List<MP3> list;
|
||||
private List<MP3> list_ys;
|
||||
|
||||
public Adapter(List<MP3> list) {
|
||||
this.list = list;
|
||||
list_ys = list;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public MyViewHoder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new MyViewHoder(ListMp3Binding.bind(LayoutInflater.from(parent.getContext()).
|
||||
inflate(R.layout.list_mp3,
|
||||
parent, false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull MyViewHoder holder, int position) {
|
||||
MP3 x = list.get(position);
|
||||
holder.binding.text1.setText(String.valueOf(position + 1));
|
||||
holder.binding.name.setText(x.name);
|
||||
holder.binding.zz.setText(x.zz);
|
||||
if (bfqkz.xm != null && x.id.equals(bfqkz.xm.id)) {
|
||||
TypedValue typedValue = new TypedValue();
|
||||
holder.itemView.getContext().getTheme().resolveAttribute(
|
||||
com.google.android.material.R.attr.colorSurfaceVariant, typedValue, true);
|
||||
int colorSurface = typedValue.data;
|
||||
// 这里 colorSurface 就是你要找的颜色值
|
||||
holder.binding.getRoot().setCardBackgroundColor(colorSurface);
|
||||
}else{
|
||||
holder.binding.getRoot().setCardBackgroundColor(ContextCompat
|
||||
.getColor(holder.itemView.getContext(), android.R.color.transparent));
|
||||
}
|
||||
// holder.binding.zz.setTextColor(color);
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (bfqkz.list!=list) {
|
||||
bfqkz.list.clear();
|
||||
bfqkz.list.addAll(list);
|
||||
}
|
||||
bfq.startactivity(holder.getContext(), x);
|
||||
});
|
||||
holder.itemView.setOnLongClickListener(view -> {
|
||||
List<String> stringList = new ArrayList<>();
|
||||
boolean getlike = bfq_an.getlike(x);
|
||||
if (getlike) {
|
||||
stringList.add("取消喜欢");
|
||||
} else {
|
||||
stringList.add("喜欢歌曲");
|
||||
}
|
||||
if (wj.cz(wj.mp3 + x.id)) {
|
||||
stringList.add("删除下载");
|
||||
} else {
|
||||
stringList.add("下载歌曲");
|
||||
}
|
||||
stringList.add("复制名字");
|
||||
String[] array = stringList.toArray(new String[0]);
|
||||
new MaterialAlertDialogBuilder(view.getContext()).
|
||||
setItems(array, (dialog, id) -> {
|
||||
switch (array[id]) {
|
||||
case "下载歌曲":
|
||||
new FileDownloader(view.getContext()).downloadFile(x);
|
||||
break;
|
||||
case "删除下载":
|
||||
wj.sc(wj.mp3 + x.id);
|
||||
/* if (sc&&) {
|
||||
list.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
}*/
|
||||
break;
|
||||
case "喜欢歌曲":
|
||||
case "取消喜欢":
|
||||
try {
|
||||
Gson gson = new Gson();
|
||||
Type type = new TypeToken<List<MP3>>() {
|
||||
}.getType();
|
||||
List<MP3> list = gson.fromJson(wj.dqwb(wj.gd + "mp3_like.json"), type);
|
||||
if (list == null) {
|
||||
list = new ArrayList<>();
|
||||
}
|
||||
if (list.contains(x))
|
||||
list.remove(x);
|
||||
else
|
||||
list.add(x);
|
||||
wj.xrwb(wj.gd + "mp3_like.json", gson.toJson(list));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
break;
|
||||
case "复制名字":
|
||||
gj.fz(view.getContext(), x.name);
|
||||
break;
|
||||
|
||||
}
|
||||
}).show();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
}
|
230
app/src/main/java/com/muqingbfq/fragment/search.java
Normal file
230
app/src/main/java/com/muqingbfq/fragment/search.java
Normal file
|
@ -0,0 +1,230 @@
|
|||
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.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;
|
||||
@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 com.muqingbfq.fragment.mp3.Adapter(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 com.muqingbfq.fragment.gd.baseadapter(getActivity(),
|
||||
list, true));
|
||||
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();
|
||||
}
|
||||
}
|
||||
List<Fragment> fragments=new ArrayList<>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
70
app/src/main/java/com/muqingbfq/fragment/sz.java
Normal file
70
app/src/main/java/com/muqingbfq/fragment/sz.java
Normal file
|
@ -0,0 +1,70 @@
|
|||
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.home;
|
||||
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://rust.coldmint.top/ftp/muqing/");
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
450
app/src/main/java/com/muqingbfq/fragment/wode.java
Normal file
450
app/src/main/java/com/muqingbfq/fragment/wode.java
Normal file
|
@ -0,0 +1,450 @@
|
|||
package com.muqingbfq.fragment;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.graphics.drawable.Drawable;
|
||||
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.core.content.ContextCompat;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
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.bfqkz;
|
||||
import com.muqingbfq.databinding.FragmentWdBinding;
|
||||
import com.muqingbfq.databinding.ListGdBBinding;
|
||||
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.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
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, "开发中", ""}
|
||||
};
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
public static baseadapter adaper;
|
||||
|
||||
@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}, holder.textView);
|
||||
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(main.sp.getString("Cookie", "")).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) {
|
||||
new threadLogin(main.sp.getString("Cookie", ""));
|
||||
}
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
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();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public void sx() {
|
||||
adaper.list.clear();
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
try {
|
||||
File file = new File(wj.filesdri + "gd");
|
||||
File[] files = file.listFiles();
|
||||
for (File a : files) {
|
||||
String id = a.getName();
|
||||
if (id.endsWith(".json")) {
|
||||
continue;
|
||||
}
|
||||
String dqwb = wj.dqwb(a.toString());
|
||||
JSONObject jsonObject = new JSONObject(dqwb);
|
||||
String name = jsonObject.getString("name");
|
||||
String picUrl = jsonObject.getString("picUrl");
|
||||
String message = jsonObject.getString("message");
|
||||
adaper.list.add(new XM(id, name, message, picUrl));
|
||||
}
|
||||
main.handler.post(() -> {
|
||||
adaper.notifyDataSetChanged();
|
||||
if (adaper.list.isEmpty()) {
|
||||
binding.recyclerview2Text.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.recyclerview2Text.setVisibility(View.GONE);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
public static void addlist(XM xm) {
|
||||
if (adaper != null) {
|
||||
adaper.list.add(xm);
|
||||
main.handler.post(() -> adaper.notifyItemChanged(adaper.list.size() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
public static void removelist(XM xm) {
|
||||
if (adaper != null) {
|
||||
int i = adaper.list.indexOf(xm);
|
||||
adaper.list.remove(xm);
|
||||
adaper.notifyItemRemoved(i);
|
||||
}
|
||||
}
|
||||
|
||||
//登陆 获取用户信息 头像 昵称 签名
|
||||
class threadLogin extends Thread {
|
||||
String cookie;
|
||||
|
||||
public threadLogin(String cookie) {
|
||||
this.cookie = cookie;
|
||||
|
||||
}
|
||||
|
||||
public void run() {
|
||||
String hq = wl.hq("/user/account?cookie=" + 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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class baseadapter extends RecyclerView.Adapter<gd.VH> {
|
||||
public List<XM> list = new ArrayList<>();
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public gd.VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new gd.VH(ListGdBBinding.bind(LayoutInflater.from(getContext())
|
||||
.inflate(R.layout.list_gd_b, parent, false)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull gd.VH holder, int position) {
|
||||
XM xm = list.get(position);
|
||||
holder.itemView.setOnClickListener(v -> mp3.start(getActivity(), new String[]{xm.id, xm.name}, v));
|
||||
holder.itemView.setOnLongClickListener(v -> {
|
||||
String[] stringArray = {"更新歌单", "删除歌单"};
|
||||
new MaterialAlertDialogBuilder(getContext()).
|
||||
setItems(stringArray, (dialog, id) -> {
|
||||
switch (stringArray[id]) {
|
||||
case "更新歌单":
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
super.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);
|
||||
xm.name = fh.name;
|
||||
xm.picurl = fh.picurl;
|
||||
xm.message = fh.message;
|
||||
// json.put(fh.id, json);
|
||||
wj.xrwb(wj.gd + xm.id, json.toString());
|
||||
requireActivity().runOnUiThread(() -> notifyItemChanged(holder.getAdapterPosition()));
|
||||
} catch (JSONException e) {
|
||||
gj.sc("list gd onclick thear " + e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
break;
|
||||
case "删除歌单":
|
||||
// 删除项目
|
||||
try {
|
||||
wj.sc(wj.gd + xm.id);
|
||||
list.remove(xm);
|
||||
notifyItemRemoved(position);
|
||||
} catch (Exception e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// 在这里处理菜单项的点击事件
|
||||
}).show();
|
||||
return false;
|
||||
});
|
||||
|
||||
holder.bindingB.text2.setText(xm.message);
|
||||
holder.bindingB.text1.setText(xm.name);
|
||||
holder.bindingB.kg.setOnClickListener(view1 -> {
|
||||
ImageView tx = (ImageView) view1;
|
||||
new Thread() {
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
boolean an = playlist.hq(bfqkz.list, xm.id);
|
||||
if (bfqkz.ms == 2) {
|
||||
Collections.shuffle(bfqkz.list);
|
||||
}
|
||||
main.handler.post(() -> {
|
||||
if (an) {
|
||||
com.muqingbfq.bfq_an.xyq();
|
||||
tx.setImageResource(R.drawable.bf);
|
||||
com.muqingbfq.fragment.gd.gdid = xm.id;
|
||||
}
|
||||
notifyDataSetChanged();
|
||||
});
|
||||
}
|
||||
}.start();
|
||||
});
|
||||
Drawable color_kg = ContextCompat.getDrawable(getContext(), R.drawable.zt);
|
||||
if (xm.id.equals(com.muqingbfq.fragment.gd.gdid)) {
|
||||
color_kg = ContextCompat.getDrawable(getContext(), R.drawable.bf);
|
||||
}
|
||||
holder.bindingB.kg.setImageDrawable(color_kg);
|
||||
Glide.with(holder.itemView.getContext())
|
||||
.load(xm.picurl)
|
||||
.apply(new RequestOptions()
|
||||
.placeholder(R.drawable.ic_launcher_foreground)
|
||||
.error(R.drawable.ic_launcher_foreground))
|
||||
.into(holder.bindingB.image);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
if (list.isEmpty()) {
|
||||
binding.recyclerview2Text.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
binding.recyclerview2Text.setVisibility(View.GONE);
|
||||
}
|
||||
return list.size();
|
||||
}
|
||||
}
|
||||
}
|
239
app/src/main/java/com/muqingbfq/home.java
Normal file
239
app/src/main/java/com/muqingbfq/home.java
Normal file
|
@ -0,0 +1,239 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v4.media.MediaBrowserCompat;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.jaeger.library.StatusBarUtil;
|
||||
import com.muqingbfq.databinding.ActivityHomeBinding;
|
||||
import com.muqingbfq.fragment.gd_adapter;
|
||||
import com.muqingbfq.fragment.wode;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class home extends AppCompatActivity<ActivityHomeBinding> {
|
||||
public MediaBrowserCompat mBrowser;
|
||||
|
||||
@Override
|
||||
protected ActivityHomeBinding getViewBindingObject(LayoutInflater layoutInflater) {
|
||||
return ActivityHomeBinding.inflate(layoutInflater);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
setTheme(R.style.Theme_muqing);
|
||||
super.onCreate(savedInstanceState);
|
||||
wl.Cookie = main.sp.getString("Cookie", "");
|
||||
if (wl.Cookie.isEmpty()) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
String hq = wl.hq("/register/anonimous");
|
||||
try {
|
||||
JSONObject jsonObject = new JSONObject(hq);
|
||||
wl.setcookie(jsonObject.getString("cookie"));
|
||||
home.this.runOnUiThread(() -> UI());
|
||||
} catch (Exception e) {
|
||||
com.muqingbfq.mq.gj.sc(e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
} else {
|
||||
UI();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
public static ComponentName componentName;
|
||||
|
||||
|
||||
public void toolbar() {
|
||||
// binding.bar.setupWithDrawer
|
||||
setSupportActionBar(binding.toolbar);
|
||||
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
|
||||
this, binding.chct, binding.toolbar, R.string.app_name, R.string.app_name);
|
||||
binding.chct.addDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
binding.toolbar.setOnClickListener(v -> activity_search.start(home.this, v));
|
||||
}
|
||||
|
||||
public void UI() {
|
||||
StatusBarUtil.setTransparent(home.this);
|
||||
setContentView();
|
||||
toolbar();
|
||||
//初始化侧滑
|
||||
binding.chb.setNavigationItemSelectedListener(item -> {
|
||||
com.muqingbfq.fragment.sz.switch_sz(home.this, item.getItemId());
|
||||
return false;
|
||||
});
|
||||
//初始化播放器组件
|
||||
// 启动Service
|
||||
if (componentName == null) {
|
||||
componentName = new ComponentName(getApplicationContext(), bfqkz.class);
|
||||
mBrowser = new MediaBrowserCompat(
|
||||
getApplicationContext(), componentName
|
||||
,//绑定服务端
|
||||
browserConnectionCallback,//设置连接回调
|
||||
null
|
||||
);
|
||||
mBrowser.connect();
|
||||
}
|
||||
//检测更新
|
||||
new gj.jianchagengxin(home.this);
|
||||
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.setSaveEnabled(false);
|
||||
// 将 ViewPager2 绑定到 TabLayou
|
||||
binding.tablayout.setOnItemSelectedListener(item -> {
|
||||
int itemId = item.getItemId();
|
||||
if (itemId == R.id.a) {
|
||||
binding.viewPager.setCurrentItem(0);
|
||||
} else if (itemId == R.id.c) {
|
||||
binding.viewPager.setCurrentItem(1);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
binding.viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
switch (position) {
|
||||
case 0:
|
||||
binding.tablayout.setSelectedItemId(R.id.a);
|
||||
break;
|
||||
case 1:
|
||||
binding.tablayout.setSelectedItemId(R.id.c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
//在销毁 Activity 之前,系统会先调用 onDestroy()。系统调用此回调的原因如下:
|
||||
// 保存列表数据
|
||||
SharedPreferences sharedPreferences = getSharedPreferences("list", Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sharedPreferences.edit();
|
||||
String jsonList = new com.google.gson.Gson().toJson(bfqkz.list);
|
||||
editor.putString("listData", jsonList);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
moveTaskToBack(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 连接状态的回调接口,连接成功时会调用onConnected()方法
|
||||
*/
|
||||
private final MediaBrowserCompat.ConnectionCallback browserConnectionCallback =
|
||||
new MediaBrowserCompat.ConnectionCallback() {
|
||||
@Override
|
||||
public void onConnected() {
|
||||
//必须在确保连接成功的前提下执行订阅的操作
|
||||
if (mBrowser.isConnected()) {
|
||||
//mediaId即为MediaBrowserService.onGetRoot的返回值
|
||||
//若Service允许客户端连接,则返回结果不为null,其值为数据内容层次结构的根ID
|
||||
//若拒绝连接,则返回null
|
||||
String mediaId = mBrowser.getRoot();
|
||||
|
||||
//Browser通过订阅的方式向Service请求数据,发起订阅请求需要两个参数,其一为mediaId
|
||||
//而如果该mediaId已经被其他Browser实例订阅,则需要在订阅之前取消mediaId的订阅者
|
||||
//虽然订阅一个 已被订阅的mediaId 时会取代原Browser的订阅回调,但却无法触发onChildrenLoaded回调
|
||||
|
||||
//ps:虽然基本的概念是这样的,但是Google在官方demo中有这么一段注释...
|
||||
// This is temporary: A bug is being fixed that will make subscribe
|
||||
// consistently call onChildrenLoaded initially, no matter if it is replacing an existing
|
||||
// subscriber or not. Currently this only happens if the mediaID has no previous
|
||||
// subscriber or if the media content changes on the service side, so we need to
|
||||
// unsubscribe first.
|
||||
//大概的意思就是现在这里还有BUG,即只要发送订阅请求就会触发onChildrenLoaded回调
|
||||
//所以无论怎样我们发起订阅请求之前都需要先取消订阅
|
||||
mBrowser.unsubscribe(mediaId);
|
||||
//之前说到订阅的方法还需要一个参数,即设置订阅回调SubscriptionCallback
|
||||
//当Service获取数据后会将数据发送回来,此时会触发SubscriptionCallback.onChildrenLoaded回调
|
||||
mBrowser.subscribe(mediaId, browserSubscriptionCallback);
|
||||
}
|
||||
gj.sc("连接成功");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConnectionFailed() {
|
||||
gj.sc("连接失败!");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 向媒体服务器(MediaBrowserService)发起数据订阅请求的回调接口
|
||||
*/
|
||||
private final MediaBrowserCompat.SubscriptionCallback browserSubscriptionCallback =
|
||||
new MediaBrowserCompat.SubscriptionCallback() {
|
||||
@Override
|
||||
public void onChildrenLoaded(@NonNull String parentId,
|
||||
@NonNull List<MediaBrowserCompat.MediaItem> children) {
|
||||
gj.sc("onChildrenLoaded------");
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void finish() {
|
||||
super.finish();
|
||||
// 断开连接并释放资源
|
||||
if (mBrowser != null && mBrowser.isConnected()) {
|
||||
mBrowser.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
@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) {
|
||||
startActivity(new Intent(this, activity_search.class));
|
||||
}
|
||||
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 {
|
||||
}
|
218
app/src/main/java/com/muqingbfq/login/user_logs.java
Normal file
218
app/src/main/java/com/muqingbfq/login/user_logs.java
Normal file
|
@ -0,0 +1,218 @@
|
|||
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 androidx.annotation.NonNull;
|
||||
|
||||
import com.muqingbfq.databinding.ActivityUserLogsBinding;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.AppCompatActivity;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wl;
|
||||
|
||||
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(view -> new CloudUser());
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
//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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
finish(false);
|
||||
}
|
||||
|
||||
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");
|
||||
new user_message(nickname, signature, avatarUrl);
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
71
app/src/main/java/com/muqingbfq/login/user_message.java
Normal file
71
app/src/main/java/com/muqingbfq/login/user_message.java
Normal file
|
@ -0,0 +1,71 @@
|
|||
package com.muqingbfq.login;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.main;
|
||||
import com.muqingbfq.mq.gj;
|
||||
import com.muqingbfq.mq.wj;
|
||||
|
||||
public class user_message{
|
||||
public String name,qiangming, picurl;
|
||||
|
||||
public user_message() {
|
||||
wj.sc(wj.filesdri + "user.mq");
|
||||
}
|
||||
public user_message(String nickname, String signature, String avatarUrl) {
|
||||
name = nickname;
|
||||
qiangming = signature;
|
||||
picurl = avatarUrl;
|
||||
String s = new Gson().toJson(new user_logs.USER(name, qiangming, picurl));
|
||||
wj.xrwb(wj.filesdri + "user.mq", s);
|
||||
}
|
||||
|
||||
|
||||
public string get() throws Exception {
|
||||
/* JSONObject post = wl.jsonpost("/php/user.php?action=getSpaceInfo",
|
||||
new String[]{
|
||||
"account"
|
||||
},
|
||||
new String[]{
|
||||
main.account
|
||||
});
|
||||
gj.sc(post);
|
||||
if (!TextUtils.isEmpty(post.toString()) &&
|
||||
post.getInt("code") == 0) {
|
||||
JSONObject data = post.getJSONObject("data");
|
||||
String headIcon = data.getString("headIcon");//头像
|
||||
String account = data.getString("account");//账号
|
||||
String userName = data.getString("userName");//名称
|
||||
String introduce = data.getString("introduce");//签名
|
||||
String cover = data.getString("cover");//背景
|
||||
if (headIcon.startsWith("..")) {
|
||||
headIcon = "https://rust.coldmint.top" + headIcon.substring(2);
|
||||
}
|
||||
if (cover.startsWith("..")) {
|
||||
cover = "https://rust.coldmint.top" + cover.substring(2);
|
||||
}
|
||||
String gender = data.getString("gender");*/
|
||||
return new string(new String[]{
|
||||
name, qiangming, picurl
|
||||
});
|
||||
}
|
||||
|
||||
public static class string {
|
||||
private final String[] strings;
|
||||
public string(String[] strings) {
|
||||
this.strings = strings;
|
||||
}
|
||||
|
||||
public String picurl() {
|
||||
return strings[2];
|
||||
}
|
||||
|
||||
public String qianming() {
|
||||
return strings[1];
|
||||
}
|
||||
|
||||
public String userName() {
|
||||
return strings[0];
|
||||
}
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
||||
}
|
169
app/src/main/java/com/muqingbfq/main.java
Normal file
169
app/src/main/java/com/muqingbfq/main.java
Normal file
|
@ -0,0 +1,169 @@
|
|||
package com.muqingbfq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Activity;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
|
||||
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;
|
||||
private int count = 0;
|
||||
@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();
|
||||
if (bfqkz.xm != null) {
|
||||
new Thread() {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
MP3 hq = com.muqingbfq.api.url.hq(bfqkz.xm);
|
||||
if (hq == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
bfqkz.mt.DataSource(hq);
|
||||
} catch (Exception e) {
|
||||
com.muqingbfq.mq.gj.sc(e);
|
||||
}
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
|
||||
@Override
|
||||
public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {
|
||||
}
|
||||
@Override
|
||||
public void onActivityStarted(@NonNull Activity activity) {
|
||||
if (count == 0) { //后台切换到前台
|
||||
if (FloatingLyricsService.lei != null) {
|
||||
stopService(new Intent(main.this, FloatingLyricsService.class));
|
||||
}
|
||||
}
|
||||
count++;
|
||||
}
|
||||
@Override
|
||||
public void onActivityResumed(@NonNull Activity activity) {
|
||||
}
|
||||
@Override
|
||||
public void onActivityPaused(@NonNull Activity activity) {
|
||||
}
|
||||
@Override
|
||||
public void onActivityStopped(@NonNull Activity activity) {
|
||||
count--;
|
||||
if (count == 0) { //后台切换到前台
|
||||
new Thread(){
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
gj.sc(e);
|
||||
}
|
||||
if (count != 0) {
|
||||
return;
|
||||
}
|
||||
if (!FloatingLyricsService.get()) {
|
||||
return;
|
||||
}
|
||||
if (Settings.canDrawOverlays(main.this)) {
|
||||
startService(new Intent(main.this, FloatingLyricsService.class));
|
||||
}
|
||||
super.run();
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
|
||||
}
|
||||
@Override
|
||||
public void onActivityDestroyed(@NonNull Activity activity) {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
25
app/src/main/java/com/muqingbfq/mq/AppCompatActivity.java
Normal file
25
app/src/main/java/com/muqingbfq/mq/AppCompatActivity.java
Normal file
|
@ -0,0 +1,25 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
import com.jaeger.library.StatusBarUtil;
|
||||
|
||||
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() {
|
||||
super.setContentView(getViewBinding().getRoot());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.media.AudioManager;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
|
||||
import com.muqingbfq.MyButtonClickReceiver;
|
||||
|
||||
public class BluetoothMusicController {
|
||||
private Context context;
|
||||
private MediaSessionCompat mediaSession;
|
||||
|
||||
public BluetoothMusicController(Context context) {
|
||||
this.context = context;
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(BluetoothAdapter.ACTION_CONNECTION_STATE_CHANGED);
|
||||
intentFilter.addAction("android.intent.action.HEADSET_PLUG");
|
||||
context.registerReceiver(new MyButtonClickReceiver(), intentFilter);
|
||||
registerHeadsetReceiver();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
unregisterHeadsetReceiver();
|
||||
}
|
||||
|
||||
// 注册媒体按钮事件接收器
|
||||
@SuppressLint("ObsoleteSdkInt")
|
||||
public void registerHeadsetReceiver() {
|
||||
AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
ComponentName name = new ComponentName(context.getPackageName(), MyButtonClickReceiver.class.getName());
|
||||
audioManager.registerMediaButtonEventReceiver(name);
|
||||
}
|
||||
|
||||
// 注销媒体按钮事件接收器
|
||||
public void unregisterHeadsetReceiver() {
|
||||
if (mediaSession != null) {
|
||||
mediaSession.setActive(false);
|
||||
mediaSession.release();
|
||||
mediaSession = null;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.renderscript.Allocation;
|
||||
import android.renderscript.Element;
|
||||
import android.renderscript.RenderScript;
|
||||
import android.renderscript.ScriptIntrinsicBlur;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
|
||||
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
|
||||
public class BlurGradientTransformation extends BitmapTransformation {
|
||||
private static final String TAG = "BlurGradientTrans";
|
||||
private RenderScript mRenderScript;
|
||||
private ScriptIntrinsicBlur mScriptIntrinsicBlur;
|
||||
private float mRadius = 25f;
|
||||
private int[] mGradientColors = new int[]{0x66000000, 0xcc000000};
|
||||
private float[] mGradientPositions = new float[]{0f, 1f};
|
||||
|
||||
public BlurGradientTransformation(Context context) {
|
||||
super();
|
||||
mRenderScript = RenderScript.create(context);
|
||||
mScriptIntrinsicBlur = ScriptIntrinsicBlur.create(mRenderScript, Element.U8_4(mRenderScript));
|
||||
}
|
||||
|
||||
public BlurGradientTransformation(Context context, float radius) {
|
||||
this(context);
|
||||
mRadius = radius;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Bitmap transform(@NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) {
|
||||
Bitmap source = pool.get(toTransform.getWidth(), toTransform.getHeight(), toTransform.getConfig());
|
||||
if (source == null) {
|
||||
source = Bitmap.createBitmap(toTransform.getWidth(), toTransform.getHeight(), toTransform.getConfig());
|
||||
}
|
||||
Canvas canvas = new Canvas(source);
|
||||
Paint paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setShader(new BitmapShader(toTransform, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
|
||||
canvas.drawRoundRect(new RectF(0, 0, toTransform.getWidth(), toTransform.getHeight()), 30f, 30f, paint);
|
||||
Allocation input = Allocation.createFromBitmap(mRenderScript, source, Allocation.MipmapControl.MIPMAP_NONE, Allocation.USAGE_SCRIPT);
|
||||
Allocation output = Allocation.createTyped(mRenderScript, input.getType());
|
||||
mScriptIntrinsicBlur.setInput(input);
|
||||
mScriptIntrinsicBlur.setRadius(mRadius);
|
||||
mScriptIntrinsicBlur.forEach(output);
|
||||
output.copyTo(source);
|
||||
|
||||
// 绘制渐变色
|
||||
Canvas canvas1 = new Canvas(source);
|
||||
Paint paint1 = new Paint();
|
||||
paint1.setAntiAlias(true);
|
||||
paint1.setShader(new android.graphics.LinearGradient(0, 0, 0, source.getHeight(), mGradientColors, mGradientPositions, Shader.TileMode.CLAMP));
|
||||
paint1.setXfermode(null);
|
||||
canvas1.drawRect(new Rect(0, 0, source.getWidth(), source.getHeight()), paint1);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) {
|
||||
messageDigest.update("BlurGradientTransformation".getBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return BlurGradientTransformation.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj instanceof BlurGradientTransformation;
|
||||
}
|
||||
|
||||
public BlurGradientTransformation setRadius(float radius) {
|
||||
mRadius = radius;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlurGradientTransformation setGradientColors(int[] colors) {
|
||||
mGradientColors = colors;
|
||||
return this;
|
||||
}
|
||||
|
||||
public BlurGradientTransformation setGradientPositions(float[] positions) {
|
||||
mGradientPositions = positions;
|
||||
return this;
|
||||
}
|
||||
}
|
167
app/src/main/java/com/muqingbfq/mq/EditViewDialog.java
Normal file
167
app/src/main/java/com/muqingbfq/mq/EditViewDialog.java
Normal file
|
@ -0,0 +1,167 @@
|
|||
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());
|
||||
}
|
||||
|
||||
public void setNegative(View.OnClickListener a) {
|
||||
buttona.setOnClickListener(a);
|
||||
}
|
||||
|
||||
public EditViewDialog setPositive(View.OnClickListener a) {
|
||||
buttonb.setOnClickListener(a);
|
||||
return this;
|
||||
}
|
||||
|
||||
public interface OnClickListener extends View.OnClickListener {
|
||||
@Override
|
||||
void onClick(View view);
|
||||
}
|
||||
}
|
257
app/src/main/java/com/muqingbfq/mq/FloatingLyricsService.java
Normal file
257
app/src/main/java/com/muqingbfq/mq/FloatingLyricsService.java
Normal file
|
@ -0,0 +1,257 @@
|
|||
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 android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.dirror.lyricviewx.LyricEntry;
|
||||
import com.dirror.lyricviewx.LyricViewX;
|
||||
import com.google.android.material.card.MaterialCardView;
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.databinding.FloatLrcviewBinding;
|
||||
import com.muqingbfq.main;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class FloatingLyricsService extends Service implements View.OnClickListener, View.OnTouchListener {
|
||||
private WindowManager windowManager;
|
||||
FloatLrcviewBinding binding;
|
||||
|
||||
public Runnable updateSeekBar = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int index = 0;
|
||||
for (int i = 0; i < LyricViewX.lyricEntryList.size(); i++) {
|
||||
LyricEntry lineLrc = LyricViewX.lyricEntryList.get(i);
|
||||
if (lineLrc.time <= bfqkz.mt.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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
lei = this;
|
||||
File file = new File(wj.filesdri + "FloatingLyricsService.json");
|
||||
try {
|
||||
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);
|
||||
}
|
||||
// 创建悬浮窗歌词的 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.setOnClickListener(this);
|
||||
// params.gravity = Gravity.CENTER;
|
||||
|
||||
|
||||
// 获取 WindowManager 并将悬浮窗歌词添加到窗口中
|
||||
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
windowManager.addView(binding.getRoot(), params);
|
||||
if (setup.i == 2) {
|
||||
setyc();
|
||||
|
||||
} else {
|
||||
show();
|
||||
}
|
||||
/*
|
||||
if (setup.i == 0)
|
||||
setup.i = 1;
|
||||
baocun();*/
|
||||
handler.post(updateSeekBar); // 在播放开始时启动更新进度
|
||||
} catch (Exception e) {
|
||||
wj.sc(file.toString());
|
||||
gj.sc(getClass() + ":" + e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
// 在 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));
|
||||
}
|
||||
|
||||
private int initialY;
|
||||
private float initialTouchY;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouch(View view, MotionEvent motionEvent) {
|
||||
switch (motionEvent.getAction()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
// 记录触摸事件的初始位置和坐标
|
||||
initialY = params.y;
|
||||
initialTouchY = motionEvent.getRawY();
|
||||
return true;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
// 计算触摸事件的偏移量,将悬浮窗口的位置设置为初始位置加上偏移量
|
||||
int offsetY = (int) (motionEvent.getRawY() - initialTouchY);
|
||||
setup.Y = initialY + offsetY;
|
||||
params.y = setup.Y;
|
||||
windowManager.updateViewLayout(binding.getRoot(), params);
|
||||
return true;
|
||||
case MotionEvent.ACTION_UP:
|
||||
baocun();
|
||||
break;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
int id = view.getId();
|
||||
if (id == R.id.kg) {
|
||||
ImageView kg = (ImageView) view;
|
||||
if (bfqkz.mt == null) {
|
||||
return;
|
||||
}
|
||||
if (bfqkz.mt.isPlaying()) {
|
||||
bfqkz.mt.pause();
|
||||
kg.setImageResource(R.drawable.zt);
|
||||
} else {
|
||||
bfqkz.mt.start();
|
||||
kg.setImageResource(R.drawable.bf);
|
||||
}
|
||||
} else if (id == R.id.lock) {
|
||||
setyc();
|
||||
}
|
||||
}
|
||||
|
||||
public void setyc() {
|
||||
setup.i = 2;
|
||||
params.flags = lock();
|
||||
binding.lock.setVisibility(View.GONE);
|
||||
windowManager.updateViewLayout(binding.getRoot(), params);
|
||||
baocun();
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
public void show() {
|
||||
setup.i = 1;
|
||||
params.flags = lock();
|
||||
binding.lock.setVisibility(View.VISIBLE);
|
||||
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);
|
||||
}
|
53
app/src/main/java/com/muqingbfq/mq/FragmentActivity.java
Normal file
53
app/src/main/java/com/muqingbfq/mq/FragmentActivity.java
Normal file
|
@ -0,0 +1,53 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import androidx.viewbinding.ViewBinding;
|
||||
|
||||
import com.jaeger.library.StatusBarUtil;
|
||||
import com.muqingbfq.R;
|
||||
|
||||
public abstract class FragmentActivity<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;
|
||||
@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(int view) {
|
||||
super.setContentView(view);
|
||||
}
|
||||
public void setContentView() {
|
||||
binding = getViewBindingObject(getLayoutInflater());
|
||||
setContentView(binding.getRoot());
|
||||
setToolbar();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setContentView(View view) {
|
||||
super.setContentView(view);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,176 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Build;
|
||||
import android.support.v4.media.MediaMetadataCompat;
|
||||
|
||||
import androidx.core.app.ActivityCompat;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.muqingbfq.MyButtonClickReceiver;
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.bfqkz;
|
||||
import com.muqingbfq.yc;
|
||||
|
||||
public class NotificationManagerCompat {
|
||||
bfqkz context;
|
||||
public NotificationCompat.Builder notificationBuilder;
|
||||
public androidx.core.app.NotificationManagerCompat notificationManager;
|
||||
private String name, zz;
|
||||
|
||||
public NotificationManagerCompat(bfqkz context) {
|
||||
this.context = context;
|
||||
name = context.getString(R.string.app_name);
|
||||
zz = context.getString(R.string.zz);
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name,
|
||||
NotificationManager.IMPORTANCE_LOW);
|
||||
channel.setDescription(zz);
|
||||
NotificationManager systemService = context.getSystemService(NotificationManager.class);
|
||||
systemService.createNotificationChannel(channel);
|
||||
if (!systemService.areNotificationsEnabled()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// 适配12.0及以上
|
||||
|
||||
// 设置启动的程序,如果存在则找出,否则新的启动
|
||||
Intent my = new Intent(context, MyButtonClickReceiver.class);
|
||||
pendingIntent_kg = getBroadcast(context, my.
|
||||
setAction("kg"));
|
||||
pendingIntent_syq = getBroadcast(context, my.
|
||||
setAction("syq"));
|
||||
pendingIntent_xyq = getBroadcast(context, my.
|
||||
setAction("xyq"));
|
||||
pendingIntent_lrc = getBroadcast(context, my.
|
||||
setAction("lrc"));
|
||||
pendingIntent_like = getBroadcast(context, my.
|
||||
setAction("like"));
|
||||
style = new androidx.media.app.NotificationCompat.MediaStyle()
|
||||
.setShowActionsInCompactView(1, 2, 3)
|
||||
.setMediaSession(context.mSession.getSessionToken());
|
||||
notificationManager = androidx.core.app.NotificationManagerCompat.from(context);
|
||||
notificationBuilder = getNotificationBuilder(context)
|
||||
.setSmallIcon(R.drawable.foreground)
|
||||
.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||
.setOngoing(true).setColorized(true).setShowWhen(false)
|
||||
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
|
||||
.setContentIntent(context.pendingIntent)
|
||||
.setStyle(style);
|
||||
tzl();
|
||||
} catch (Exception e) {
|
||||
yc.start(context, e);
|
||||
}
|
||||
}
|
||||
|
||||
androidx.media.app.NotificationCompat.MediaStyle style;
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
public void tzl_button() {
|
||||
if (notificationBuilder == null) {
|
||||
return;
|
||||
}
|
||||
notificationBuilder.mActions.clear();
|
||||
notificationBuilder
|
||||
.addAction(R.drawable.like, "like", pendingIntent_like) // #0
|
||||
.addAction(R.drawable.syq, "syq", pendingIntent_syq) // #0
|
||||
.addAction(bfqkz.mt.isPlaying() ? R.drawable.bf : R.drawable.zt
|
||||
, "kg", pendingIntent_kg) // #1
|
||||
.addAction(R.drawable.xyq, "xyq", pendingIntent_xyq)
|
||||
.addAction(R.drawable.lock, "lrc", pendingIntent_lrc)
|
||||
.setOngoing(bfqkz.mt.isPlaying());
|
||||
notificationManager_notify();
|
||||
}
|
||||
|
||||
public void setbitmap(Bitmap bitmap) {
|
||||
notificationBuilder
|
||||
.setLargeIcon(bitmap);
|
||||
|
||||
context.builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, name)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, zz)
|
||||
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, zz)
|
||||
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
|
||||
.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, 100);
|
||||
|
||||
context.mSession.setMetadata(context.builder.build());
|
||||
notificationManager_notify();
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
public void tzl() {
|
||||
if (notificationBuilder == null) {
|
||||
return;
|
||||
}
|
||||
if (bfqkz.xm != null) {
|
||||
name = bfqkz.xm.name;
|
||||
zz = bfqkz.xm.zz;
|
||||
}
|
||||
notificationBuilder.mActions.clear();
|
||||
notificationBuilder
|
||||
.addAction(R.drawable.like, "like", pendingIntent_like) // #0
|
||||
.addAction(R.drawable.syq, "syq", pendingIntent_syq) // #0
|
||||
.addAction(bfqkz.mt.isPlaying() ? R.drawable.bf : R.drawable.zt
|
||||
, "kg", pendingIntent_kg) // #1
|
||||
.addAction(R.drawable.xyq, "xyq", pendingIntent_xyq)
|
||||
.addAction(R.drawable.lock, "lrc", pendingIntent_lrc)
|
||||
.setContentTitle(name)
|
||||
.setContentText(zz)
|
||||
.setOngoing(bfqkz.mt.isPlaying());
|
||||
|
||||
|
||||
context.mSession.setMetadata(context.builder.build());
|
||||
notificationManager_notify();
|
||||
}
|
||||
|
||||
private PendingIntent pendingIntent_kg,
|
||||
pendingIntent_syq,
|
||||
pendingIntent_xyq,
|
||||
pendingIntent_lrc,
|
||||
pendingIntent_like;
|
||||
|
||||
private final String CHANNEL_ID = "MediaSessionCompat";
|
||||
|
||||
public void notificationManager_notify() {
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
|
||||
return;
|
||||
}
|
||||
notificationManager.notify(0, notificationBuilder.build());
|
||||
}
|
||||
|
||||
private NotificationCompat.Builder getNotificationBuilder(Context context) {
|
||||
// 适用于Android 8.0及以上版本
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
|
||||
return new NotificationCompat.Builder(context, CHANNEL_ID);
|
||||
}
|
||||
|
||||
private PendingIntent getBroadcast(Context context, Intent intent) {
|
||||
int flag;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
flag = PendingIntent.FLAG_IMMUTABLE;
|
||||
} else {
|
||||
flag = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
}
|
||||
return PendingIntent.getBroadcast(context, 0, intent, flag);
|
||||
}
|
||||
|
||||
|
||||
public static PendingIntent getActivity(Context context, Intent intent) {
|
||||
int flag;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
flag = PendingIntent.FLAG_IMMUTABLE;
|
||||
} else {
|
||||
flag = PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
}
|
||||
return PendingIntent.getActivity(context, 0, intent, flag);
|
||||
}
|
||||
|
||||
}
|
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;
|
||||
}
|
||||
}
|
172
app/src/main/java/com/muqingbfq/mq/floating.java
Normal file
172
app/src/main/java/com/muqingbfq/mq/floating.java
Normal file
|
@ -0,0 +1,172 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.Service;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.os.Build;
|
||||
import android.os.IBinder;
|
||||
import android.provider.Settings;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.muqingbfq.R;
|
||||
import com.muqingbfq.main;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class floating extends Service {
|
||||
private static RecyclerView.Adapter<ViewHolder> lbspq;
|
||||
|
||||
class spq extends RecyclerView.Adapter<ViewHolder> {
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
return new ViewHolder(new TextView(parent.getContext()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
String s = list.get(position);
|
||||
holder.textView.setText(s);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return list.size();
|
||||
}
|
||||
}
|
||||
|
||||
public static void start(Context context) {
|
||||
if (Settings.canDrawOverlays(context)) {
|
||||
context.startService(new Intent(context, floating.class));
|
||||
}
|
||||
}
|
||||
|
||||
public static void end(Context context) {
|
||||
Intent serviceIntent = new Intent(context, floating.class);
|
||||
context.stopService(serviceIntent);
|
||||
}
|
||||
|
||||
public static List<String> list;
|
||||
private WindowManager windowManager;
|
||||
private View view;
|
||||
private View image, layout;
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
public static void addtext(String str) {
|
||||
if (lbspq == null || list == null) {
|
||||
return;
|
||||
}
|
||||
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss", Locale.CHINA); // 创建一个 SimpleDateFormat 对象,指定时间格式
|
||||
String formattedDate = sdf.format(new Date()); // 格式化当前时间
|
||||
list.add(0, formattedDate + ": " + str);
|
||||
main.handler.post(lbspq::notifyDataSetChanged);
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
list = new ArrayList<>();
|
||||
lbspq = new spq();
|
||||
view = LayoutInflater.from(this).inflate(R.layout.floating_sc, null);
|
||||
layout = view.findViewById(R.id.view1);
|
||||
ViewGroup.LayoutParams layoutParams = layout.getLayoutParams();
|
||||
DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
|
||||
layoutParams.height = displayMetrics.heightPixels - displayMetrics.heightPixels / 2 / 2;
|
||||
layoutParams.width = displayMetrics.widthPixels - displayMetrics.widthPixels / 2 / 2;
|
||||
layout.setLayoutParams(layoutParams);
|
||||
layout.setVisibility(View.GONE);
|
||||
image = view.findViewById(R.id.image);
|
||||
image.setOnClickListener(vw -> {
|
||||
layout.setVisibility(View.VISIBLE);
|
||||
vw.setVisibility(View.GONE);
|
||||
});
|
||||
view.findViewById(R.id.text4).setOnClickListener(view -> {
|
||||
layout.setVisibility(View.GONE);
|
||||
image.setVisibility(View.VISIBLE);
|
||||
});
|
||||
RecyclerView recyclerView = view.findViewById(R.id.list);
|
||||
recyclerView.setAdapter(lbspq);
|
||||
addtext("Android stdio 2022.3.1版-调试器");
|
||||
|
||||
//清空按钮
|
||||
view.findViewById(R.id.text1).setOnClickListener(view -> {
|
||||
list.clear();
|
||||
lbspq.notifyDataSetChanged();
|
||||
});
|
||||
//复制按钮
|
||||
view.findViewById(R.id.text2).setOnClickListener(view -> {
|
||||
// 获取剪贴板管理器
|
||||
ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("label", list.get(0)));
|
||||
gj.ts(this,"成功复制了第一个数据");
|
||||
});
|
||||
//关闭按钮
|
||||
view.findViewById(R.id.text3).setOnClickListener(view -> stopSelf());
|
||||
int i = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
|
||||
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
WindowManager.LayoutParams params = new WindowManager.LayoutParams(
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
WindowManager.LayoutParams.WRAP_CONTENT,
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ?
|
||||
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY :
|
||||
WindowManager.LayoutParams.TYPE_PHONE,
|
||||
i,
|
||||
PixelFormat.TRANSLUCENT
|
||||
);
|
||||
params.x = -displayMetrics.widthPixels;
|
||||
|
||||
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
||||
windowManager.addView(view, params);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (view != null && windowManager != null) {
|
||||
lbspq = null;
|
||||
list = null;
|
||||
windowManager.removeView(view);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public TextView textView;
|
||||
|
||||
public ViewHolder(View itemview) {
|
||||
super(itemview);
|
||||
textView = (TextView) itemview;
|
||||
textView.setTextColor(Color.WHITE);
|
||||
textView.setOnLongClickListener(view -> {
|
||||
// 获取剪贴板管理器
|
||||
ClipboardManager clipboard = (ClipboardManager) view.getContext().
|
||||
getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
clipboard.setPrimaryClip(ClipData.newPlainText("label", list.get(0)));
|
||||
gj.ts(view.getContext(), "复制成功");
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
200
app/src/main/java/com/muqingbfq/mq/gj.java
Normal file
200
app/src/main/java/com/muqingbfq/mq/gj.java
Normal file
|
@ -0,0 +1,200 @@
|
|||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
floating.addtext(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 class jianchagengxin extends Thread {
|
||||
Context context;
|
||||
|
||||
public jianchagengxin(Context context) {
|
||||
this.context = context;
|
||||
if (!wj.cz(wj.filesdri + "gx.mq")) {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
jianchagengxin(context);
|
||||
}
|
||||
}
|
||||
|
||||
public static int jianchagengxin(Context context) {
|
||||
try {
|
||||
String versionName = context.getPackageManager()
|
||||
.getPackageInfo(context.getPackageName(), 0).versionName;
|
||||
String hq = wl.get(main.http + "/muqingbfq?bb=" + versionName);
|
||||
final JSONObject jsonObject = new JSONObject(hq);
|
||||
boolean code = jsonObject.getInt("code") == 1;
|
||||
String msg = jsonObject.getString("msg");
|
||||
if (code) {
|
||||
String url = jsonObject.getString("url");
|
||||
String bb = jsonObject.getString("bb");
|
||||
main.handler.post(() -> new MaterialAlertDialogBuilder(context)
|
||||
.setTitle("更新" + bb)
|
||||
.setMessage(msg)
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("更新", (dialogInterface, i) -> context.startActivity(new Intent(Intent.ACTION_VIEW,
|
||||
Uri.parse(url))))
|
||||
.show());
|
||||
}
|
||||
//1表示需要更新
|
||||
return code ? 1 : 0;
|
||||
} catch (Exception e) {
|
||||
sc(e);
|
||||
}
|
||||
return 400;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
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;
|
||||
}
|
||||
}
|
107
app/src/main/java/com/muqingbfq/mq/wl.java
Normal file
107
app/src/main/java/com/muqingbfq/mq/wl.java
Normal file
|
@ -0,0 +1,107 @@
|
|||
package com.muqingbfq.mq;
|
||||
|
||||
|
||||
import com.muqingbfq.XM;
|
||||
import com.muqingbfq.main;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
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 JSONObject jsonpost(String str, String[][] a) {
|
||||
try {
|
||||
return new JSONObject(post(str, a));
|
||||
} catch (JSONException 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;
|
||||
}
|
||||
|
||||
public static class xz extends Thread {
|
||||
String url;
|
||||
XM x;
|
||||
|
||||
public xz(String url, XM x) {
|
||||
this.url = url;
|
||||
this.x = x;
|
||||
start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
261
app/src/main/java/com/muqingbfq/sz.java
Normal file
261
app/src/main/java/com/muqingbfq/sz.java
Normal file
|
@ -0,0 +1,261 @@
|
|||
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.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.fragment.app.Fragment;
|
||||
|
||||
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();
|
||||
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;
|
||||
boolean is = true;
|
||||
ActivityResultLauncher<Intent> LyricsService =
|
||||
registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
|
||||
if (Settings.canDrawOverlays(getContext())) {
|
||||
getContext().startService(new Intent(getContext(), FloatingLyricsService.class));
|
||||
} else {
|
||||
binding.switchA3.setChecked(false);
|
||||
binding.slide1.setEnabled(true);
|
||||
|
||||
}
|
||||
});
|
||||
FloatingLyricsService.SETUP setup;
|
||||
|
||||
class ThreadLrc extends Thread {
|
||||
@Override
|
||||
public void run() {
|
||||
super.run();
|
||||
while (is) {
|
||||
int index = 0;
|
||||
for (int i = 0; i < LyricViewX.lyricEntryList.size(); i++) {
|
||||
LyricEntry lineLrc = LyricViewX.lyricEntryList.get(i);
|
||||
if (lineLrc.time <= bfqkz.mt.getCurrentPosition()) {
|
||||
index = i;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < LyricViewX.lyricEntryList.size()) {
|
||||
LyricEntry currentLrc = LyricViewX.lyricEntryList.get(index);
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
if (currentLrc.secondText != null) {
|
||||
binding.lrcViewMessage.setText(currentLrc.secondText);
|
||||
} else {
|
||||
binding.lrcViewMessage.setText("");
|
||||
}
|
||||
binding.lrcView.setText(currentLrc.text);
|
||||
});
|
||||
}
|
||||
gj.sc("ThreadLrc");
|
||||
try {
|
||||
sleep(1000);
|
||||
} catch (InterruptedException e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
binding = ActivitySzSetlrcBinding.inflate(inflater, container, false);
|
||||
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);
|
||||
}
|
||||
new ThreadLrc().start();
|
||||
}
|
||||
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.slide1.setLabelFormatter(value -> String.valueOf((int) value));
|
||||
binding.slide1.addOnChangeListener(this);
|
||||
binding.slide1.addOnChangeListener(this);
|
||||
binding.slide1.addOnSliderTouchListener(this);
|
||||
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
is = false;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
|
||||
}
|
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;
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user