Pq преди 1 година
родител
ревизия
c0e5810b42
променени са 71 файла, в които са добавени 5730 реда и са изтрити 0 реда
  1. 6 0
      BaseLibrary/src/main/java/com/cooleshow/base/router/RouterPath.kt
  2. 1 0
      accompany/.gitignore
  3. 69 0
      accompany/build.gradle
  4. 0 0
      accompany/consumer-rules.pro
  5. 21 0
      accompany/proguard-rules.pro
  6. 26 0
      accompany/src/androidTest/java/com/daya/orchestra/accompany/ExampleInstrumentedTest.java
  7. 9 0
      accompany/src/main/AndroidManifest.xml
  8. BIN
      accompany/src/main/assets/lottie/accompany/images/img_0.png
  9. BIN
      accompany/src/main/assets/lottie/accompany/images/img_1.png
  10. BIN
      accompany/src/main/assets/lottie/accompany/images/img_10.png
  11. BIN
      accompany/src/main/assets/lottie/accompany/images/img_11.png
  12. BIN
      accompany/src/main/assets/lottie/accompany/images/img_12.png
  13. BIN
      accompany/src/main/assets/lottie/accompany/images/img_13.png
  14. BIN
      accompany/src/main/assets/lottie/accompany/images/img_14.png
  15. BIN
      accompany/src/main/assets/lottie/accompany/images/img_15.png
  16. BIN
      accompany/src/main/assets/lottie/accompany/images/img_16.png
  17. BIN
      accompany/src/main/assets/lottie/accompany/images/img_17.png
  18. BIN
      accompany/src/main/assets/lottie/accompany/images/img_18.png
  19. BIN
      accompany/src/main/assets/lottie/accompany/images/img_19.png
  20. BIN
      accompany/src/main/assets/lottie/accompany/images/img_2.png
  21. BIN
      accompany/src/main/assets/lottie/accompany/images/img_20.png
  22. BIN
      accompany/src/main/assets/lottie/accompany/images/img_21.png
  23. BIN
      accompany/src/main/assets/lottie/accompany/images/img_22.png
  24. BIN
      accompany/src/main/assets/lottie/accompany/images/img_23.png
  25. BIN
      accompany/src/main/assets/lottie/accompany/images/img_24.png
  26. BIN
      accompany/src/main/assets/lottie/accompany/images/img_25.png
  27. BIN
      accompany/src/main/assets/lottie/accompany/images/img_26.png
  28. BIN
      accompany/src/main/assets/lottie/accompany/images/img_27.png
  29. BIN
      accompany/src/main/assets/lottie/accompany/images/img_28.png
  30. BIN
      accompany/src/main/assets/lottie/accompany/images/img_29.png
  31. BIN
      accompany/src/main/assets/lottie/accompany/images/img_3.png
  32. BIN
      accompany/src/main/assets/lottie/accompany/images/img_4.png
  33. BIN
      accompany/src/main/assets/lottie/accompany/images/img_5.png
  34. BIN
      accompany/src/main/assets/lottie/accompany/images/img_6.png
  35. BIN
      accompany/src/main/assets/lottie/accompany/images/img_7.png
  36. BIN
      accompany/src/main/assets/lottie/accompany/images/img_8.png
  37. BIN
      accompany/src/main/assets/lottie/accompany/images/img_9.png
  38. 35 0
      accompany/src/main/java/com/daya/orchestra/accompany/api/DownloadApi.java
  39. 0 0
      accompany/src/main/java/com/daya/orchestra/accompany/bean/alipay/AuthResult.java
  40. 1 0
      accompany/src/main/java/com/daya/orchestra/accompany/bean/alipay/PayResult.java
  41. 74 0
      accompany/src/main/java/com/daya/orchestra/accompany/bean/weixinpay/WeixinPayInfo.java
  42. 10 0
      accompany/src/main/java/com/daya/orchestra/accompany/callback/ResultCallback.java
  43. 17 0
      accompany/src/main/java/com/daya/orchestra/accompany/contract/AccompanyContract.java
  44. 248 0
      accompany/src/main/java/com/daya/orchestra/accompany/helper/AccompanyHelper.java
  45. 543 0
      accompany/src/main/java/com/daya/orchestra/accompany/helper/AccompanyPlayHelper.java
  46. 22 0
      accompany/src/main/java/com/daya/orchestra/accompany/helper/DeviceDelayCheckHelper.java
  47. 199 0
      accompany/src/main/java/com/daya/orchestra/accompany/helper/ShareHelper.java
  48. 754 0
      accompany/src/main/java/com/daya/orchestra/accompany/js/JsInterfaceAccomPanyUtils.java
  49. 164 0
      accompany/src/main/java/com/daya/orchestra/accompany/player/CustomPlayer.java
  50. 14 0
      accompany/src/main/java/com/daya/orchestra/accompany/presenter/AccompanyPresenter.java
  51. 630 0
      accompany/src/main/java/com/daya/orchestra/accompany/web/AccompanyActivity.java
  52. 2569 0
      accompany/src/main/java/com/daya/orchestra/accompany/web/AccompanyFragment.java
  53. BIN
      accompany/src/main/res/drawable-xhdpi/bg_accompany_loading.png
  54. BIN
      accompany/src/main/res/drawable-xhdpi/ic_accompany_record.png
  55. BIN
      accompany/src/main/res/drawable-xxhdpi/bg_accompany_loading.png
  56. BIN
      accompany/src/main/res/drawable-xxhdpi/flash_off.png
  57. BIN
      accompany/src/main/res/drawable-xxhdpi/ic_accompany_back.png
  58. BIN
      accompany/src/main/res/drawable-xxhdpi/ic_accompany_video_record_save.png
  59. BIN
      accompany/src/main/res/drawable-xxhdpi/ic_video_record_play.png
  60. BIN
      accompany/src/main/res/drawable-xxhdpi/switch_camera.png
  61. 12 0
      accompany/src/main/res/drawable/progress_bar_ff8057_4dp.xml
  62. 12 0
      accompany/src/main/res/drawable/progress_bar_ffffff_4dp.xml
  63. 24 0
      accompany/src/main/res/drawable/shape_accompany_loading_progress_drawable.xml
  64. 19 0
      accompany/src/main/res/layout/activity_accompany.xml
  65. 72 0
      accompany/src/main/res/layout/fragment_accompany.xml
  66. 139 0
      accompany/src/main/res/layout/record_video_layout.xml
  67. 0 0
      accompany/src/main/res/raw/accompany_loading.json
  68. 17 0
      accompany/src/test/java/com/daya/orchestra/accompany/ExampleUnitTest.java
  69. 1 0
      settings.gradle
  70. 1 0
      student/build.gradle
  71. 21 0
      usercenter/src/main/java/com/cooleshow/usercenter/helper/UserHelper.java

+ 6 - 0
BaseLibrary/src/main/java/com/cooleshow/base/router/RouterPath.kt

@@ -222,4 +222,10 @@ object RouterPath {
         }
     }
 
+    class Accompany {
+        companion object {
+            const val ACTIVITY_ACCOMPANY_HTML = "/accompany/web/AccompanyActivity"
+        }
+    }
+
 }

+ 1 - 0
accompany/.gitignore

@@ -0,0 +1 @@
+/build

+ 69 - 0
accompany/build.gradle

@@ -0,0 +1,69 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android-extensions'
+apply plugin: 'kotlin-kapt'
+apply plugin: 'org.jetbrains.kotlin.android'
+
+
+kapt {
+    arguments {
+        arg("AROUTER_MODULE_NAME", project.getName())
+        // 是否生成路由文档,"enable":生成文档,其他字符串不生成路由文档
+        arg("AROUTER_GENERATE_DOC", "enable")
+    }
+}
+android {
+    compileSdkVersion 30
+
+    defaultConfig {
+        minSdkVersion 21
+        targetSdkVersion 30
+
+        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        consumerProguardFiles "consumer-rules.pro"
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility JavaVersion.VERSION_1_8
+        targetCompatibility JavaVersion.VERSION_1_8
+    }
+    kotlinOptions {
+        jvmTarget = '1.8'
+    }
+
+    packagingOptions {
+        resources.excludes.add("META-INF/*")
+    }
+
+    buildFeatures{
+        viewBinding = true
+    }
+}
+
+dependencies {
+
+    implementation 'androidx.appcompat:appcompat:1.3.0'
+    implementation 'com.google.android.material:material:1.4.0'
+    implementation project(path: ':usercenter')
+    testImplementation 'junit:junit:4.13.2'
+    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
+    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
+
+
+    implementation project(':BaseLibrary')
+    api project(path: ':camerakit')
+//    api project(path: ':ffmpegCmd')
+    api project(path: ':midiplaylib')
+    api project(path: ':musictuner')
+    api project(path: ':metronome')
+//    implementation project(path: ':rong_im:kit')
+    //ARouter
+    implementation "com.alibaba:arouter-api:$rootProject.ext.android.arouter_api_version"
+    kapt "com.alibaba:arouter-compiler:$rootProject.ext.android.arouter_api_version"
+}

+ 0 - 0
accompany/consumer-rules.pro


+ 21 - 0
accompany/proguard-rules.pro

@@ -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

+ 26 - 0
accompany/src/androidTest/java/com/daya/orchestra/accompany/ExampleInstrumentedTest.java

@@ -0,0 +1,26 @@
+package com.daya.orchestra.accompany;
+
+import android.content.Context;
+
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumented test, which will execute on an Android device.
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+    @Test
+    public void useAppContext() {
+        // Context of the app under test.
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+        assertEquals("com.daya.orchestra.accompany.test", appContext.getPackageName());
+    }
+}

+ 9 - 0
accompany/src/main/AndroidManifest.xml

@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.daya.orchestra.accompany">
+    <application>
+        <activity android:name=".web.AccompanyActivity"
+            android:configChanges="orientation|screenSize|keyboardHidden|fontScale|smallestScreenSize|screenLayout"
+            android:screenOrientation="landscape"/>
+    </application>
+</manifest>

BIN
accompany/src/main/assets/lottie/accompany/images/img_0.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_1.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_10.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_11.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_12.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_13.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_14.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_15.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_16.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_17.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_18.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_19.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_2.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_20.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_21.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_22.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_23.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_24.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_25.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_26.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_27.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_28.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_29.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_3.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_4.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_5.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_6.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_7.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_8.png


BIN
accompany/src/main/assets/lottie/accompany/images/img_9.png


+ 35 - 0
accompany/src/main/java/com/daya/orchestra/accompany/api/DownloadApi.java

@@ -0,0 +1,35 @@
+package com.daya.orchestra.accompany.api;
+
+import io.reactivex.rxjava3.core.Observable;
+import okhttp3.ResponseBody;
+import retrofit2.Call;
+import retrofit2.http.GET;
+import retrofit2.http.Headers;
+import retrofit2.http.Streaming;
+import retrofit2.http.Url;
+
+/**
+ * Author by pq, Date on 2022/12/19.
+ */
+public interface DownloadApi {
+    /**
+     * 文件下载
+     * @param url
+     * @return
+     */
+    @Headers({"Content-Type:application/octet-stream"})
+    @Streaming  //文件特别大的时候可以防止内存溢出
+    @GET
+    Call<ResponseBody> download(@Url String url);
+
+
+    /**
+     * 文件下载
+     *
+     * @return
+     */
+    @Streaming
+    @GET
+    @Headers("User-Agent:Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:0.9.4)")
+    Observable<ResponseBody> downloadFileWithFixedUrl(@Url String url);
+}

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
accompany/src/main/java/com/daya/orchestra/accompany/bean/alipay/AuthResult.java


+ 1 - 0
accompany/src/main/java/com/daya/orchestra/accompany/bean/alipay/PayResult.java

@@ -0,0 +1 @@
+package com.daya.orchestra.accompany.bean.alipay;

import android.text.TextUtils;

import java.util.Map;

public class PayResult {
	private String resultStatus;
	private String result;
	private String memo;

	public PayResult(Map<String, String> rawResult) {
		if (rawResult == null) {
			return;
		}

		for (String key : rawResult.keySet()) {
			if (TextUtils.equals(key, "resultStatus")) {
				resultStatus = rawResult.get(key);
			} else if (TextUtils.equals(key, "result")) {
				result = rawResult.get(key);
			} else if (TextUtils.equals(key, "memo")) {
				memo = rawResult.get(key);
			}
		}
	}

	@Override
	public String toString() {
		return "resultStatus={" + resultStatus + "};memo={" + memo
				+ "};result={" + result + "}";
	}

	/**
	 * @return the resultStatus
	 */
	public String getResultStatus() {
		return resultStatus;
	}

	/**
	 * @return the memo
	 */
	public String getMemo() {
		return memo;
	}

	/**
	 * @return the result
	 */
	public String getResult() {
		return result;
	}
}

+ 74 - 0
accompany/src/main/java/com/daya/orchestra/accompany/bean/weixinpay/WeixinPayInfo.java

@@ -0,0 +1,74 @@
+package com.daya.orchestra.accompany.bean.weixinpay;
+
+/**
+ * 创建日期:2022/5/25 14:47
+ *
+ * @author Ryan
+ * 类说明:
+ */
+public class WeixinPayInfo {
+
+    private String appid;
+    private String noncestr;
+    private String packageValue;
+    private String partnerid;
+    private String prepayid;
+    private String sign;
+    private String timestamp;
+
+    public String getAppid() {
+        return appid;
+    }
+
+    public void setAppid(String appid) {
+        this.appid = appid;
+    }
+
+    public String getNoncestr() {
+        return noncestr;
+    }
+
+    public void setNoncestr(String noncestr) {
+        this.noncestr = noncestr;
+    }
+
+    public String getPackageValue() {
+        return packageValue;
+    }
+
+    public void setPackageValue(String packageValue) {
+        this.packageValue = packageValue;
+    }
+
+    public String getPartnerid() {
+        return partnerid;
+    }
+
+    public void setPartnerid(String partnerid) {
+        this.partnerid = partnerid;
+    }
+
+    public String getPrepayid() {
+        return prepayid;
+    }
+
+    public void setPrepayid(String prepayid) {
+        this.prepayid = prepayid;
+    }
+
+    public String getSign() {
+        return sign;
+    }
+
+    public void setSign(String sign) {
+        this.sign = sign;
+    }
+
+    public String getTimestamp() {
+        return timestamp;
+    }
+
+    public void setTimestamp(String timestamp) {
+        this.timestamp = timestamp;
+    }
+}

+ 10 - 0
accompany/src/main/java/com/daya/orchestra/accompany/callback/ResultCallback.java

@@ -0,0 +1,10 @@
+package com.daya.orchestra.accompany.callback;
+
+/**
+ * Author by pq, Date on 2022/12/19.
+ */
+public interface ResultCallback<Result> {
+    void onSuccess(Result result);
+
+    void onFail(int errorCode, String errorStr);
+}

+ 17 - 0
accompany/src/main/java/com/daya/orchestra/accompany/contract/AccompanyContract.java

@@ -0,0 +1,17 @@
+package com.daya.orchestra.accompany.contract;
+
+import com.cooleshow.base.presenter.view.BaseView;
+
+/**
+ * 创建日期:2022/6/8 14:22
+ *
+ * @author Ryan
+ * 类说明:
+ */
+public interface AccompanyContract {
+    interface AccompanyView extends BaseView {
+
+    }
+    interface Presenter {
+    }
+}

+ 248 - 0
accompany/src/main/java/com/daya/orchestra/accompany/helper/AccompanyHelper.java

