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