Browse Source

1.修改语音播放功能

王鹏鹏 2 years ago
parent
commit
97fdd2b5de

+ 9 - 1
baselib/build.gradle

@@ -30,6 +30,9 @@ android {
     buildTypes {
         debug {
             minifyEnabled false
+            buildConfigField "String", "BAIDU_APPID", "\"37371987\""
+            buildConfigField "String", "BAIDU_APPKEY", "\"RzdZg1lVNxAB1NpQi0SSwLIe\""
+            buildConfigField "String", "BAIDU_SECRETKEY", "\"4AfYdqAyA9PtLOoNFFsyUogDfxoMBmqS\""
             buildConfigField "Boolean", "SINGLE_MODULE", "${singleModule}"
             buildConfigField "String", "BUGLY_APPID", "\"d400f20398\""
             buildConfigField "String", "API_URL", "\"http://192.168.0.118:8110\""
@@ -38,6 +41,9 @@ android {
 
         release {
             minifyEnabled false
+            buildConfigField "String", "BAIDU_APPID", "\"37371987\""
+            buildConfigField "String", "BAIDU_APPKEY", "\"RzdZg1lVNxAB1NpQi0SSwLIe\""
+            buildConfigField "String", "BAIDU_SECRETKEY", "\"4AfYdqAyA9PtLOoNFFsyUogDfxoMBmqS\""
             buildConfigField "Boolean", "SINGLE_MODULE", "${singleModule}"
             buildConfigField "String", "BUGLY_APPID", "\"ad3db4d529\""
             buildConfigField "String", "API_URL", "\"http://192.168.0.118:8110\""
@@ -70,6 +76,7 @@ android {
 
 dependencies {
 
+    api fileTree(exclude: '*.bak', dir: 'libs')
     implementation(rootProject.ext.androidx.kotlin_lib)
     implementation(rootProject.ext.androidx.kotlin_core)
     implementation(rootProject.ext.androidx.appcompat)
@@ -116,5 +123,6 @@ dependencies {
     api(rootProject.ext.dependencies.live_event_bus_x)
     api(rootProject.ext.dependencies.glide)
     kapt(rootProject.ext.dependencies.glide_compiler)
-    api( rootProject.ext.dependencies.mmkv_ktx )
+    api(rootProject.ext.dependencies.mmkv_ktx)
+    debugApi(rootProject.ext.dependencies.leakcanary)
 }

+ 0 - 0
webview/libs/com.baidu.tts_2.6.2.2.20200629_44818d4.jar → baselib/libs/com.baidu.tts_2.6.2.2.20200629_44818d4.jar


+ 155 - 0
baselib/src/main/java/com/yingyangfly/baselib/voice/PlayVoice.kt

@@ -0,0 +1,155 @@
+package com.yingyangfly.baselib.voice
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.os.Handler
+import android.os.Message
+import android.util.Log
+import com.baidu.tts.chainofresponsibility.logger.LoggerProxy
+import com.baidu.tts.client.SpeechSynthesizer
+import com.baidu.tts.client.SpeechSynthesizerListener
+import com.baidu.tts.client.TtsMode
+import com.yingyangfly.baselib.BuildConfig
+import com.yingyangfly.baselib.voice.control.InitConfig
+import com.yingyangfly.baselib.voice.listener.UiMessageListener
+import com.yingyangfly.baselib.voice.util.AutoCheck
+import com.yingyangfly.baselib.voice.util.IOfflineResourceConst
+
+/**
+ * @author 王鹏鹏
+ */
+class PlayVoice {
+
+    /**
+     * 发布时请替换成自己申请的appId appKey 和 secretKey。注意如果需要离线合成功能,请在您申请的应用中填写包名。
+     * 本demo的包名是com.baidu.tts.sample,定义在build.gradle中。
+     */
+    private var appId: String? = null
+
+    private var appKey: String? = null
+
+    private var secretKey: String? = null
+
+    private var context: Context? = null
+
+    // ===============初始化参数设置完毕,更多合成参数请至getParams()方法中设置 =================
+    private var mSpeechSynthesizer: SpeechSynthesizer? = null
+
+    private var mainHandler: Handler? = null
+
+    // TtsMode.MIX; 离在线融合,在线优先; TtsMode.ONLINE 纯在线; 没有纯离线
+    private val ttsMode: TtsMode = IOfflineResourceConst.DEFAULT_SDK_TTS_MODE
+
+    @SuppressLint("HandlerLeak")
+    fun setContext(context: Context) {
+        this.context = context
+        this.appId = BuildConfig.BAIDU_APPID
+        this.appKey = BuildConfig.BAIDU_APPKEY
+        this.secretKey = BuildConfig.BAIDU_SECRETKEY
+        mainHandler = object : Handler() {
+            override fun handleMessage(msg: Message) {
+                super.handleMessage(msg)
+                if (msg.obj != null) {
+                    print(msg.obj.toString())
+                }
+            }
+        }
+        initTTs()
+    }
+
+    @SuppressLint("HandlerLeak")
+    private fun initTTs() {
+        LoggerProxy.printable(true) // 日志打印在logcat中
+        // 日志更新在UI中,可以换成MessageListener,在logcat中查看日志
+        val listener: SpeechSynthesizerListener = UiMessageListener(mainHandler)
+        // 1. 获取实例
+        mSpeechSynthesizer = SpeechSynthesizer.getInstance()
+        mSpeechSynthesizer?.setContext(context)
+        // 2. 设置listener
+        mSpeechSynthesizer?.setSpeechSynthesizerListener(listener)
+        // 3. 设置appId,appKey.secretKey
+        var result = mSpeechSynthesizer?.setAppId(appId)
+        if (result != null) {
+            checkResult(result, "setAppId")
+        }
+        result = mSpeechSynthesizer?.setApiKey(appKey, secretKey)
+        if (result != null) {
+            checkResult(result, "setApiKey")
+        }
+        // 5. 以下setParam 参数选填。不填写则默认值生效
+        // 设置在线发声音人: 0 普通女声(默认) 1 普通男声  3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
+        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_SPEAKER, "4")
+        // 设置合成的音量,0-15 ,默认 5
+        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_VOLUME, "5")
+        // 设置合成的语速,0-15 ,默认 5
+        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_SPEED, "5")
+        // 设置合成的语调,0-15 ,默认 5
+        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_PITCH, "5")
+
+        // x. 额外 : 自动so文件是否复制正确及上面设置的参数
+        val params: Map<String, String> = HashMap()
+        // 检测参数,通过一次后可以去除,出问题再打开debug
+        val initConfig = InitConfig(appId, appKey, secretKey, ttsMode, params, listener)
+        AutoCheck.getInstance(context).check(initConfig, object : Handler() {
+            /**
+             * 开新线程检查,成功后回调
+             */
+            override fun handleMessage(msg: Message) {
+                if (msg.what == 100) {
+                    val autoCheck: AutoCheck = msg.obj as AutoCheck
+                    synchronized(autoCheck) {
+                        val message: String = autoCheck.obtainDebugMessage()
+                        print(message) // 可以用下面一行替代,在logcat中查看代码
+                    }
+                }
+            }
+        })
+        // 6. 初始化
+        result = mSpeechSynthesizer?.initTts(ttsMode)
+        if (result != null) {
+            checkResult(result, "initTts")
+        }
+
+    }
+
+    private fun print(message: String) {
+        Log.i("wpp", message)
+    }
+
+    private fun checkResult(result: Int, method: String) {
+        if (result != 0) {
+            Log.e("wpp", "error code :$result method:$method")
+        }
+    }
+
+    /**
+     * 播放语音
+     */
+    fun speak(str: String) {
+        /* 以下参数每次合成时都可以修改
+         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
+         *  设置在线发声音人: 0 普通女声(默认) 1 普通男声  3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
+         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5"); 设置合成的音量,0-9 ,默认 5
+         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5"); 设置合成的语速,0-9 ,默认 5
+         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5"); 设置合成的语调,0-9 ,默认 5
+         *
+         */
+        if (mSpeechSynthesizer == null) {
+            print("[ERROR], 初始化失败")
+            return
+        }
+        if (str.isNotEmpty()) {
+            val result: Int = mSpeechSynthesizer!!.speak(str)
+            Log.e("wpp", "合成并播放 按钮已经点击")
+            checkResult(result, "speak")
+        }
+    }
+
+    fun stop() {
+        if (mSpeechSynthesizer != null) {
+            mSpeechSynthesizer!!.stop()
+            mSpeechSynthesizer!!.release()
+            mSpeechSynthesizer = null
+        }
+    }
+}

+ 100 - 0
baselib/src/main/java/com/yingyangfly/baselib/voice/control/InitConfig.java

@@ -0,0 +1,100 @@
+package com.yingyangfly.baselib.voice.control;
+
+import com.baidu.tts.client.SpeechSynthesizerListener;
+import com.baidu.tts.client.TtsMode;
+import com.yingyangfly.baselib.voice.util.IOfflineResourceConst;
+
+import java.util.Map;
+
+/**
+ * 合成引擎的初始化参数
+ * <p>
+ *
+ * @author fujiayi
+ * @date 2017/9/13
+ */
+public class InitConfig {
+    /**
+     * appId appKey 和 secretKey。注意如果需要离线合成功能,请在您申请的应用中填写包名。
+     * 本demo的包名是com.baidu.tts.sample,定义在build.gradle中。
+     */
+    private String appId;
+
+    private String appKey;
+
+    private String secretKey;
+
+    private String sn;
+
+    /**
+     * 纯在线或者离在线融合
+     */
+    private TtsMode ttsMode;
+
+
+    /**
+     * 初始化的其它参数,用于setParam
+     */
+    private Map<String, String> params;
+
+    /**
+     * 合成引擎的回调
+     */
+    private SpeechSynthesizerListener listener;
+
+    private InitConfig() {
+
+    }
+
+    // 离在线SDK用
+    public InitConfig(String appId, String appKey, String secretKey, TtsMode ttsMode,
+                      Map<String, String> params, SpeechSynthesizerListener listener) {
+        this.appId = appId;
+        this.appKey = appKey;
+        this.secretKey = secretKey;
+        this.ttsMode = ttsMode;
+        this.params = params;
+        this.listener = listener;
+    }
+
+
+    // 纯离线SDK用
+    public InitConfig(String appId, String appKey, String secretKey, String sn, TtsMode ttsMode,
+                      Map<String, String> params, SpeechSynthesizerListener listener) {
+        this(appId, appKey, secretKey, ttsMode, params, listener);
+        this.sn = sn;
+        if (sn != null) {
+            // 纯离线sdk 才有的参数;离在线版本没有
+            params.put(IOfflineResourceConst.PARAM_SN_NAME, sn);
+        }
+    }
+
+    public SpeechSynthesizerListener getListener() {
+        return listener;
+    }
+
+    public Map<String, String> getParams() {
+        return params;
+    }
+
+
+    public String getAppId() {
+        return appId;
+    }
+
+    public String getAppKey() {
+        return appKey;
+    }
+
+    public String getSecretKey() {
+        return secretKey;
+    }
+
+    public TtsMode getTtsMode() {
+        return ttsMode;
+    }
+
+    public String getSn() {
+        return sn;
+    }
+}

+ 13 - 0
baselib/src/main/java/com/yingyangfly/baselib/voice/listener/MainHandlerConstant.java

@@ -0,0 +1,13 @@
+package com.yingyangfly.baselib.voice.listener;
+
+/**
+ * Created by fujiayi on 2017/9/13.
+ */
+
+public interface MainHandlerConstant {
+    static final int PRINT = 0;
+    static final int UI_CHANGE_INPUT_TEXT_SELECTION = 1;
+    static final int UI_CHANGE_SYNTHES_TEXT_SELECTION = 2;
+
+    static final int INIT_SUCCESS = 2;
+}

+ 111 - 0
baselib/src/main/java/com/yingyangfly/baselib/voice/listener/MessageListener.java

@@ -0,0 +1,111 @@
+package com.yingyangfly.baselib.voice.listener;
+
+import android.util.Log;
+
+import com.baidu.tts.client.SpeechError;
+import com.baidu.tts.client.SpeechSynthesizerListener;
+
+/**
+ * SpeechSynthesizerListener 简单地实现,仅仅记录日志
+ * Created by fujiayi on 2017/5/19.
+ */
+
+public class MessageListener implements SpeechSynthesizerListener, MainHandlerConstant {
+    private static final String TAG = "MessageListener";
+
+    /**
+     * 播放开始,每句播放开始都会回调
+     *
+     * @param utteranceId
+     */
+    @Override
+    public void onSynthesizeStart(String utteranceId) {
+        sendMessage("准备开始合成,序列号:" + utteranceId);
+    }
+
+    /**
+     * 语音流 16K采样率 16bits编码 单声道 。
+     *
+     * @param utteranceId
+     * @param bytes       二进制语音 ,注意可能有空data的情况,可以忽略
+     * @param progress    如合成“百度语音问题”这6个字, progress肯定是从0开始,到6结束。 但progress无法和合成到第几个字对应。
+     *                    engineType 下版本提供。1:音频数据由离线引擎合成; 0:音频数据由在线引擎(百度服务器)合成。
+     */
+
+    public void onSynthesizeDataArrived(String utteranceId, byte[] bytes, int progress) {
+        Log.i(TAG, "合成进度回调, progress:" + progress + ";序列号:" + utteranceId);
+        // + ";" + (engineType == 1? "离线合成":"在线合成"));
+    }
+
+    @Override
+    // engineType 下版本提供。1:音频数据由离线引擎合成; 0:音频数据由在线引擎(百度服务器)合成。
+    public void onSynthesizeDataArrived(String utteranceId, byte[] bytes, int progress, int engineType) {
+        onSynthesizeDataArrived(utteranceId, bytes, progress);
+    }
+
+    /**
+     * 合成正常结束,每句合成正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
+     *
+     * @param utteranceId
+     */
+    @Override
+    public void onSynthesizeFinish(String utteranceId) {
+        sendMessage("合成结束回调, 序列号:" + utteranceId);
+    }
+
+    @Override
+    public void onSpeechStart(String utteranceId) {
+        sendMessage("播放开始回调, 序列号:" + utteranceId);
+    }
+
+    /**
+     * 播放进度回调接口,分多次回调
+     *
+     * @param utteranceId
+     * @param progress    如合成“百度语音问题”这6个字, progress肯定是从0开始,到6结束。 但progress无法保证和合成到第几个字对应。
+     */
+    @Override
+    public void onSpeechProgressChanged(String utteranceId, int progress) {
+        //  Log.i(TAG, "播放进度回调, progress:" + progress + ";序列号:" + utteranceId );
+    }
+
+    /**
+     * 播放正常结束,每句播放正常结束都会回调,如果过程中出错,则回调onError,不再回调此接口
+     *
+     * @param utteranceId
+     */
+    @Override
+    public void onSpeechFinish(String utteranceId) {
+        sendMessage("播放结束回调, 序列号:" + utteranceId);
+    }
+
+    /**
+     * 当合成或者播放过程中出错时回调此接口
+     *
+     * @param utteranceId
+     * @param speechError 包含错误码和错误信息
+     */
+    @Override
+    public void onError(String utteranceId, SpeechError speechError) {
+        sendErrorMessage("错误发生:" + speechError.description + ",错误编码:"
+                + speechError.code + ",序列号:" + utteranceId);
+    }
+
+    private void sendErrorMessage(String message) {
+        sendMessage(message, true);
+    }
+
+
+    private void sendMessage(String message) {
+        sendMessage(message, false);
+    }
+
+    protected void sendMessage(String message, boolean isError) {
+        if (isError) {
+            Log.e(TAG, message);
+        } else {
+            Log.i(TAG, message);
+        }
+
+    }
+}

+ 74 - 0
baselib/src/main/java/com/yingyangfly/baselib/voice/listener/UiMessageListener.java

@@ -0,0 +1,74 @@
+package com.yingyangfly.baselib.voice.listener;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+/**
+ * 在 MessageListener的基础上,和UI配合。
+ * Created by fujiayi on 2017/9/14.
+ */
+
+public class UiMessageListener extends MessageListener {
+
+    private Handler mainHandler;
+
+    private static final String TAG = "UiMessageListener";
+
+    public UiMessageListener(Handler mainHandler) {
+        super();
+        this.mainHandler = mainHandler;
+    }
+
+    /**
+     * 语音流 16K采样率 16bits编码 单声道 。
+     * <p>
+     * 合成数据和进度的回调接口,分多次回调。
+     * 注意:progress表示进度,与播放到哪个字无关
+     *
+     * @param utteranceId
+     * @param data        合成的音频数据。该音频数据是采样率为16K,2字节精度,单声道的pcm数据。
+     * @param progress    文本按字符划分的进度,比如:你好啊 进度是0-3
+     * engineType  下版本提供 1:音频数据由离线引擎合成; 0:音频数据由在线引擎(百度服务器)合成。
+     */
+    @Override
+    public void onSynthesizeDataArrived(String utteranceId, byte[] data, int progress) {
+        // sendMessage("onSynthesizeDataArrived");
+        super.onSynthesizeDataArrived(utteranceId, data, progress);
+        mainHandler.sendMessage(mainHandler.obtainMessage(UI_CHANGE_SYNTHES_TEXT_SELECTION, progress, 0));
+    }
+
+    /**
+     * 播放进度回调接口,分多次回调
+     * 注意:progress表示进度,与播放到哪个字无关
+     *
+     * @param utteranceId
+     * @param progress    文本按字符划分的进度,比如:你好啊 进度是0-3
+     */
+    @Override
+    public void onSpeechProgressChanged(String utteranceId, int progress) {
+        // sendMessage("onSpeechProgressChanged");
+        mainHandler.sendMessage(mainHandler.obtainMessage(UI_CHANGE_INPUT_TEXT_SELECTION, progress, 0));
+    }
+
+    protected void sendMessage(String message) {
+        sendMessage(message, false);
+    }
+
+    @Override
+    protected void sendMessage(String message, boolean isError) {
+        sendMessage(message, isError, PRINT);
+    }
+
+
+    protected void sendMessage(String message, boolean isError, int action) {
+        super.sendMessage(message, isError);
+        if (mainHandler != null) {
+            Message msg = Message.obtain();
+            msg.what = action;
+            msg.obj = message + "\n";
+            mainHandler.sendMessage(msg);
+            Log.i(TAG, message);
+        }
+    }
+}

+ 458 - 0
baselib/src/main/java/com/yingyangfly/baselib/voice/util/AutoCheck.java

@@ -0,0 +1,458 @@
+package com.yingyangfly.baselib.voice.util;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.Message;
+
+import androidx.core.content.ContextCompat;
+
+import com.baidu.tts.client.SpeechSynthesizer;
+import com.baidu.tts.client.SynthesizerTool;
+import com.baidu.tts.client.TtsMode;
+import com.yingyangfly.baselib.voice.control.InitConfig;
+
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.TreeSet;
+
+/**
+ * 自动排查工具,用于集成后发现错误。
+ * <p>
+ * 可以检测如下错误:
+ * 1. PermissionCheck : AndroidManifest,xml 需要的部分权限
+ * 2. JniCheck: 检测so文件是否安装在指定目录
+ * 3. AppInfoCheck: 联网情况下 , 检测appId appKey secretKey是否正确
+ * 4. ApplicationIdCheck: 显示包名applicationId, 提示用户手动去官网检查
+ * 5. ParamKeyExistCheck: 检查key是否存在,目前检查 SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE
+ * 和PARAM_TTS_SPEECH_MODEL_FILE
+ * 6.  OfflineResourceFileCheck 检查离线资源文件(需要从assets目录下复制),是否存在
+ * <p>
+ * <p>
+ * 示例使用代码:
+ * AutoCheck.getInstance(getApplicationContext()).check(initConfig, new Handler() {
+ *
+ * @Override public void handleMessage(Message msg) {
+ * if (msg.what == 100) {
+ * AutoCheck autoCheck = (AutoCheck) msg.obj;
+ * synchronized (autoCheck) {
+ * String message = autoCheck.obtainDebugMessage();
+ * toPrint(message); // 可以用下面一行替代,在logcat中查看代码
+ * //Log.w("AutoCheckMessage",message);
+ * }
+ * }
+ * }
+ * <p>
+ * });
+ */
+public class AutoCheck {
+
+    private static AutoCheck instance;
+
+    private LinkedHashMap<String, Check> checks;
+
+    private static Context context;
+
+    private boolean hasError = false;
+
+    volatile boolean isFinished = false;
+
+    /**
+     * 获取实例,非线程安全
+     *
+     * @return
+     */
+    public static AutoCheck getInstance(Context context) {
+        if (instance == null || AutoCheck.context != context) {
+            instance = new AutoCheck(context);
+        }
+        return instance;
+    }
+
+    public void check(final InitConfig initConfig, final Handler handler) {
+        Thread t = new Thread(() -> {
+            AutoCheck obj = innerCheck(initConfig);
+            isFinished = true;
+            synchronized (obj) { // 偶发,同步线程信息
+                Message msg = handler.obtainMessage(100, obj);
+                handler.sendMessage(msg);
+            }
+        });
+        t.start();
+
+    }
+
+    private AutoCheck innerCheck(InitConfig config) {
+        boolean isOnlineSdk = TtsMode.ONLINE.equals(config.getTtsMode());
+        checks.put("检查申请的Android权限", new PermissionCheck(context));
+        checks.put("检查4个so文件是否存在", new JniCheck(context, isOnlineSdk));
+        checks.put("检查AppId AppKey SecretKey",
+                new AppInfoCheck(config.getAppId(), config.getAppKey(), config.getSecretKey()));
+        checks.put("检查包名", new ApplicationIdCheck(context, config.getAppId()));
+
+        if (!isOnlineSdk) {
+            Map<String, String> params = config.getParams();
+            String fileKey = SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE;
+            checks.put("检查离线资TEXT文件参数", new ParamKeyExistCheck(params, fileKey,
+                    "SpeechSynthesizer.PARAM_TTS_TEXT_MODEL_FILE未设置 ,"));
+            checks.put("检查离线资源TEXT文件", new OfflineResourceFileCheck(params.get(fileKey)));
+            fileKey = SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE;
+            checks.put("检查离线资Speech文件参数", new ParamKeyExistCheck(params, fileKey,
+                    "SpeechSynthesizer.PARAM_TTS_SPEECH_MODEL_FILE未设置 ,"));
+            checks.put("检查离线资源Speech文件", new OfflineResourceFileCheck(params.get(fileKey)));
+        }
+
+        for (Map.Entry<String, Check> e : checks.entrySet()) {
+            Check check = e.getValue();
+            check.check();
+            if (check.hasError()) {
+                break;
+            }
+        }
+        return this;
+    }
+
+    public String obtainErrorMessage() {
+        PrintConfig config = new PrintConfig();
+        return formatString(config);
+    }
+
+    public String obtainDebugMessage() {
+        PrintConfig config = new PrintConfig();
+        config.withInfo = true;
+        return formatString(config);
+    }
+
+    public String obtainAllMessage() {
+        PrintConfig config = new PrintConfig();
+        config.withLog = true;
+        config.withInfo = true;
+        return formatString(config);
+    }
+
+    public String formatString(PrintConfig config) {
+        StringBuilder sb = new StringBuilder();
+        hasError = false;
+
+        for (HashMap.Entry<String, Check> entry : checks.entrySet()) {
+            Check check = entry.getValue();
+            String testName = entry.getKey();
+            if (check.hasError()) {
+                if (!hasError) {
+                    hasError = true;
+                }
+
+                sb.append("【错误】【").append(testName).append(" 】  ").append(check.getErrorMessage()).append("\n");
+                if (check.hasFix()) {
+                    sb.append("【修复方法】【").append(testName).append(" 】  ").append(check.getFixMessage()).append("\n");
+                }
+            }
+            if (config.withInfo && check.hasInfo()) {
+                sb.append("【请手动检查】【").append(testName).append("】 ").append(check.getInfoMessage()).append("\n");
+            }
+            if (config.withLog && (config.withLogOnSuccess || hasError) && check.hasLog()) {
+                sb.append("【log】:" + check.getLogMessage()).append("\n");
+            }
+        }
+        if (!hasError) {
+            sb.append("集成自动排查工具: 恭喜没有检测到任何问题\n");
+        }
+        return sb.toString();
+    }
+
+    public void clear() {
+        checks.clear();
+        hasError = false;
+    }
+
+    private AutoCheck(Context context) {
+        this.context = context;
+        checks = new LinkedHashMap<>();
+    }
+
+    private static class PrintConfig {
+        public boolean withFix = true;
+        public boolean withInfo = false;
+        public boolean withLog = false;
+        public boolean withLogOnSuccess = false;
+    }
+
+    private static class PermissionCheck extends Check {
+        private Context context;
+
+        public PermissionCheck(Context context) {
+            this.context = context;
+        }
+
+        @Override
+        public void check() {
+            String[] permissions = {
+                    Manifest.permission.INTERNET,
+                    Manifest.permission.ACCESS_NETWORK_STATE,
+                    Manifest.permission.MODIFY_AUDIO_SETTINGS,
+                    // Manifest.permission.WRITE_EXTERNAL_STORAGE,
+                    // Manifest.permission.WRITE_SETTINGS,
+                    Manifest.permission.ACCESS_WIFI_STATE,
+                    // Manifest.permission.CHANGE_WIFI_STATE
+            };
+
+            ArrayList<String> toApplyList = new ArrayList<String>();
+
+            for (String perm : permissions) {
+                if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(context, perm)) {
+                    toApplyList.add(perm);
+                    // 进入到这里代表没有权限.
+                }
+            }
+            if (!toApplyList.isEmpty()) {
+                errorMessage = "缺少权限:" + toApplyList;
+                fixMessage = "请从AndroidManifest.xml复制相关权限";
+            }
+        }
+    }
+
+    private static class JniCheck extends Check {
+        private Context context;
+
+        private String[] soNames;
+
+        public JniCheck(Context context, boolean isOnlineSdk) {
+            this.context = context;
+            if (isOnlineSdk) {
+                soNames = new String[]{"libBDSpeechDecoder_V1.so"};
+            } else {
+                soNames = new String[]{"libbd_etts.so", "libBDSpeechDecoder_V1.so", "libgnustl_shared.so"};
+            }
+        }
+
+        @Override
+        public void check() {
+            String path = context.getApplicationInfo().nativeLibraryDir;
+            appendLogMessage("Jni so文件目录 " + path);
+            File[] files = new File(path).listFiles();
+            TreeSet<String> set = new TreeSet<>();
+            if (files != null) {
+                for (File file : files) {
+                    if (file.canRead()) {
+                        set.add(file.getName());
+                    }
+                }
+            }
+            appendLogMessage("Jni目录内文件: " + set.toString());
+            for (String name : soNames) {
+                if (!set.contains(name)) {
+                    errorMessage = "Jni目录" + path + " 缺少可读的so文件:" + name + ", 该目录文件列表: " + set.toString();
+                    fixMessage = "如果您的app内没有其它so文件,请复制demo里的src/main/jniLibs至同名目录。"
+                            + " 如果app内有so文件,请合并目录放一起(注意目录取交集,多余的目录删除)。";
+                    break;
+                }
+            }
+        }
+    }
+
+    private static class ParamKeyExistCheck extends Check {
+        private Map<String, String> params;
+        private String key;
+        private String prefixErrorMessage;
+
+        public ParamKeyExistCheck(Map<String, String> params, String key, String prefixErrorMessage) {
+            this.params = params;
+            this.key = key;
+            this.prefixErrorMessage = prefixErrorMessage;
+        }
+
+        @Override
+        public void check() {
+            if (params == null || !params.containsKey(key)) {
+                errorMessage = prefixErrorMessage + " 参数中没有设置:" + key;
+                fixMessage = "请参照demo在设置 " + key + "参数";
+            }
+        }
+    }
+
+    private static class OfflineResourceFileCheck extends Check {
+        private String filename;
+
+        public OfflineResourceFileCheck(String filename) {
+            this.filename = filename;
+        }
+
+        @Override
+        public void check() {
+            File file = new File(filename);
+            if (!file.exists()) {
+                errorMessage = "资源文件不存在:" + filename;
+            } else if (!file.canRead()) {
+                errorMessage = "资源文件不可读:" + filename;
+            } else if (!SynthesizerTool.verifyModelFile(filename)) {
+                errorMessage = "SDK verifyModelFile方法判断模型文件不是有效的,请重新复制:" + filename;
+            }
+            if (hasError()) {
+                fixMessage = "请将demo中src/main/assets目录下同名文件复制到 " + filename;
+            }
+        }
+    }
+
+    private static class ApplicationIdCheck extends Check {
+
+        private String appId;
+        private Context context;
+
+        public ApplicationIdCheck(Context context, String appId) {
+            this.appId = appId;
+            this.context = context;
+        }
+
+        @Override
+        public void check() {
+            infoMessage = "如果您集成过程中遇见离线合成初始化问题,请检查网页上appId:" + appId
+                    + " 应用是否开通了合成服务,并且网页上的应用填写了Android包名:"
+                    + getApplicationId();
+        }
+
+        private String getApplicationId() {
+            return context.getPackageName();
+        }
+    }
+
+
+    private static class AppInfoCheck extends Check {
+        private String appId;
+        private String appKey;
+        private String secretKey;
+
+        public AppInfoCheck(String appId, String appKey, String secretKey) {
+            this.appId = appId;
+            this.appKey = appKey;
+            this.secretKey = secretKey;
+        }
+
+
+        public void check() {
+            do {
+                appendLogMessage("try to check appId " + appId + " ,appKey=" + appKey + " ,secretKey" + secretKey);
+                if (appId == null || appId.isEmpty()) {
+                    errorMessage = "appId 为空";
+                    fixMessage = "填写appID";
+                    break;
+                }
+                if (appKey == null || appKey.isEmpty()) {
+                    errorMessage = "appKey 为空";
+                    fixMessage = "填写appID";
+                    break;
+                }
+                if (secretKey == null || secretKey.isEmpty()) {
+                    errorMessage = "secretKey 为空";
+                    fixMessage = "secretKey";
+                    break;
+                }
+            } while (false);
+            try {
+                checkOnline();
+            } catch (UnknownHostException e) {
+                infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
+            } catch (Exception e) {
+                errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
+                fixMessage = " 重新检测appId, appKey, appSecret是否正确";
+            }
+        }
+
+        public void checkOnline() throws Exception {
+            String urlpath = "https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id="
+                    + appKey + "&client_secret=" + secretKey;
+            URL url = new URL(urlpath);
+            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
+            conn.setRequestMethod("GET");
+            conn.setConnectTimeout(1000);
+            InputStream is = conn.getInputStream();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            StringBuilder result = new StringBuilder();
+            String line = "";
+            do {
+                line = reader.readLine();
+                if (line != null) {
+                    result.append(line);
+                }
+            } while (line != null);
+            String res = result.toString();
+            appendLogMessage("openapi return " + res);
+            JSONObject jsonObject = new JSONObject(res);
+            String error = jsonObject.optString("error");
+            if (error != null && !error.isEmpty()) {
+                throw new Exception("appkey secretKey 错误" + ", error:" + error + ", json is" + result);
+            }
+            String token = jsonObject.getString("access_token");
+            if (token == null || !token.endsWith("-" + appId)) {
+                throw new Exception("appId 与 appkey及 appSecret 不一致。appId = " + appId + " ,token = " + token);
+            }
+        }
+
+
+    }
+
+    private abstract static class Check {
+        protected String errorMessage = null;
+
+        protected String fixMessage = null;
+
+        protected String infoMessage = null;
+
+        protected StringBuilder logMessage;
+
+        public Check() {
+            logMessage = new StringBuilder();
+        }
+
+        public abstract void check();
+
+        public boolean hasError() {
+            return errorMessage != null;
+        }
+
+        public boolean hasFix() {
+            return fixMessage != null;
+        }
+
+        public boolean hasInfo() {
+            return infoMessage != null;
+        }
+
+        public boolean hasLog() {
+            return !logMessage.toString().isEmpty();
+        }
+
+        public void appendLogMessage(String message) {
+            logMessage.append(message + "\n");
+        }
+
+        public String getErrorMessage() {
+            return errorMessage;
+        }
+
+        public String getFixMessage() {
+            return fixMessage;
+        }
+
+        public String getInfoMessage() {
+            return infoMessage;
+        }
+
+        public String getLogMessage() {
+            return logMessage.toString();
+        }
+
+
+    }
+}

+ 33 - 0
baselib/src/main/java/com/yingyangfly/baselib/voice/util/IOfflineResourceConst.java

@@ -0,0 +1,33 @@
+package com.yingyangfly.baselib.voice.util;
+
+import com.baidu.tts.client.TtsMode;
+
+/**
+ * @author 王鹏鹏
+ */
+public interface IOfflineResourceConst {
+
+    TtsMode DEFAULT_SDK_TTS_MODE = TtsMode.ONLINE;
+
+    /** 下面参数 纯离线SDK版本才有 */
+
+    String VOICE_FEMALE = "F";
+
+    String VOICE_MALE = "M";
+
+    String VOICE_DUYY = "Y";
+
+    String VOICE_DUXY = "X";
+
+    String TEXT_MODEL = null;
+
+    String VOICE_MALE_MODEL = null;
+
+    String VOICE_FEMALE_MODEL = null;
+
+    String VOICE_DUXY_MODEL = null;
+
+    String VOICE_DUYY_MODEL = null;
+
+    String PARAM_SN_NAME = null;
+}

+ 2 - 1
config.gradle

@@ -113,7 +113,8 @@ ext {
             "utilcode": 'com.blankj:utilcode:1.30.7',
             //极光推送
             "jpush": 'cn.jiguang.sdk:jpush:5.0.0',
-            "gson":"com.google.code.gson:gson:2.8.5"
+            "gson":"com.google.code.gson:gson:2.8.5",
+            "leakcanary": "com.squareup.leakcanary:leakcanary-android:2.10"
 
     ]
 }

+ 9 - 107
webview/build.gradle

@@ -1,123 +1,25 @@
-if (singleModule.toBoolean()) {
-    // 作为独立app应用运行
-    apply plugin: 'com.android.application'
-} else {
-    // 作为组件运行
-    apply plugin: 'com.android.library'
-}
-
+apply from: "../module.build.gradle"
 apply plugin: 'kotlin-android'
-// ARouter
 apply plugin: 'kotlin-kapt'
-/*序列化插件*/
-apply plugin: 'kotlin-parcelize'
-
-kapt {
-    arguments {
-        arg("AROUTER_MODULE_NAME", project.getName())
-    }
-}
-
-def singleRun = singleModule.toBoolean()
 
 android {
-    compileSdkVersion rootProject.ext.android.compileSdkVersion
-    buildToolsVersion rootProject.ext.android.buildToolsVersion
-
     defaultConfig {
-        // 如果是独立模块,则使用当前组件的包名
-        if (singleRun) {
-            applicationId rootProject.ext.android.applicationId
-        }
-        minSdkVersion rootProject.ext.android.minSdkVersion
-        targetSdkVersion rootProject.ext.android.targetSdkVersion
-        versionCode rootProject.ext.android.versionCode
-        versionName rootProject.ext.android.versionName
-        flavorDimensions "versionCode"
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-        vectorDrawables.useSupportLibrary = true
-        multiDexEnabled true
-        // 只保留指定和默认的资源
-        resConfigs "zh"//去掉无用的国际语言
-        ndk {
-            abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
-        }
-        generatedDensities = []
-
         // 如果是独立模块,则使用当前组件的包名
         if (singleModule.toBoolean()) {
             applicationId "com.yingyangfly.webview"
         }
     }
     // 统一资源前缀,规范资源引用
-    resourcePrefix "webview_"
-
-    buildTypes {
-        debug {
-            minifyEnabled false // 打开混淆
-            shrinkResources false // 是否清理无用资源,依赖于minifyEnabled
-            zipAlignEnabled false // 是否启用zipAlign压缩
-            buildConfigField "String", "BAIDU_APPID", "\"37371987\""
-            buildConfigField "String", "BAIDU_APPKEY", "\"RzdZg1lVNxAB1NpQi0SSwLIe\""
-            buildConfigField "String", "BAIDU_SECRETKEY", "\"4AfYdqAyA9PtLOoNFFsyUogDfxoMBmqS\""
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-            debuggable true
-        }
-        release {
-            minifyEnabled false // 打开混淆
-            shrinkResources false // 是否清理无用资源,依赖于minifyEnabled
-            zipAlignEnabled false // 是否启用zipAlign压缩
-            buildConfigField "String", "BAIDU_APPID", "\"37371987\""
-            buildConfigField "String", "BAIDU_APPKEY", "\"RzdZg1lVNxAB1NpQi0SSwLIe\""
-            buildConfigField "String", "BAIDU_SECRETKEY", "\"4AfYdqAyA9PtLOoNFFsyUogDfxoMBmqS\""
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-            debuggable true
-        }
-    }
-
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_1_8
-        targetCompatibility JavaVersion.VERSION_1_8
-    }
-    kotlinOptions {
-        jvmTarget = '1.8'
-    }
-    buildFeatures {
-        dataBinding true
-    }
-
-    sourceSets {
-        main {
-            jniLibs.srcDirs = ['libs']
-            if (singleRun) {
-                // 如果是application,则 编译主目录下AndroidManifest.xml文件
-                manifest.srcFile 'src/main/AndroidManifest.xml'
-            } else {
-                // 如果是library,合并到宿主,则 编译manifest目录下AndroidManifest.xml文件
-                manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
-                resources {
-                    // 正式版本时,排除manifest文件夹下所有的调试文件
-                    exclude 'src/main/manifest/*'
-                }
-            }
-        }
-    }
+    resourcePrefix "webview"
 }
 
