Selaa lähdekoodia

转换盒增加语音播报,增加app异常重启功能

weizhengliang 2 vuotta sitten
vanhempi
commit
03d9179848

+ 6 - 0
app/src/main/AndroidManifest.xml

@@ -10,9 +10,15 @@
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <!-- 允许程序改变网络链接状态 -->
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <!-- 允许程序访问访问WIFI网络状态信息 -->
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
 
     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
 
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+
     <application
         android:name="com.wdkl.ncs.s433.transbox.Application"
         android:allowBackup="true"

+ 3 - 0
app/src/main/java/com/wdkl/ncs/s433/transbox/Application.java

@@ -5,6 +5,7 @@ import android.serialport.SerialPort;
 import android.serialport.SerialPortFinder;
 import android.util.Log;
 
+import com.wdkl.ncs.s433.transbox.utils.AnrFcExceptionUtil;
 import com.wdkl.ncs.s433.transbox.utils.SettingConfig;
 
 import java.io.IOException;
@@ -21,6 +22,8 @@ public class Application extends android.app.Application {
     public void onCreate() {
         super.onCreate();
         context = getApplicationContext();
+
+        AnrFcExceptionUtil.getInstance(this).initFCException();
     }
 
     /**

+ 23 - 2
app/src/main/java/com/wdkl/ncs/s433/transbox/MainActivity.java

@@ -9,6 +9,7 @@ import android.net.ConnectivityManager;
 import android.net.Network;
 import android.net.NetworkRequest;
 import android.os.Bundle;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
 import android.widget.Button;
@@ -32,6 +33,8 @@ import com.wdkl.ncs.s433.transbox.utils.AlarmMessageUtil;
 import com.wdkl.ncs.s433.transbox.utils.AutoRebootUtil;
 import com.wdkl.ncs.s433.transbox.utils.NetUtil;
 import com.wdkl.ncs.s433.transbox.utils.SettingConfig;
+import com.wdkl.ncs.s433.transbox.utils.SpeechUtil;
+import com.wdkl.ncs.s433.transbox.utils.StringUtils;
 
 import org.apache.commons.lang3.ArrayUtils;
 import org.greenrobot.eventbus.EventBus;
@@ -146,6 +149,7 @@ public class MainActivity extends SerialPortActivity  {
             }).start();
         }
 
+        SpeechUtil.getInstance().init(this, null);
 
         //默认打开网络调试
         openNetworkDebug();
@@ -163,6 +167,7 @@ public class MainActivity extends SerialPortActivity  {
         EventBus.getDefault().unregister(this);
         connectivityManager.unregisterNetworkCallback(callback);
 //        unregisterReceiver(networkConnectChangedReceiver);
+        SpeechUtil.getInstance().release();
     }
 
     @Override
@@ -381,6 +386,7 @@ public class MainActivity extends SerialPortActivity  {
                     if (tcpModel.getAction() == TcpAction.DeviceAction.APP_UPDATE) {
                         //
                     } else if (tcpModel.getAction() == TcpAction.DeviceAction.RESTART) {
+                        SpeechUtil.getInstance().stopSpeak();
                         AutoRebootUtil.restartApp();
                     } else if (tcpModel.getAction() == TcpAction.DeviceAction.REBOOT) {
                         AutoRebootUtil.reboot();
@@ -406,10 +412,25 @@ public class MainActivity extends SerialPortActivity  {
                             || tcpModel.getAction() == TcpAction.S433Action.CANCEL) {
                         //解析数据并通过433发射出去
                         if (tcpData != null) {
+                            //语音播报
+                            String title = tcpData.getString("title");
+                            if (tcpModel.getAction() == TcpAction.S433Action.SOS_CALL || tcpModel.getAction() == TcpAction.S433Action.CALL) {
+                                if (!TextUtils.isEmpty(title)) {
+                                    String speech = StringUtils.appendSpace(title);
+                                    SpeechUtil.getInstance().addSpeech(speech, false);
+                                }
+                            } else if (tcpModel.getAction() == TcpAction.S433Action.CANCEL) {
+                                if (TextUtils.isEmpty(title)) {
+                                    SpeechUtil.getInstance().stopSpeak();
+                                } else {
+                                    String speech = StringUtils.appendSpace(title);
+                                    SpeechUtil.getInstance().removeSpeak(speech);
+                                }
+                            }
+
                             if (tcpData.getString("receiverMacs") != null) {
                                 //给多个点阵屏发送
                                 JSONArray macs = JSON.parseArray(tcpData.getString("receiverMacs"));
-                                String title = tcpData.getString("title");
                                 data.put("title", title);
                                 s433Send.put("data", data);
                                 for (int i = 0; i < macs.size(); i++) {
@@ -418,7 +439,7 @@ public class MainActivity extends SerialPortActivity  {
                             } else if (tcpData.getString("receiverMac") != null) {
                                 //给单个点阵屏发送
                                 String mac = tcpData.getString("receiverMac");
-                                data.put("title", tcpData.getString("title"));
+                                data.put("title", title);
                                 s433Send.put("data", data);
 
                                 send433DataByMac(mac, s433Send.toJSONString());

+ 3 - 0
app/src/main/java/com/wdkl/ncs/s433/transbox/common/Constants.java

@@ -18,4 +18,7 @@ public class Constants {
    public static Boolean TCP_CONNCETED = false;
    public static Boolean SERIAL_PORT_STATUS = false;
    public static String SERIAL_PORT_FAIL_RESON="";
+
+   //tts服务是否可用: 0-不可用, 1-可用但不支持中文, 2-可用且支持中文
+   public static int TTS_STATUS = 0;
 }

+ 175 - 0
app/src/main/java/com/wdkl/ncs/s433/transbox/utils/AnrFcExceptionUtil.java

@@ -0,0 +1,175 @@
+package com.wdkl.ncs.s433.transbox.utils;
+
+import android.app.AlarmManager;
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Environment;
+import android.util.Log;
+
+import com.github.anrwatchdog.ANRError;
+import com.github.anrwatchdog.ANRWatchDog;
+import com.wdkl.ncs.s433.transbox.MainActivity;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * //=========================FC&ANR异常处理类=========================//
+ */
+
+public class AnrFcExceptionUtil implements Thread.UncaughtExceptionHandler {
+
+    private static ANRWatchDog mANRWatchDog;
+    private Thread.UncaughtExceptionHandler mDefaultHandler;
+    public static final String TAG = "AnrFcExceptionUtil";
+    private static Application application;
+
+    private static AnrFcExceptionUtil mAnrFcExceptionUtil;
+
+    /**
+     * 存储异常和参数信息
+     */
+    private Map<String, String> paramsMap = new HashMap<>();
+    /**
+     * 格式化时间
+     */
+    private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
+
+
+    public static AnrFcExceptionUtil getInstance(Application application) {
+        if (mAnrFcExceptionUtil == null) {
+            mAnrFcExceptionUtil = new AnrFcExceptionUtil(application);
+        }
+        return mAnrFcExceptionUtil;
+    }
+
+    private AnrFcExceptionUtil(Application application) {
+        //获取系统默认的UncaughtException处理器
+        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+        this.application = application;
+    }
+
+    @Override
+    public void uncaughtException(Thread thread, Throwable ex) {
+        if (!handleException(ex) && mDefaultHandler != null) {
+            //如果用户没有处理则让系统默认的异常处理器来处理
+            mDefaultHandler.uncaughtException(thread, ex);
+        } else {
+            try {
+                Thread.sleep(2000);
+            } catch (InterruptedException e) {
+                Log.e(TAG, "error : ", e);
+            }
+
+            restartApp();
+        }
+    }
+
+    private void restartApp() {
+        //重新启动app
+        Intent mStartActivity = new Intent(application.getApplicationContext(), MainActivity.class);
+        int mPendingIntentId = 123456;
+        PendingIntent mPendingIntent = PendingIntent.getActivity(application.getApplicationContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+        AlarmManager mgr = (AlarmManager) application.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
+        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 1500, mPendingIntent);
+
+        android.os.Process.killProcess(android.os.Process.myPid());
+        System.exit(0);
+    }
+
+    /**
+     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
+     *
+     * @param ex
+     * @return true:如果处理了该异常信息;否则返回false.
+     */
+    private boolean handleException(Throwable ex) {
+        if (ex == null) {
+            return false;
+        }
+
+        saveCrashInfo2File(ex);
+
+        return true;
+    }
+
+    /**
+     * 保存错误信息到文件中
+     *
+     * @param ex
+     * @return 返回文件名称
+     */
+    private String saveCrashInfo2File(Throwable ex) {
+        StringBuffer sb = new StringBuffer();
+        for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
+            String key = entry.getKey();
+            String value = entry.getValue();
+            sb.append(key + "=" + value + "\n");
+        }
+
+        Writer writer = new StringWriter();
+        PrintWriter printWriter = new PrintWriter(writer);
+        ex.printStackTrace(printWriter);
+        Throwable cause = ex.getCause();
+        while (cause != null) {
+            cause.printStackTrace(printWriter);
+            cause = cause.getCause();
+        }
+        printWriter.close();
+        String result = writer.toString();
+        sb.append(result);
+
+        try {
+            long timestamp = System.currentTimeMillis();
+            String time = format.format(new Date());
+            String fileName = "crash-" + time + "-" + timestamp + ".txt";
+            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
+                String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/crash/";
+                File dir = new File(path);
+                if (!dir.exists()) {
+                    dir.mkdirs();
+                }
+                FileOutputStream fos = new FileOutputStream(path + fileName);
+                fos.write(sb.toString().getBytes());
+                Log.e(TAG, "saveCrashInfo2File: "+sb.toString());
+                fos.close();
+            }
+            return fileName;
+        } catch (Exception e) {
+            Log.e(TAG, "an error occured while writing file...", e);
+        }
+        return null;
+    }
+
+    /**
+     * ===================================================崩溃异常处理===================================================
+     */
+    public void initFCException() {
+        //设置该CrashHandler为程序的默认处理器
+        AnrFcExceptionUtil catchExcep = AnrFcExceptionUtil.getInstance(application);
+        Thread.setDefaultUncaughtExceptionHandler(catchExcep);
+        mANRWatchDog = new ANRWatchDog(8000);
+        mANRWatchDog.setInterruptionListener(new ANRWatchDog.InterruptionListener() {
+            @Override
+            public void onInterrupted(InterruptedException exception) {
+            }
+        }).setIgnoreDebugger(true).setANRListener(new ANRWatchDog.ANRListener() {
+            @Override
+            public void onAppNotResponding(ANRError error) {
+                Log.e("anr", "Anr restart app..." + error.toString());
+
+                restartApp();
+            }
+        }).start();
+
+    }
+}