@@ -0,0 +1,248 @@
+package com.daya.orchestra.accompany.helper;
+
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.cooleshow.base.data.net.RetrofitClientNoToken;
+import com.cooleshow.base.utils.FileUtils;
+import com.cooleshow.base.utils.LogUtils;
+import com.cooleshow.base.utils.Utils;
+//import com.cooleshow.ffmpegcmd.FFmpegCmd;
+//import com.cooleshow.ffmpegcmd.util.FFmpegUtil;
+import com.daya.orchestra.accompany.api.DownloadApi;
+import com.daya.orchestra.accompany.callback.ResultCallback;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+import okhttp3.ResponseBody;
+import retrofit2.Response;
+
+/**
+ * Author by pq, Date on 2022/12/19.
+ */
+public class AccompanyHelper {
+    private static AccompanyHelper instance;
+    public static final String TAG = "AccompanyHelper";
+    public static final String BASE_PATH = FileUtils.getCacheDir(Utils.getApp(), "accompany");
+    public static final String accompanimentMp3Path = BASE_PATH + File.separator + "accompaniment.mp3";
+    public static final String videoMp3Path = BASE_PATH + File.separator + "videoBgm.mp3";
+    public static final String mergeMp3Path = BASE_PATH + File.separator + "merge.mp3";
+    public static final String onlyVideoPath = BASE_PATH + File.separator + "onlyVideoPath.mp4";
+    public static String resultPathPath = BASE_PATH + File.separator + "result.mp4";
+
+    private AccompanyHelper() {
+
+    }
+
+    public static AccompanyHelper getInstance() {
+        if (instance == null) {
+            synchronized (AccompanyHelper.class) {
+                if (instance == null) {
+                    instance = new AccompanyHelper();
+                }
+            }
+        }
+        return instance;
+    }
+
+    public void download(String url, ResultCallback<String> resultCallback) {
+        Observable.create(new ObservableOnSubscribe<String>() {
+            @Override
+            public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
+                deleteAllTempFile();
+                FileUtils.createOrExistsFile(accompanimentMp3Path);
+                File file = new File(accompanimentMp3Path);
+                downloadAccompany(url, file);
+                if (emitter != null) {
+                    emitter.onNext(file.getAbsolutePath());
+                }
+            }
+        }).subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<String>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(@NonNull String s) {
+                        if (resultCallback != null) {
+                            resultCallback.onSuccess(s);
+                        }
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        e.printStackTrace();
+                        if (resultCallback != null) {
+                            resultCallback.onFail(-1, "download error");
+                        }
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+    }
+
+    private void downloadAccompany(String url, File file) {
+        if (TextUtils.isEmpty(url)) {
+            return;
+        }
+        try {
+            Response<ResponseBody> response = RetrofitClientNoToken.getInstance().getRetrofit().create(DownloadApi.class)
+                    .download(url).execute();
+            if (response == null || response.body() == null) {
+                return;
+            }
+            long total = response.body().contentLength();//需要下载的总大小
+            long current = 0;
+            InputStream inputStream = response.body().byteStream();
+            FileOutputStream fileOutputStream = new FileOutputStream(file);
+            byte[] bytes = new byte[1024];
+            int len = 0;
+            while ((len = inputStream.read(bytes)) != -1) {
+                fileOutputStream.write(bytes, 0, len);
+                fileOutputStream.flush();
+                current = current + len;
+                Log.e(TAG, "已经下载=" + current + " 需要下载=" + total);
+            }
+            fileOutputStream.flush();
+            fileOutputStream.close();
+            inputStream.close();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void startMix(String videoPath, String bgmPath, ResultCallback<String> resultCallback) {
+        Observable.create(new ObservableOnSubscribe<String>() {
+            @Override
+            public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Throwable {
+                Log.i(TAG, "转换开始");
+                int getVideoMp3Result = getVideoMp3_2(videoPath, videoMp3Path);
+                Log.i(TAG, "getVideoMp3 complete:" + getVideoMp3Result);
+                //mix原音和视频bgm mp3
+                Log.i(TAG, "mixMp3 start");
+                int mixMp3Result = mixMp3(bgmPath, videoMp3Path, mergeMp3Path);
+                Log.i(TAG, "mixMp3 complete:" + mixMp3Result);
+                //获取纯视频
+                int getOnlyVideoResult = getOnlyVideo(videoPath, onlyVideoPath);
+                Log.i(TAG, "extractVideo complete:" + getOnlyVideoResult);
+                //merge音频和视频
+                resultPathPath = getLastResultPath();
+                int mergeResult = megreMp3AndMp4(onlyVideoPath, mergeMp3Path, resultPathPath);
+                Log.i(TAG, "mergeMp4 complete:" + mergeResult);
+                Log.i(TAG, "转换完成");
+                if (emitter != null) {
+                    boolean isSuccess = getVideoMp3Result == 0 && mixMp3Result == 0 && getOnlyVideoResult == 0 && mergeResult == 0;
+                    Log.i(TAG, "转换是否全部isSuccess:" + isSuccess);
+                    if (isSuccess) {
+                        emitter.onNext(resultPathPath);
+                    } else {
+                        emitter.onNext(videoPath);
+                    }
+                }
+            }
+        }).subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<String>() {
+                    @Override
+                    public void onSubscribe(@NonNull Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(@NonNull String s) {
+                        if (resultCallback != null) {
+                            resultCallback.onSuccess(s);
+                        }
+                    }
+
+                    @Override
+                    public void onError(@NonNull Throwable e) {
+                        e.printStackTrace();
+                        if (resultCallback != null) {
+                            resultCallback.onFail(-1, "mix error");
+                        }
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+    }
+
+    private String getLastResultPath() {
+        String timeStamp = String.valueOf(System.currentTimeMillis());
+        String lastPath = BASE_PATH + File.separator + "VIDEO_RESULT_" + timeStamp + ".mp4";
+        LogUtils.i(TAG,"lastPath:"+lastPath);
+        return lastPath;
+    }
+
+    private void deleteAllTempFile() {
+        boolean b = FileUtils.deleteAllInDir(BASE_PATH);
+        LogUtils.i(TAG, "deleteAllTempFile:" + b);
+//        FileUtils.delete(videoMp3Path);
+//        FileUtils.delete(mergeMp3Path);
+//        FileUtils.delete(onlyVideoPath);
+//        FileUtils.delete(resultPathPath);
+    }
+
+    public int getVideoMp3_2(String videoPath, String outPath) {
+        //ffmpeg -i video.mp4 -vn audio.mp3
+        Log.i(TAG, "getVideoMp3 start videoPath:" + videoPath + "\noutPath:" + outPath);
+        String[] commands = new String[5];
+        commands[0] = "ffmpeg";
+        commands[1] = "-i";
+        commands[2] = videoPath;
+        commands[3] = "-vn";
+        commands[4] = outPath;
+        if (commands != null) {
+//            return FFmpegCmd.executeSync(commands);
+        }
+        return -1;
+    }
+
+    private int mixMp3(String audioPath, String bgmPath, String outputPath) {
+//        String[] commands = FFmpegUtil.mixAudio(audioPath, bgmPath, outputPath);
+//        if (commands != null) {
+//            return FFmpegCmd.executeSync(commands);
+//        }
+        return -1;
+    }
+
+    private int getOnlyVideo(String videoPath, String out) {
+        Log.i(TAG, "extractVideo start");
+//        String[] commands1 = FFmpegUtil.extractVideo(videoPath, out);
+//        if (commands1 != null) {
+//            return FFmpegCmd.executeSync(commands1);
+//        }
+        return -1;
+    }
+
+
+    private int megreMp3AndMp4(String videoPath, String mp3Path, String outPath) {
+        Log.i(TAG, "mergeMp4 start");
+//        String[] commands = FFmpegUtil.mediaMux(videoPath, mp3Path, true, outPath);
+//        if (commands != null) {
+//            return FFmpegCmd.executeSync(commands);
+//        }
+        return -1;
+    }
+
+}

+ 543 - 0
accompany/src/main/java/com/daya/orchestra/accompany/helper/AccompanyPlayHelper.java

@@ -0,0 +1,543 @@
+package com.daya.orchestra.accompany.helper;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import android.webkit.ValueCallback;
+import android.webkit.WebView;
+
+import com.cooleshow.base.recorder.AudioChunk;
+import com.cooleshow.base.recorder.AudioRecordConfig;
+import com.cooleshow.base.recorder.MsRecorder;
+import com.cooleshow.base.recorder.PullTransport;
+import com.cooleshow.base.recorder.Recorder;
+import com.cooleshow.base.utils.FileUtils;
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.base.utils.MyFileUtils;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.websocket.JWebSocketClient;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.orchestra.accompany.player.CustomPlayer;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.util.ArrayList;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+/**
+ * Author by pq, Date on 2023/8/4.
+ */
+public class AccompanyPlayHelper implements MediaPlayer.OnCompletionListener {
+    public static final String TAG = "AccPlayHelper";
+
+    public static long realPlayStartTime = -1;
+    private Context mContext;
+    private ArrayList<Integer> delayCheckResults = new ArrayList<>();
+    private String musicSrc;
+    private String tuneSrc;
+    private int[] delaySample = new int[]{800, 800};
+    private int playDelayTime = 0;
+    public static final String BLANK_DEVICE_DELAY_CACHE_KEY = "delay_cache";
+    public static final String DELAY_FOR_CURRENT_CACHE_KEY = "delayForCurrent";
+    private Recorder wavRecorder;
+    private CustomPlayer player;
+    private long startPlayTime = 0;
+    private boolean isTag = true;
+
+    boolean isSendRecordStartTime = false;
+    long sendFirstTime = 0;
+    boolean isSendAudioData = false;
+    long startOffsetTime = 0;
+
+    private JWebSocketClient webSocketClient;
+    private WebView webView;
+    private Handler loopHandler;
+
+    private Runnable mTuneRunnable = new Runnable() {
+        @Override
+        public void run() {
+            if (player == null) {
+                return;
+            }
+            int cu = player.getCu();
+            int duration = player.getT();
+            if (cu > 500) {
+                if (isTag) {
+                    LOG.i(TAG, "cu:" + cu);
+                    LOG.i(TAG, "duration:" + duration);
+                    long cTime = System.currentTimeMillis();
+                    long endTime = cTime - cu;
+                    long delayTime = endTime - startPlayTime;
+                    playDelayTime = (int) delayTime;
+                    LOG.i(TAG, "校音延迟delayTime:" + delayTime);
+                    isTag = false;
+                }
+            }
+            loopHandler.postDelayed(mTuneRunnable, 10);
+        }
+    };
+
+    private Runnable mRunnable2 = new Runnable() {
+        @Override
+        public void run() {
+            CustomPlayer player = getPlayer();
+            int cu = player.getCu();
+            int duration = player.getT();
+            if (cu > 1000) {
+                if (isTag) {
+                    LOG.i(TAG, "第二段cu:" + cu);
+                    LOG.i(TAG, "第二段duration:" + duration);
+                    long cTime = System.currentTimeMillis();
+                    long endTime = cTime - cu;
+                    realPlayStartTime = endTime;
+                    long delayTime = endTime - startPlayTime;
+                    LOG.i(TAG, "第二段 delayTime:" + delayTime);
+                    LOG.i(TAG, "第二段 endTime:" + endTime);
+                    sendOffsetMessage(delayTime);
+                    isTag = false;
+                }
+            }
+            sendPlayProgressMessage(cu, duration);
+            loopHandler.postDelayed(mRunnable2, 10);
+        }
+    };
+
+
+    public AccompanyPlayHelper(Context context, JWebSocketClient jWebSocketClient, WebView webView) {
+        mContext = context;
+        this.webSocketClient = jWebSocketClient;
+        this.webView = webView;
+        this.loopHandler = new Handler(Looper.getMainLooper());
+    }
+
+    public void setWebSocketClient(JWebSocketClient jWebSocketClient) {
+        LOG.i(TAG, "setWebSocketClient:" + jWebSocketClient);
+        this.webSocketClient = jWebSocketClient;
+    }
+
+    public void createMusicPlayer(JSONObject message) {
+        LOG.i(TAG, "createMusicPlayer:" + message.toString());
+        JSONObject content = message.optJSONObject("content");
+        if (content != null) {
+            tuneSrc = content.optString("tuneSrc");
+            musicSrc = content.optString("musicSrc");
+        }
+    }
+
+    public void startTune(JSONObject message) {
+        JSONObject contentJson = message.optJSONObject("content");
+        if (contentJson != null) {
+            String count = contentJson.optString("count", "");
+            if (TextUtils.equals(count, "0")) {
+                delayCheckResults.clear();
+            }
+        } else {
+            delayCheckResults.clear();
+        }
+        if (!TextUtils.isEmpty(tuneSrc)) {
+            startTune();
+        }
+    }
+
+    public void startTune() {
+        if (webSocketClient != null && webSocketClient.isOpen()) {
+            try {
+                JSONObject jsonObject = new JSONObject();
+                JSONObject headerObject = new JSONObject();
+                headerObject.put("commond", "recordStart");
+                headerObject.put("type", "DELAY_CHECK");
+                headerObject.put("status", 200);
+                jsonObject.put("header", headerObject);
+                JSONObject bodyJsonObject = new JSONObject();
+                bodyJsonObject.put("HZ", delayCheckResults.size() == 0 ? delaySample[0] : delaySample[1]);//校音标准音的节拍器第一个音的音高
+                jsonObject.put("body", bodyJsonObject);
+                sendWebSocketMessage(jsonObject.toString());
+                initRecorder();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        } else {
+            LOG.i(TAG, "startTune fail because webSocket not open");
+        }
+    }
+
+    public void endTune(JSONObject message) {
+        stopTune();
+    }
+
+    public void getDeviceDelay(JSONObject jsonObject) {
+        JSONObject contentJson;
+        contentJson = jsonObject.optJSONObject("content");
+        if (contentJson == null) {
+            contentJson = new JSONObject();
+        }
+        if (contentJson != null) {
+            int customCache = UserHelper.getCustomCacheForInt(BLANK_DEVICE_DELAY_CACHE_KEY);
+            try {
+                contentJson.put("value", customCache);
+                jsonObject.put("content", contentJson);
+                onSendMessage(jsonObject.toString());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void stopTune() {
+        stopPlay();
+        releaseRecorder();
+        if (loopHandler != null) {
+            loopHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        JSONObject jsonObject = new JSONObject();
+                        JSONObject headerObject = new JSONObject();
+                        headerObject.put("commond", "recordEnd");
+                        headerObject.put("type", "DELAY_CHECK");
+                        headerObject.put("status", 200);
+                        jsonObject.put("header", headerObject);
+                        sendWebSocketMessage(jsonObject.toString());
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }, 500);
+        }
+    }
+
+    private void sendWebSocketMessage(String message) {
+        if (webSocketClient != null && webSocketClient.isOpen()) {
+            try {
+                LOG.i(TAG, "发送webSocket消息:" + message);
+                webSocketClient.send(message);
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private void initRecorder() {
+        playDelayTime = 0;
+        isSendRecordStartTime = true;
+        if (wavRecorder != null) {
+            wavRecorder.stopRecording();
+            FileUtils.deleteFile(getVoicePath());
+            wavRecorder.startRecording(mContext);
+        } else {
+            Observable.create((ObservableOnSubscribe<String>) emitter -> {
+                        wavRecorder = MsRecorder.wav(
+                                new File(getVoicePath()),
+                                new AudioRecordConfig(),
+                                // AudioRecordConfig(MediaRecorder.AudioSource.MIC, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_IN_MONO, 44100),
+                                new PullTransport.Default().setOnAudioChunkPulledListener(new PullTransport.OnAudioChunkPulledListener() {
+                                    @Override
+                                    public void onAudioChunkPulled(AudioChunk audioChunk) {
+                                        if (webSocketClient != null && webSocketClient.isOpen()) {
+                                            webSocketClient.send(audioChunk.toBytes());
+                                            if (isSendAudioData) {
+                                                LOG.i(TAG, "开始send: " + audioChunk.toBytes());
+                                            }
+                                            if (isSendRecordStartTime) {
+                                                isSendRecordStartTime = false;
+                                                emitter.onNext("-2");
+                                            }
+                                        } else {
+                                            emitter.onNext("-1");
+                                        }
+                                    }
+                                }));
+                        if (wavRecorder != null) {
+                            wavRecorder.startRecording(getContext());
+                        }
+                        emitter.onNext("1");
+                    }).subscribeOn(Schedulers.newThread())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new Observer<String>() {
+                        @Override
+                        public void onSubscribe(@NonNull Disposable d) {
+
+                        }
+
+                        @Override
+                        public void onNext(@NonNull String recorder) {
+                            LOG.i(TAG, "startRecording_onNext: " + recorder);
+                            if ("-2".equals(recorder)) {
+                                long bufferDuration = (long) wavRecorder.getBufferDuration();
+                                long time = System.currentTimeMillis();
+                                long result = time - bufferDuration;
+                                toPlayTuneMusic(result);
+                            } else if ("1".equals(recorder)) {
+//                                onSendMessage(message.toString());
+                            } else {
+                                LOG.i(TAG, "startRecording_onNextonError: ");
+                                JSONObject object = new JSONObject();
+                                JSONObject reson = new JSONObject();
+                                try {
+                                    reson.put("reson", "服务异常,请重试");
+                                    object.put("api", "cancelEvaluating");
+                                    object.put("content", reson);
+                                    onSendMessage(object.toString());
+                                    if (wavRecorder != null) {
+                                        wavRecorder.stopRecording();
+                                    }
+                                } catch (JSONException e2) {
+                                    e2.printStackTrace();
+                                }
+                            }
+                        }
+
+                        @Override
+                        public void onError(@NonNull Throwable e) {
+                            LOG.i(TAG, "startRecording_onNextonError: " + e.getMessage());
+                            JSONObject object = new JSONObject();
+                            JSONObject reson = new JSONObject();
+                            try {
+                                reson.put("reson", "录制错误,请重试");
+                                object.put("api", "cancelEvaluating");
+                                object.put("content", reson);
+                                onSendMessage(object.toString());
+                                if (wavRecorder != null) {
+                                    wavRecorder.stopRecording();
+                                }
+                            } catch (JSONException e2) {
+                                e2.printStackTrace();
+                            }
+                        }
+
+                        @Override
+                        public void onComplete() {
+
+                        }
+                    });
+
+
+        }
+    }
+
+
+    private void toPlayTuneMusic(long startTime) {
+        isTag = true;
+        startPlayTime = startTime;
+        LOG.i(TAG, "校音startTime:" + startPlayTime);
+        loopHandler.removeCallbacks(mTuneRunnable);
+        loopHandler.postDelayed(mTuneRunnable, 10);
+        getPlayer().stop();
+        getPlayer().play(false, tuneSrc);
+//        String fileName;
+//        LOG.i("pq", "toPlayTuneMusic" + delayCheckResults.size());
+//        if (delayCheckResults.size() == 0) {
+//            fileName = checkFileName[0];
+//        } else {
+//            fileName = checkFileName[1];
+//        }
+//        String path = FileUtils.getCacheDir(getContext()) + "/" + fileName;
+//        File file = new File(path);
+//        Uri uri = Uri.fromFile(file);
+//        player.play(getContext(), uri);
+    }
+
+    public void onFinishTune(JSONObject jsonObject) {
+        int availableCount = 0;
+        int availableValueSum = 0;
+        for (int i = delayCheckResults.size() - 1; i >= 0; i--) {
+            Integer integer = delayCheckResults.get(i);
+            boolean availableValue = DeviceDelayCheckHelper.isAvailableValue(integer);
+            if (availableValue) {
+                LOG.i(TAG, "达标样本值:" + integer);
+                availableCount++;
+                availableValueSum += integer;
+            }
+        }
+        LOG.i(TAG, "延迟符合标本样本个数:" + availableCount);
+        LOG.i(TAG, "样本总数:" + delayCheckResults.size());
+        boolean isSuccess = false;
+        if (availableCount >= (delayCheckResults.size() / 2)) {
+            int countValue = availableValueSum / availableCount;
+//            ToastUtil.getInstance().show(getContext(), "设备延迟:" + countValue + "ms");
+            UserHelper.setCustomCache(BLANK_DEVICE_DELAY_CACHE_KEY, countValue);
+            isSuccess = true;
+        } else {
+            UserHelper.setCustomCache(BLANK_DEVICE_DELAY_CACHE_KEY, -1);
+            ToastUtil.getInstance().show(getContext(), "设备延迟检测失败,请重试");
+            isSuccess = false;
+        }
+        LOG.i(TAG, "delayCheckResults.clear");
+        delayCheckResults.clear();
+
+        try {
+            JSONObject contentJson = jsonObject.optJSONObject("content");
+            if (contentJson == null) {
+                contentJson = new JSONObject();
+            }
+            contentJson.put("result", isSuccess ? 1 : 0);
+            jsonObject.put("content", contentJson);
+            onSendMessage(jsonObject.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    public void parseServerMsg(String serverMsg) {
+        try {
+            JSONObject jsonObject = new JSONObject(serverMsg);
+            JSONObject headerObject = jsonObject.optJSONObject("header");
+            if (headerObject == null) {
+                return;
+            }
+            String commond = headerObject.optString("commond", "");
+            String type = headerObject.optString("type", "");
+            if (TextUtils.equals(commond, "recordEnd") && TextUtils.equals(type, "DELAY_CHECK")) {
+                JSONObject bodyJsonObject = jsonObject.optJSONObject("body");
+                if (bodyJsonObject == null) {
+                    return;
+                }
+                double allDelay = bodyJsonObject.optDouble("firstNoteDelayDuration", -1);
+                LOG.i(TAG, "receive allDelay:" + allDelay);
+                if (allDelay != -1) {
+                    int blankDeviceDelay = (int) allDelay - playDelayTime;//空白延迟(物理延迟)
+                    LOG.i(TAG, "count blankDeviceDelay:" + blankDeviceDelay);
+                    delayCheckResults.add(blankDeviceDelay);
+                    LOG.i(TAG, "delayCheckResults add");
+                }
+            }
+
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    private void sendPlayProgressMessage(int cu, int duration) {
+        JSONObject jsonObject = new JSONObject();
+        JSONObject contentObject = new JSONObject();
+        try {
+            jsonObject.put("api", "playProgress");
+            contentObject.put("currentTime", cu);
+            contentObject.put("totalDuration", duration);
+            jsonObject.put("content", contentObject);
+            onSendMessage(jsonObject.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void sendOffsetMessage(long delayTime) {
+        int customCache = UserHelper.getCustomCacheForInt(BLANK_DEVICE_DELAY_CACHE_KEY);
+        int allDelay = (int) (delayTime + customCache);
+        UserHelper.setCustomCache(DELAY_FOR_CURRENT_CACHE_KEY, allDelay);
+        try {
+            JSONObject jsonObject = new JSONObject();
+            JSONObject headerObject = new JSONObject();
+            JSONObject bodyObject = new JSONObject();
+            headerObject.put("commond", "audioPlayStart");
+            headerObject.put("type", "SOUND_COMPARE");
+            bodyObject.put("offsetTime", delayTime);
+            bodyObject.put("micDelay", customCache);
+            headerObject.put("status", 200);
+            jsonObject.put("header", headerObject);
+            jsonObject.put("body", bodyObject);
+            sendWebSocketMessage(jsonObject.toString());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void stopPlay() {
+        if (getPlayer() != null) {
+            getPlayer().stop();
+            loopHandler.removeCallbacks(mTuneRunnable);
+            loopHandler.removeCallbacks(mRunnable2);
+        }
+    }
+
+    private void releaseRecorder() {
+        if (wavRecorder == null) {
+            return;
+        }
+        wavRecorder.stopRecording();
+        wavRecorder = null;
+    }
+
+    private CustomPlayer getPlayer() {
+        if (player == null) {
+            player = new CustomPlayer();
+            player.setOnCompletionListener(this);
+        }
+        return player;
+    }
+
+    private Context getContext() {
+        return mContext;
+    }
+
+    // 录音文件存储名称
+    private String getVoicePath() {
+        return MyFileUtils.getRecordFilePath();
+    }
+
+    public void onSendMessage(String message) {
+        if (webView == null) {
+            return;
+        }
+        LOG.i(TAG, "onSendMessage: " + message);
+        webView.evaluateJavascript("postMessage('" + message + "')", new ValueCallback<String>() {
+            @Override
+            public void onReceiveValue(String s) {
+            }
+        });
+    }
+
+    public void stop() {
+        stopPlay();
+        releaseRecorder();
+    }
+
+    public void release() {
+        if (loopHandler != null) {
+            loopHandler.removeCallbacksAndMessages(null);
+        }
+        if (player != null) {
+            player.stop();
+            player.release();
+        }
+    }
+
+    public void toPlay2(long startTime, boolean isMute) {
+        if (TextUtils.isEmpty(musicSrc)) {
+            return;
+        }
+        realPlayStartTime = -1;
+        UserHelper.setCustomCache(DELAY_FOR_CURRENT_CACHE_KEY, 0);
+        isTag = true;
+        startPlayTime = startTime;
+        Log.i("pq", "评测曲子startTime:" + startPlayTime);
+        loopHandler.removeCallbacksAndMessages(null);
+        loopHandler.postDelayed(mRunnable2, 10);
+        getPlayer().stop();
+        getPlayer().play(isMute, musicSrc);
+    }
+
+    @Override
+    public void onCompletion(MediaPlayer mp) {
+        LOG.i(TAG, "play onCompletion");
+        if (loopHandler != null) {
+            loopHandler.removeCallbacks(mRunnable2);
+            loopHandler.removeCallbacks(mTuneRunnable);
+            sendPlayProgressMessage(player.getT(), player.getT());
+        }
+    }
+}

+ 22 - 0
accompany/src/main/java/com/daya/orchestra/accompany/helper/DeviceDelayCheckHelper.java

@@ -0,0 +1,22 @@
+package com.daya.orchestra.accompany.helper;
+
+/**
+ * Author by pq, Date on 2023/6/14.
+ */
+public class DeviceDelayCheckHelper {
+    public static final int DELAY_MIN_VALUE = 50;
+    public static final int DELAY_MAX_VALUE = 300;
+
+    public static boolean isAvailableValue(Integer value) {
+        if (value == null) {
+            return false;
+        }
+        if (value < DELAY_MIN_VALUE) {
+            return false;
+        }
+        if (value > DELAY_MAX_VALUE) {
+            return false;
+        }
+        return true;
+    }
+}

+ 199 - 0
accompany/src/main/java/com/daya/orchestra/accompany/helper/ShareHelper.java

@@ -0,0 +1,199 @@
+package com.daya.orchestra.accompany.helper;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.provider.MediaStore;
+import android.text.TextUtils;
+import android.util.Base64;
+import android.util.Log;
+
+import com.cooleshow.base.utils.FileUtils;
+import com.cooleshow.base.utils.MyFileUtils;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.utils.Utils;
+import com.umeng.socialize.ShareAction;
+import com.umeng.socialize.UMShareAPI;
+import com.umeng.socialize.UMShareListener;
+import com.umeng.socialize.bean.SHARE_MEDIA;
+import com.umeng.socialize.media.UMImage;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+/**
+ * Author by pq, Date on 2022/5/31.
+ */
+public class ShareHelper {
+    public static final String WECHAT_TAG = "wechat";
+    public static final String WECHAT_CIRCLE_TAG = "wechat_circle";
+    public static final String SINA_TAG = "sina";
+    public static final String IMAGE_TAG = "image";
+    public static final String VIDEO_TAG = "video";
+
+    public static void saveImg(Context context, String base64, ResultCallBack resultCallBack) {
+        Observable.create(new ObservableOnSubscribe<File>() {
+            @Override
+            public void subscribe(ObservableEmitter<File> emitter) throws Exception {
+                File file = saveImgToLocalFile(base64);
+                emitter.onNext(file);
+            }
+        }).subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Observer<File>() {
+                    @Override
+                    public void onSubscribe(Disposable d) {
+
+                    }
+
+                    @Override
+                    public void onNext(File file) {
+                        if (context != null) {
+                            try {
+                                MediaStore.Images.Media.insertImage(context.getContentResolver(),
+                                        file.getAbsolutePath(), file.getName(), null);
+                                // 最后通知图库更新
+                                context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE,
+                                        Uri.fromFile(new File(file.getPath()))));
+                                if (resultCallBack != null) {
+                                    resultCallBack.onResult(true);
+                                }
+                            } catch (FileNotFoundException e) {
+                                ToastUtil.getInstance().show(context, "保存失败");
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+
+                    @Override
+                    public void onError(Throwable e) {
+                        if (resultCallBack != null) {
+                            resultCallBack.onResult(false);
+                        }
+                    }
+
+                    @Override
+                    public void onComplete() {
+
+                    }
+                });
+    }
+
+
+
+
+    private static File saveImgToLocalFile(String base64) {
+        byte[] bytes;
+        if (base64.startsWith("data:image/png;base64")) {
+            String[] split = base64.split(",");
+            bytes = Base64.decode(split[1], Base64.DEFAULT);
+        } else {
+            bytes = Base64.decode(base64, Base64.DEFAULT);
+        }
+        Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
+        File parentFile = new File(FileUtils.getCacheDir(Utils.getApp()) + File.separator + "share");
+        if (!parentFile.exists()) {
+            parentFile.mkdirs();
+        }
+        String targetFileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".png";
+        File file = new File(parentFile, targetFileName);
+        FileUtils.saveImageToLocal(bitmap, file.getAbsolutePath());
+        return new File(file.getAbsolutePath());
+    }
+
+    private static File saveImgToLocalFile(Bitmap bitmap) {
+        File parentFile = new File(FileUtils.getCacheDir(Utils.getApp()) + File.separator + "share");
+        if (!parentFile.exists()) {
+            parentFile.mkdirs();
+        }
+        String targetFileName = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".png";
+        File file = new File(parentFile, targetFileName);
+        FileUtils.saveImageToLocal(bitmap, file.getAbsolutePath());
+        return new File(file.getAbsolutePath());
+    }
+
+
+    public static void parseShareContactData(String base64, Intent data) {
+//        String targetId = data.getStringExtra("targetId");
+//        int conversationValue = data.getIntExtra("conversation", -1);
+//        if (!TextUtils.isEmpty(base64)) {
+//            Conversation.ConversationType conversationType = OpenChatHelper.getConversationType(conversationValue);
+//            shareImgToChatGroup(base64, targetId, conversationType, new ResultCallBack() {
+//                @Override
+//                public void onResult(boolean isSuccess) {
+//                    if (isSuccess) {
+//                        ToastUtil.getInstance().showShort("分享成功");
+//                    } else {
+//                        ToastUtil.getInstance().showShort("分享失败");
+//                    }
+////                    sendCallBack("shareAchievements", "", true);
+//                }
+//            });
+//        }
+    }
+
+    public static void parseShareActivity(Activity activity, JSONObject jsonObject, UMShareListener shareListener) {
+        JSONObject content = null;
+        try {
+            content = jsonObject.getJSONObject("content");
+            String shareTitle = content.getString("title");
+            String shareDesc = content.getString("desc");
+            String type = content.getString("type");
+            String shareType = content.getString("shareType");
+            SHARE_MEDIA share_media = SHARE_MEDIA.WEIXIN;
+            if (TextUtils.equals(shareType, WECHAT_TAG)) {
+                //分享微信
+                share_media = SHARE_MEDIA.WEIXIN;
+            }
+            if (TextUtils.equals(shareType, WECHAT_CIRCLE_TAG)) {
+                //分享朋友圈
+                share_media = SHARE_MEDIA.WEIXIN_CIRCLE;
+            }
+            if (TextUtils.equals(shareType, SINA_TAG)) {
+                //分享新浪微博
+                share_media = SHARE_MEDIA.SINA;
+            }
+            if (!UMShareAPI.get(Utils.getApp()).isInstall(activity, share_media)) {
+                ToastUtil.getInstance().show(Utils.getApp(), "应用未安装,分享失败");
+                return;
+            }
+            if (TextUtils.equals(type, IMAGE_TAG)) {
+                //分享图片
+                String imageData = content.getString("image");
+                UMImage image = new UMImage(activity, MyFileUtils.base64ToBitmap(imageData.split(",")[1]));//bitmap文件
+                image.setThumb(image);
+                image.compressFormat =Bitmap.CompressFormat.PNG;
+                image.compressStyle = UMImage.CompressStyle.SCALE;
+                image.setTitle(shareTitle);
+                image.setDescription(shareDesc);
+                new ShareAction(activity).withMedia(image)
+                        .setPlatform(share_media)
+                        .setCallback(shareListener)
+                        .share();
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public interface ResultCallBack {
+        void onResult(boolean isSuccess);
+    }
+}

+ 754 - 0
accompany/src/main/java/com/daya/orchestra/accompany/js/JsInterfaceAccomPanyUtils.java

@@ -0,0 +1,754 @@
+package com.daya.orchestra.accompany.js;
+
+import android.app.Activity;
+import android.net.Uri;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Window;
+import android.view.WindowManager;
+import android.webkit.JavascriptInterface;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.cooleshow.base.common.WebApi;
+import com.cooleshow.base.common.WebConstants;
+import com.cooleshow.base.router.RouterPath;
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.base.utils.LogUtils;
+import com.cooleshow.usercenter.constants.UserConstants;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.gyf.immersionbar.ImmersionBar;
+
+import org.json.JSONObject;
+
+
+/**
+ * Description:
+ * Copyright  : Copyright (c) 2016
+ * Company    : 大雅
+ * Author     : 刘瑞
+ * Date       : 2018/11/12 15:23
+ */
+public class JsInterfaceAccomPanyUtils extends Object {
+    private static final String TAG = "JsInterfaceAccomPany";
+
+    private Activity activity;
+    JSONObject resultJson;
+
+    public JsInterfaceAccomPanyUtils(Activity activity) {
+        this.activity = activity;
+
+    }
+
+    @JavascriptInterface
+    public String getTokenSync() {
+        Log.i(TAG, "getTokenSync");
+        String userToken = UserHelper.getUserToken();
+        return userToken;
+    }
+
+    /*
+     *统一方法
+     */
+    @JavascriptInterface
+    public void postMessage(String message) {
+        activity.runOnUiThread(() -> {
+            try {
+                LOG.i("AccomPanyUtils", "receiveMessage:" + message);
+                JSONObject jsonObject = new JSONObject(message);
+                String api = jsonObject.getString("api");
+                if ("back".equals(api)) {
+                    activity.finish();
+                } else if ("login".equals(api)) {
+                    UserHelper.saveUserToken("");
+                    ARouter.getInstance().build(RouterPath.UserCenter.PATH_VERIFY_LOGIN)
+                            .withString(UserConstants.PHONE_NUM_KEY, UserHelper.getUserPhone())
+                            .navigation();
+                    activity.finish();
+                } else if ("cloudLoading".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudLoading(jsonObject);
+                    }
+                } else if ("openConversationActivity".equals(api)) {
+//                    JSONObject contentBean = jsonObject.getJSONObject("content");
+//                    if (null != contentBean) {
+//                        RongIM.getInstance().startPrivateChat(activity, contentBean.getString("userId"), contentBean.getString("name"));
+//                        if (!TextUtils.isEmpty(contentBean.getString("img"))) {
+//                            RongIM.getInstance().refreshUserInfoCache(new UserInfo(contentBean.getString("userId"),
+//                                    contentBean.getString("name"),
+//                                    Uri.parse(contentBean.getString("img"))));
+//                        }
+//                    }
+                } else if ("videoUpdate".equals(api)) {
+                    if (onListener != null) {
+                        onListener.videoUpdate(jsonObject);
+                    }
+                } else if ("setRequestedOrientation".equals(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    int orientation = content.getInt("orientation");
+                    activity.setRequestedOrientation(orientation);
+                    if (onListener != null)
+                        onListener.onSendMessage(content.toString());
+                } else if ("keepScreenLongLight".equals(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    boolean isOpenLight = content.getBoolean("isOpenLight");
+                    keepScreenLongLight(isOpenLight);
+                    if (onListener != null) {
+                        onListener.keepScreenLongLight(jsonObject);
+                    }
+                } else if ("setStatusBarTextColor".equals(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    boolean statusBarTextColor = content.getBoolean("statusBarTextColor");
+                    if (onListener != null)
+                        onListener.setStatusBarTextColor(statusBarTextColor, jsonObject);
+                } else if ("isSpecialShapedScreen".equals(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    content.put("isSpecialShapedScreen", ImmersionBar.hasNotchScreen(activity));
+                    content.put("notchHeight", ImmersionBar.getNotchHeight(activity));
+                    if (onListener != null)
+                        onListener.onSendMessage(jsonObject.toString());
+                } else if ("openWebView".equals(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    boolean isHideTitle = false;
+                    boolean statusBarTextColor = false;
+                    boolean isOpenLight = false;
+                    boolean showLoadingAnim = false;
+                    int orientation = -1;
+                    int c_orientation = -1;
+                    try {
+                        isHideTitle = content.optBoolean("isHideTitle");
+                        statusBarTextColor = content.optBoolean("statusBarTextColor");
+                        isOpenLight = content.optBoolean("isOpenLight");
+                        showLoadingAnim = content.optBoolean("showLoadingAnim");
+                        orientation = content.optInt("orientation");
+                        c_orientation = content.optInt("c_orientation");
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+
+                    String path = RouterPath.WebCenter.ACTIVITY_HTML;
+                    ARouter.getInstance().build(path)
+                            .withString(WebConstants.WEB_URL, content.optString("url"))
+                            .withBoolean("isHideTitle", isHideTitle)
+                            .withBoolean("statusBarTextColor", statusBarTextColor)
+                            .withBoolean("isOpenLight", isOpenLight)
+                            .withBoolean("showLoadingAnim", showLoadingAnim)
+                            .withInt("orientation", orientation)
+                            .withInt("c_orientation", c_orientation)
+                            .navigation();
+                } else if ("openAccompanyWebView".equals(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    boolean isHideTitle = false;
+                    boolean statusBarTextColor = false;
+                    boolean isOpenLight = false;
+                    int orientation = -1;
+                    int c_orientation = -1;
+                    try {
+                        isHideTitle = content.optBoolean("isHideTitle");
+                        statusBarTextColor = content.optBoolean("statusBarTextColor");
+                        isOpenLight = content.optBoolean("isOpenLight");
+                        orientation = content.optInt("orientation");
+                        c_orientation = content.optInt("c_orientation");
+                    } catch (Exception e) {
+                    }
+
+                    ARouter.getInstance().build(RouterPath.Accompany.ACTIVITY_ACCOMPANY_HTML)
+                            .withString("url", content.optString("url"))
+                            .withBoolean("isHideTitle", isHideTitle)
+                            .withBoolean("statusBarTextColor", statusBarTextColor)
+                            .withBoolean("isOpenLight", isOpenLight)
+                            .withInt("orientation", orientation)
+                            .withInt("c_orientation", c_orientation)
+                            .navigation();
+
+                } else if ("startEvaluating".equals(api)) {
+                    if (onListener != null) {
+                        onListener.startEvaluating(jsonObject);
+                    }
+                } else if ("endEvaluating".equals(api)) {
+                    if (onListener != null) {
+                        onListener.endEvaluating(jsonObject);
+                    }
+                } else if ("cancelEvaluating".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cancelEvaluating();
+                        onListener.onSendMessage(message);
+                    }
+                } else if ("startRecording".equals(api)) {
+                    if (onListener != null) {
+                        onListener.startRecording(jsonObject);
+                    }
+                } else if ("pauseRecording".equals(api)) {
+                    if (onListener != null) {
+                        onListener.pauseRecording(jsonObject);
+                    }
+                } else if ("resumeRecording".equals(api)) {
+                    if (onListener != null) {
+                        onListener.resumeRecording(jsonObject);
+                    }
+                } else if ("isWiredHeadsetOn".equals(api)) {
+                    if (onListener != null) {
+                        onListener.isWiredHeadsetOn(jsonObject);
+                    }
+                } else if ("proxyMessage".equals(api)) {
+                    if (onListener != null) {
+                        onListener.proxyMessage(jsonObject);
+                    }
+                } else if ("openCamera".equals(api)) {
+                    if (onListener != null) {
+                        onListener.openCamera(jsonObject);
+                    }
+                } else if ("closeCamera".equals(api)) {
+                    if (onListener != null) {
+                        onListener.closeCamera(jsonObject);
+                    }
+                } else if ("startCapture".equals(api)) {
+                    if (onListener != null) {
+                        onListener.startCapture(jsonObject);
+                    }
+                } else if ("endCapture".equals(api)) {
+                    if (onListener != null) {
+                        onListener.endCapture(jsonObject);
+                    }
+                } else if ("endRecording".equals(api)) {
+                    if (onListener != null) {
+                        onListener.endRecording(jsonObject);
+                    }
+                } else if ("startSoundCheck".equals(api)) {
+                    if (onListener != null) {
+                        onListener.startSoundCheck(jsonObject);
+                    }
+                } else if ("endSoundCheck".equals(api)) {
+                    if (onListener != null) {
+                        onListener.endSoundCheck(jsonObject);
+                    }
+                } else if ("proxyServiceMessage".equals(api)) {
+                    if (onListener != null) {
+                        onListener.proxyServiceMessage(jsonObject);
+                    }
+                }
+                //分享
+                else if ("shareAchievements".equals(api)) {
+                    if (onListener != null) {
+                        onListener.shareAchievements(jsonObject);
+                    }
+                }
+                // 分段上传
+                else if ("measureStart".equals(api)) {
+                    if (onListener != null) {
+                        onListener.measureStart(jsonObject);
+                    }
+                }
+                //分段上传结束
+                else if ("allMeasureEnd".equals(api)) {
+                    if (onListener != null) {
+                        onListener.allMeasureEnd(jsonObject);
+                    }
+                }
+                //初始化
+                else if ("cloudDetail".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudDetail(jsonObject);
+                    }
+                }
+                //开始播放
+                else if ("cloudPlay".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudPlay(jsonObject);
+                    }
+                }
+                //播放暂停
+                else if ("cloudSuspend".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudSuspend(jsonObject);
+                    }
+                }
+                //播放进度跳转
+                else if ("cloudSetCurrentTime".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudSetCurrentTime(jsonObject);
+                    }
+                }
+                //调速
+                else if ("cloudChangeSpeed".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudChangeSpeed(jsonObject);
+                    }
+                }
+                //实现原音伴奏切换,基于传入参数控制声道
+                else if ("cloudSwitch".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudSwitch(jsonObject);
+                    }
+                }
+                //实现音量控制基于传入参数控制声道
+                else if ("cloudVolume".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudVolume(jsonObject);
+                    }
+                } else if ("cloudGetMediaStatus".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudGetMediaStatus(jsonObject);
+                    }
+                } else if ("cloudMetronome".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudMetronome(jsonObject);
+                    }
+                } else if ("cloudDestroy".equals(api)) {
+                    if (onListener != null) {
+                        onListener.cloudDestroy(jsonObject);
+                    }
+                } else if ("joinLiveRoom".equals(api)) {
+                    //进入直播间
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    if (onListener != null) {
+                        onListener.joinLiveRoom(content.getString("roomId"), content.getString("teacherId"));
+                    }
+                } else if ("joinChatGroup".equals(api)) {
+                    //进入聊天
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    if (onListener != null) {
+                        onListener.joinChatGroup(content.getString("type"), content.getString("id"));
+                    }
+                } else if ("setAddress".equals(api)) {
+                    //跳转地址页面
+                    ARouter.getInstance().build(RouterPath.BaseCenter.MINE_ADDRESS_LIST)
+                            .navigation(activity, 1011);
+                } else if ("paymentOrder".equals(api)) {
+                    //支付
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    if (onListener != null) {
+                        onListener.paymentOrder(content.getString("orderNo"), content.getString("payChannel"), content.getString("payInfo"));
+                    }
+                }
+                if ("savePicture".equalsIgnoreCase(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    if (onListener != null) {
+                        onListener.savePicture(content.getString("base64"), content.getString("uuid"));
+                    }
+                }
+
+                if ("getToken".equals(api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    String userToken = UserHelper.getUserToken();
+                    String[] s = userToken.split(" ");
+                    content.put("tokenType", s[0]);
+                    content.put("accessToken", s[1]);
+
+                    if (onListener != null) {
+                        onListener.onSendMessage(jsonObject.toString());
+                    }
+                    return;
+                }
+
+                if (TextUtils.equals("goBack", api)) {
+                    if (onListener != null) {
+                        onListener.onBackPress();
+                    }
+                    return;
+                }
+
+                if (TextUtils.equals("cloudAccompanyMessage", api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    if (onListener != null) {
+                        String accompanyUrl = content.isNull("accompanyUrl") ? null : content.optString("accompanyUrl");
+                        onListener.saveAccompanimentMp3(accompanyUrl);
+                    }
+                    return;
+                }
+
+                if (TextUtils.equals(WebApi.SET_EVENT_TRACKING, api)) {
+                    //事件埋点
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    String eventId = content.optString("type");
+//                    if (!TextUtils.isEmpty(eventId)) {
+//                        EventHelper.addEvent(eventId);
+//                    }
+                    return;
+                }
+
+
+                if (TextUtils.equals(WebApi.LIMIT_SCREEN_RECORD, api)) {
+                    //录屏截屏事件控制
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    String type = content.optString("type");
+                    if (activity == null) {
+                        return;
+                    }
+                    if (TextUtils.equals(type, "1")) {
+                        activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+                    } else {
+                        activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
+                    }
+                    return;
+                }
+
+                //跟练模式
+                if (TextUtils.equals(WebApi.CLOUD_TOGGLE_FOLLOW, api)) {
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    if (onListener != null) {
+                        onListener.cloudToggleFollow(content.getString("state"));
+                    }
+                    return;
+                }
+
+                if (TextUtils.equals(WebApi.API_SET_CACHE, api)) {
+                    //获取用户信息
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    String key = content.optString("key");
+                    String value = content.optString("value");
+                    UserHelper.setCustomCache(key, value);
+                    return;
+                }
+
+                if (TextUtils.equals(WebApi.API_GET_CACHE, api)) {
+                    //获取用户信息
+                    JSONObject content = jsonObject.getJSONObject("content");
+                    String key = content.optString("key");
+                    String customCache = UserHelper.getCustomCache(key);
+                    content.put("value", customCache);
+                    if (onListener != null) {
+                        onListener.onSendMessage(jsonObject.toString());
+                    }
+                    return;
+                }
+
+                if (TextUtils.equals(WebApi.API_OPEN_ADJUST_RECORDING, api)) {
+                    if (onListener != null) {
+                        onListener.openAdjustRecording(jsonObject);
+                    }
+                    return;
+                }
+
+                if (TextUtils.equals("endTune", api)) {
+                    if (onListener != null) {
+                        onListener.endTune(jsonObject);
+                    }
+                }
+                if (TextUtils.equals("createMusicPlayer", api)) {
+                    if (onListener != null) {
+                        onListener.createMusicPlayer(jsonObject);
+                    }
+                }
+                if (TextUtils.equals("startTune", api)) {
+                    if (onListener != null) {
+                        onListener.startTune(jsonObject);
+                    }
+                }
+
+                if (TextUtils.equals("getDeviceDelay", api)) {
+                    if (onListener != null) {
+                        onListener.getDeviceDelay(jsonObject);
+                    }
+                }
+
+                if (TextUtils.equals("finishTune", api)) {
+                    if (onListener != null) {
+                        onListener.onFinishTune(jsonObject);
+                    }
+                }
+
+            } catch (Exception e) {
+
+            }
+        });
+
+    }
+
+    private void keepScreenLongLight(boolean isOpenLight) {
+        Window window = activity.getWindow();
+        if (isOpenLight) {
+            window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        } else {
+            window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+        }
+
+    }
+
+    public onGetMethodsListener onListener;
+
+    public void setOnGetMethodsListener(onGetMethodsListener onListener) {
+        this.onListener = onListener;
+    }
+
+    public interface onGetMethodsListener {
+        void onSendMessage(String message);
+
+        void setStatusBarTextColor(boolean statusBarTextColor, JSONObject message);
+
+        void startEvaluating(JSONObject message);
+
+        void cloudLoading(JSONObject message);
+
+
+        void startRecording(JSONObject message);
+
+        void endRecording(JSONObject message);
+
+        void keepScreenLongLight(JSONObject message);
+
+        /**
+         * 开始校音
+         *
+         * @param message
+         */
+        void startSoundCheck(JSONObject message);
+
+        void endSoundCheck(JSONObject message);
+
+        void endEvaluating(JSONObject jsonObject);
+
+        void cancelEvaluating();
+
+        void resumeRecording(JSONObject jsonObject);
+
+        void pauseRecording(JSONObject jsonObject);
+
+        void isWiredHeadsetOn(JSONObject jsonObject);
+
+        void proxyMessage(JSONObject jsonObject);
+
+        void proxyServiceMessage(JSONObject jsonObject);
+
+        void openCamera(JSONObject jsonObject);
+
+        void closeCamera(JSONObject jsonObject);
+
+        void startCapture(JSONObject jsonObject);
+
+        void endCapture(JSONObject jsonObject);
+
+        void videoUpdate(JSONObject jsonObject);
+
+        void shareAchievements(JSONObject message);
+
+        void measureStart(JSONObject message);
+
+        void allMeasureEnd(JSONObject message);
+
+        /**
+         * 进入页面初始化方法
+         * 初始化midi文件,初始速度,默认拍号的分子和分母
+         * {
+         * api: 'cloudDetail',
+         * content: {
+         * midi: '',
+         * denominator: 4,
+         * numerator: 4,
+         * // xml整体原始速度
+         * originalSpeed: 90,
+         * }
+         *
+         * @param message
+         */
+        void cloudDetail(JSONObject message);
+
+        /**
+         * 播放
+         * {
+         * api: 'cloudPlay',
+         * content: {
+         * // 当前曲目id
+         * songID: 0,
+         * // xml整体原始速度
+         * originalSpeed: 90,
+         * // 当前选择速度
+         * speed: 90,
+         * // 开始时间(ms)
+         * startTime: 0
+         * }
+         * })
+         * <p>
+         * // 返回数据
+         * <p>
+         * {
+         * api: 'cloudPlay',
+         * content: {
+         * // 当前曲目id
+         * songID: 0,
+         * // xml整体原始速度
+         * originalSpeed: 90,
+         * // 当前选择速度
+         * speed: 90,
+         * // 开始时间(ms)
+         * startTime: 0
+         * }
+         * }
+         *
+         * @param message
+         */
+        void cloudPlay(JSONObject message);
+
+        /**
+         * 暂停
+         * {
+         * api: 'cloudSuspend',
+         * content: {
+         * // 当前曲目id
+         * songID: 0,
+         * }
+         * })
+         * <p>
+         * // 返回数据
+         * {
+         * api: 'cloudSuspend',
+         * content: {
+         * // 当前曲目id
+         * songID: 0,
+         * // 暂停位置时间(ms)
+         * currentTime: 0
+         * }
+         * }
+         *
+         * @param message
+         */
+        void cloudSuspend(JSONObject message);
+
+
+        /**
+         * 跳转指定位置
+         * {
+         * api: 'cloudSetCurrentTime',
+         * content: {
+         * // 当前曲目id
+         * songID: 0,
+         * // 指定位置时间(ms)
+         * currentTime: 0
+         * }
+         * }
+         *
+         * @param message
+         */
+        void cloudSetCurrentTime(JSONObject message);
+
+        /**
+         * 调速 范围(45-270整数)
+         * {
+         * api: 'cloudChangeSpeed',
+         * content: {
+         * // 当前曲目id
+         * songID: 0,
+         * // 调整速度
+         * speed: 90
+         * // xml整体原始速度
+         * originalSpeed: 90,
+         * }
+         * }
+         *
+         * @param message
+         */
+        void cloudChangeSpeed(JSONObject message);
+
+        /**
+         * 原声/伴奏切换
+         * {
+         * api: 'cloudSwitch',
+         * content: {
+         * // 当前曲目id
+         * songID: 0,
+         * // 需要静音的渠道,为空全部不静音
+         * parts: [
+         * ''
+         * ]
+         * }
+         * }
+         *
+         * @param message
+         */
+        void cloudSwitch(JSONObject message);
+
+        void cloudVolume(JSONObject message);
+
+        /**
+         * 获取播放状态
+         * {
+         * api: 'cloudGetMediaStatus'
+         * })
+         * <p>
+         * // 返回
+         * <p>
+         * {
+         * api: 'cloudGetMediaStatus',
+         * content: {
+         * status: 'init' | 'play' | 'suspend'
+         * }
+         * }
+         *
+         * @param message
+         */
+        void cloudGetMediaStatus(JSONObject message);
+
+        /**
+         * 节拍器控制
+         *
+         * @param message
+         */
+        void cloudMetronome(JSONObject message);
+
+        /**
+         * 销毁
+         *
+         * @param message
+         */
+        void cloudDestroy(JSONObject message);
+
+        /**
+         * 进入直播间
+         *
+         * @param roomId
+         * @param teacherId
+         */
+        void joinLiveRoom(String roomId, String teacherId);
+
+        /**
+         * 进入聊天
+         *
+         * @param type // single 单人 multi 多人
+         * @param id
+         */
+        void joinChatGroup(String type, String id);
+
+        /**
+         * 支付
+         *
+         * @param orderNo
+         * @param payChannel
+         * @param payInfo
+         */
+        void paymentOrder(String orderNo, String payChannel, String payInfo);
+
+        /**
+         * 保存图片
+         *
+         * @param base64
+         */
+        void savePicture(String base64, String uuid);
+
+        /**
+         * 跟练模式
+         */
+        void cloudToggleFollow(String mode);
+
+        /**
+         * 下载伴奏
+         *
+         * @param url
+         */
+        void saveAccompanimentMp3(String url);
+
+        void onBackPress();
+
+        void startTune(JSONObject message);
+
+        void endTune(JSONObject message);
+
+        void createMusicPlayer(JSONObject message);
+
+        void getDeviceDelay(JSONObject jsonObject);
+
+        void onFinishTune(JSONObject jsonObject);
+
+        void openAdjustRecording(JSONObject message);
+    }
+
+}

+ 164 - 0
accompany/src/main/java/com/daya/orchestra/accompany/player/CustomPlayer.java

@@ -0,0 +1,164 @@
+package com.daya.orchestra.accompany.player;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.util.Log;
+import android.view.SurfaceHolder;
+
+import com.cooleshow.base.utils.LOG;
+
+import java.io.IOException;
+
+/**
+ * Author by pq, Date on 2023/5/11.
+ */
+public class CustomPlayer implements MediaPlayer.OnErrorListener, MediaPlayer.OnCompletionListener, MediaPlayer.OnPreparedListener {
+    public static final String TAG = "CustomPlayer";
+    private Context mContext;
+    MediaPlayer mPlayer;
+    private boolean hasPrepared;
+    private boolean isStoped = false;
+
+    private boolean isNeedMute = false;
+    private AudioManager mAudioManager;
+
+    public CustomPlayer() {
+        initPlayer();
+    }
+
+    private void initPlayer() {
+        if (null == mPlayer) {
+            mPlayer = new MediaPlayer();
+            mPlayer.setOnErrorListener(this);
+            mPlayer.setOnCompletionListener(this);
+            mPlayer.setOnPreparedListener(this);
+        }
+    }
+
+    public void setOnCompletionListener(MediaPlayer.OnCompletionListener listener) {
+        if (mPlayer != null) {
+            mPlayer.setOnCompletionListener(listener);
+        }
+    }
+
+    public void play(Context context, Uri dataSource) {
+        hasPrepared = false; // 开始播放前讲Flag置为不可操作
+        isStoped = false;
+        initPlayer(); // 如果是第一次播放/player已经释放了,就会重新创建、初始化
+        try {
+            mPlayer.reset();
+            mPlayer.setDataSource(context, dataSource); // 设置曲目资源
+            mPlayer.prepareAsync(); // 异步的准备方法
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public void play(boolean isMute, String path) {
+        hasPrepared = false; // 开始播放前讲Flag置为不可操作
+        isStoped = false; //
+        this.isNeedMute = isMute;
+        initPlayer(); // 如果是第一次播放/player已经释放了,就会重新创建、初始化
+        try {
+            mPlayer.reset();
+            mPlayer.setDataSource(path); // 设置曲目资源
+            mPlayer.prepareAsync(); // 异步的准备方法
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public int getCu() {
+        if (mPlayer != null) {
+            int currentPosition = mPlayer.getCurrentPosition();
+            return currentPosition;
+        }
+        return -1;
+    }
+
+
+    public int getT() {
+        if (mPlayer != null) {
+            int duration = mPlayer.getDuration();
+            return duration;
+        }
+        return -1;
+    }
+
+    public void start() {
+        if (isStoped) {
+            return;
+        }
+        // release()会释放player、将player置空,所以这里需要判断一下
+        if (null != mPlayer && hasPrepared) {
+            mPlayer.start();
+            setVolume(isNeedMute ? 0f : 1f);
+        }
+    }
+
+    public void setVolume(float volume) {
+        if (mPlayer != null) {
+            LOG.i("setVolume:" + volume + "hasPrepared:" + hasPrepared);
+            mPlayer.setVolume(volume, volume);
+        }
+    }
+
+    public void pause() {
+        if (null != mPlayer && hasPrepared) {
+            mPlayer.pause();
+        }
+    }
+
+    public void seekTo(int position) {
+        if (null != mPlayer && hasPrepared) {
+            mPlayer.seekTo(position);
+        }
+    }
+
+    // 对于播放视频来说,通过设置SurfaceHolder来设置显示Surface。这个方法不需要判断状态、也不会改变player状态
+    public void setDisplay(SurfaceHolder holder) {
+        if (null != mPlayer) {
+            mPlayer.setDisplay(holder);
+        }
+    }
+
+    public void release() {
+        hasPrepared = false;
+        isStoped = true;
+        mPlayer.stop();
+        mPlayer.release();
+        mPlayer = null;
+    }
+
+    public void stop() {
+        if (mPlayer == null) {
+            return;
+        }
+        isStoped = true;
+        hasPrepared = false;
+        Log.i(TAG, "player to stop:" + hasPrepared + "-thread:" + Thread.currentThread().getName());
+        mPlayer.stop();
+    }
+
+    @Override
+    public void onPrepared(MediaPlayer mp) {
+        hasPrepared = true; // 准备完成后回调到这里
+        start();
+    }
+
+    @Override
+    public void onCompletion(MediaPlayer mp) {
+        hasPrepared = false;
+        LOG.i(TAG, "onCompletion");
+        // 通知调用处,调用play()方法进行下一个曲目的播放
+    }
+
+    @Override
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        hasPrepared = false;
+        return true;
+    }
+
+}

+ 14 - 0
accompany/src/main/java/com/daya/orchestra/accompany/presenter/AccompanyPresenter.java

@@ -0,0 +1,14 @@
+package com.daya.orchestra.accompany.presenter;
+
+import com.cooleshow.base.presenter.BasePresenter;
+import com.daya.orchestra.accompany.contract.AccompanyContract;
+
+/**
+ * 创建日期:2022/6/8 14:22
+ *
+ * @author Ryan
+ * 类说明:
+ */
+public class AccompanyPresenter extends BasePresenter<AccompanyContract.AccompanyView> implements AccompanyContract.Presenter {
+
+}

+ 630 - 0
accompany/src/main/java/com/daya/orchestra/accompany/web/AccompanyActivity.java

@@ -0,0 +1,630 @@
+package com.daya.orchestra.accompany.web;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.FrameLayout;
+
+import com.alibaba.android.arouter.facade.annotation.Route;
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.cooleshow.base.router.RouterPath;
+import com.cooleshow.base.service.PlayMusicService;
+import com.cooleshow.base.ui.activity.BaseActivity;
+import com.cooleshow.base.ui.activity.BaseMVPActivity;
+import com.cooleshow.base.utils.FileUtils;
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.base.utils.MyFileUtils;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.utils.Utils;
+import com.cooleshow.base.utils.helper.MidiFileHelper;
+import com.cooleshow.base.utils.helper.QMUIStatusBarHelper;
+import com.cooleshow.base.utils.helper.WebParamsHelper;
+import com.cooleshow.base.utils.helper.upload.UploadHelper;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.orchestra.accompany.R;
+import com.daya.orchestra.accompany.callback.ResultCallback;
+import com.daya.orchestra.accompany.databinding.ActivityAccompanyBinding;
+import com.daya.orchestra.accompany.helper.AccompanyHelper;
+import com.daya.orchestra.accompany.helper.AccompanyPlayHelper;
+import com.gyf.immersionbar.ImmersionBar;
+import com.tbruyelle.rxpermissions3.RxPermissions;
+import com.wonderkiln.camerakit.CameraKit;
+import com.wonderkiln.camerakit.CameraView;
+import com.wonderkiln.camerakit.events.CameraKitError;
+import com.wonderkiln.camerakit.events.CameraKitEvent;
+import com.wonderkiln.camerakit.events.CameraKitEventListener;
+import com.wonderkiln.camerakit.events.CameraKitImage;
+import com.wonderkiln.camerakit.events.CameraKitVideo;
+import com.wonderkiln.camerakit.events.CameraStartRecord;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+
+import androidx.annotation.Nullable;
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.functions.Consumer;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+
+import static com.cooleshow.base.common.WebConstants.WEB_URL;
+
+/**
+ * 创建日期:2022/6/8 14:19
+ *
+ * @author Ryan
+ * 类说明:
+ */
+@Route(path = RouterPath.Accompany.ACTIVITY_ACCOMPANY_HTML)
+public class AccompanyActivity extends BaseActivity<ActivityAccompanyBinding> {
+    public static final String TAG = "AccompanyActivity";
+    FrameLayout camera;
+    FrameLayout fl_webview;
+
+    private final int PORTRAIT_REQUEST_CODE = 1002;
+    private final int ADD_ACCOMPANIMENT_CODE = 1003;
+    private final int SAVE_PRACTICE_REQUEST_CODE = 1004;
+    private String authorization;
+    private String webViewUrl;
+
+    CameraView cameraView;
+    private String filePath;
+    private String videoDerectoryName = "/gytTaskVideo";
+    private View cameraGroupView;
+    private String recordVideFilePath; //评测视频
+    private String accompanimentUrl; //当前曲子伴奏mp3地址
+    private AccompanyFragment accompanyFragment;
+    private JSONObject baseJsonObject;
+    private Intent intentOne;
+    private boolean isNeedResetScreenOrientation = true;
+
+    private Handler mHandler = new Handler(Looper.getMainLooper());
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        QMUIStatusBarHelper.hideStatusBar(this);
+    }
+
+    private long startRecordRealTime = -1;
+
+    @Override
+    protected void initView() {
+        camera = viewBinding.camera;
+        fl_webview = viewBinding.flWebview;
+        Intent intent = getIntent();
+        String url = intent.getStringExtra(WEB_URL);
+        if (TextUtils.isEmpty(url)) {
+            url = intent.getStringExtra("url");
+        }
+        if (TextUtils.isEmpty(url)) {
+            finish();
+            Log.i("AccompanyActivity", "url can not null");
+            return;
+        }
+        int orientation = intent.getIntExtra("orientation", -1);
+        int c_orientation = intent.getIntExtra("c_orientation", ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        isNeedResetScreenOrientation = c_orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
+        LOG.i("c_orientation:" + c_orientation + "--isNeedResetScreenOrientation:" + isNeedResetScreenOrientation);
+        boolean isOpenLight = intent.getBooleanExtra("isOpenLight", false);
+
+        if (isOpenLight) {
+            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+        }
+        if (orientation >= 0) {
+            setRequestedOrientation(orientation);
+        }
+
+        setStatusBarTextColor(intent.getBooleanExtra("statusBarTextColor", true));
+        webViewUrl = url;
+        accompanyFragment = AccompanyFragment.newInstance(webViewUrl);
+        accompanyFragment.setOnAccompanyListener(new AccompanyFragment.onListener() {
+
+
+            @Override
+            public void setStatusBarTextColor(boolean statusBarTextColor) {
+//                setStatusBarTextColor(statusBarTextColor);
+            }
+
+            /**
+             * 打开相机
+             */
+            @Override
+            public void openCamera() {
+                if (cameraView == null) {
+                    cameraGroupView = LayoutInflater.from(AccompanyActivity.this).inflate(R.layout.record_video_layout, camera, false);
+                    camera.addView(cameraGroupView);
+                    cameraView = cameraGroupView.findViewById(R.id.camera);
+                    //Camera1里面录制的视频码率是9M多点,是720P推荐的最底码率了差不多,如果要调高记得去改里面的码率
+                    cameraView.setVideoQuality(CameraKit.Constants.VIDEO_QUALITY_720P);
+                    cameraView.start();
+                    cameraView.addCameraKitListener(new CameraKitEventListener() {
+
+                        @Override
+                        public void onEvent(CameraKitEvent cameraKitEvent) {
+                            if (TextUtils.equals(cameraKitEvent.getType(), CameraKitEvent.TYPE_CAMERA_OPEN)) {
+                                if (cameraView != null) {
+                                    //cameraView的onMeasure会根据mAdjustViewBounds计算宽高,最好保证cameraView里面的getPreviewSize有值以后再刷新一下根据cameraView的mAdjustViewBounds确认宽高,不然部分手机最终显示宽高有问题
+                                    cameraView.requestLayout();
+                                }
+                            } else if (TextUtils.equals(cameraKitEvent.getType(), CameraKitEvent.TYPE_START_RECORD_EVENT)) {
+                                if (cameraKitEvent instanceof CameraStartRecord) {
+                                    CameraStartRecord recordEvent = (CameraStartRecord) cameraKitEvent;
+                                    startRecordRealTime = recordEvent.getTime() + 500;//真正录制时间还要更大一点,无法确定准确值,增加300
+                                }
+                            }
+                        }
+
+                        @Override
+                        public void onError(CameraKitError cameraKitError) {
+
+                        }
+
+                        @Override
+                        public void onImage(CameraKitImage cameraKitImage) {
+
+                        }
+
+                        @Override
+                        public void onVideo(CameraKitVideo cameraKitVideo) {
+                            try {
+                                if (!checkActivityExist()) {
+                                    return;
+                                }
+                                File file = cameraKitVideo.getVideoFile();
+                                filePath = file.getPath();
+//                                handleFileResult(filePath);
+//                                if (!TextUtils.isEmpty(accompanimentUrl)) {
+//                                    mixMp3AndMp4(accompanimentUrl, filePath);
+//                                } else {
+//                                    handleFileResult(filePath);
+//                                }
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    });
+                }
+            }
+
+            /**
+             * 关闭相机
+             */
+            @Override
+            public void closeCamera() {
+                if (cameraView != null) {
+                    filePath = "";
+                    startRecordRealTime = -1;
+                    cameraView.stop();
+                    cameraView.destroyDrawingCache();
+                    camera.removeView(cameraGroupView);
+                    cameraGroupView = null;
+                    cameraView = null;
+                }
+            }
+
+            /**
+             * 录视频
+             */
+            @Override
+            public void startCapture() {
+                if (cameraView != null) {
+                    startRecordRealTime = -1;
+                    toStartCapture();
+                }
+            }
+
+            @Override
+            public void videoUpdate(JSONObject jsonObject) {
+                baseJsonObject = jsonObject;
+                File file = new File(recordVideFilePath);
+                if (!file.exists()) {
+                    ToastUtil.getInstance().showShort("未找到该视频,请重试");
+                    MyFileUtils.deleteFile(recordVideFilePath);
+                }
+                String bucket = WebParamsHelper.getParams(jsonObject, "bucket");
+                String uploadPath = WebParamsHelper.getParams(jsonObject, "path");
+                UploadHelper uploadHelper = new UploadHelper(AccompanyActivity.this, bucket, uploadPath);
+                uploadHelper.uploadFile(file);
+                uploadHelper.setUpLoadCallBack(new UploadHelper.UpLoadCallBack() {
+                    @Override
+                    public void onSuccess(String url) {
+                        try {
+                            JSONObject contentObj = baseJsonObject.getJSONObject("content");
+                            String uuid = contentObj.getString("uuid");
+                            contentObj.put("type", "success");
+                            contentObj.put("filePath", url);
+                            contentObj.put("uuid", uuid);
+                            contentObj.put("message", "上传成功");
+                            accompanyFragment.onSendMessage(baseJsonObject.toString());
+                        } catch (Exception e) {
+                            e.printStackTrace();
+                        }
+                    }
+
+                    @Override
+                    public void onFailure() {
+                        upLoadFileFaile();
+                        ToastUtil.getInstance().showShort("上传失败,请重新选择");
+                    }
+                });
+
+            }
+
+            @Override
+            public void onDownloadAccompaniment(String accompanyUrl) {
+                AccompanyActivity.this.accompanimentUrl = accompanyUrl;
+                if (TextUtils.isEmpty(accompanimentUrl)) {
+//                    runOnUiThread(new Runnable() {
+//                        @Override
+//                        public void run() {
+//                            ToastUtil.getInstance().showShort("当前曲目无mp3伴奏");
+//                        }
+//                    });
+                }
+            }
+
+            @Override
+            public void openAdjustRecording(String recordId, String title, String coverImg) {
+                goAdjustMusic(recordId, title, coverImg);
+            }
+
+            /**
+             * 停止录视频
+             */
+            @Override
+            public void endCapture() {
+                if (!checkActivityExist()) {
+                    FileUtils.delete(recordVideFilePath);
+                    return;
+                }
+                if (cameraView != null && cameraView.getVisibility() == View.VISIBLE) {
+//                    showLoading("正在处理视频中");
+                    mHandler.postDelayed(new Runnable() {
+                        @Override
+                        public void run() {
+                            cameraView.stopVideo();//结束录像
+                        }
+                    }, 1000);
+                }
+            }
+        });
+
+        getSupportFragmentManager().beginTransaction()
+                .add(R.id.fl_webview, accompanyFragment)
+                .commitAllowingStateLoss();
+
+
+        intentOne = new Intent(this, PlayMusicService.class);
+        startService(intentOne);
+    }
+
+    @SuppressLint("CheckResult")
+    private void toStartCapture() {
+        Observable.create(new ObservableOnSubscribe<Object>() {
+                    @Override
+                    public void subscribe(@NonNull ObservableEmitter<Object> emitter) {
+                        if (cameraView != null) {
+                            recordVideFilePath = MyFileUtils.getPublicDirectory(videoDerectoryName) + File.separator
+                                    + "VID_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.CHINA).format(new Date()) + ".mp4";
+                            cameraView.captureVideo(new File(recordVideFilePath));//开始录像
+                        }
+                    }
+                }).subscribeOn(Schedulers.io())
+                .observeOn(Schedulers.io())
+                .subscribe(new Consumer<Object>() {
+                    @Override
+                    public void accept(Object o) {
+
+                    }
+                });
+    }
+
+    @Override
+    protected void initData() {
+        super.initData();
+        requestPermission();
+    }
+
+    private int getVideoDelay() {
+        if (startRecordRealTime != -1 && AccompanyPlayHelper.realPlayStartTime != -1) {
+            int time = (int) (startRecordRealTime - AccompanyPlayHelper.realPlayStartTime);
+            LOG.i("pq", "视频录制消耗:" + time);
+            return time;
+        }
+        return 0;
+    }
+
+    private void goAdjustMusic(String recordId, String title, String coverImg) {
+        if (TextUtils.isEmpty(accompanimentUrl)) {
+            ToastUtil.getInstance().showShort("无法找到伴奏文件");
+            return;
+        }
+        //此字段用于评测播放缓冲延迟以及设备延迟总和
+        int customCacheForInt = UserHelper.getCustomCacheForInt(AccompanyPlayHelper.DELAY_FOR_CURRENT_CACHE_KEY, 0);
+        customCacheForInt = -customCacheForInt;//这里要取反是因为录制在前 播放在后
+        int evaluateDelay =customCacheForInt;
+        String recordFilePath = MyFileUtils.getRecordFilePath();
+        if (!TextUtils.isEmpty(filePath)) {
+            recordFilePath = filePath;
+            customCacheForInt = getVideoDelay();
+        }
+
+        ARouter.getInstance().build(RouterPath.MusicTuner.MUSIC_MERGE_PAGE)
+                .withString("accompanyUrl", accompanimentUrl)
+                .withString("recordFilePath", recordFilePath)
+                .withString("recordId", recordId)
+                .withString("title", title)
+                .withString("coverImg", coverImg)
+                .withInt("defaultDelay", customCacheForInt)
+                .withInt("evaluateDelay", evaluateDelay)//此字段给IOS使用
+                .withInt("c_orientation", ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)
+                .navigation(this, SAVE_PRACTICE_REQUEST_CODE);
+    }
+
+    /**
+     * 申请存储权限
+     */
+    private void requestPermission() {
+        new RxPermissions(this)
+                .request(android.Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                .subscribe(permission -> {
+                    if (permission) {
+                        MidiFileHelper.initMidFile();
+                    }
+                });
+    }
+
+
+    private void mixMp3AndMp4(String accompanimentUrl, String videoPath) {
+        AccompanyHelper.getInstance().download(accompanimentUrl, new ResultCallback<String>() {
+            @Override
+            public void onSuccess(String s) {
+                AccompanyHelper.getInstance().startMix(videoPath, s, new ResultCallback<String>() {
+                    @Override
+                    public void onSuccess(String s) {
+                        Log.i(TAG, "混音success:" + s);
+                        recordVideFilePath = s;
+                        handleFileResult(s);
+
+                    }
+
+                    @Override
+                    public void onFail(int errorCode, String errorStr) {
+                        handleFileResult(filePath);
+                    }
+                });
+            }
+
+            @Override
+            public void onFail(int errorCode, String errorStr) {
+                handleFileResult(filePath);
+            }
+        });
+    }
+
+    private void handleFileResult(String filePath) {
+        hideLoading();
+        saveVideoToGallery(filePath);
+        ToastUtil.getInstance().showShort("保存成功");
+    }
+
+    private void saveVideoToGallery(String filePath) {
+        Observable.create(new ObservableOnSubscribe<Object>() {
+                    @Override
+                    public void subscribe(@NonNull ObservableEmitter<Object> emitter) throws Throwable {
+                        Log.i("pq", "saveVideoToGallery:" + Thread.currentThread().getName());
+                        FileUtils.saveVideoToGallery(AccompanyActivity.this.getApplicationContext(), filePath);
+                    }
+                }).subscribeOn(Schedulers.newThread())
+                .observeOn(AndroidSchedulers.mainThread())
+                .subscribe(new Consumer<Object>() {
+                    @Override
+                    public void accept(Object o) throws Throwable {
+
+                    }
+                });
+    }
+
+    public void setStatusBarTextColor(boolean statusBarTextColor) {
+        if (statusBarTextColor) {
+            setStatusBarColor();
+        } else {
+            setStatusBlackBarColor();
+        }
+    }
+
+    public void setStatusBarColor() {
+        ImmersionBar.with(this)
+                .keyboardEnable(true)
+                .transparentStatusBar()
+                .statusBarDarkFont(false, 0.2f) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度
+                .autoDarkModeEnable(false)
+                .flymeOSStatusBarFontColor(com.cooleshow.base.R.color.white)  //修改flyme OS状态栏字体颜色
+                .init();  //必须调用方可沉浸
+
+    }
+
+
+    public void setStatusBlackBarColor() {
+        ImmersionBar.with(this)
+                .keyboardEnable(true)
+                .transparentStatusBar()
+                .statusBarDarkFont(true, 0.2f) //原理:如果当前设备支持状态栏字体变色,会设置状态栏字体为黑色,如果当前设备不支持状态栏字体变色,会使当前状态栏加上透明度,否则不执行透明度
+                .autoDarkModeEnable(false)
+                .flymeOSStatusBarFontColor(com.cooleshow.base.R.color.common_black)  //修改flyme OS状态栏字体颜色
+                .init();  //必须调用方可沉浸
+    }
+
+    @Override
+    protected ActivityAccompanyBinding getLayoutView() {
+        return ActivityAccompanyBinding.inflate(getLayoutInflater());
+    }
+
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        if (cameraView != null) {
+            cameraView.setVisibility(View.VISIBLE);
+            mHandler.postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    if (cameraView != null) {
+                        cameraView.start();
+                    }
+                }
+            }, 1000);
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        if (mHandler != null) {
+            mHandler.removeCallbacksAndMessages(null);
+        }
+        if (intentOne != null) {
+            stopService(intentOne);
+        }
+    }
+
+
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
+        super.onActivityResult(requestCode, resultCode, data);
+        if (resultCode == Activity.RESULT_OK) {
+            if (requestCode == AccompanyFragment.SHARE_CHAT_REQUEST_CODE) {
+                //选择群聊
+                if (accompanyFragment != null) {
+                    accompanyFragment.parseShareContactData(data);
+                }
+                return;
+            }
+            if (requestCode == SAVE_PRACTICE_REQUEST_CODE) {
+                if (data != null) {
+                    int result = data.getIntExtra("saveWorksStatus", 0);
+                    if (result == 1) {
+                        sendSaveSuccessMsg();
+                    }
+                }
+                return;
+            }
+        }
+        if (requestCode == ADD_ACCOMPANIMENT_CODE) {
+            try {
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("api", "reload");
+//                onSendMessage(jsonObject.toString());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        } else if (requestCode == 1000) {
+            if (data != null) {
+                try {
+                    String filePath = data.getStringExtra("filePath");
+                    JSONObject jsonObject = new JSONObject();
+
+                    jsonObject.put("api", "uploadVideo");
+
+                    jsonObject.put("url", filePath);
+//                    webView.evaluateJavascript("sendMessage('" + jsonObject.toString() + "')", new ValueCallback<String>() {
+//                        @Override
+//                        public void onReceiveValue(String s) {
+//                        }
+//                    });
+
+                } catch (JSONException e) {
+                    e.printStackTrace();
+                }
+            }
+        } else if (requestCode == 1011) {
+            String selectAddress = data.getStringExtra("selectAddress");
+            if (null != accompanyFragment) {
+                accompanyFragment.selectAddress(selectAddress);
+            }
+        }
+    }
+
+    private void sendSaveSuccessMsg() {
+        JSONObject json = new JSONObject();
+        try {
+            JSONObject content = new JSONObject();
+            json.put("api", "hideComplexButton");
+            json.put("content", content);
+            accompanyFragment.onSendMessage(json.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    protected void onStop() {
+        if (cameraView != null) {
+            cameraView.stop();
+            cameraView.destroyDrawingCache();
+        }
+
+        super.onStop();
+
+    }
+
+    private void upLoadFileFaile() {
+        try {
+            JSONObject contentObj = baseJsonObject.getJSONObject("content");
+            String uuid = contentObj.getString("uuid");
+            contentObj.put("type", "error");
+            contentObj.put("uuid", uuid);
+            contentObj.put("message", "上传失败");
+            accompanyFragment.onSendMessage(baseJsonObject.toString());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+    }
+
+    @Override
+    public void finish() {
+        //为了适配华为mata40曲面屏,此页面是横屏,前一页面是竖屏,返回回去的时候会有UI闪动,所以这样处理
+        //观察发现横屏模式时候没有达到曲面最大效果(竖屏可以达到),猜测横屏模式就不是曲面模式了,这个时候回到前面(竖屏)页面,屏幕的最外层的容器可能会变化导致UI闪动
+        checkScreenOrientation();
+        super.finish();
+    }
+
+    @Override
+    public void onBackPressed() {
+        //为了适配华为mata40曲面屏,此页面是横屏,前一页面是竖屏,返回回去的时候会有UI闪动,所以这样处理
+        //观察发现横屏模式时候没有达到曲面最大效果(竖屏可以达到),猜测横屏模式就不是曲面模式了,这个时候回到前面(竖屏)页面,屏幕的最外层的容器可能会变化导致UI闪动
+        checkScreenOrientation();
+        super.onBackPressed();
+    }
+
+    private void checkScreenOrientation() {
+        LOG.i("isNeedResetScreenOrientation:" + isNeedResetScreenOrientation);
+        if (isNeedResetScreenOrientation) {
+            if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
+                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+            }
+        }
+    }
+}

+ 2569 - 0
accompany/src/main/java/com/daya/orchestra/accompany/web/AccompanyFragment.java

@@ -0,0 +1,2569 @@
+package com.daya.orchestra.accompany.web;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.bluetooth.BluetoothHeadset;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.SystemClock;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.webkit.ValueCallback;
+import android.webkit.WebResourceError;
+import android.webkit.WebResourceRequest;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.alibaba.android.arouter.launcher.ARouter;
+import com.alipay.sdk.app.PayTask;
+import com.cooleshow.base.bean.WxPayResult;
+import com.cooleshow.base.common.BaseApplication;
+import com.cooleshow.base.common.WebConstants;
+import com.cooleshow.base.constanst.Constants;
+import com.cooleshow.base.data.net.RetrofitClientNoToken;
+import com.cooleshow.base.recorder.AudioChunk;
+import com.cooleshow.base.recorder.AudioRecordConfig;
+import com.cooleshow.base.recorder.MsRecorder;
+import com.cooleshow.base.recorder.PullTransport;
+import com.cooleshow.base.recorder.Recorder;
+import com.cooleshow.base.router.RouterPath;
+import com.cooleshow.base.ui.fragment.BaseMVPFragment;
+import com.cooleshow.base.utils.AppUtils;
+import com.cooleshow.base.utils.ClipboardUtils;
+import com.cooleshow.base.utils.EncodeUtils;
+import com.cooleshow.base.utils.HeadsetPlugListener;
+import com.cooleshow.base.utils.HeadsetPlugReceiver;
+import com.cooleshow.base.utils.LOG;
+import com.cooleshow.base.utils.LogUtils;
+import com.cooleshow.base.utils.MyFileUtils;
+import com.cooleshow.base.utils.PermissionUtils;
+import com.cooleshow.base.utils.ToastUtil;
+import com.cooleshow.base.utils.Utils;
+import com.cooleshow.base.utils.WebParamsUtils;
+import com.cooleshow.base.websocket.JWebSocketClient;
+import com.cooleshow.base.widgets.BaseDialog;
+import com.cooleshow.base.widgets.CommonDialog;
+import com.cooleshow.base.widgets.DialogUtil;
+import com.cooleshow.base.widgets.LollipopFixedWebView;
+import com.cooleshow.base.widgets.ViewConvertListener;
+import com.cooleshow.base.widgets.ViewHolder;
+import com.cooleshow.musictuner.utils.MusicTunerHelper;
+import com.cooleshow.usercenter.helper.UserHelper;
+import com.daya.orchestra.accompany.BuildConfig;
+import com.daya.orchestra.accompany.api.DownloadApi;
+import com.daya.orchestra.accompany.bean.alipay.AuthResult;
+import com.daya.orchestra.accompany.bean.alipay.PayResult;
+import com.daya.orchestra.accompany.bean.weixinpay.WeixinPayInfo;
+import com.daya.orchestra.accompany.databinding.FragmentAccompanyBinding;
+import com.daya.orchestra.accompany.helper.AccompanyPlayHelper;
+import com.daya.orchestra.accompany.helper.ShareHelper;
+import com.daya.orchestra.accompany.js.JsInterfaceAccomPanyUtils;
+import com.daya.orchestra.accompany.presenter.AccompanyPresenter;
+import com.google.gson.Gson;
+import com.jinmingyunle.midiplaylib.MidiPlayerUtils;
+import com.tbruyelle.rxpermissions3.RxPermissions;
+import com.tencent.mm.opensdk.modelpay.PayReq;
+import com.tencent.mm.opensdk.openapi.IWXAPI;
+import com.tencent.mm.opensdk.openapi.WXAPIFactory;
+import com.umeng.socialize.ShareAction;
+import com.umeng.socialize.UMShareAPI;
+import com.umeng.socialize.UMShareListener;
+import com.umeng.socialize.bean.SHARE_MEDIA;
+import com.umeng.socialize.media.UMImage;
+import com.umeng.socialize.media.UMWeb;
+import com.umeng.socialize.shareboard.SnsPlatform;
+import com.umeng.socialize.utils.ShareBoardlistener;
+
+import org.greenrobot.eventbus.EventBus;
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
+import org.java_websocket.enums.ReadyState;
+import org.java_websocket.handshake.ServerHandshake;
+import org.jetbrains.annotations.NotNull;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.io.File;
+import java.lang.ref.WeakReference;
+import java.net.URI;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Map;
+
+import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
+import io.reactivex.rxjava3.annotations.NonNull;
+import io.reactivex.rxjava3.core.Observable;
+import io.reactivex.rxjava3.core.ObservableEmitter;
+import io.reactivex.rxjava3.core.ObservableOnSubscribe;
+import io.reactivex.rxjava3.core.Observer;
+import io.reactivex.rxjava3.disposables.Disposable;
+import io.reactivex.rxjava3.schedulers.Schedulers;
+import okhttp3.ResponseBody;
+
+import static com.luck.picture.lib.thread.PictureThreadUtils.runOnUiThread;
+
+/**
+ * 创建日期:2022/6/8 14:32
+ *
+ * @author Ryan
+ * 类说明:
+ */
+public class AccompanyFragment extends BaseMVPFragment<FragmentAccompanyBinding, AccompanyPresenter> implements JsInterfaceAccomPanyUtils.onGetMethodsListener {
+    private final static String TAG = "AccompanyFragmenttag";
+    private IWXAPI api;
+    private static final int SDK_PAY_FLAG = 1;
+    private static final int SDK_AUTH_FLAG = 2;
+    public static final int SHARE_CHAT_REQUEST_CODE = 501;
+    public static final int MESSAGE_TYPE_UPDATE_LOADING_START = 5010;//开始更新加载loading
+    public static final int MESSAGE_TYPE_UPDATE_LOADING = 5011;//更新加载loading
+    public static final int MESSAGE_TYPE_UPDATE_LOADING_COMPLETE = 5012;//完成加载loading
+    public static final int MESSAGE_TYPE_HIDE_LOADING = 5013;//隐藏加载loading
+    FrameLayout viewParent;
+    WebView webView;
+    private UMShareListener mShareListener;
+    private String url;
+    private Recorder wavRecorder;
+    private JWebSocketClient webSocketClient;
+    private ShareAction mShareAction;
+    private URI webSocketUri = URI.create("BuildConfig.BASE_STU_SOCKET_URL");
+    private String mImageBase64;
+    private MusicTunerHelper mMusicTunerHelper;
+    private int currentProgressCount = 0;
+    private Handler mLoadHandler = new Handler(Looper.getMainLooper()) {
+        @Override
+        public void handleMessage(@androidx.annotation.NonNull Message msg) {
+            if (msg.what == MESSAGE_TYPE_UPDATE_LOADING) {
+                currentProgressCount++;
+                if (currentProgressCount <= 10) {
+                    updateProgress(4);
+                } else if (currentProgressCount <= 30) {
+                    updateProgress(2);
+                } else {
+                    updateProgress(1);
+                }
+                sendProgressMessage(MESSAGE_TYPE_UPDATE_LOADING, 1000);
+                return;
+            }
+
+            if (msg.what == MESSAGE_TYPE_UPDATE_LOADING_START) {
+                showLoadingAnim();
+                sendProgressMessage(MESSAGE_TYPE_UPDATE_LOADING, 1000);
+                return;
+            }
+
+            if (msg.what == MESSAGE_TYPE_UPDATE_LOADING_COMPLETE) {
+                if (mViewBinding == null || isDetached()) {
+                    return;
+                }
+                mViewBinding.progress.setProgress(100);
+                sendProgressMessage(MESSAGE_TYPE_HIDE_LOADING, 300);
+                return;
+            }
+
+            if (msg.what == MESSAGE_TYPE_HIDE_LOADING) {
+                hideLoadingAnim();
+                return;
+            }
+        }
+    };
+    private AccompanyPlayHelper mPlayHelper;
+
+    public static AccompanyFragment newInstance(String url) {
+        AccompanyFragment fragment = new AccompanyFragment();
+        Bundle args = new Bundle();
+        args.putString("url", url);
+        fragment.setArguments(args);
+        return fragment;
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        EventBus.getDefault().register(this);
+//        api = WXAPIFactory.createWXAPI(getContext(), null);
+//        api.registerApp("wx149a928c415c137a");
+    }
+
+    @Override
+    protected FragmentAccompanyBinding getLayoutView() {
+        return FragmentAccompanyBinding.inflate(getLayoutInflater());
+    }
+
+    @Override
+    protected AccompanyPresenter createPresenter() {
+        return new AccompanyPresenter();
+    }
+
+    @Override
+    protected void initView(View rootView) {
+        viewParent = mViewBinding.viewParent;
+        if (getArguments() != null) {
+            url = getArguments().getString("url");
+        }
+        String localUserId = UserHelper.getUserId();
+        webSocketUri = URI.create(WebConstants.getWebSocketUrl() + "/" + localUserId);
+        try {
+            if (Build.VERSION.SDK_INT >= 21 && Build.VERSION.SDK_INT < 23) {
+                webView = new LollipopFixedWebView(getActivity());
+            } else {
+                webView = new WebView(getActivity());
+            }
+        } catch (Exception e) {
+
+        }
+        if (null == webView) {
+            return;
+        }
+        JsInterfaceAccomPanyUtils jsInterfaceUtils = new JsInterfaceAccomPanyUtils(getActivity());
+        jsInterfaceUtils.setOnGetMethodsListener(this);
+        webView.addJavascriptInterface(jsInterfaceUtils, WebConstants.WEB_JS_INTERFACE);
+        viewParent.addView(webView, new FrameLayout.LayoutParams(
+                FrameLayout.LayoutParams.MATCH_PARENT,
+                FrameLayout.LayoutParams.MATCH_PARENT));
+        webView.setBackgroundColor(0);
+        initWebView();
+        webView.setWebViewClient(new WebClient());
+        LOG.i("url:" + url);
+        webView.loadUrl(url);
+
+    }
+
+
+    @Override
+    protected void initData() {
+        mPlayHelper = new AccompanyPlayHelper(getContext(), webSocketClient, webView);
+
+        mShareListener = new CustomShareListener(getActivity());
+        mViewBinding.ivLoadingBack.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                if (getActivity() != null) {
+                    getActivity().onBackPressed();
+                }
+            }
+        });
+    }
+
+
+    private void initWebView() {
+        //声明WebSettings子类
+        WebSettings webSettings = webView.getSettings();
+        webSettings.setUserAgentString(webSettings.getUserAgentString() + WebConstants.WEB_UA_PARAMS + WebConstants.getCustomUAParams());
+        webSettings.setGeolocationDatabasePath(getContext().getFilesDir().getPath());
+        webSettings.setGeolocationEnabled(true);
+        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
+        //如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript
+        webSettings.setJavaScriptEnabled(true);
+        webSettings.setMediaPlaybackRequiresUserGesture(false);//false允许自动播放音视频
+        //是否启用缓存
+        webSettings.setAppCacheEnabled(true);
+
+        // 开启DOM缓存,默认状态下是不支持LocalStorage的
+        webSettings.setDomStorageEnabled(true);
+        // 开启数据库缓存
+        webSettings.setDatabaseEnabled(true);
+
+        //设置自适应屏幕,两者合用
+        webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
+        webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
+        //缩放操作
+        webSettings.setSupportZoom(false); //支持缩放,默认为true。是下面那个的前提。
+        // 设置允许JS弹窗
+        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
+        //其他细节操作
+        webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE); //关闭webview中缓存
+        webSettings.setAllowFileAccess(true); //设置可以访问文件
+        webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
+        webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片
+        webSettings.setDefaultTextEncodingName("UTF-8");//设置编码格式
+
+        webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NARROW_COLUMNS);  //富文本适配
+        webSettings.setAppCacheMaxSize(Long.MAX_VALUE);
+        webSettings.setAppCachePath(getActivity().getDir("appcache", 0).getPath());
+        webSettings.setDatabasePath(getActivity().getDir("databases", 0).getPath());
+        webSettings.setGeolocationDatabasePath(getActivity().getDir("geolocation", 0)
+                .getPath());
+        webSettings.setPluginState(WebSettings.PluginState.ON_DEMAND);
+        if (BuildConfig.DEBUG) {
+            webView.setWebContentsDebuggingEnabled(true);
+        }
+        webSettings.setTextZoom(100);//设置字体默认的缩放比例,以避免手机系统的字体修改对页面字体及布局造成影响。
+        webView.setHorizontalScrollBarEnabled(false);
+        webView.setVerticalScrollBarEnabled(false);
+
+
+    }
+
+    private void updateProgress(int progress) {
+        if (mViewBinding == null || isDetached()) {
+            return;
+        }
+        int currentProgress = mViewBinding.progress.getProgress();
+        int totalProgress = currentProgress + progress;
+        if (totalProgress > 99) {
+            totalProgress = 99;
+        }
+        mViewBinding.progress.setProgress(totalProgress);
+    }
+
+    @Override
+    public void onSendMessage(String message) {
+        Log.i("accom", "message:" + message);
+        if (isDetached()) {
+            return;
+        }
+        if (webView != null) {
+            webView.evaluateJavascript("postMessage('" + message + "')", new ValueCallback<String>() {
+                @Override
+                public void onReceiveValue(String s) {
+                }
+            });
+        }
+    }
+
+    private int setColorCount = 0;
+
+    @Override
+    public void setStatusBarTextColor(boolean statusBarTextColor, JSONObject message) {
+        setColorCount++;
+        if (setColorCount == 1) {
+            if (onAccompanyListener != null)
+                onAccompanyListener.setStatusBarTextColor(false);
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    onSendMessage(message.toString());
+                }
+            }, 1500);
+        }
+
+    }
+
+    boolean isSendStartEvaluatingMessage = false;
+    boolean isWiredHeadsetOn = false;
+
+    /**
+     * 开始评测
+     *
+     * @param message
+     */
+    @SuppressLint("CheckResult")
+    @Override
+    public void startEvaluating(JSONObject message) {
+        isWiredHeadsetOn = false;
+        isSendStartEvaluatingMessage = true;
+        new RxPermissions(this)
+                .request(Manifest.permission.RECORD_AUDIO)
+                .subscribe(permission -> {
+                    if (permission) {
+//                        startWebSocketClient(message);
+                        if (webSocketClient != null && !webSocketClient.isOpen()) {
+                            if (webSocketClient.getReadyState() == ReadyState.NOT_YET_CONNECTED) {
+                                if (webSocketClient.isClosed()) {
+                                    webSocketClient.reconnectBlocking();
+                                } else {
+                                    webSocketClient.reconnectBlocking();
+                                }
+                            } else if (webSocketClient.getReadyState() == ReadyState.CLOSED) {
+                                webSocketClient.reconnectBlocking();
+                            }
+                            onSendMessage(message.toString());
+                            try {
+                                JSONObject jsonObject = new JSONObject();
+                                JSONObject headerObject = new JSONObject();
+                                headerObject.put("commond", "musicXml");
+                                headerObject.put("type", "SOUND_COMPARE");
+                                headerObject.put("status", 200);
+                                jsonObject.put("header", headerObject);
+                                jsonObject.put("body", message.getJSONObject("content"));
+                                webSocketClient.send(jsonObject.toString());
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                        } else {
+                            startWebSocketClient(message);
+                        }
+
+
+                    } else {
+                        try {
+                            JSONObject content = message.getJSONObject("content");
+                            content.put("reson", "没有麦克风权限");
+                            onSendMessage(message.toString());
+                            isSendStartEvaluatingMessage = false;
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                        }
+                        DialogUtil.showInCenter(getChildFragmentManager(), com.cooleshow.base.R.layout.accompany_permissions_popu, (holder, dialog) -> {
+                            TextView tvTitle = holder.getView(com.cooleshow.base.R.id.tv_title);
+                            TextView tvContent = holder.getView(com.cooleshow.base.R.id.tv_content);
+                            ImageView btncancel = holder.getView(com.cooleshow.base.R.id.btn_cancel);
+                            ImageView btnCommit = holder.getView(com.cooleshow.base.R.id.btn_commit);
+                            tvTitle.setText("提示");
+                            tvContent.setText("请开启麦克风访问权限");
+                            btncancel.setOnClickListener(view1 -> {
+
+                                dialog.dismiss();
+
+                            });
+                            btnCommit.setOnClickListener(view1 -> {
+                                PermissionUtils.toSelfSetting(getContext());
+                                dialog.dismiss();
+                            });
+                        });
+                    }
+                });
+
+    }
+
+    @Override
+    public void cloudLoading(JSONObject jsonObject) {
+        try {
+            JSONObject content = jsonObject.getJSONObject("content");
+            boolean show = content.optBoolean("show", false);
+
+            if (mLoadHandler != null) {
+                mLoadHandler.removeCallbacksAndMessages(null);
+                if (show) {
+                    sendProgressMessage(MESSAGE_TYPE_UPDATE_LOADING_START, 0);
+                } else {
+                    sendProgressMessage(MESSAGE_TYPE_UPDATE_LOADING_COMPLETE, 0);
+                }
+            }
+            onSendMessage(jsonObject.toString());
+        } catch (Exception e) {
+            sendProgressMessage(MESSAGE_TYPE_HIDE_LOADING, 0);
+            e.printStackTrace();
+        }
+    }
+
+    private void showLoadingAnim() {
+        if (mViewBinding.llLoading.getVisibility() != View.VISIBLE) {
+            currentProgressCount = 0;
+            mViewBinding.progress.setProgress(0);
+            mViewBinding.ivLoadingBack.setVisibility(View.VISIBLE);
+            mViewBinding.llLoading.setVisibility(View.VISIBLE);
+            mViewBinding.viewLoadingAnim.playAnimation();
+        }
+    }
+
+
+    private void sendProgressMessage(int type, int delayedTime) {
+        Message message = Message.obtain();
+        message.what = type;
+        mLoadHandler.sendMessageDelayed(message, delayedTime);
+    }
+
+    private void hideLoadingAnim() {
+        currentProgressCount = 0;
+        mViewBinding.ivLoadingBack.setVisibility(View.GONE);
+        mViewBinding.viewLoadingAnim.cancelAnimation();
+        mViewBinding.viewLoadingAnim.clearAnimation();
+        mViewBinding.llLoading.setVisibility(View.GONE);
+    }
+
+    JSONObject msg = null;
+
+    private void startWebSocketClient(JSONObject message) throws JSONException {
+        msg = message;
+        if (webSocketClient == null) {
+            Observable.create(new ObservableOnSubscribe<String>() {
+                        @Override
+                        public void subscribe(@NonNull ObservableEmitter<String> emitter) throws Exception {
+                            Map<String, String> httpHeaders = new HashMap<String, String>();
+                            httpHeaders.put("Authorization", UserHelper.getUserToken());
+                            webSocketClient = new JWebSocketClient(webSocketUri, httpHeaders) {
+                                @Override
+                                public void onMessage(String message) {
+                                    emitter.onNext(message);
+                                }
+
+                                @Override
+                                public void onError(Exception ex) {
+                                    super.onError(ex);
+                                    emitter.onNext("-2");
+                                }
+
+                                @Override
+                                public void onOpen(ServerHandshake handshakedata) {
+                                    super.onOpen(handshakedata);
+                                    emitter.onNext("-1");
+
+                                }
+
+                                @Override
+                                public void onClose(int code, String reason, boolean remote) {
+                                    super.onClose(code, reason, remote);
+                                    emitter.onNext("-2");
+                                }
+                            };
+                            webSocketClient.setConnectionLostTimeout(60 * 1000);
+                            webSocketClient.connect();
+                            mPlayHelper.setWebSocketClient(webSocketClient);
+                        }
+                    }).subscribeOn(Schedulers.newThread())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new Observer<String>() {
+                        @Override
+                        public void onSubscribe(@NonNull Disposable d) {
+
+                        }
+
+                        @Override
+                        public void onNext(@NonNull String serverMsg) {
+                            Log.i("pq", "serverMsg:" + serverMsg);
+                            if ("-1".equals(serverMsg) && isWiredHeadsetOn) {
+                                if (msg != null) {
+                                    onSendMessage(msg.toString());
+                                }
+
+                            } else if ("-1".equals(serverMsg) && isSendStartEvaluatingMessage) {
+
+                                if (msg != null) {
+                                    onSendMessage(msg.toString());
+                                }
+                                try {
+                                    JSONObject jsonObject = new JSONObject();
+                                    JSONObject headerObject = new JSONObject();
+                                    headerObject.put("commond", "musicXml");
+                                    headerObject.put("type", "SOUND_COMPARE");
+                                    headerObject.put("status", 200);
+                                    jsonObject.put("header", headerObject);
+                                    jsonObject.put("body", msg.getJSONObject("content"));
+                                    webSocketClient.send(jsonObject.toString());
+                                } catch (Exception e) {
+                                    e.printStackTrace();
+                                }
+                            } else if ("-2".equals(serverMsg)) {
+                                if (isSendStartEvaluatingMessage) {
+                                    isSendStartEvaluatingMessage = false;
+
+                                    try {
+                                        if (msg != null) {
+                                            JSONObject content = msg.getJSONObject("content");
+                                            content.put("reson", "服务异常,请重试");
+                                            onSendMessage(msg.toString());
+                                            msg = null;
+                                        }
+                                    } catch (Exception e3) {
+                                        e3.printStackTrace();
+                                    }
+                                } else if (isWiredHeadsetOn) {
+                                    isWiredHeadsetOn = false;
+                                    return;
+                                }
+//                                } else {
+//                                    LOG.i(TAG, "onNext: serverMsg333" + isWiredHeadsetOn);
+//                                    JSONObject object = new JSONObject();
+//                                    JSONObject reson = new JSONObject();
+//                                    try {
+//                                        reson.put("reson", "服务异常,请重试");
+//                                        object.put("api", "cancelEvaluating");
+//                                        object.put("content", reson);
+//                                        onSendMessage(object.toString());
+//                                    } catch (JSONException e2) {
+//                                        e2.printStackTrace();
+//                                    }
+//                                }
+                            } else {
+                                JSONObject object = new JSONObject();
+                                try {
+                                    object.put("api", "sendResult");
+                                    object.put("content", new JSONObject(serverMsg));
+                                    onSendMessage(object.toString());
+                                } catch (Exception e) {
+                                    e.printStackTrace();
+                                }
+                                if (mPlayHelper != null) {
+                                    mPlayHelper.parseServerMsg(serverMsg);
+                                }
+                            }
+
+                            msg = null;
+
+                        }
+
+                        @Override
+                        public void onError(@NonNull Throwable e) {
+
+                            if (isSendStartEvaluatingMessage) {
+                                isSendStartEvaluatingMessage = false;
+                                try {
+                                    JSONObject content = msg.getJSONObject("content");
+                                    content.put("reson", "服务异常,请重试");
+                                    onSendMessage(msg.toString());
+                                    msg = null;
+                                } catch (Exception e3) {
+                                    e3.printStackTrace();
+                                }
+                            } else if (isWiredHeadsetOn) {
+                                isWiredHeadsetOn = false;
+                            } else {
+                                JSONObject object = new JSONObject();
+                                JSONObject reson = new JSONObject();
+                                try {
+                                    reson.put("reson", "服务异常,请重试");
+                                    object.put("api", "cancelEvaluating");
+                                    object.put("content", reson);
+                                    onSendMessage(object.toString());
+                                } catch (Exception e2) {
+                                    e.printStackTrace();
+                                }
+                            }
+                            msg = null;
+                        }
+
+                        @Override
+                        public void onComplete() {
+
+                        }
+                    });
+
+        } else if (webSocketClient.isClosed() || webSocketClient.isClosing()) {
+
+            if (isWiredHeadsetOn) {
+                isSendStartEvaluatingMessage = false;
+            } else {
+                isSendStartEvaluatingMessage = true;
+            }
+            webSocketClient.reconnect();
+        } else {
+            if (isWiredHeadsetOn) {
+                isSendStartEvaluatingMessage = false;
+            } else if (isSendStartEvaluatingMessage) {
+                isWiredHeadsetOn = false;
+                try {
+                    JSONObject jsonObject = new JSONObject();
+                    JSONObject headerObject = new JSONObject();
+                    headerObject.put("commond", "musicXml");
+                    headerObject.put("type", "SOUND_COMPARE");
+                    headerObject.put("status", 200);
+                    jsonObject.put("header", headerObject);
+                    jsonObject.put("body", message.getJSONObject("content"));
+                    webSocketClient.send(jsonObject.toString());
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+            onSendMessage(message.toString());
+
+
+        }
+    }
+
+    boolean isSendRecordStartTime = false;
+
+    /**
+     * 开始录音
+     *
+     * @param message
+     */
+    public void startRecording(JSONObject message) {
+        onSendMessage(message.toString());
+
+        isSendRecordStartTime = true;
+        if (webSocketClient != null && webSocketClient.isOpen()) {
+            try {
+                JSONObject jsonObject = new JSONObject();
+                JSONObject headerObject = new JSONObject();
+                headerObject.put("commond", "recordStart");
+                headerObject.put("type", "SOUND_COMPARE");
+                headerObject.put("status", 200);
+                jsonObject.put("header", headerObject);
+                webSocketClient.send(jsonObject.toString());
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        if (wavRecorder != null) {
+            wavRecorder.stopRecording();
+            MyFileUtils.deleteFile(getVoicePath());
+            wavRecorder.startRecording(getContext());
+        } else {
+            Observable.create((ObservableOnSubscribe<String>) emitter -> {
+                        wavRecorder = MsRecorder.wav(
+                                new File(getVoicePath()),
+                                new AudioRecordConfig(),
+                                // AudioRecordConfig(MediaRecorder.AudioSource.MIC, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_IN_MONO, 44100),
+                                new PullTransport.Default().setOnAudioChunkPulledListener(new PullTransport.OnAudioChunkPulledListener() {
+                                    @Override
+                                    public void onAudioChunkPulled(AudioChunk audioChunk) {
+                                        if (webSocketClient != null && webSocketClient.isOpen()) {
+                                            webSocketClient.send(audioChunk.toBytes());
+                                            if (isSendRecordStartTime) {
+                                                isSendRecordStartTime = false;
+                                                emitter.onNext("-2");
+                                            }
+                                        } else {
+                                            emitter.onNext("-1");
+                                        }
+                                    }
+                                }));
+                        if (wavRecorder != null) {
+                            wavRecorder.startRecording(getContext());
+                        }
+                        emitter.onNext("1");
+                    }).subscribeOn(Schedulers.newThread())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new Observer<String>() {
+                        @Override
+                        public void onSubscribe(@NonNull Disposable d) {
+
+                        }
+
+                        @Override
+                        public void onNext(@NonNull String recorder) {
+                            if ("-2".equals(recorder) && isSendStartEvaluatingMessage) {
+//                                try {
+//                                    JSONObject json = new JSONObject();
+//                                    JSONObject content = new JSONObject();
+//                                    content.put("inteveral", System.currentTimeMillis());
+//                                    json.put("api", "recordStartTime");
+//                                    json.put("content", content);
+//                                    onSendMessage(json.toString());
+//
+//                                } catch (JSONException e) {
+//                                    e.printStackTrace();
+//                                }
+                                long bufferDuration = (long) wavRecorder.getBufferDuration();
+                                long time = System.currentTimeMillis();
+                                long result = time - bufferDuration;
+                                mPlayHelper.toPlay2(result,isMuteMode(message));
+                            } else if ("1".equals(recorder)) {
+//                                onSendMessage(message.toString());
+                            } else {
+                                JSONObject object = new JSONObject();
+                                JSONObject reson = new JSONObject();
+                                try {
+                                    reson.put("reson", "服务异常,请重试");
+                                    object.put("api", "cancelEvaluating");
+                                    object.put("content", reson);
+                                    onSendMessage(object.toString());
+                                    if (wavRecorder != null) {
+                                        wavRecorder.stopRecording();
+                                    }
+                                } catch (JSONException e2) {
+                                    e2.printStackTrace();
+                                }
+                            }
+                        }
+
+                        @Override
+                        public void onError(@NonNull Throwable e) {
+                            JSONObject object = new JSONObject();
+                            JSONObject reson = new JSONObject();
+                            try {
+                                reson.put("reson", "录制错误,请重试");
+                                object.put("api", "cancelEvaluating");
+                                object.put("content", reson);
+                                onSendMessage(object.toString());
+                                if (wavRecorder != null) {
+                                    wavRecorder.stopRecording();
+                                }
+                            } catch (JSONException e2) {
+                                e2.printStackTrace();
+                            }
+                        }
+
+                        @Override
+                        public void onComplete() {
+
+                        }
+                    });
+
+
+        }
+    }
+
+    private boolean isMuteMode(JSONObject jsonObject) {
+        if (jsonObject == null) {
+            return false;
+        }
+        JSONObject contentJson = jsonObject.optJSONObject("content");
+        //1是开启伴奏 0是静音伴奏
+        int accompanimentState = contentJson.optInt("accompanimentState", 1);
+        return accompanimentState == 0;
+    }
+
+    /**
+     * 结束录音
+     *
+     * @param message
+     */
+    @Override
+    public void endRecording(JSONObject message) {
+        if (wavRecorder == null) {
+            return;
+        }
+        wavRecorder.stopRecording();
+        if (webSocketClient != null) {
+            webSocketClient.close();
+        }
+        onSendMessage(message.toString());
+    }
+
+
+    @Override
+    public void keepScreenLongLight(JSONObject message) {
+        onSendMessage(message.toString());
+        isWiredHeadsetOn = true;
+        if (webSocketClient == null) {
+            new RxPermissions(this)
+                    .request(Manifest.permission.RECORD_AUDIO)
+                    .subscribe(permission -> {
+                        if (permission) {
+                            startWebSocketClient(message);
+                        } else {
+                            DialogUtil.showInCenter(getChildFragmentManager(), com.cooleshow.base.R.layout.accompany_permissions_popu, (holder, dialog) -> {
+                                TextView tvTitle = holder.getView(com.cooleshow.base.R.id.tv_title);
+                                TextView tvContent = holder.getView(com.cooleshow.base.R.id.tv_content);
+                                ImageView btncancel = holder.getView(com.cooleshow.base.R.id.btn_cancel);
+                                ImageView btnCommit = holder.getView(com.cooleshow.base.R.id.btn_commit);
+                                tvTitle.setText("提示");
+                                tvContent.setText("请开启麦克风访问权限");
+                                btncancel.setOnClickListener(view1 -> {
+
+                                    dialog.dismiss();
+
+                                });
+                                btnCommit.setOnClickListener(view1 -> {
+                                    PermissionUtils.toSelfSetting(getContext());
+                                    dialog.dismiss();
+                                });
+                            });
+                        }
+                    });
+        }
+    }
+
+    boolean isSoundCheckStartTime = false;
+
+    /**
+     * 开始校音
+     *
+     * @param message
+     */
+    @Override
+    public void startSoundCheck(JSONObject message) {
+        //开录制 实时把给后台传音频数据
+        onSendMessage(message.toString());
+        isSoundCheckStartTime = true;
+        if (webSocketClient != null && webSocketClient.isOpen()) {
+            try {
+                JSONObject jsonObject2 = new JSONObject();
+                JSONObject headerObject2 = new JSONObject();
+                headerObject2.put("commond", "start");
+                headerObject2.put("type", "PITCH_DETECTION");
+                headerObject2.put("status", 200);
+                jsonObject2.put("header", headerObject2);
+                webSocketClient.send(jsonObject2.toString());
+            } catch (Exception e) {
+
+            }
+        }
+
+        if (wavRecorder != null) {
+            wavRecorder.stopRecording();
+            MyFileUtils.deleteFile(getVoicePath());
+            wavRecorder.startRecording(getContext());
+        } else {
+            Observable.create((ObservableOnSubscribe<String>) emitter -> {
+                        wavRecorder = MsRecorder.wav(
+                                new File(getVoicePath()),
+                                new AudioRecordConfig(),
+                                // AudioRecordConfig(MediaRecorder.AudioSource.MIC, AudioFormat.ENCODING_PCM_16BIT, AudioFormat.CHANNEL_IN_MONO, 44100),
+                                new PullTransport.Default().setOnAudioChunkPulledListener(new PullTransport.OnAudioChunkPulledListener() {
+                                    @Override
+                                    public void onAudioChunkPulled(AudioChunk audioChunk) {
+                                        if (webSocketClient != null && webSocketClient.isOpen()) {
+                                            webSocketClient.send(audioChunk.toBytes());
+                                            if (isSoundCheckStartTime) {
+                                                isSoundCheckStartTime = false;
+                                                emitter.onNext("-2");
+                                            }
+                                        } else {
+                                            emitter.onNext("-1");
+                                        }
+                                    }
+                                }));
+                        if (wavRecorder != null) {
+                            wavRecorder.startRecording(getContext());
+                        }
+                        emitter.onNext("1");
+                    }).subscribeOn(Schedulers.newThread())
+                    .observeOn(AndroidSchedulers.mainThread())
+                    .subscribe(new Observer<String>() {
+                        @Override
+                        public void onSubscribe(@NonNull Disposable d) {
+                        }
+
+                        @Override
+                        public void onNext(@NonNull String recorder) {
+                            if ("-2".equals(recorder)) {
+                                try {
+                                    JSONObject json = new JSONObject();
+                                    JSONObject content = new JSONObject();
+                                    content.put("inteveral", System.currentTimeMillis());
+                                    json.put("api", "recordStartTime");
+                                    json.put("content", content);
+                                    onSendMessage(json.toString());
+
+                                } catch (JSONException e) {
+                                    e.printStackTrace();
+                                }
+                            } else if ("1".equals(recorder)) {
+//                                onSendMessage(message.toString());
+                            } else {
+                                JSONObject object = new JSONObject();
+                                JSONObject reson = new JSONObject();
+                                try {
+                                    reson.put("reson", "服务异常,请重试");
+                                    object.put("api", "cancelEvaluating");
+                                    object.put("content", reson);
+                                    onSendMessage(object.toString());
+                                    if (wavRecorder != null) {
+                                        wavRecorder.stopRecording();
+                                    }
+                                } catch (JSONException e2) {
+                                    e2.printStackTrace();
+                                }
+                            }
+                        }
+
+                        @Override
+                        public void onError(@NonNull Throwable e) {
+                            JSONObject object = new JSONObject();
+                            JSONObject reson = new JSONObject();
+                            try {
+                                reson.put("reson", "录制错误,请重试");
+                                object.put("api", "cancelEvaluating");
+                                object.put("content", reson);
+                                onSendMessage(object.toString());
+                                if (wavRecorder != null) {
+                                    wavRecorder.stopRecording();
+                                }
+                            } catch (JSONException e2) {
+                                e2.printStackTrace();
+                            }
+                        }
+
+                        @Override
+                        public void onComplete() {
+                        }
+                    });
+
+
+        }
+    }
+
+    /**
+     * 结束校音
+     *
+     * @param message
+     */
+    @Override
+    public void endSoundCheck(JSONObject message) {
+        //结束校音  校音结束 这个就是结束给后台传音频 停止录音
+        onSendMessage(message.toString());
+        if (wavRecorder == null) {
+            return;
+        }
+        wavRecorder.stopRecording();
+        wavRecorder = null;
+
+    }
+
+    // 录音文件存储名称
+    private String getVoicePath() {
+        return MyFileUtils.getRecordFilePath();
+    }
+
+    /**
+     * 结束评测
+     *
+     * @param jsonObject
+     */
+    @Override
+    public void endEvaluating(JSONObject jsonObject) {
+        if (wavRecorder == null) {
+            return;
+        }
+        wavRecorder.stopRecording();
+        wavRecorder = null;
+        if (mPlayHelper != null) {
+            mPlayHelper.stop();
+        }
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                if (webSocketClient != null && webSocketClient.isOpen()) {
+                    try {
+                        JSONObject jsonObject = new JSONObject();
+                        JSONObject headerObject = new JSONObject();
+                        headerObject.put("commond", "recordEnd");
+                        headerObject.put("type", "SOUND_COMPARE");
+                        headerObject.put("status", 200);
+                        jsonObject.put("header", headerObject);
+                        webSocketClient.send(jsonObject.toString());
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+                onSendMessage(jsonObject.toString());
+            }
+        }, 1000);
+
+//        Map<String, RequestBody> data = new HashMap<>();
+//        try {
+//            JSONObject content = jsonObject.getJSONObject("content");
+//            data.put("musicScoreId", RequestBodyUtil.convertToRequestBody(content.getString("musicScoreId")));
+//        } catch (JSONException e) {
+//            e.printStackTrace();
+//        }
+//        presenter.soundCompare(RequestBodyUtil.filesToMultipartBodyParts(new File(getVoicePath()), "record", "audio/wav"), data, jsonObject);
+    }
+
+    /**
+     * 取消评测
+     */
+    @Override
+    public void cancelEvaluating() {
+        if (wavRecorder == null) {
+            return;
+        }
+        wavRecorder.stopRecording();
+        MyFileUtils.deleteFile(getVoicePath());
+    }
+
+    /**
+     * 恢复录音
+     *
+     * @param jsonObject
+     */
+    @Override
+    public void resumeRecording(JSONObject jsonObject) {
+        if (wavRecorder != null) {
+            wavRecorder.resumeRecording(getContext());
+            onSendMessage(jsonObject.toString());
+        }
+    }
+
+    /**
+     * 暂停
+     *
+     * @param jsonObject
+     */
+    @Override
+    public void pauseRecording(JSONObject jsonObject) {
+        if (wavRecorder != null) {
+            wavRecorder.pauseRecording();
+            onSendMessage(jsonObject.toString());
+        }
+    }
+
+    /**
+     * 检测 耳机或者其他设备
+     *
+     * @param jsonObject
+     */
+    @SuppressLint("CheckResult")
+    @Override
+    public void isWiredHeadsetOn(JSONObject jsonObject) {
+
+
+        try {
+            JSONObject content = jsonObject.getJSONObject("content");
+            content.put("checkIsWired", !TextUtils.isEmpty(checkType()));
+            content.put("type", checkType());
+            onSendMessage(jsonObject.toString());
+
+            if (webSocketClient != null) {
+                if (webSocketClient.getReadyState() == ReadyState.NOT_YET_CONNECTED) {
+                    if (webSocketClient.isClosed()) {
+                        webSocketClient.reconnectBlocking();
+                    } else {
+                        webSocketClient.connectBlocking();
+                    }
+                } else if (webSocketClient.getReadyState() == ReadyState.CLOSED) {
+                    webSocketClient.reconnectBlocking();
+                }
+            } else {
+                startWebSocketClient(jsonObject);
+            }
+
+        } catch (JSONException | InterruptedException e) {
+            e.printStackTrace();
+        }
+
+
+    }
+
+    @Override
+    public void proxyMessage(JSONObject jsonObject) {
+        if (webSocketClient != null && webSocketClient.isOpen()) {
+            try {
+                JSONObject json = new JSONObject();
+                JSONObject headerObject = new JSONObject();
+                headerObject.put("commond", "proxyMessage");
+                json.put("header", headerObject);
+                json.put("body", jsonObject.getJSONObject("content"));
+                webSocketClient.send(json.toString());
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public void proxyServiceMessage(JSONObject mesage) {
+        // 直接把H5那边返回的 content转成string 给socket服务
+        try {
+            JSONObject content = null;
+            content = mesage.getJSONObject("content");
+            String type = content.getJSONObject("header").getString("type");
+            if ("SOUND_CHECK".equals(type)) {
+                return;
+            } else {
+                new Handler().postDelayed(new Runnable() {
+                    @Override
+                    public void run() {
+                        if (webSocketClient != null && webSocketClient.isOpen()) {
+                            try {
+                                JSONObject content = null;
+                                content = mesage.getJSONObject("content");
+                                webSocketClient.send(content.toString());
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                        }
+                        onSendMessage(mesage.toString());
+                    }
+                }, 1000);
+            }
+        } catch (JSONException e) {
+        }
+
+    }
+
+    /**
+     * 打开相册
+     *
+     * @param message
+     */
+    @Override
+    public void openCamera(JSONObject message) {
+        new RxPermissions(this)
+                .request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                .subscribe(permission -> {
+                    if (permission) {
+                        if (onAccompanyListener != null)
+                            onAccompanyListener.openCamera();
+                        onSendMessage(message.toString());
+                    } else {
+                        try {
+                            JSONObject content = message.getJSONObject("content");
+                            content.put("reson", "没有摄像头,麦克风,储存权限");
+                            onSendMessage(message.toString());
+                            isSendStartEvaluatingMessage = false;
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                        }
+                        DialogUtil.showInCenter(getChildFragmentManager(), com.cooleshow.base.R.layout.accompany_permissions_popu, (holder, dialog) -> {
+                            TextView tvTitle = holder.getView(com.cooleshow.base.R.id.tv_title);
+                            TextView tvContent = holder.getView(com.cooleshow.base.R.id.tv_content);
+                            ImageView btncancel = holder.getView(com.cooleshow.base.R.id.btn_cancel);
+                            ImageView btnCommit = holder.getView(com.cooleshow.base.R.id.btn_commit);
+                            tvTitle.setText("提示");
+                            tvContent.setText("请开启摄像头、麦克风、储存访问权限");
+                            btncancel.setOnClickListener(view1 -> {
+
+                                dialog.dismiss();
+
+                            });
+                            btnCommit.setOnClickListener(view1 -> {
+                                PermissionUtils.toSelfSetting(getContext());
+                                dialog.dismiss();
+                            });
+                        });
+                    }
+                });
+
+    }
+
+    @Override
+    public void closeCamera(JSONObject jsonObject) {
+        if (onAccompanyListener != null) {
+            onAccompanyListener.closeCamera();
+            onSendMessage(jsonObject.toString());
+        }
+    }
+
+    @Override
+    public void startCapture(JSONObject message) {
+        new RxPermissions(this)
+                .request(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO, Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                .subscribe(permission -> {
+                    if (permission) {
+                        if (onAccompanyListener != null) {
+                            onAccompanyListener.startCapture();
+                            onSendMessage(message.toString());
+                        }
+                    } else {
+                        try {
+                            JSONObject content = message.getJSONObject("content");
+                            content.put("reson", "没有摄像头,麦克风,储存权限");
+                            onSendMessage(message.toString());
+                            isSendStartEvaluatingMessage = false;
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                        }
+                        DialogUtil.showInCenter(getChildFragmentManager(), com.cooleshow.base.R.layout.accompany_permissions_popu, (holder, dialog) -> {
+                            TextView tvContent = holder.getView(com.cooleshow.base.R.id.tv_content);
+                            ImageView btncancel = holder.getView(com.cooleshow.base.R.id.btn_cancel);
+                            ImageView btnCommit = holder.getView(com.cooleshow.base.R.id.btn_commit);
+                            tvContent.setText("请开启摄像头、麦克风、储存访问权限");
+                            btncancel.setOnClickListener(view1 -> {
+
+                                dialog.dismiss();
+                            });
+                            btnCommit.setOnClickListener(view1 -> {
+                                PermissionUtils.toSelfSetting(getContext());
+                                dialog.dismiss();
+                            });
+                        });
+                    }
+                });
+
+    }
+
+    @Override
+    public void endCapture(JSONObject jsonObject) {
+        if (onAccompanyListener != null)
+            onAccompanyListener.endCapture();
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                onSendMessage(jsonObject.toString());
+            }
+        }, 1500);
+    }
+
+    private String checkType() {
+        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+            AudioDeviceInfo[] devices = audioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
+            for (AudioDeviceInfo device : devices) {
+                int deviceType = device.getType();
+                if (deviceType == AudioDeviceInfo.TYPE_WIRED_HEADSET || deviceType == AudioDeviceInfo.TYPE_WIRED_HEADPHONES) {
+                    return "有线耳机";
+                }
+                if (deviceType == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP
+                        || deviceType == AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
+                    return "蓝牙耳机";
+                }
+                if (deviceType == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
+                    if (Constants.HEADSET_PLUE_TAG.isEmpty()) {
+                        return null;
+                    } else {
+                        return "有线耳机";
+                    }
+                }
+            }
+        } else {
+            if (audioManager.isWiredHeadsetOn()) {
+                return "有线耳机";
+            }
+            if (audioManager.isBluetoothScoOn() || audioManager.isBluetoothA2dpOn()) {
+                return "蓝牙耳机";
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public void videoUpdate(JSONObject jsonObject) {
+        if (onAccompanyListener != null)
+            onAccompanyListener.videoUpdate(jsonObject);
+        new Handler().postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                onSendMessage(jsonObject.toString());
+            }
+        }, 1500);
+    }
+
+    private class WebClient extends WebViewClient {
+        //页面开始载入时调用
+        private int webViewPreviousState;
+        private final int PAGE_STARTED = 0x1;
+        private final int PAGE_REDIRECTED = 0x2;
+
+        @Override
+        public void onPageStarted(WebView view, String url, Bitmap favicon) {
+            super.onPageStarted(view, url, favicon);
+            webViewPreviousState = PAGE_STARTED;
+        }
+
+        //页面载入结束时调用
+        @Override
+        public void onPageFinished(WebView view, String url) {
+            super.onPageFinished(view, url);
+            if (webViewPreviousState == PAGE_STARTED) {
+                if (listener != null) {
+                    listener.onPageFinished();
+                }
+            }
+        }
+
+        //截取url请求,在当前视图加载,避免在跳转到自带浏览器
+        @Override
+        public boolean shouldOverrideUrlLoading(WebView view, String request) {
+            webViewPreviousState = PAGE_REDIRECTED;
+            view.loadUrl(request);
+            return true;
+        }
+
+        //处理报错信息
+        @Override
+        public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
+            super.onReceivedError(view, request, error);
+        }
+    }
+
+
+    public void sendMessage(String message) {
+        LogUtils.i("sendMessage:" + message);
+        webView.evaluateJavascript("postMessage('" + message + "','*')", new ValueCallback<String>() {
+            @Override
+            public void onReceiveValue(String s) {
+            }
+        });
+    }
+
+
+    //注册蓝牙连接状态
+    public void registRecevier() {
+        IntentFilter intentFilter = new IntentFilter();
+        intentFilter.addAction(Intent.ACTION_HEADSET_PLUG);
+        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+        mContext.registerReceiver(new HeadsetPlugReceiver(new HeadsetPlugListener() {
+            @Override
+            public void onHeadsetPlug(boolean isPlug, String type) {
+                getActivity().runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        JSONObject jsonObject = new JSONObject();
+                        try {
+                            jsonObject.put("api", "listenerWiredStatus");
+                            JSONObject content = new JSONObject();
+                            content.put("checkIsWired", isPlug);
+                            content.put("type", type);
+                            jsonObject.put("content", content);
+                            onSendMessage(jsonObject.toString());
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                });
+            }
+        }), intentFilter);
+    }
+
+    @Override
+    public void onPause() {
+        super.onPause();
+
+        if (webView != null) {
+            try {
+                JSONObject jsonObject = new JSONObject();
+                jsonObject.put("api", "suspendPlay");
+                onSendMessage(jsonObject.toString());
+            } catch (JSONException e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mPlayHelper != null) {
+            mPlayHelper.release();
+        }
+        if (mHandler != null) {
+            mHandler.removeCallbacksAndMessages(null);
+        }
+        if (loopHandler != null) {
+            loopHandler.removeCallbacksAndMessages(null);
+        }
+        if (mLoadHandler != null) {
+            mLoadHandler.removeCallbacksAndMessages(null);
+        }
+        mViewBinding.viewLoadingAnim.cancelAnimation();
+        super.onDestroy();
+        if (null != webView) {
+            webView.destroy();
+        }
+        if (wavRecorder != null) {
+            wavRecorder.stopRecording();
+        }
+        if (webSocketClient != null) {
+            webSocketClient.close();
+        }
+        EventBus.getDefault().unregister(this);
+        MidiPlayerUtils.getInstance().stopPlay();
+        UMShareAPI.get(mContext).release();
+        if (mMusicTunerHelper != null) {
+            mMusicTunerHelper.release();
+        }
+    }
+
+    @Override
+    public void onStop() {
+        super.onStop();
+        isReady = true;
+        if (null != soundpool) {
+            soundpool.release();
+        }
+        loopHandler.removeMessages(1);
+        //取消评测
+        if (MidiPlayerUtils.getInstance().isPlaying()) {
+            MidiPlayerUtils.getInstance().stopPlay();
+            try {
+                JSONObject content = new JSONObject();
+                content.put("songID", midiSongId);
+                JSONObject json = new JSONObject();
+                json.put("api", "cloudplayed");
+                json.put("content", content);
+                onSendMessage(json.toString());
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public onListener onAccompanyListener;
+
+    public void setOnAccompanyListener(onListener onAccompanyListener) {
+        this.onAccompanyListener = onAccompanyListener;
+    }
+
+    public interface onListener {
+        void setStatusBarTextColor(boolean statusBarTextColor);
+
+        void openCamera();
+
+
+        void closeCamera();
+
+        void startCapture();
+
+        void endCapture();
+
+
+        void videoUpdate(JSONObject jsonObject);
+
+        /**
+         * mp3伴奏下载
+         *
+         * @param url
+         */
+        void onDownloadAccompaniment(String url);
+
+        void openAdjustRecording(String recordId, String title, String coverImg);
+    }
+
+    public WebViewListener listener;
+
+    public void setOnWebViewListener(WebViewListener listener) {
+        this.listener = listener;
+    }
+
+    public interface WebViewListener {
+        void onPageFinished();
+
+    }
+
+
+    @Override
+    public void shareAchievements(JSONObject message) {
+        JSONObject content = null;
+        try {
+            content = message.getJSONObject("content");
+
+            String shareTitle = content.getString("title");
+            String shareDesc = content.getString("desc");
+            if ("video".equals(content.getString("type"))) {
+                String videoUrl = content.getString("video");
+                /*增加自定义按钮的分享面板*/
+                mShareAction = new ShareAction(getActivity()).setDisplayList(
+                                SHARE_MEDIA.WEIXIN, SHARE_MEDIA.WEIXIN_CIRCLE, SHARE_MEDIA.SINA)
+                        .setShareboardclickCallback(new ShareBoardlistener() {
+                            @Override
+                            public void onclick(SnsPlatform snsPlatform, SHARE_MEDIA share_media) {
+                                UMWeb video = new UMWeb(videoUrl);
+                                video.setTitle(shareTitle);
+                                video.setDescription(shareDesc.isEmpty() ? shareTitle : shareDesc);
+                                new ShareAction(getActivity()).withMedia(video)
+                                        .setPlatform(share_media)
+                                        .setCallback(mShareListener)
+                                        .share();
+
+                            }
+                        });
+                mShareAction.open();
+            } else {
+                mImageBase64 = content.getString("image");
+                String shareUrl = WebParamsUtils.getShareUrl(content);
+                /*增加自定义按钮的分享面板*/
+                mShareAction = new ShareAction(getActivity()).setDisplayList(
+                                SHARE_MEDIA.WEIXIN, SHARE_MEDIA.WEIXIN_CIRCLE, SHARE_MEDIA.SINA)
+                        .setShareboardclickCallback(new ShareBoardlistener() {
+                            @Override
+                            public void onclick(SnsPlatform snsPlatform, SHARE_MEDIA share_media) {
+                                if (share_media == null) {
+                                    if (TextUtils.equals(snsPlatform.mKeyword, "chat")) {
+                                        //分享群聊
+                                        ARouter.getInstance().build(RouterPath.ChatCenter.CHAT_SELECT_CONTACT)
+                                                .navigation(getActivity(), SHARE_CHAT_REQUEST_CODE);
+                                    } else if (TextUtils.equals(snsPlatform.mKeyword, "copy")) {
+                                        if (!TextUtils.isEmpty(shareUrl)) {
+                                            ClipboardUtils.copyText(shareUrl);
+                                            ToastUtil.getInstance().showShort("复制成功");
+                                        }
+                                    }
+                                } else {
+                                    if (!UMShareAPI.get(Utils.getApp()).isInstall(getActivity(), share_media)) {
+                                        ToastUtil.getInstance().show(Utils.getApp(), "应用未安装,分享失败");
+                                        return;
+                                    }
+                                    UMImage image = new UMImage(mContext, MyFileUtils.base64ToBitmap(mImageBase64.split(",")[1]));//bitmap文件
+                                    image.setThumb(image);
+                                    image.compressFormat = Bitmap.CompressFormat.PNG;
+                                    image.compressStyle = UMImage.CompressStyle.SCALE;
+                                    new ShareAction(getActivity()).withMedia(image)
+                                            .setPlatform(share_media)
+                                            .setCallback(mShareListener)
+                                            .share();
+                                }
+
+                            }
+                        });
+                mShareAction.addButton("群聊", "chat", "icon_share_chat_group", "icon_share_chat_group");
+                if (WebParamsUtils.isCopyLink(content)) {
+                    mShareAction.addButton("复制链接", "copy", "icon_share_copy_link", "icon_share_copy_link");
+                }
+                mShareAction.open();
+            }
+        } catch (JSONException e) {
+
+        }
+    }
+
+
+    private static class CustomShareListener implements UMShareListener {
+
+        private WeakReference<AccompanyActivity> mActivity;
+
+        private CustomShareListener(Activity activity) {
+            mActivity = new WeakReference(activity);
+        }
+
+        @Override
+        public void onStart(SHARE_MEDIA platform) {
+
+        }
+
+        @Override
+        public void onResult(SHARE_MEDIA platform) {
+
+            if (platform.name().equals("WEIXIN_FAVORITE")) {
+                ToastUtil.getInstance().showShort("收藏成功啦");
+            } else {
+                if (platform != SHARE_MEDIA.MORE && platform != SHARE_MEDIA.SMS
+                        && platform != SHARE_MEDIA.EMAIL
+                        && platform != SHARE_MEDIA.FLICKR
+                        && platform != SHARE_MEDIA.FOURSQUARE
+                        && platform != SHARE_MEDIA.TUMBLR
+                        && platform != SHARE_MEDIA.POCKET
+                        && platform != SHARE_MEDIA.PINTEREST
+
+                        && platform != SHARE_MEDIA.INSTAGRAM
+                        && platform != SHARE_MEDIA.YNOTE
+                        && platform != SHARE_MEDIA.EVERNOTE) {
+                    ToastUtil.getInstance().showShort("分享成功啦");
+                }
+
+            }
+        }
+
+        @Override
+        public void onError(SHARE_MEDIA platform, Throwable t) {
+            if (platform != SHARE_MEDIA.MORE && platform != SHARE_MEDIA.SMS
+                    && platform != SHARE_MEDIA.EMAIL
+                    && platform != SHARE_MEDIA.FLICKR
+                    && platform != SHARE_MEDIA.FOURSQUARE
+                    && platform != SHARE_MEDIA.TUMBLR
+                    && platform != SHARE_MEDIA.POCKET
+                    && platform != SHARE_MEDIA.PINTEREST
+
+                    && platform != SHARE_MEDIA.INSTAGRAM
+                    && platform != SHARE_MEDIA.YNOTE
+                    && platform != SHARE_MEDIA.EVERNOTE) {
+                ToastUtil.getInstance().showShort("分享失败啦");
+
+            }
+
+        }
+
+        @Override
+        public void onCancel(SHARE_MEDIA platform) {
+            ToastUtil.getInstance().showShort("分享取消了");
+        }
+    }
+
+    @Override
+    public void onConfigurationChanged(@androidx.annotation.NonNull @NotNull Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+        if (mShareAction != null) {
+            mShareAction.close();
+        }
+
+    }
+
+    /**
+     * 分段上传
+     *
+     * @param message
+     */
+    @Override
+    public void measureStart(JSONObject message) {
+
+    }
+
+    /**
+     * 结束分段
+     *
+     * @param message
+     */
+    @Override
+    public void allMeasureEnd(JSONObject message) {
+    }
+
+    /**
+     * h5传递的midi文件地址
+     */
+    private String midiFilePath = null;
+    /**
+     * 初始速度
+     */
+    private int midiFileOriginalSpeed = 120;
+    /**
+     * 设定后速度,默认与初始速度一致
+     */
+    private int midiFileSpeed = 120;
+    /**
+     * 全曲时长,用以同步播放进度的基数
+     */
+    private long midiFileDuration = 0;
+
+    /**
+     * 回报进度间隔
+     */
+    int processInterval = 16;
+    /**
+     * 曲目id
+     */
+    private String midiSongId = null;
+
+    private int hertz = 440;
+
+    private SoundPool soundpool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
+    private boolean isReady = true;
+
+    @Override
+    public void cloudDetail(JSONObject message) {
+        try {
+            JSONObject obj = message.optJSONObject("content");
+            String midiFile = obj.optString("midi");
+            midiFileOriginalSpeed = obj.optInt("originalSpeed", 120);
+            midiFileSpeed = midiFileOriginalSpeed;
+            midiFileDuration = obj.optLong("duration");
+            processInterval = obj.optInt("interval", 16);
+            if (TextUtils.isEmpty(midiFile)) {
+                ToastUtil.getInstance().showShort("文件异常");
+                return;
+            }
+            new RxPermissions(this)
+                    .request(Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE)
+                    .subscribe(permission -> {
+                        if (permission) {
+                            try {
+                                String type = midiFile.substring(midiFile.lastIndexOf(".") + 1);
+                                if (TextUtils.isEmpty(type)) {
+                                    ToastUtil.getInstance().showShort("文件异常");
+                                    return;
+                                }
+                                String path = MyFileUtils.getPublicDirectory("file");
+                                String name = hashKeyForDisk(midiFile) + "." + type;
+                                File file = new File(path + "/" + name);
+                                if (file.exists() && file.length() > 0) {
+                                    //本地文件存在
+                                    midiFilePath = file.getAbsolutePath();
+                                    MidiPlayerUtils.getInstance().init(BaseApplication.context, midiFilePath);
+                                    double tempDuration = MidiPlayerUtils.getInstance().getTotalLength2Second();
+                                    if (Math.abs(midiFileDuration - tempDuration * 1000) > 2000) {
+                                        midiFileDuration = (long) (tempDuration * 1000);
+                                    }
+                                    JSONObject contentJsonObject = message.optJSONObject("content");
+                                    if (contentJsonObject != null) {
+                                        contentJsonObject.put("midiDuration", midiFileDuration);
+                                        contentJsonObject.put("midi", EncodeUtils.urlEncode(midiFile));
+                                    }
+                                    onSendMessage(message.toString());
+                                } else {
+                                    RetrofitClientNoToken.getInstance().getRetrofit().create(DownloadApi.class)
+                                            .downloadFileWithFixedUrl(midiFile)
+                                            .subscribeOn(Schedulers.io())
+                                            .observeOn(Schedulers.io())
+                                            .subscribeWith(new Observer<ResponseBody>() {
+                                                @Override
+                                                public void onSubscribe(Disposable d) {
+
+                                                }
+
+                                                @Override
+                                                public void onNext(ResponseBody response) {
+                                                    if (MyFileUtils.writeFileToSDCard(response, path, name)) {
+                                                        runOnUiThread(() -> {
+                                                            midiFilePath = path + File.separator + name;
+                                                            MidiPlayerUtils.getInstance().init(BaseApplication.context, midiFilePath);
+                                                            double tempDuration = MidiPlayerUtils.getInstance().getTotalLength2Second();
+                                                            if (Math.abs(midiFileDuration - tempDuration * 1000) > 2000) {
+                                                                midiFileDuration = (long) (tempDuration * 1000);
+                                                            }
+                                                            try {
+                                                                JSONObject contentJsonObject = message.optJSONObject("content");
+                                                                if (contentJsonObject != null) {
+                                                                    contentJsonObject.put("midiDuration", midiFileDuration);
+                                                                    contentJsonObject.put("midi", EncodeUtils.urlEncode(midiFile));
+                                                                }
+                                                            } catch (JSONException e) {
+                                                                e.printStackTrace();
+                                                            } finally {
+                                                                onSendMessage(message.toString());
+                                                            }
+
+                                                        });
+                                                    } else {
+                                                        runOnUiThread(() -> {
+                                                            ToastUtil.getInstance().showShort("获取文件失败");
+                                                            midiFilePath = null;
+                                                            onSendMessage(message.toString());
+                                                        });
+                                                    }
+                                                }
+
+                                                @Override
+                                                public void onError(Throwable e) {
+                                                    runOnUiThread(() -> {
+                                                        hideLoading();
+                                                        ToastUtil.getInstance().showShort("文件异常");
+                                                        midiFilePath = null;
+                                                        onSendMessage(message.toString());
+                                                    });
+                                                }
+
+                                                @Override
+                                                public void onComplete() {
+
+                                                }
+                                            });
+                                }
+
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                        } else {
+                            DialogUtil.showInCenter(getChildFragmentManager(), com.cooleshow.base.R.layout.accompany_permissions_popu, (holder, dialog) -> {
+                                TextView tvContent = holder.getView(com.cooleshow.base.R.id.tv_content);
+                                ImageView btncancel = holder.getView(com.cooleshow.base.R.id.btn_cancel);
+                                ImageView btnCommit = holder.getView(com.cooleshow.base.R.id.btn_commit);
+                                tvContent.setText("请开启储存访问权限");
+                                btncancel.setOnClickListener(view1 -> {
+                                    dialog.dismiss();
+                                });
+                                btnCommit.setOnClickListener(view1 -> {
+                                    PermissionUtils.toSelfSetting(getContext());
+                                    dialog.dismiss();
+                                });
+                            });
+                        }
+                    });
+        } catch (Exception e) {
+
+        }
+    }
+
+    private String hashKeyForDisk(String key) {
+        String cacheKey;
+        try {
+            final MessageDigest mDigest = MessageDigest.getInstance("MD5");
+            mDigest.update(key.getBytes());
+            cacheKey = bytesToHexString(mDigest.digest());
+        } catch (NoSuchAlgorithmException e) {
+            cacheKey = String.valueOf(key.hashCode());
+        }
+        return cacheKey;
+    }
+
+    private String bytesToHexString(byte[] bytes) {
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < bytes.length; i++) {
+            String hex = Integer.toHexString(0xFF & bytes[i]);
+            if (hex.length() == 1) {
+                sb.append('0');
+            }
+            sb.append(hex);
+        }
+        return sb.toString();
+    }
+
+    @Override
+    public void cloudPlay(JSONObject message) {
+        try {
+            JSONObject obj = message.optJSONObject("content");
+            midiFileSpeed = obj.optInt("speed", 120);
+            midiSongId = obj.optString("songID");
+            hertz = obj.optInt("hertz", 440);
+            if (MidiPlayerUtils.getInstance().isPause()) {
+                MidiPlayerUtils.getInstance().resumePlay();
+                loopHandler.sendEmptyMessage(1);
+                onSendMessage(message.toString());
+            } else {
+                loopHandler.removeMessages(1);
+                MidiPlayerUtils.getInstance().playSound(
+                        (float) midiFileSpeed / (float) midiFileOriginalSpeed,
+                        hertz,
+                        fp -> {
+                            onSendMessage(message.toString());
+                            loopHandler.sendEmptyMessage(1);
+                        },
+                        fp -> {
+                            loopHandler.removeMessages(1);
+                            try {
+                                JSONObject content = new JSONObject();
+                                content.put("songID", obj.optString("songID"));
+                                JSONObject json = new JSONObject();
+                                json.put("api", "cloudplayed");
+                                json.put("content", content);
+                                onSendMessage(json.toString());
+                            } catch (Exception e) {
+                                e.printStackTrace();
+                            }
+                        });
+                onSendMessage(message.toString());
+                loopHandler.sendEmptyMessage(1);
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void cloudSuspend(JSONObject message) {
+        try {
+            MidiPlayerUtils.getInstance().pausePlay();
+            loopHandler.removeMessages(1);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            try {
+                message.optJSONObject("content").put("currentTime", MidiPlayerUtils.getInstance().getCurrentPosition() * (midiFileDuration == 0 ? MidiPlayerUtils.getInstance().midiTotalTime : midiFileDuration) / MidiPlayerUtils.getInstance().getTotalLength());
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+            onSendMessage(message.toString());
+        }
+
+
+    }
+
+    @Override
+    public void cloudSetCurrentTime(JSONObject message) {
+        try {
+            MidiPlayerUtils.getInstance().seekTo(message.optJSONObject("content").optInt("currentTime") * MidiPlayerUtils.getInstance().getTotalLength() / midiFileDuration);
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            onSendMessage(message.toString());
+        }
+    }
+
+    @Override
+    public void cloudChangeSpeed(JSONObject message) {
+        try {
+            JSONObject obj = message.optJSONObject("content");
+            midiFileSpeed = obj.optInt("speed", 120);
+            MidiPlayerUtils.getInstance().setSpeed((float) midiFileSpeed / (float) midiFileOriginalSpeed);
+        } catch (Exception e) {
+
+        }
+    }
+
+    @Override
+    public void cloudSwitch(JSONObject message) {
+        try {
+            JSONObject obj = message.optJSONObject("content");
+            JSONArray array = obj.optJSONArray("parts");
+            for (int i = 0; i < MidiPlayerUtils.getInstance().getMidiTracks().size(); i++) {
+                MidiPlayerUtils.getInstance().setTrackVolume(i, 0.7f);
+            }
+            for (int i = 0; i < array.length(); i++) {
+                MidiPlayerUtils.getInstance().setTrackVolumeByName(array.optString(i), 0);
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void cloudVolume(JSONObject message) {
+        try {
+            JSONObject obj = message.optJSONObject("content");
+            if (obj != null) {
+                int activeMidiId = obj.optInt("activeMidiId");
+                int activeMidiVolume = obj.optInt("activeMidiVolume", 70);
+                if (activeMidiId == -1) {
+                    return;
+                }
+                MidiPlayerUtils.getInstance().setTrackVolumeById(activeMidiId, (float) activeMidiVolume / 100f);
+            }
+            sendMessage(message.toString());
+        } catch (Exception e) {
+            e.printStackTrace();
+            sendMessage(message.toString());
+        } finally {
+            for (int i = 0; i < MidiPlayerUtils.getInstance().getMidiTracks().size(); i++) {
+                Log.i(TAG, "track " + i + " vol is " + MidiPlayerUtils.getInstance().getTrackVolume(i));
+            }
+        }
+    }
+
+    @Override
+    public void cloudGetMediaStatus(JSONObject message) {
+        try {
+            if (MidiPlayerUtils.getInstance().isPlaying()) {
+                message.optJSONObject("content").put("status", "play");
+            } else if (MidiPlayerUtils.getInstance().isPause()) {
+                message.optJSONObject("content").put("status", "suspend");
+            } else if (MidiPlayerUtils.getInstance().isStop()) {
+                message.optJSONObject("content").put("status", "suspend");
+            } else {
+                message.optJSONObject("content").put("status", "suspend");
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            sendMessage(message.toString());
+        }
+    }
+
+    @Override
+    public void cloudDestroy(JSONObject message) {
+        try {
+            MidiPlayerUtils.getInstance().stopPlay();
+        } catch (Exception e) {
+            e.printStackTrace();
+        } finally {
+            sendMessage(message.toString());
+        }
+    }
+
+    @Override
+    public void joinLiveRoom(String roomId, String teacherId) {
+
+    }
+
+    @Override
+    public void joinChatGroup(String type, String id) {
+
+    }
+
+
+    public void selectAddress(String addressJson) {
+        onSendMessage("'getAddress'," + addressJson);
+    }
+
+    @Override
+    public void paymentOrder(String orderNo, String payChannel, String payInfo) {
+        if (payChannel.equals("ali_app")) {
+            if (!AppUtils.checkAliPayInstalled(getContext())) {
+                sendPayResult(Constants.PAY_ERROR_BY_NOT_INSTALL);
+                return;
+            }
+            String orderInfo = payInfo;
+            final Runnable payRunnable = new Runnable() {
+                @Override
+                public void run() {
+                    PayTask alipay = new PayTask(getActivity());
+                    Map<String, String> result = alipay.payV2(orderInfo, true);
+                    Log.i("msp", result.toString());
+
+                    Message msg = new Message();
+                    msg.what = SDK_PAY_FLAG;
+                    msg.obj = result;
+                    mHandler.sendMessage(msg);
+                }
+            };
+
+            // 必须异步调用
+            Thread payThread = new Thread(payRunnable);
+            payThread.start();
+        } else {
+            //微信
+//            if (!api.isWXAppInstalled()) {
+//                sendPayResult(Constants.PAY_ERROR_BY_NOT_INSTALL);
+//                return;
+//            }
+//            Gson gson = new Gson();
+//            WeixinPayInfo weixinPayInfo = gson.fromJson(payInfo, WeixinPayInfo.class);
+//            PayReq req = new PayReq();
+//            req.appId = weixinPayInfo.getAppid();
+//            req.partnerId = weixinPayInfo.getPartnerid();
+//            req.prepayId = weixinPayInfo.getPrepayid();
+//            req.nonceStr = weixinPayInfo.getNoncestr();
+//            req.timeStamp = weixinPayInfo.getTimestamp();
+//            req.packageValue = weixinPayInfo.getPackageValue();
+//            req.sign = weixinPayInfo.getSign();
+////                    req.extData			= "app data"; // optional
+//            //正在前往微信支付
+//            // 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信
+//            goPay = true;
+//            api.sendReq(req);
+        }
+    }
+
+    @Override
+    public void savePicture(String base64, String uuid) {
+        try {
+            File file = com.cooleshow.base.utils.FileUtils.saveBase64ImgToLocalFile(base64);
+            sendSavePicCallBack("savePicture", "success", uuid);
+        } catch (Exception e) {
+            sendSavePicCallBack("savePicture", "fail", uuid);
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void cloudToggleFollow(String mode) {
+        new RxPermissions(this)
+                .request(Manifest.permission.RECORD_AUDIO)
+                .subscribe(permission -> {
+                    if (permission) {
+                        handleCloudFollow(mode);
+                    }
+                });
+    }
+
+    @Override
+    public void saveAccompanimentMp3(String accompanyUrl) {
+        if (onAccompanyListener != null) {
+            onAccompanyListener.onDownloadAccompaniment(accompanyUrl);
+        }
+    }
+
+    @Override
+    public void onBackPress() {
+        if (webView != null) {
+            if (webView.canGoBack()) {
+                webView.goBack();
+                return;
+            }
+            if (getActivity() != null) {
+                getActivity().onBackPressed();
+            }
+        }
+    }
+
+    @Override
+    public void startTune(JSONObject message) {
+        new RxPermissions(this)
+                .request(Manifest.permission.RECORD_AUDIO)
+                .subscribe(permission -> {
+                    if (permission) {
+                        if (mPlayHelper != null) {
+                            mPlayHelper.startTune(message);
+                        }
+                    } else {
+                        DialogUtil.showInCenter(getChildFragmentManager(), com.cooleshow.base.R.layout.accompany_permissions_popu, (holder, dialog) -> {
+                            TextView tvTitle = holder.getView(com.cooleshow.base.R.id.tv_title);
+                            TextView tvContent = holder.getView(com.cooleshow.base.R.id.tv_content);
+                            View btncancel = holder.getView(com.cooleshow.base.R.id.btn_cancel);
+                            View btnCommit = holder.getView(com.cooleshow.base.R.id.btn_commit);
+                            tvTitle.setText("提示");
+                            tvContent.setText("请开启麦克风访问权限");
+                            btncancel.setOnClickListener(view1 -> {
+
+                                dialog.dismiss();
+
+                            });
+                            btnCommit.setOnClickListener(view1 -> {
+                                PermissionUtils.toSelfSetting(getContext());
+                                dialog.dismiss();
+                            });
+                        });
+                    }
+                });
+    }
+
+    @Override
+    public void endTune(JSONObject message) {
+        if (mPlayHelper != null) {
+            mPlayHelper.endTune(message);
+        }
+    }
+
+    @Override
+    public void createMusicPlayer(JSONObject message) {
+        if (mPlayHelper != null) {
+            mPlayHelper.createMusicPlayer(message);
+        }
+    }
+
+    @Override
+    public void getDeviceDelay(JSONObject jsonObject) {
+        if (mPlayHelper != null) {
+            mPlayHelper.getDeviceDelay(jsonObject);
+        }
+    }
+
+    @Override
+    public void onFinishTune(JSONObject jsonObject) {
+        if (mPlayHelper != null) {
+            mPlayHelper.onFinishTune(jsonObject);
+        }
+    }
+
+    @Override
+    public void openAdjustRecording(JSONObject message) {
+        if (message != null) {
+            JSONObject contentJson = message.optJSONObject("content");
+            if (contentJson != null) {
+                String recordId = contentJson.optString("recordId");
+                String title = contentJson.optString("title");
+                String coverImg = contentJson.optString("coverImg");
+                if (onAccompanyListener != null) {
+                    onAccompanyListener.openAdjustRecording(recordId, title, coverImg);
+                }
+            }
+        }
+    }
+
+    private void handleCloudFollow(String mode) {
+        if (mMusicTunerHelper == null) {
+            mMusicTunerHelper = new MusicTunerHelper(new MusicTunerHelper.OnEventListener() {
+                @Override
+                public void onResult(float pitchInHz) {
+                    runOnUiThread(new Runnable() {
+                        @Override
+                        public void run() {
+                            sendCloudToggleFollowResult(pitchInHz);
+                        }
+                    });
+                }
+            });
+        }
+        if (TextUtils.equals(mode, "start")) {
+            mMusicTunerHelper.start();
+        } else {
+            mMusicTunerHelper.stop();
+        }
+    }
+
+    private void sendSavePicCallBack(String api, String result, String uuid) {
+        JSONObject jsonObject = new JSONObject();
+        JSONObject contentJson = new JSONObject();
+        try {
+            jsonObject.put("api", api);
+            contentJson.put("status", result);
+            contentJson.put("uuid", uuid);
+            jsonObject.put("content", contentJson);
+            sendMessage(jsonObject.toString());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    private boolean goPay = false;
+
+    @Subscribe(threadMode = ThreadMode.MAIN)
+    public void loadEventBus(WxPayResult payResult) {
+        if (goPay) {
+            if (payResult.resultCode == 0) {
+                sendPayResult(Constants.PAY_SUCCESS);
+            } else {
+                sendPayResult(Constants.PAY_ERROR);
+            }
+            goPay = false;
+        }
+    }
+
+    @SuppressLint("HandlerLeak")
+    private Handler mHandler = new Handler() {
+        @SuppressWarnings("unused")
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case SDK_PAY_FLAG: {
+                    @SuppressWarnings("unchecked")
+                    PayResult payResult = new PayResult((Map<String, String>) msg.obj);
+                    /**
+                     * 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。
+                     */
+                    String resultInfo = payResult.getResult();// 同步返回需要验证的信息
+                    String resultStatus = payResult.getResultStatus();
+                    // 判断resultStatus 为9000则代表支付成功
+                    if (TextUtils.equals(resultStatus, "9000")) {
+                        // 该笔订单是否真实支付成功,需要依赖服务端的异步通知。
+//                        isAlipayOk = true;
+                        ToastUtil.getInstance().showShort("支付成功");
+                        sendPayResult(Constants.PAY_SUCCESS);
+                    } else {
+                        // 该笔订单真实的支付结果,需要依赖服务端的异步通知。
+                        ToastUtil.getInstance().showShort("支付失败" + resultInfo);
+                        sendPayResult(Constants.PAY_ERROR);
+                    }
+                    break;
+                }
+                case SDK_AUTH_FLAG: {
+                    @SuppressWarnings("unchecked")
+                    AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true);
+                    String resultStatus = authResult.getResultStatus();
+
+                    // 判断resultStatus 为“9000”且result_code
+                    // 为“200”则代表授权成功,具体状态码代表含义可参考授权接口文档
+                    if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) {
+                        // 获取alipay_open_id,调支付时作为参数extern_token 的value
+                        // 传入,则支付账户为该授权账户
+                        sendPayResult(Constants.PAY_SUCCESS);
+                    } else {
+                        // 其他状态值则为授权失败
+                        sendPayResult(Constants.PAY_ERROR);
+                    }
+                    break;
+                }
+                default:
+                    break;
+            }
+        }
+
+    };
+
+
+    @Override
+    public void cloudMetronome(JSONObject message) {
+        try {
+            JSONObject obj = message.optJSONObject("content");
+            int repeat = obj.optInt("repeat", 1);
+            int denominator = obj.optInt("denominator", 4);
+            int numerator = obj.optInt("repeat", 4);
+            if (soundpool != null) {
+                soundpool.release();
+                soundpool = null;
+            }
+            isReady = false;
+            HashMap<Integer, Integer> map = new HashMap<>();
+            if (numerator == 2) {
+                soundpool = new SoundPool(2, AudioManager.STREAM_MUSIC, 0);
+                map.put(0, soundpool.load(getContext(), com.cooleshow.base.R.raw.midstrong, 1));
+                map.put(1, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+            } else if (numerator == 3) {
+                soundpool = new SoundPool(3, AudioManager.STREAM_MUSIC, 0);
+                map.put(0, soundpool.load(getContext(), com.cooleshow.base.R.raw.midstrong, 1));
+                map.put(1, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+                map.put(2, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+            } else if (numerator == 6) {
+                soundpool = new SoundPool(6, AudioManager.STREAM_MUSIC, 0);
+                map.put(0, soundpool.load(getContext(), com.cooleshow.base.R.raw.midstrong, 1));
+                map.put(1, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+                map.put(2, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+                map.put(3, soundpool.load(getContext(), com.cooleshow.base.R.raw.midstrong, 1));
+                map.put(4, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+                map.put(5, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+            } else {
+                soundpool = new SoundPool(4, AudioManager.STREAM_MUSIC, 0);
+                map.put(0, soundpool.load(getContext(), com.cooleshow.base.R.raw.midstrong, 1));
+                map.put(1, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+                map.put(2, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+                map.put(3, soundpool.load(getContext(), com.cooleshow.base.R.raw.feeble, 1));
+            }
+            int speed;
+            if (denominator == 4) {
+                if (midiFileSpeed > 0) {
+                    speed = midiFileSpeed;
+                } else {
+                    speed = 60;
+                }
+            } else {
+                if (midiFileSpeed * 2 > 0) {
+                    speed = midiFileSpeed * 2;
+                } else {
+                    speed = 60;
+                }
+            }
+            long sleepTime = (long) (60000.0 / (speed));
+            showCountDownDialog(map.size(), message);
+            new Thread(() -> {
+                playPreSound(map, sleepTime, repeat);
+                //播放完成开始录音
+                runOnUiThread(() -> {
+                    if (null != preCountDialog) {
+                        preCountDialog.dismiss();
+                    }
+                    if (soundpool != null) {
+                        try {
+                            message.optJSONObject("content").put("status", "finish");
+                        } catch (JSONException e) {
+                            e.printStackTrace();
+                        }
+                        onSendMessage(message.toString());
+                    }
+                });
+            }).start();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+
+    private CommonDialog preCountDialog;
+    private ImageView iv1;
+    private ImageView iv2;
+    private ImageView iv3;
+    private ImageView iv4;
+    private ImageView iv5;
+    private ImageView iv6;
+
+    private void showCountDownDialog(int count, JSONObject message) {
+        //弹出预备拍
+        preCountDialog = CommonDialog.init().setLayoutId(com.cooleshow.base.R.layout.dialog_student_precount);
+        preCountDialog.setConvertListener(new ViewConvertListener() {
+                    @Override
+                    public void convertView(ViewHolder holder, BaseDialog dialog) {
+                        if (count == 2) {
+                            iv1 = holder.getView(com.cooleshow.base.R.id.iv_3);
+                            iv2 = holder.getView(com.cooleshow.base.R.id.iv_4);
+                            iv3 = holder.getView(com.cooleshow.base.R.id.iv_1);
+                            iv4 = holder.getView(com.cooleshow.base.R.id.iv_2);
+                            iv5 = holder.getView(com.cooleshow.base.R.id.iv_5);
+                            iv6 = holder.getView(com.cooleshow.base.R.id.iv_6);
+                            iv3.setVisibility(View.INVISIBLE);
+                            iv4.setVisibility(View.INVISIBLE);
+                            iv5.setVisibility(View.INVISIBLE);
+                            iv6.setVisibility(View.INVISIBLE);
+                        } else if (count == 3) {
+                            iv1 = holder.getView(com.cooleshow.base.R.id.iv_3);
+                            iv2 = holder.getView(com.cooleshow.base.R.id.iv_4);
+                            iv3 = holder.getView(com.cooleshow.base.R.id.iv_5);
+                            iv4 = holder.getView(com.cooleshow.base.R.id.iv_2);
+                            iv5 = holder.getView(com.cooleshow.base.R.id.iv_1);
+                            iv6 = holder.getView(com.cooleshow.base.R.id.iv_6);
+                            iv4.setVisibility(View.INVISIBLE);
+                            iv5.setVisibility(View.INVISIBLE);
+                            iv6.setVisibility(View.INVISIBLE);
+                        } else if (count == 4) {
+                            iv1 = holder.getView(com.cooleshow.base.R.id.iv_2);
+                            iv2 = holder.getView(com.cooleshow.base.R.id.iv_3);
+                            iv3 = holder.getView(com.cooleshow.base.R.id.iv_4);
+                            iv4 = holder.getView(com.cooleshow.base.R.id.iv_5);
+                            iv5 = holder.getView(com.cooleshow.base.R.id.iv_1);
+                            iv6 = holder.getView(com.cooleshow.base.R.id.iv_6);
+                            iv5.setVisibility(View.INVISIBLE);
+                            iv6.setVisibility(View.INVISIBLE);
+                        }
+                        iv1.setImageResource(com.cooleshow.base.R.drawable.bg_play_metronome_gray_dots_shape);
+                        iv2.setImageResource(com.cooleshow.base.R.drawable.bg_play_metronome_gray_dots_shape);
+                        iv3.setImageResource(com.cooleshow.base.R.drawable.bg_play_metronome_gray_dots_shape);
+                        iv4.setImageResource(com.cooleshow.base.R.drawable.bg_play_metronome_gray_dots_shape);
+                        iv5.setImageResource(com.cooleshow.base.R.drawable.bg_play_metronome_gray_dots_shape);
+                        iv6.setImageResource(com.cooleshow.base.R.drawable.bg_play_metronome_gray_dots_shape);
+                        ImageView iv_dialog_student_precount = holder.getView(com.cooleshow.base.R.id.iv_dialog_student_precount);
+                        iv_dialog_student_precount.setOnClickListener(v -> {
+                            dialog.dismiss();
+                            if (soundpool != null) {
+                                soundpool.release();
+                                soundpool = null;
+                            }
+                            try {
+                                message.optJSONObject("content").put("status", "cancel");
+                            } catch (JSONException e) {
+                                e.printStackTrace();
+                            }
+                            onSendMessage(message.toString());
+                        });
+                    }
+                })
+                .setDimAmount(0.6f)
+                .setOutCancel(true)
+                .setGravity(Gravity.CENTER)
+                .show(getChildFragmentManager());
+    }
+
+    private void changeImageState(int size, int now) {
+        if (null != preCountDialog && preCountDialog.isVisible()) {
+            int resId = -1;
+            if (now < size) {
+                resId = com.cooleshow.base.R.drawable.bg_play_metronome_white_dots_shape;
+            }
+            now = now % size;
+            if (now == 0) {
+                iv1.setImageResource(com.cooleshow.base.R.drawable.bg_play_metronome_green_dots_shape);
+            } else if (now == 1) {
+                iv2.setImageResource(resId);
+            } else if (now == 2) {
+                iv3.setImageResource(resId);
+            } else if (now == 3) {
+                iv4.setImageResource(resId);
+            } else if (now == 4) {
+                iv5.setImageResource(resId);
+            } else if (now == 5) {
+                iv6.setImageResource(resId);
+            }
+        }
+    }
+
+    private void playPreSound(HashMap<Integer, Integer> map, long sleepTime, int repeat) {
+        SystemClock.sleep(500);
+        repeat--;
+        for (int i = 0; i < map.size(); i++) {
+            if (!isReady) {
+                SystemClock.sleep(sleepTime);
+                if (soundpool != null) {
+                    soundpool.play(map.get(i), 1, 1, 0, 0, 1);
+                    int finalI = i;
+                    runOnUiThread(() -> changeImageState(map.size(), finalI));
+                } else {
+                    return;
+                }
+            }
+        }
+        if (repeat > 0) {
+            playPreSound(map, sleepTime, repeat);
+        } else {
+            SystemClock.sleep(sleepTime);
+        }
+    }
+
+
+    private Handler loopHandler = new Handler() {
+        @Override
+        public void handleMessage(@NotNull Message msg) {
+            super.handleMessage(msg);
+            if (msg.what == 1) {
+                checkPrecess();
+                loopHandler.removeMessages(1);
+                loopHandler.sendEmptyMessageDelayed(1, processInterval);
+            }
+        }
+    };
+
+    private void checkPrecess() {
+        if (!MidiPlayerUtils.getInstance().isPlaying()) {
+            return;
+        }
+        int tick = MidiPlayerUtils.getInstance().getCurrentTICK();
+        int totalTick = MidiPlayerUtils.getInstance().getTotalTICK();
+        float percent = (float) tick / (float) totalTick;
+        long totalTime = (midiFileDuration == 0 ? MidiPlayerUtils.getInstance().midiTotalTime : midiFileDuration);
+//        long dur = MidiPlayerUtils.getInstance().getCurrentPosition() * (midiFileDuration == 0 ? MidiPlayerUtils.getInstance().midiTotalTime : midiFileDuration) / MidiPlayerUtils.getInstance().getTotalLength();
+        long dur = (long) (percent * totalTime);
+        LogUtils.i("pq", "dur:" + dur);
+
+        try {
+            if (TextUtils.isEmpty(midiSongId)) {
+                return;
+            }
+            JSONObject content = new JSONObject();
+            content.put("currentTime", dur);
+            content.put("songID", midiSongId);
+            JSONObject json = new JSONObject();
+            json.put("api", "cloudTimeUpdae");
+            json.put("content", content);
+            onSendMessage(json.toString());
+        } catch (Exception e) {
+
+        }
+
+    }
+
+    public void parseShareContactData(Intent data) {
+        //分享至群聊
+        ShareHelper.parseShareContactData(mImageBase64, data);
+    }
+
+    private void sendCloudToggleFollowResult(float result) {
+        try {
+            JSONObject jsonObject = new JSONObject();
+            JSONObject contentJson = new JSONObject();
+            jsonObject.put("api", "cloudFollowTime");
+            contentJson.put("frequency", result);
+            jsonObject.put("content", contentJson);
+            onSendMessage(jsonObject.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 发送支付结果
+     *
+     * @param payResult
+     */
+    private void sendPayResult(int payResult) {
+        JSONObject jsonObject = new JSONObject();
+        JSONObject contentJson = new JSONObject();
+        try {
+            jsonObject.put("api", "paymentOperation");
+            //支付成功
+            if (payResult == Constants.PAY_SUCCESS) {
+                contentJson.put("status", "success");
+            }
+            //支付失败
+            if (payResult == Constants.PAY_ERROR) {
+                contentJson.put("status", "error");
+            }
+            //支付未安装
+            if (payResult == Constants.PAY_ERROR_BY_NOT_INSTALL) {
+                contentJson.put("status", "fail");
+            }
+            //支付取消
+            if (payResult == Constants.PAY_CANCEL) {
+                contentJson.put("status", "cancel");
+            }
+            jsonObject.put("content", contentJson);
+            onSendMessage(jsonObject.toString());
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+    }
+}

BIN
accompany/src/main/res/drawable-xhdpi/bg_accompany_loading.png


BIN
accompany/src/main/res/drawable-xhdpi/ic_accompany_record.png


BIN
accompany/src/main/res/drawable-xxhdpi/bg_accompany_loading.png


BIN
accompany/src/main/res/drawable-xxhdpi/flash_off.png


BIN
accompany/src/main/res/drawable-xxhdpi/ic_accompany_back.png


BIN
accompany/src/main/res/drawable-xxhdpi/ic_accompany_video_record_save.png


BIN
accompany/src/main/res/drawable-xxhdpi/ic_video_record_play.png


BIN
accompany/src/main/res/drawable-xxhdpi/switch_camera.png


+ 12 - 0
accompany/src/main/res/drawable/progress_bar_ff8057_4dp.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- solid指定形状的填充色,只有android:color一个属性 -->
+    <solid android:color="@color/color_ff8057" />
+    <!-- padding设置内容区域离边界的间距 -->
+    <!-- corners设置圆角,只适用于rectangle -->
+    <corners
+        android:bottomLeftRadius="4dp"
+        android:topLeftRadius="4dp"
+        android:bottomRightRadius="4dp"
+        android:topRightRadius="4dp"/>
+</shape>

+ 12 - 0
accompany/src/main/res/drawable/progress_bar_ffffff_4dp.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- solid指定形状的填充色,只有android:color一个属性 -->
+    <solid android:color="@color/white" />
+    <!-- padding设置内容区域离边界的间距 -->
+    <!-- corners设置圆角,只适用于rectangle -->
+    <corners
+        android:bottomLeftRadius="4dp"
+        android:topLeftRadius="4dp"
+        android:bottomRightRadius="4dp"
+        android:topRightRadius="4dp"/>
+</shape>

+ 24 - 0
accompany/src/main/res/drawable/shape_accompany_loading_progress_drawable.xml

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@android:id/background">
+        <shape>
+            <corners android:radius="4dip" />
+            <!--            <solid android:color="@color/color_eeeeee"/>-->
+            <stroke android:color="@color/white" android:width="1dp"/>
+        </shape>
+    </item>
+    <item android:id="@android:id/progress">
+        <!--        <clip>-->
+        <!--            <shape>-->
+        <!--                <corners android:radius="4dip" />-->
+        <!--                <solid android:color="@color/color_4877ff"/>-->
+        <!--            </shape>-->
+        <!--        </clip>-->
+
+        <!--
+   -->
+        <scale android:scaleWidth="100%"
+            android:drawable="@drawable/progress_bar_ffffff_4dp"/>
+    </item>
+</layer-list>

+ 19 - 0
accompany/src/main/res/layout/activity_accompany.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/activity_record"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/white">
+
+    <FrameLayout
+        android:id="@+id/camera"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"/>
+
+    <FrameLayout
+        android:id="@+id/fl_webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+</FrameLayout>

+ 72 - 0
accompany/src/main/res/layout/fragment_accompany.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical">
+
+    <FrameLayout
+        android:id="@+id/view_parent"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:scrollbars="none" />
+
+    <LinearLayout
+        android:id="@+id/ll_loading"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@drawable/bg_accompany_loading"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:visibility="gone"
+        tools:visibility="visible">
+
+        <com.airbnb.lottie.LottieAnimationView
+            android:id="@+id/view_loading_anim"
+            android:layout_width="300dp"
+            android:layout_height="200dp"
+            android:layout_gravity="center"
+            app:layout_constraintBottom_toBottomOf="@+id/iv_shop_car"
+            app:layout_constraintLeft_toLeftOf="@+id/iv_shop_car"
+            app:layout_constraintRight_toRightOf="@+id/iv_shop_car"
+            app:lottie_autoPlay="false"
+            app:lottie_imageAssetsFolder="lottie/accompany/images/"
+            app:lottie_loop="true"
+            app:lottie_rawRes="@raw/accompany_loading" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dp"
+            android:text="小酷正在努力加载,请稍等..."
+            android:textColor="@color/white"
+            android:textSize="@dimen/sp_18" />
+
+        <ProgressBar
+            android:id="@+id/progress"
+            style="@style/Widget.AppCompat.ProgressBar.Horizontal"
+            android:layout_width="350dp"
+            android:layout_height="8dp"
+            android:layout_marginStart="34dp"
+            android:layout_marginTop="16dp"
+            android:layout_marginEnd="34dp"
+            android:max="100"
+            android:progress="0"
+            android:layout_marginBottom="40dp"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintRight_toRightOf="parent"
+            app:layout_constraintLeft_toLeftOf="parent"
+            android:progressDrawable="@drawable/shape_accompany_loading_progress_drawable" />
+    </LinearLayout>
+
+    <ImageView
+        android:id="@+id/iv_loading_back"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="20dp"
+        android:layout_marginTop="20dp"
+        android:padding="14dp"
+        android:src="@drawable/icon_back_black"
+        android:visibility="gone" />
+</FrameLayout>

+ 139 - 0
accompany/src/main/res/layout/record_video_layout.xml

@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:id="@+id/activity_record"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@color/black">
+
+    <com.wonderkiln.camerakit.CameraView
+        android:id="@+id/camera"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:adjustViewBounds="true"
+        android:layout_gravity="center_vertical"
+        app:ckFacing="front"
+        app:ckFlash="off"
+        app:ckFocus="tapWithMarker"
+        app:ckMethod="standard"
+        app:ckVideoQuality="highest"
+        app:deviceOrientation="horizontal" />
+
+    <FrameLayout
+        android:id="@+id/fl_webview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+
+    <RelativeLayout
+        android:layout_width="match_parent"
+        android:layout_height="70dp"
+        android:layout_gravity="bottom"
+        android:background="#2E000000"
+        android:visibility="gone">
+
+        <ImageView
+            android:id="@+id/btn_back"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            android:layout_centerVertical="true"
+            android:layout_marginStart="@dimen/dp_24"
+            android:padding="12dp"
+            android:scaleType="center"
+            android:src="@drawable/ic_accompany_back" />
+
+        <ImageView
+            android:id="@+id/video_flash_light"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentLeft="true"
+            android:layout_marginLeft="20dp"
+            android:layout_marginBottom="10dp"
+            android:padding="@dimen/dp_10"
+            android:src="@drawable/flash_off"
+            android:visibility="gone" />
+
+        <Chronometer
+            android:id="@+id/video_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerHorizontal="true"
+            android:layout_marginTop="10dp"
+            android:layout_marginBottom="10dp"
+            android:textColor="#FFFFFF"
+            android:textSize="16sp"
+            android:visibility="gone" />
+
+        <ImageView
+            android:id="@+id/swicth_camera"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentRight="true"
+            android:layout_gravity="right"
+            android:layout_marginRight="20dp"
+            android:padding="@dimen/dp_10"
+            android:src="@drawable/switch_camera"
+            android:textColor="#FFFFFF"
+            android:visibility="gone" />
+
+
+        <ImageView
+            android:id="@+id/record_button"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_centerHorizontal="true"
+            android:padding="@dimen/dp_10"
+            android:src="@drawable/ic_accompany_record" />
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:id="@+id/rl_preview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:background="@color/black_444"
+        android:visibility="gone">
+
+        <ImageView
+            android:id="@+id/iv_video"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentTop="true"
+            android:background="@color/black" />
+
+        <ImageView
+            android:id="@+id/iv_del"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentStart="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="@dimen/dp_20"
+            android:padding="@dimen/dp_15"
+            android:scaleType="centerCrop"
+            android:src="@drawable/ic_accompany_back" />
+
+        <ImageView
+            android:id="@+id/iv_save"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_alignParentEnd="true"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="@dimen/dp_20"
+            android:padding="@dimen/dp_15"
+            android:scaleType="centerCrop"
+            android:src="@drawable/ic_accompany_video_record_save" />
+
+        <ImageView
+            android:id="@+id/iv_play"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:layout_marginTop="@dimen/dp_20"
+            android:padding="@dimen/dp_15"
+            android:scaleType="centerCrop"
+            android:src="@drawable/ic_video_record_play" />
+    </RelativeLayout>
+
+</FrameLayout>

Файловите разлики са ограничени, защото са твърде много
+ 0 - 0
accompany/src/main/res/raw/accompany_loading.json


+ 17 - 0
accompany/src/test/java/com/daya/orchestra/accompany/ExampleUnitTest.java

@@ -0,0 +1,17 @@
+package com.daya.orchestra.accompany;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
+ */
+public class ExampleUnitTest {
+    @Test
+    public void addition_isCorrect() {
+        assertEquals(4, 2 + 2);
+    }
+}

+ 1 - 0
settings.gradle

@@ -84,3 +84,4 @@ include ':tclive'
 include ':classRoom'
 include ':institution'
 include ':musicMerge'
+include ':accompany'

+ 1 - 0
student/build.gradle

@@ -127,6 +127,7 @@ dependencies {
     implementation project(path: ':classRoom')
     implementation project(path: ':institution')
     implementation project(path: ':musicMerge')
+    implementation project(path: ':accompany')
     implementation "com.alibaba:arouter-api:$rootProject.ext.android.arouter_api_version"
     kapt "com.alibaba:arouter-compiler:$rootProject.ext.android.arouter_api_version"
 

+ 21 - 0
usercenter/src/main/java/com/cooleshow/usercenter/helper/UserHelper.java

@@ -266,6 +266,27 @@ public class UserHelper {
                 .navigation();
     }
 
+    public static void setCustomCache(String key, String value) {
+        SPUtils.getInstance().put(key + "_custom", value);
+    }
+
+    public static String getCustomCache(String key) {
+        return SPUtils.getInstance().getString(key + "_custom");
+    }
+
+
+    public static void setCustomCache(String key, int value) {
+        SPUtils.getInstance().put(key + "_custom", value);
+    }
+
+    public static int getCustomCacheForInt(String key) {
+        return SPUtils.getInstance().getInt(key + "_custom",-1);
+    }
+
+    public static int getCustomCacheForInt(String key,int defaultValue) {
+        return SPUtils.getInstance().getInt(key + "_custom",defaultValue);
+    }
+
     public static boolean isTenantAccount(String tenantId) {
         LOG.i("isTenantAccount:"+tenantId);
         if (TextUtils.isEmpty(tenantId)) {

Някои файлове не бяха показани, защото твърде много файлове са промени