-
 dependencies {
-    api fileTree(exclude: '*.bak', dir: 'libs')
-    // 基类lib包
     implementation project(path: ':baselib')
-    // ARtouter
-    implementation(rootProject.ext.dependencies.arouter_api)
-    kapt(rootProject.ext.dependencies.arouter_compiler)
-    api(rootProject.ext.androidx.kotlin_lib)
-    api(rootProject.ext.androidx.kotlin_core)
-    api(rootProject.ext.androidx.appcompat)
-    api(rootProject.ext.androidx.material)
-    api(rootProject.ext.androidx.constraintlayout)
-    testApi(rootProject.ext.androidx.junit)
-    androidTestApi(rootProject.ext.androidx.runner)
-    androidTestApi(rootProject.ext.androidx.espresso_core)
+    api fileTree(exclude: '*.bak', dir: 'libs')
+    implementation(rootProject.ext.androidx.kotlin_lib)
+    implementation(rootProject.ext.androidx.kotlin_core)
+    implementation(rootProject.ext.androidx.appcompat)
+    implementation(rootProject.ext.androidx.material)
+    implementation(rootProject.ext.androidx.constraintlayout)
+
 }

+ 1 - 141
webview/src/main/java/com/yingyangfly/webview/BridgeWebActivity.kt