+ 311 - 0
app/src/main/java/com/wdkl/ncs/s433/transbox/utils/SpeechUtil.java

@@ -0,0 +1,311 @@
+package com.wdkl.ncs.s433.transbox.utils;
+
+import android.content.Context;
+import android.media.AudioManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.UtteranceProgressListener;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.wdkl.ncs.s433.transbox.common.Constants;
+
+import java.util.ArrayList;
+import java.util.Locale;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+public class SpeechUtil {
+    private static final String TAG = "SpeechUtil";
+
+    private TextToSpeech textToSpeech;
+    private static SpeechUtil speech;
+    private int loopIndex = 0;
+    private int speakIndex = 0;
+    private int loopCount = 3;
+    private boolean isStop = true;
+    public volatile static ArrayList<String> speechTextList = new ArrayList<>();
+    private boolean isSpeechLoop = true;
+    private String speakSpeech;
+    private final Object lockObject = new Object();
+    private Bundle params;
+
+    private final ExecutorService threadPool = Executors.newSingleThreadExecutor();
+    private SpeechRunnable speechRunnable;
+    private PowerManager.WakeLock mWakeLock;
+
+    public static SpeechUtil getInstance() {
+        if (speech == null) {
+            synchronized (SpeechUtil.class) {
+                if (speech == null) {
+                    speech = new SpeechUtil();
+                }
+            }
+        }
+        return speech;
+    }
+
+    public void init(Context context, final TtsCallback callback) {
+        final String language = "zh";
+        //8.0以下版本自动设置tts
+        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O ) {
+            if (Locale.CHINESE.getLanguage().equals(language)) {
+                textToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
+                    @Override
+                    public void onInit(int status) {
+                        if (status == TextToSpeech.SUCCESS) {
+                            int supported = textToSpeech.setLanguage(new Locale(language));
+                            if ((supported != TextToSpeech.LANG_AVAILABLE) && (supported != TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
+                                Constants.TTS_STATUS = 1;
+                                Log.d(TAG, "onInit: 当前不支持" + language);
+                                if (callback != null) {
+                                    callback.onInit(false);
+                                }
+                            } else {
+                                Constants.TTS_STATUS = 2;
+                                Log.d(TAG, "onInit: 支持" + language);
+                                if (callback != null) {
+                                    callback.onInit(true);
+                                }
+                            }
+                            Log.d(TAG, "onInit: TTS引擎初始化成功");
+                        } else {
+                            Constants.TTS_STATUS = 0;
+                            Log.d(TAG, "onInit: TTS引擎初始化失败");
+                            if (callback != null) {
+                                callback.onInit(false);
+                            }
+                        }
+                    }
+                }, "com.iflytek.speechcloud");
+            } else {
+                textToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
+                    @Override
+                    public void onInit(int status) {
+                        if (status == TextToSpeech.SUCCESS) {
+                            int supported = textToSpeech.setLanguage(new Locale(language));
+                            if ((supported != TextToSpeech.LANG_AVAILABLE) && (supported != TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
+                                Constants.TTS_STATUS = 1;
+                                Log.d(TAG, "onInit: 当前不支持" + language);
+                                if (callback != null) {
+                                    callback.onInit(false);
+                                }
+                            } else {
+                                Constants.TTS_STATUS = 2;
+                                Log.d(TAG, "onInit: 支持" + language);
+                                if (callback != null) {
+                                    callback.onInit(true);
+                                }
+                                textToSpeech.setSpeechRate(0.9f);
+                            }
+                            Log.d(TAG, "onInit: TTS引擎初始化成功");
+                        } else {
+                            Constants.TTS_STATUS = 0;
+                            Log.d(TAG, "onInit: TTS引擎初始化失败");
+                            if (callback != null) {
+                                callback.onInit(false);
+                            }
+                        }
+                    }
+                }, "com.google.android.tts");
+            }
+        } else {
+            //8.0以上版本需要手动设置tts
+            textToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() {
+                @Override
+                public void onInit(int status) {
+                    if (status == TextToSpeech.SUCCESS) {
+                        int supported = textToSpeech.setLanguage(new Locale(language));
+                        if ((supported != TextToSpeech.LANG_AVAILABLE) && (supported != TextToSpeech.LANG_COUNTRY_AVAILABLE)) {
+                            Constants.TTS_STATUS = 1;
+                            Log.d(TAG, "onInit: 当前不支持" + language);
+                            if (callback != null) {
+                                callback.onInit(false);
+                            }
+                        } else {
+                            Constants.TTS_STATUS = 2;
+                            Log.d(TAG, "onInit: 支持" + language);
+                            if (callback != null) {
+                                callback.onInit(true);
+                            }
+                            if (!Locale.CHINESE.getLanguage().equals(language)) {
+                                textToSpeech.setSpeechRate(0.9f);
+                            }
+                        }
+                        Log.d(TAG, "onInit: TTS引擎初始化成功");
+                    } else {
+                        Constants.TTS_STATUS = 0;
+                        Log.d(TAG, "onInit: TTS引擎初始化失败");
+                        if (callback != null) {
+                            callback.onInit(false);
+                        }
+                    }
+                }
+            });
+        }
+        //textToSpeech.setSpeechRate(0.5f);
+
+        params = new Bundle();
+        params.putInt(TextToSpeech.Engine.KEY_PARAM_STREAM, AudioManager.STREAM_MUSIC);
+
+        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Speech_tag");
+
+        startSpeechThread();
+    }
+
+    public void addSpeech(String text, boolean emergency) {
+        synchronized (lockObject) {
+            Log.d(TAG, "start add text speech: " + text);
+
+            if (speechTextList.contains(text)) {
+                return;
+            }
+            Log.d(TAG, "truely add text speech: " + text);
+
+            if (emergency) {
+                stopSpeak();
+                speechTextList.add(0, text);
+            } else {
+                speechTextList.add(text);
+            }
+            //startSpeechThread();
+        }
+    }
+
+    public synchronized void speak(final String text) {
+        Log.d(TAG, "tts speak: " + text);
+
+        isStop = false;
+        textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, params, "uniqueId");
+        textToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() {
+            @Override
+            public void onStart(String utteranceId) {
+                //LogUtil.d(TAG, "speak onStart..." + utteranceId);
+            }
+
+            @Override
+            public void onDone(String utteranceId) {
+                speakIndex++;
+                //LogUtil.d(TAG, "speak onDone...index: " + speakIndex + ", loop: " + loopCount);
+                if (speakIndex < loopCount) {
+                    //循环播报
+                    speak(text);
+                } else {
+                    //语音播报完毕
+                    speakIndex = 0;
+                    isStop = true;
+                }
+            }
+
+            @Override
+            public void onError(String utteranceId) {
+                isStop = true;
+                Log.d(TAG, "speak onError..." + utteranceId);
+            }
+        });
+    }
+
+    public void stopSpeak() {
+        speechTextList.clear();
+        if (textToSpeech != null && textToSpeech.isSpeaking()) {
+            textToSpeech.stop();
+            isStop = true;
+            speakIndex = 0;
+            loopIndex = 0;
+
+            Log.d(TAG, "stop speak");
+        }
+    }
+
+    public void removeSpeak(String text) {
+        synchronized (lockObject) {
+            if (!TextUtils.isEmpty(text) && !TextUtils.isEmpty(speakSpeech)) {
+                Log.d(TAG, "remove speak: " + text);
+
+                if (text.equals(speakSpeech) && textToSpeech.isSpeaking()) {
+                    textToSpeech.stop();
+                    speechTextList.remove(text);
+                    isStop = true;
+                    speakIndex = 0;
+                } else {
+                    speechTextList.remove(text);
+                }
+            }
+        }
+    }
+
+    public void setSpeechLoopCount(int count) {
+        loopCount = count;
+    }
+
+    public void release() {
+        speechTextList.clear();
+        isStop = true;
+        speakIndex = 0;
+        if (textToSpeech != null) {
+            textToSpeech.stop();
+            textToSpeech.shutdown();
+            textToSpeech = null;
+        }
+        releaseWakeLock();
+    }
+
+    public void startSpeechThread() {
+        if (null == speechRunnable) {
+            speechRunnable = new SpeechRunnable();
+        }
+
+        acquireWakeLock();
+        threadPool.execute(speechRunnable);
+    }
+
+    public class SpeechRunnable implements Runnable {
+        public void run() {
+            while (isSpeechLoop) {
+                if (speechTextList.size() > 0 && isStop) {
+                    /*speakSpeech = speechTextList.get(0);
+                    Log.d(TAG, "speakSpeech: " + speakSpeech);
+                    speak(speakSpeech);
+                    speechTextList.remove(speakSpeech);*/
+
+                    //循环播报
+                    if (loopIndex >= speechTextList.size()) {
+                        loopIndex = 0;
+                    }
+                    speakSpeech = speechTextList.get(loopIndex);
+
+                    Log.d(TAG, "speakSpeech: " + speakSpeech + ", speak loopIndex: " + loopIndex + ", speech size: " + speechTextList.size());
+                    speak(speakSpeech);
+
+                    loopIndex++;
+                }
+
+                try {
+                    Thread.sleep(500);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    private void acquireWakeLock() {
+        if (mWakeLock != null) {
+            mWakeLock.acquire();
+        }
+    }
+
+    private void releaseWakeLock() {
+        if (mWakeLock != null) {
+            mWakeLock.release();
+        }
+    }
+
+
+    public interface TtsCallback {
+        void onInit(boolean success);
+    }
+}

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1424 - 0
app/src/main/java/com/wdkl/ncs/s433/transbox/utils/StringUtils.java