@@ -4,27 +4,16 @@ import android.annotation.SuppressLint
 import android.content.pm.ActivityInfo
 import android.os.Build
 import android.os.Bundle
-import android.os.Handler
-import android.os.Message
-import android.util.Log
 import android.webkit.WebResourceRequest
 import android.webkit.WebSettings
 import android.webkit.WebView
 import android.webkit.WebViewClient
 import com.alibaba.android.arouter.facade.annotation.Route
-import com.baidu.tts.chainofresponsibility.logger.LoggerProxy
-import com.baidu.tts.client.SpeechSynthesizer
-import com.baidu.tts.client.SpeechSynthesizerListener
-import com.baidu.tts.client.TtsMode
 import com.github.lzyzsd.jsbridge.BridgeHandler
 import com.github.lzyzsd.jsbridge.CallBackFunction
 import com.yingyangfly.baselib.base.BaseActivity
 import com.yingyangfly.baselib.router.RouterUrlCommon
 import com.yingyangfly.baselib.utils.User
-import com.yingyangfly.control.InitConfig
-import com.yingyangfly.listener.UiMessageListener
-import com.yingyangfly.util.AutoCheck
-import com.yingyangfly.util.IOfflineResourceConst
 import com.yingyangfly.webview.databinding.ActivityBridgeWebBinding
 
 /**
@@ -33,25 +22,6 @@ import com.yingyangfly.webview.databinding.ActivityBridgeWebBinding
 @Route(path = RouterUrlCommon.WEB_VIEW_INTERACTION_JS)
 class BridgeWebActivity : BaseActivity<ActivityBridgeWebBinding>() {
 
-    // ================== 精简版初始化参数设置开始 ==========================
-    /**
-     * 发布时请替换成自己申请的appId appKey 和 secretKey。注意如果需要离线合成功能,请在您申请的应用中填写包名。
-     * 本demo的包名是com.baidu.tts.sample,定义在build.gradle中。
-     */
-    private var appId: String? = null
-
-    private var appKey: String? = null
-
-    private var secretKey: String? = null
-
-    private var mainHandler: Handler? = null
-
-    // ===============初始化参数设置完毕,更多合成参数请至getParams()方法中设置 =================
-    private var mSpeechSynthesizer: SpeechSynthesizer? = null
-
-    // TtsMode.MIX; 离在线融合,在线优先; TtsMode.ONLINE 纯在线; 没有纯离线
-    private val ttsMode: TtsMode = IOfflineResourceConst.DEFAULT_SDK_TTS_MODE
-
     private lateinit var webSettings: WebSettings
     private var gameUrl: String = ""
     private var url: String = ""
@@ -67,10 +37,6 @@ class BridgeWebActivity : BaseActivity<ActivityBridgeWebBinding>() {
 
     override fun initViews() {
         gameUrl = url + "?level=" + level + "&" + "isGoOn=yes&userToken=" + User.getToken()
-        Log.e("wpp", gameUrl)
-        appId = BuildConfig.BAIDU_APPID
-        appKey = BuildConfig.BAIDU_APPKEY
-        secretKey = BuildConfig.BAIDU_SECRETKEY
         initWebView()
     }
 
@@ -78,17 +44,8 @@ class BridgeWebActivity : BaseActivity<ActivityBridgeWebBinding>() {
 
     }
 
-    @SuppressLint("HandlerLeak")
     override fun initData() {
-        mainHandler = object : Handler() {
-            override fun handleMessage(msg: Message) {
-                super.handleMessage(msg)
-                if (msg.obj != null) {
-                    print(msg.obj.toString())
-                }
-            }
-        }
-        initTTs()
+
     }
 
     @SuppressLint("JavascriptInterface", "SetJavaScriptEnabled")
@@ -142,7 +99,6 @@ class BridgeWebActivity : BaseActivity<ActivityBridgeWebBinding>() {
 
             override fun onPageFinished(view: WebView?, url: String?) {
                 super.onPageFinished(view, url)
-//                speak()
             }
         }
         binding.webView.loadUrl(gameUrl)
@@ -150,12 +106,6 @@ class BridgeWebActivity : BaseActivity<ActivityBridgeWebBinding>() {
 
     override fun onDestroy() {
         destoryWebView()
-        if (mSpeechSynthesizer != null) {
-            mSpeechSynthesizer!!.stop()
-            mSpeechSynthesizer!!.release()
-            mSpeechSynthesizer = null
-            Log.e("wpp", "释放资源成功")
-        }
         super.onDestroy()
     }
 
@@ -179,94 +129,4 @@ class BridgeWebActivity : BaseActivity<ActivityBridgeWebBinding>() {
         }
         super.onResume()
     }
-
-    /**
-     * 初始化百度语音合成
-     */
-    @SuppressLint("HandlerLeak")
-    private fun initTTs() {
-        LoggerProxy.printable(true) // 日志打印在logcat中
-        // 日志更新在UI中,可以换成MessageListener,在logcat中查看日志
-        val listener: SpeechSynthesizerListener = UiMessageListener(mainHandler)
-        // 1. 获取实例
-        mSpeechSynthesizer = SpeechSynthesizer.getInstance()
-        mSpeechSynthesizer?.setContext(this)
-        // 2. 设置listener
-        mSpeechSynthesizer?.setSpeechSynthesizerListener(listener)
-        // 3. 设置appId,appKey.secretKey
-        var result = mSpeechSynthesizer?.setAppId(appId)
-        if (result != null) {
-            checkResult(result, "setAppId")
-        }
-        result = mSpeechSynthesizer?.setApiKey(appKey, secretKey)
-        if (result != null) {
-            checkResult(result, "setApiKey")
-        }
-        // 5. 以下setParam 参数选填。不填写则默认值生效
-        // 设置在线发声音人: 0 普通女声(默认) 1 普通男声  3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
-        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_SPEAKER, "4")
-        // 设置合成的音量,0-15 ,默认 5
-        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_VOLUME, "5")
-        // 设置合成的语速,0-15 ,默认 5
-        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_SPEED, "5")
-        // 设置合成的语调,0-15 ,默认 5
-        mSpeechSynthesizer?.setParam(SpeechSynthesizer.PARAM_PITCH, "5")
-
-        // x. 额外 : 自动so文件是否复制正确及上面设置的参数
-        val params: Map<String, String> = HashMap()
-        // 检测参数,通过一次后可以去除,出问题再打开debug
-        val initConfig = InitConfig(appId, appKey, secretKey, ttsMode, params, listener)
-        AutoCheck.getInstance(applicationContext).check(initConfig, object : Handler() {
-            /**
-             * 开新线程检查,成功后回调
-             */
-            override fun handleMessage(msg: Message) {
-                if (msg.what == 100) {
-                    val autoCheck: AutoCheck = msg.obj as AutoCheck
-                    synchronized(autoCheck) {
-                        val message: String = autoCheck.obtainDebugMessage()
-                        print(message) // 可以用下面一行替代,在logcat中查看代码
-                    }
-                }
-            }
-        })
-        // 6. 初始化
-        result = mSpeechSynthesizer?.initTts(ttsMode)
-        if (result != null) {
-            checkResult(result, "initTts")
-        }
-    }
-
-    private fun print(message: String) {
-        Log.i("wpp", message)
-    }
-
-    private fun checkResult(result: Int, method: String) {
-        if (result != 0) {
-            Log.e("wpp", "error code :$result method:$method")
-        }
-    }
-
-    /**
-     * 播放语音
-     */
-    private fun speak() {
-        /* 以下参数每次合成时都可以修改
-         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEAKER, "0");
-         *  设置在线发声音人: 0 普通女声(默认) 1 普通男声  3 情感男声<度逍遥> 4 情感儿童声<度丫丫>
-         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_VOLUME, "5"); 设置合成的音量,0-9 ,默认 5
-         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_SPEED, "5"); 设置合成的语速,0-9 ,默认 5
-         *  mSpeechSynthesizer.setParam(SpeechSynthesizer.PARAM_PITCH, "5"); 设置合成的语调,0-9 ,默认 5
-         *
-         */
-        if (mSpeechSynthesizer == null) {
-            print("[ERROR], 初始化失败")
-            return
-        }
-        if (desn.isNotEmpty()) {
-            val result: Int = mSpeechSynthesizer!!.speak(desn)
-            Log.e("wpp", "合成并播放 按钮已经点击")
-            checkResult(result, "speak")
-        }
-    }
 }

+ 15 - 3
workbenches/src/main/java/com/yingyang/workbenches/WorkbenchesActivity.kt

@@ -12,9 +12,8 @@ import com.yingyangfly.baselib.ext.show
 import com.yingyangfly.baselib.ext.toast
 import com.yingyangfly.baselib.mvvm.BaseMVVMActivity
 import com.yingyangfly.baselib.router.RouterUrlCommon
-import com.yingyangfly.baselib.utils.AppUtil
-import com.yingyangfly.baselib.utils.ImageUtil.loadUrl
 import com.yingyangfly.baselib.utils.JumpUtil
+import com.yingyangfly.baselib.voice.PlayVoice
 
 /**
  * 工作台
@@ -27,7 +26,12 @@ class WorkbenchesActivity : BaseMVVMActivity<ActivityWorkbenchesBinding, Workben
      */
     private val gameAdapter by lazy { GameAdapter() }
 
+    private var playVoice: PlayVoice? = null
+
     override fun initViews() {
+        playVoice = PlayVoice()
+        playVoice?.setContext(this)
+//        playVoice.speak("你好啊")
         binding {
             rvGame.layoutManager = GridLayoutManager(this@WorkbenchesActivity, 4)
             rvGame.adapter = gameAdapter
@@ -107,7 +111,8 @@ class WorkbenchesActivity : BaseMVVMActivity<ActivityWorkbenchesBinding, Workben
 
     override fun initData() {
         binding {
-            Glide.with(this@WorkbenchesActivity).load("file:///android_asset/home_doctor.gif").into(iamgeDoctor)
+            Glide.with(this@WorkbenchesActivity).load("file:///android_asset/home_doctor.gif")
+                .into(iamgeDoctor)
         }
     }
 
@@ -172,4 +177,11 @@ class WorkbenchesActivity : BaseMVVMActivity<ActivityWorkbenchesBinding, Workben
         )
     }
 
+    override fun onDestroy() {
+        if (playVoice != null) {
+            playVoice?.stop()
+        }
+        super.onDestroy()
+    }
+
 }

+ 18 - 2
workbenches/src/main/java/com/yingyang/workbenches/entity/MyTaskBean.kt

@@ -3,5 +3,21 @@ package com.yingyang.workbenches.entity
 /**
  * 是否有进行中的任务
  */
-class MyTaskBean {
-}
+data class MyTaskBean(
+    val createBy: String,
+    val createTime: String,
+    val currentTaskNum: Int,//当前任务进度
+    val gameCodes: String,//游戏编号
+    val gameNames: String,//游戏名称
+    val id: String,
+    val orgCode: String,
+    val orgName: String,
+    val status: String,//状态
+    val taskDesn: String,//任务描述
+    val taskName: String,//任务名称
+    val taskStartTime: String,//任务创建时间
+    val totalTaskNum: Int,//任务总进度
+    val updateBy: String,
+    val updateTime: String,
+    val userId: String
+)