浏览代码

心率带开发版本

slambb 3 年之前
当前提交
3a8f6e21e0
共有 100 个文件被更改,包括 4268 次插入0 次删除
  1. 35 0
      app/Readme.txt
  2. 89 0
      app/build.gradle
  3. 二进制
      app/heartRateBand.jks
  4. 二进制
      app/libs/Bluetooth-release.aar
  5. 二进制
      app/libs/android-gif-drawable-release@1.2.23.aar
  6. 二进制
      app/libs/chileaf_wear_sdk_1.0.0.aar
  7. 二进制
      app/libs/lib.5plus.base-release.aar
  8. 二进制
      app/libs/oaid_sdk_1.0.25.aar
  9. 二进制
      app/libs/uniapp-v8-release.aar
  10. 149 0
      app/proguard-rules.pro
  11. 123 0
      app/src/main/AndroidManifest.xml
  12. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappchooselocation.js
  13. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniapperror.png
  14. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappes6.js
  15. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappopenlocation.js
  16. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniapppicker.js
  17. 6 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappquill.js
  18. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappquillimageresize.js
  19. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappscan.js
  20. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappsuccess.png
  21. 25 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappview.html
  22. 8 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/app-config-service.js
  23. 1 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/app-config.js
  24. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/app-service.js
  25. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/app-view.js
  26. 1 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/manifest.json
  27. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/pages/game/subGame/subGame.js
  28. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/bandage.png
  29. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/handle.png
  30. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/hotman.png
  31. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/rope.png
  32. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/boxingb.png
  33. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/boxingw.png
  34. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/deviceright.png
  35. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/link.png
  36. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/radio-b.png
  37. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/radio-g.png
  38. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/gameCloseW.png
  39. 二进制
      app/src/main/assets/apps/__UNI__8D02B4B/www/static/logo.png
  40. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/view.css
  41. 0 0
      app/src/main/assets/apps/__UNI__8D02B4B/www/view.umd.min.js
  42. 6 0
      app/src/main/assets/data/dcloud_control.xml
  43. 92 0
      app/src/main/assets/data/dcloud_error.html
  44. 79 0
      app/src/main/assets/data/dcloud_properties.xml
  45. 32 0
      app/src/main/assets/dcloud_uniplugins.json
  46. 二进制
      app/src/main/res/drawable-xxhdpi/icon.png
  47. 二进制
      app/src/main/res/drawable-xxhdpi/push.png
  48. 二进制
      app/src/main/res/drawable-xxhdpi/splash.png
  49. 3 0
      app/src/main/res/values/strings.xml
  50. 二进制
      app/test.jks
  51. 27 0
      build.gradle
  52. 二进制
      core/.DS_Store
  53. 1 0
      core/.gitignore
  54. 29 0
      core/build.gradle
  55. 125 0
      core/core.iml
  56. 二进制
      core/libs/bdasr_V3_20210628_cfe8c44.jar
  57. 21 0
      core/proguard-rules.pro
  58. 12 0
      core/src/main/AndroidManifest.xml
  59. 3 0
      core/src/main/assets/README.txt
  60. 1 0
      core/src/main/assets/WakeUp.bin
  61. 0 0
      core/src/main/assets/baidu_speech_grammar.bsg
  62. 98 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/FileAudioInputStream.java
  63. 105 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/InFileStream.java
  64. 82 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/InPipedStream.java
  65. 100 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/MyMicrophoneInputStream.java
  66. 283 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/ActivityMiniRecog.java
  67. 269 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/ActivityMiniUnit.java
  68. 166 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/ActivityMiniWakeUp.java
  69. 472 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/AutoCheck.java
  70. 24 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/IStatus.java
  71. 142 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/MyRecognizer.java
  72. 134 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/RecogResult.java
  73. 146 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/ChainRecogListener.java
  74. 113 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/IRecogListener.java
  75. 165 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/MessageStatusRecogListener.java
  76. 112 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/RecogEventAdapter.java
  77. 110 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/StatusRecogListener.java
  78. 35 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/util/AuthUtil.java
  79. 73 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/util/FileUtil.java
  80. 59 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/util/MyLogger.java
  81. 293 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/util/bluetooth/AndroidAudioManager.java
  82. 92 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/util/bluetooth/BluetoothReceiver.java
  83. 41 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/util/bluetooth/HeadsetReceiver.java
  84. 68 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/MyWakeup.java
  85. 91 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/WakeUpResult.java
  86. 47 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/WakeupEventAdapter.java
  87. 19 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/listener/IWakeupListener.java
  88. 26 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/listener/RecogWakeupListener.java
  89. 35 0
      core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/listener/SimpleWakeupListener.java
  90. 二进制
      core/src/main/jniLibs/arm64-v8a/libBaiduSpeechSDK.so
  91. 二进制
      core/src/main/jniLibs/arm64-v8a/libbdEASRAndroid.so
  92. 二进制
      core/src/main/jniLibs/arm64-v8a/libbdSpilWakeup.so
  93. 二进制
      core/src/main/jniLibs/arm64-v8a/libbd_easr_s1_merge_normal_20151216.dat.so
  94. 二进制
      core/src/main/jniLibs/arm64-v8a/libvad.dnn.so
  95. 二进制
      core/src/main/jniLibs/armeabi-v7a/libBaiduSpeechSDK.so
  96. 二进制
      core/src/main/jniLibs/armeabi-v7a/libbdEASRAndroid.so
  97. 二进制
      core/src/main/jniLibs/armeabi-v7a/libbdSpilWakeup.so
  98. 二进制
      core/src/main/jniLibs/armeabi-v7a/libbd_easr_s1_merge_normal_20151216.dat.so
  99. 二进制
      core/src/main/jniLibs/armeabi-v7a/libvad.dnn.so
  100. 二进制
      core/src/main/jniLibs/armeabi/libBaiduSpeechSDK.so

+ 35 - 0
app/Readme.txt

@@ -0,0 +1,35 @@
+该module是引用lib.5plus.base-release.aar+第三方开放平台jar的方式集成5+离线SDK的示例demo
+lib.5plus.base-release.aar包含了5+SDK的基础模块,
+
+一:
+
+    若需要支持广告业务,须配置示例demo的入口activity为io.dcloud.PandoraEntry.java。
+    另外开发者须在AndroidManifest.xml中自行配置DCLOUD_STREAMAPP_CHANNEL该字段对应value值,
+    替换appid和adid为自己应用的appid和广告联盟会员id,5+APP的应用资源manifest.json中添加
+
+     ads广告配置信息
+
+    "plus": {
+            "ads": {
+                "push":"true|false",       // push推送广告
+                "splash":"true|false",     // 开屏广告
+                "rp":"true|false",         // 悬浮红包广告
+                "spot":"true|false",      // 插屏广告
+            }
+            // ...
+        }
+    adid广告联盟会员id
+    "plus": {
+        "adid": "广告联盟会员id "     // 开发者向后台申请后填写
+        // ...
+    }
+
+    参考http://ask.dcloud.net.cn/article/13141文档配置
+
+
+
+二:
+
+    SDK_WebApp为以WebApp方式集成5+ sdk的示例,参考http://ask.dcloud.net.cn/article/81文档配置
+    SDK_WebView为以webview控件方式集成5+ sdk的示例,参考http://ask.dcloud.net.cn/article/80文档配置
+    这两种方式不支持广告业务。

+ 89 - 0
app/build.gradle

@@ -0,0 +1,89 @@
+apply plugin: 'com.android.application'
+
+android {
+    compileSdkVersion 29
+    buildToolsVersion '29.0.2'
+    defaultConfig {
+//        applicationId "com.slambb.heartRateBand"
+        applicationId "com.yuyeTech.www"
+        minSdkVersion 21
+        targetSdkVersion 28 //建议此属性值设为21 io.dcloud.PandoraEntry 作为apk入口时   必须设置 targetSDKVersion>=21 沉浸式才生效
+
+        versionCode 20220806
+        versionName "1.0.2"
+        multiDexEnabled true
+        ndk {
+            abiFilters 'x86','armeabi-v7a'
+        }
+        compileOptions {
+            sourceCompatibility JavaVersion.VERSION_1_8
+            targetCompatibility JavaVersion.VERSION_1_8
+        }
+    }
+
+    signingConfigs {
+        config {
+            keyAlias 'key0'
+            keyPassword 'heart123456'
+            storeFile file('heartRateBand.jks')
+            storePassword 'heart123456'
+            v1SigningEnabled true
+            v2SigningEnabled true
+        }
+    }
+
+    buildTypes {
+        release {
+            signingConfig signingConfigs.config
+            zipAlignEnabled true
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+        debug {
+            signingConfig signingConfigs.config
+            zipAlignEnabled true
+            minifyEnabled true
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    packagingOptions{
+        doNotStrip "*/*/libvad.dnn.so"
+        doNotStrip "*/*/libbd_easr_s1_merge_normal_20151216.dat.so"
+    }
+    //使用uniapp时,需复制下面代码
+    /*代码开始*/
+    aaptOptions {
+        additionalParameters '--auto-add-overlay'
+        //noCompress 'foo', 'bar'
+        ignoreAssetsPattern "!.svn:!.git:.*:!CVS:!thumbs.db:!picasa.ini:!*.scc:*~"
+    }
+    /*代码结束*/
+}
+repositories {
+    flatDir {
+        dirs 'libs'
+    }
+}
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+    implementation fileTree(dir: 'libs', include: ['*.aar'])
+
+    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
+    implementation 'androidx.appcompat:appcompat:1.0.0'
+
+    /*uniapp所需库-----------------------开始*/
+    implementation 'androidx.recyclerview:recyclerview:1.0.0'
+    implementation 'com.facebook.fresco:fresco:1.13.0'
+    implementation "com.facebook.fresco:animated-gif:1.13.0"
+    /*uniapp所需库-----------------------结束*/
+    // 基座需要,必须添加
+    implementation 'com.github.bumptech.glide:glide:4.9.0'
+    implementation 'com.alibaba:fastjson:1.1.46.android'
+
+    // 添加uni-app插件
+    implementation project(':uniplugin_component')
+    implementation project(':uniplugin_module')
+    implementation project(':uniplugin_richalert')
+    implementation project(path: ':core')
+
+}

二进制
app/heartRateBand.jks


二进制
app/libs/Bluetooth-release.aar


二进制
app/libs/android-gif-drawable-release@1.2.23.aar


二进制
app/libs/chileaf_wear_sdk_1.0.0.aar


二进制
app/libs/lib.5plus.base-release.aar


二进制
app/libs/oaid_sdk_1.0.25.aar


二进制
app/libs/uniapp-v8-release.aar


+ 149 - 0
app/proguard-rules.pro

@@ -0,0 +1,149 @@
+-optimizationpasses 5
+-dontusemixedcaseclassnames
+-dontskipnonpubliclibraryclasses
+-dontpreverify
+-verbose
+
+#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
+-dontoptimize
+
+-keep public class * extends android.app.Activity
+-keep public class * extends android.app.Application
+-keep public class * extends android.app.Service
+-keep public class * extends android.content.BroadcastReceiver
+-keep public class * extends android.content.ContentProvider
+-keep public class * extends android.app.backup.BackupAgentHelper
+-keep public class * extends android.preference.Preference
+-keep public class * extends io.dcloud.common.DHInterface.IPlugin
+-keep public class * extends io.dcloud.common.DHInterface.IFeature
+-keep public class * extends io.dcloud.common.DHInterface.IBoot
+-keep public class * extends io.dcloud.common.DHInterface.IReflectAble
+
+-keep class io.dcloud.feature.speech.** {*;}
+-keep class io.dcloud.net.** {*;}
+-keep class io.dcloud.common.constant.** {*;}
+-keep class io.dcloud.common.sonic.** {*;}
+-keep class io.dcloud.common.DHInterface.** {*;}
+-keep class io.dcloud.common.util.** {*;}
+-keep class io.dcloud.common.adapter.** {*;}
+-keep class io.dcloud.feature.internal.reflect.** {*;}
+-keep class io.dcloud.feature.internal.sdk.** {*;}
+-keep class io.dcloud.feature.payment.** {*;}
+-keep class io.dcloud.sdk.** {*;}
+-keep class com.** {*;}
+-keep class io.dcloud.nineoldandroids.** {*;}
+-keep class vi.com.gdi.** {*;}
+-keep class androidx.** {*;}
+-dontwarn pl.droidsonroids.gif.**
+
+-keepclasseswithmembers class * extends io.dcloud.js.geolocation.GeoManagerBase {
+    <methods>;
+}
+
+-keep class io.dcloud.share.AbsWebviewClient
+-keepclasseswithmembers class io.dcloud.share.AbsWebviewClient {
+    <methods>;
+}
+
+-keep class io.dcloud.share.ShareAuthorizeView
+-keepclasseswithmembers class io.dcloud.share.ShareAuthorizeView {
+    <methods>;
+}
+-keep class io.dcloud.share.IFShareApi
+-keep public class * extends io.dcloud.share.IFShareApi
+-keepclasseswithmembers class io.dcloud.share.IFShareApi {
+    <methods>;
+}
+
+
+
+
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
+-keep class io.dcloud.appstream.StreamAppManager
+-keepclasseswithmembers class io.dcloud.appstream.StreamAppManager {
+    public protected <methods>;
+}
+
+-keep class io.dcloud.common.DHInterface.IReflectAble
+-keep public class * extends io.dcloud.common.DHInterface.IReflectAble{
+  public protected <methods>;
+  public protected *;
+}
+-keep class **.R
+-keep class **.R$* {
+    public static <fields>;
+}
+-keep public class * extends io.dcloud.common.DHInterface.IJsInterface{
+  public protected <methods>;
+  public protected *;
+}
+
+-keepclasseswithmembers class io.dcloud.EntryProxy {
+    <methods>;
+}
+
+-keep class * implements android.os.IInterface {
+  <methods>;
+}
+
+-keepclasseswithmembers class *{
+  public static java.lang.String getJsContent();
+}
+-keepclasseswithmembers class io.dcloud.appstream.StreamAppScriptEntry {
+    <methods>;
+}
+-keepclasseswithmembers class *{
+	public static void onReceiver1(android.content.Intent, android.content.Context);
+}
+
+-keepclasseswithmembers class *{
+  public static io.dcloud.share.AbsWebviewClient getWebviewClient(io.dcloud.share.ShareAuthorizeView);
+}
+-keepclasseswithmembers class *{
+	public java.lang.String exec(java.lang.String,java.lang.String,java.lang.String[]);
+}
+-keepattributes Exceptions,InnerClasses,Signature,Deprecated, SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet);
+}
+
+-keepclasseswithmembers class * {
+    public <init>(android.content.Context, android.util.AttributeSet, int);
+}
+
+-keep public class * extends android.app.Application{
+  public static <methods>;
+  public *;
+}
+
+-keepclassmembers class * extends android.app.Activity {
+   public void *(android.view.View);
+   public static <methods>;
+}
+
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keep class * implements android.os.Parcelable {
+  public static final android.os.Parcelable$Creator *;
+}
+
+-keepattributes Signature
+-keep class io.dcloud.encryption.K {*;}
+-dontwarn com.igexin.**
+-keep class org.json.** { *; }
+-dontwarn com.amap.**
+-dontwarn org.apache.commons.**
+-dontwarn com.sina.weibo.sdk.**
+
+
+-keep class uni.** {*;}
+-keep class pl.** {*;}
+-keep class io.** {*;}
+-keep class org.mozilla.**{*;}
+
+-keep class androidtranscoder.**{*;}
+-keep class XI.**{*;}

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

@@ -0,0 +1,123 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="com.slambb.heartRateBand">
+
+    <supports-screens
+        android:anyDensity="true"
+        android:largeScreens="true"
+        android:normalScreens="true"
+        android:resizeable="true"
+        android:smallScreens="true" />
+
+    <!-- Barcode(二维码)  begin -->
+    <uses-permission android:name="android.permission.CAMERA" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+
+    <uses-feature android:name="android.hardware.camera" />
+    <uses-feature android:name="android.hardware.camera.autofocus" />
+
+    <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.FLASHLIGHT" />
+    <!-- Barcode(二维码)  end -->
+
+    <!-- Speech   -->
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
+    <uses-permission android:name="android.permission.INTERNET" />
+    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+
+
+    <!-- 蓝牙 ibeacon权限-->
+    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
+    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+    <uses-permission android:name="android.permission.BLUETOOTH" />
+
+
+    <!-- 手机设置权限-->
+    <!--<uses-permission android:name="android.permission.WRITE_SETTINGS"/>-->
+    <!-- 屏幕保持唤醒 不锁屏  plus.device.setWakelock();plus.device.isWakelock(); -->
+    <uses-permission android:name="android.permission.WAKE_LOCK" />
+
+    <!--        android:debuggable="true" -->
+    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
+    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
+
+
+    <application
+        android:name="io.dcloud.application.DCloudApplication"
+        android:allowBackup="true"
+        android:allowClearUserData="true"
+        android:icon="@drawable/icon"
+        android:label="@string/app_name"
+        android:largeHeap="true"
+        android:supportsRtl="true">
+
+        <!-- 应用入口 -->
+        <activity
+            android:name="io.dcloud.PandoraEntry"
+            android:theme="@style/TranslucentTheme"
+            android:configChanges="orientation|keyboardHidden|screenSize|mcc|mnc|fontScale"
+            android:hardwareAccelerated="true"
+            android:windowSoftInputMode="adjustResize">
+            <intent-filter>
+                <data android:scheme="hbuilder"/>
+                <action android:name="android.intent.action.VIEW"/>
+
+                <category android:name="android.intent.category.DEFAULT"/>
+                <category android:name="android.intent.category.BROWSABLE"/>
+            </intent-filter>
+            <intent-filter>
+                <data android:mimeType="image/*"/>
+                <action android:name="android.intent.action.SEND"/>
+                <category android:name="android.intent.category.DEFAULT"/>
+            </intent-filter>
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
+            </intent-filter>
+        </activity>
+        <meta-data android:name="dcloud_uninview_background"
+            android:value="true"/>
+
+
+        <!--百度语音识别 start-->
+        <meta-data android:name="com.baidu.speech.APP_ID" android:value="26721854"/>
+        <meta-data android:name="com.baidu.speech.API_KEY" android:value="N3gIyDw0AnQRXEUeGLxNCEDY"/>
+        <meta-data android:name="com.baidu.speech.SECRET_KEY" android:value="oWUSGAt7RFcfFZIs7Lk3Vk9fGan8xr6F"/>
+        <service android:name="com.baidu.speech.VoiceRecognitionService" android:exported="false" />
+        <!--百度语音识别 end-->
+
+        <activity
+            android:name="io.dcloud.PandoraEntryActivity"
+            android:launchMode="singleTask"
+            android:configChanges="orientation|keyboardHidden|screenSize|mcc|mnc|fontScale|keyboard|smallestScreenSize|screenLayout|screenSize"
+            android:hardwareAccelerated="true"
+            android:permission="com.miui.securitycenter.permission.AppPermissionsEditor"
+            android:screenOrientation="user"
+            android:theme="@style/DCloudTheme"
+            android:windowSoftInputMode="adjustResize">
+
+            <intent-filter>
+
+                <category
+                    android:name="android.intent.category.DEFAULT" />
+
+                <category
+                    android:name="android.intent.category.BROWSABLE" />
+
+                <action
+                    android:name="android.intent.action.VIEW" />
+
+                <data
+                    android:scheme=" " />
+            </intent-filter>
+        </activity>
+        <meta-data
+            android:name="dcloud_appkey"
+            android:value="8b9914a6963a9f6b5a8022dcd43c290e" />
+    </application>
+
+</manifest>

文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappchooselocation.js


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniapperror.png


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappes6.js


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappopenlocation.js


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniapppicker.js


文件差异内容过多而无法显示
+ 6 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappquill.js


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappquillimageresize.js


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappscan.js


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappsuccess.png


+ 25 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/__uniappview.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var __UniViewStartTime__ = Date.now();
+      var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
+        CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+        (coverSupport ? ', viewport-fit=cover' : '') + '" />')
+    </script>
+    <title>View</title>
+    <link rel="stylesheet" href="view.css" />
+  </head>
+
+  <body>
+    <div id="app"></div>
+    <script src="__uniappes6.js"></script>
+    <script src="view.umd.min.js"></script>
+    <script src="app-view.js"></script>
+  </body>
+
+</html>

+ 8 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/app-config-service.js

@@ -0,0 +1,8 @@
+
+var isReady=false;var onReadyCallbacks=[];
+var isServiceReady=false;var onServiceReadyCallbacks=[];
+var __uniConfig = {"pages":["pages/index/index","pages/game/game"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"心率测试","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8"},"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":false},"appname":"Heart_Tool","compilerVersion":"3.4.18","entryPagePath":"pages/index/index","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
+var __uniRoutes = [{"path":"/pages/index/index","meta":{"isQuit":true},"window":{"navigationBarTitleText":"心率测试"}},{"path":"/pages/game/game","meta":{},"window":{"navigationBarTitleText":"","enablePullDownRefresh":false,"titleNView":false,"subNVues":[{"id":"game","path":"pages/game/subGame/subGame","type":"popup","style":{"width":"100%","position":"absolute"}}]}}];
+__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
+__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
+service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:Math.round(f/20)})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:void 0,window:void 0,document:void 0,frames:void 0,self:void 0,location:void 0,navigator:void 0,localStorage:void 0,history:void 0,Caches:void 0,screen:void 0,alert:void 0,confirm:void 0,prompt:void 0,fetch:void 0,XMLHttpRequest:void 0,WebSocket:void 0,webkit:void 0,print:void 0}}}});

+ 1 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/app-config.js

@@ -0,0 +1 @@
+(function(e){function r(r){for(var n,l,i=r[0],p=r[1],a=r[2],c=0,s=[];c<i.length;c++)l=i[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in p)Object.prototype.hasOwnProperty.call(p,n)&&(e[n]=p[n]);f&&f(r);while(s.length)s.shift()();return u.push.apply(u,a||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,i=1;i<t.length;i++){var p=t[i];0!==o[p]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={"app-config":0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"===typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e["default"]}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/";var i=this["webpackJsonp"]=this["webpackJsonp"]||[],p=i.push.bind(i);i.push=r,i=i.slice();for(var a=0;a<i.length;a++)r(i[a]);var f=p;t()})([]);

文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/app-service.js


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/app-view.js


+ 1 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/manifest.json

@@ -0,0 +1 @@
+{"@platforms":["android","iPhone","iPad"],"id":"__UNI__8D02B4B","name":"Heart_Tool","version":{"name":"1.0.2","code":20220806},"description":"","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"Bluetooth":{},"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"target":"id:1","autoclose":true,"waiting":true,"delay":0},"popGesture":"close","launchwebview":{"render":"always","id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"3.4.18","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"launch_path":"__uniappview.html"}}

文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/pages/game/subGame/subGame.js


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/bandage.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/handle.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/hotman.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesIcon/rope.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/boxingb.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/boxingw.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/deviceright.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/link.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/radio-b.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/devicesOther/radio-g.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/gameCloseW.png


二进制
app/src/main/assets/apps/__UNI__8D02B4B/www/static/logo.png


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/view.css


文件差异内容过多而无法显示
+ 0 - 0
app/src/main/assets/apps/__UNI__8D02B4B/www/view.umd.min.js


+ 6 - 0
app/src/main/assets/data/dcloud_control.xml

@@ -0,0 +1,6 @@
+
+<hbuilder>
+<apps>
+    <app appid="__UNI__8D02B4B"  appver=""/>
+</apps>
+</hbuilder>

+ 92 - 0
app/src/main/assets/data/dcloud_error.html

@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="utf-8"/>
+    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
+	<meta name="HandheldFriendly" content="true"/>
+	<meta name="MobileOptimized" content="320"/>
+	<title>Error</title>
+	<script type="text/javascript">
+// H5 plus事件处理
+var ws=null;
+function plusReady(){
+	// Android处理返回键
+	plus.key.addEventListener('backbutton',function(){
+		(history.length==1)&&ws.close();
+		var c=setTimeout(function(){
+			ws.close();
+		},1000);
+		window.onbeforeunload=function(){
+			clearTimeout(c);
+		}
+		history.go(-2);
+	},false);
+	ws=plus.webview.currentWebview();
+}
+if(window.plus){
+	plusReady();
+}else{
+	document.addEventListener('plusready',plusReady,false);
+}
+document.addEventListener('touchstart',function(){
+    return false;
+},true);
+// 禁止选择
+document.oncontextmenu=function(){
+	return false;
+};
+// 获取错误信息
+document.addEventListener("error",function(e){
+	info.innerText="请求的页面("+e.url+")无法打开";
+	console.log("请求的页面无法打开:"+e.href);
+},false);
+	</script>
+	<style>
+*{
+	-webkit-user-select: none;
+}
+html,body{
+	margin: 0px;
+	padding: 0px;
+	width: 100%;
+	height: 100%;
+	text-align: center;
+	word-break: break-all;
+	-webkit-touch-callout:none;
+	-webkit-tap-highlight-color:rgba(0,0,0,0);
+}
+.button{
+	width: 50%;
+	font-size: 18px;
+	font-weight: normal;
+	text-decoration: none;
+	text-align: center;
+	padding: .5em 0em;
+	margin: .5em auto;
+	color: #333333;
+	background-color: #EEEEEE;
+	border: 1px solid #CCCCCC;
+	-webkit-border-radius: 5px;
+	border-radius: 5px;
+}
+.button:active{
+	background-color: #CCCCCC;
+}
+	</style>
+</head>
+<body>
+	<div style="width:100%;height:20%;"></div>
+	<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 512 512" style="height:20%;width:30%"> 
+	<g id="icomoon-ignore">
+		<line stroke-width="1" x1="" y1="" x2="" y2="" stroke="#449FDB" opacity=""></line>
+	</g>
+	<path d="M256 0c-141.385 0-256 114.615-256 256s114.615 256 256 256 256-114.615 256-256-114.615-256-256-256zM352 128c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32zM160 128c17.673 0 32 14.327 32 32s-14.327 32-32 32-32-14.327-32-32 14.327-32 32-32zM352.049 390.37c-19.587-32.574-55.272-54.37-96.049-54.37s-76.462 21.796-96.049 54.37l-41.164-24.698c27.98-46.535 78.958-77.672 137.213-77.672s109.232 31.137 137.213 77.672l-41.164 24.698z" fill="#666666"></path>
+    </svg>
+	<p style="font-size:18px;font-weight:bolder;">We're sorry ...</p>
+	<p id="info" style="font-size:12px;"></p>
+	<!--<div class="button" onclick="history.back()">Retry</div>-->
+	<div class="button" onclick="if(history.length == 1){ws.close();}else{ws.back();ws.back();}">Back</div>
+	<div class="button" onclick="ws.close()">Close</div>
+	<div class="button" onclick="plus.runtime.restart()">Restart</div>
+</body>
+</html>

+ 79 - 0
app/src/main/assets/data/dcloud_properties.xml

@@ -0,0 +1,79 @@
+<properties>
+	<features>
+		<feature name="Barcode" value="io.dcloud.feature.barcode2.BarcodeFeatureImpl"/>
+        <feature name="Speech" value="io.dcloud.feature.speech.SpeechFeatureImpl">
+            <module name="iFly" value="io.dcloud.feature.speech.IflySpeechEngine"/>
+            <module name="baidu" value="io.dcloud.feature.speech.BaiduSpeechEngine"/>
+        </feature>
+		<feature name="Maps" value="io.dcloud.js.map.amap.JsMapPluginImpl"/>
+        <!--<feature name="Maps" value="io.dcloud.js.map.JsMapPluginImpl"/>-->
+		<feature name="Contacts" value="io.dcloud.feature.contacts.ContactsFeatureImpl"/>
+		<feature name="Messaging" value="io.dcloud.adapter.messaging.MessagingPluginImpl"/>
+		<feature name="Camera" value="io.dcloud.js.camera.CameraFeatureImpl"/>
+		<feature name="Console" value="io.dcloud.feature.pdr.LoggerFeatureImpl"/>
+		<feature name="Device" value="io.dcloud.feature.device.DeviceFeatureImpl"/>
+		<feature name="File" value="io.dcloud.js.file.FileFeatureImpl"/>
+		<feature name="Proximity" value="io.dcloud.feature.sensor.ProximityFeatureImpl"/>
+		<feature name="Storage" value="io.dcloud.feature.pdr.NStorageFeatureImpl"/>
+		<feature name="Cache" value="io.dcloud.feature.pdr.CoreCacheFeatureImpl"/>
+		<feature name="Invocation" value="io.dcloud.invocation.Invocation"/>
+		<feature name="Navigator" value="io.dcloud.feature.ui.navigator.NavigatorUIFeatureImpl"/>
+		<feature name="NativeUI" value="io.dcloud.feature.ui.nativeui.NativeUIFeatureImpl"/>
+		<feature name="UI" value="io.dcloud.feature.ui.UIFeatureImpl">
+			<module name="Navigator" value="io.dcloud.feature.ui.NavView"/>
+		</feature>
+		<feature name="Gallery" value="io.dcloud.js.gallery.GalleryFeatureImpl"/>
+		<feature name="Downloader" value="io.dcloud.net.DownloaderFeatureImpl"/>
+		<feature name="Uploader" value="io.dcloud.net.UploadFeature"/>
+		<feature name="Push" value="io.dcloud.feature.aps.APSFeatureImpl">
+			<module name="igexin" value="io.dcloud.feature.apsGt.GTPushService"/>
+			<!-- mkeypush -->
+		</feature>
+		<feature name="Zip" value="io.dcloud.feature.pdr.ZipFeature"/>
+		<feature name="Audio" value="io.dcloud.feature.audio.AudioFeatureImpl"/>
+		<feature name="Runtime" value="io.dcloud.feature.pdr.RuntimeFeatureImpl"/>
+        <feature name="VideoPlayer" value="io.dcloud.media.MediaFeatureImpl"/>
+        <feature name="LivePusher" value="io.dcloud.media.live.LiveMediaFeatureImpl"/>
+		<feature name="XMLHttpRequest" value="io.dcloud.net.XMLHttpRequestFeature"/>
+		<feature name="Statistic" value="io.dcloud.feature.statistics.StatisticsFeatureImpl"/>
+		<feature name="Accelerometer" value="io.dcloud.feature.sensor.AccelerometerFeatureImpl"/>
+		<feature name="Orientation" value="io.dcloud.feature.sensor.OrientationFeatureImpl"/>
+		<feature name="NativeObj" value="io.dcloud.feature.nativeObj.FeatureImpl"/>		
+		<feature name="Geolocation" value="io.dcloud.js.geolocation.GeolocationFeatureImpl"/>
+		<feature name="Payment" value="io.dcloud.feature.payment.PaymentFeatureImpl">
+			<module name="AliPay" value="io.dcloud.feature.payment.alipay.AliPay"/>
+			<module name="Payment-Weixin" value="io.dcloud.feature.payment.weixin.WeiXinPay"/>
+			<module name="Payment-Qihoo" value="io.dcloud.feature.payment.qihoopay.QihooPay"/>
+		</feature>
+		<feature name="Share" value="io.dcloud.share.ShareFeatureImpl">
+			<module name="Sina" value="io.dcloud.share.sina.SinaWeiboApiManager"/>
+			<module name="Tencent" value="io.dcloud.share.tencent.TencentWeiboApiManager"/>
+			<module name="Weixin" value="io.dcloud.share.mm.WeiXinApiManager"/>
+            <module name="QQ" value="io.dcloud.share.qq.QQApiManager"/>
+		</feature>
+		<feature name="OAuth" value="io.dcloud.feature.oauth.OAuthFeatureImpl">
+			<module name="OAuth-Weixin" value="io.dcloud.feature.oauth.weixin.WeiXinOAuthService"/>
+			<module name="OAuth-QQ" value="io.dcloud.feature.oauth.qq.QQOAuthService"/>
+			<module name="OAuth-Sina" value="io.dcloud.feature.oauth.sina.SinaOAuthService"/>
+			<module name="OAuth-Qihoo" value="io.dcloud.oauth.qihoo.QihooOAuthService"/>
+			<module name="OAuth-MiUi" value="io.dcloud.feature.oauth.miui.MiUiOAuthService"/>
+		</feature>
+		<feature name="Stream" value="io.dcloud.appstream.js.StreamAppFeatureImpl"/>
+		<feature name="Fingerprint" value="io.dcloud.feature.fingerprint.FingerPrintsImpl"/>
+		<feature name="iBeacon" value="io.dcloud.feature.iBeacon.WxBluetoothFeatureImpl"/>
+        <feature name="Bluetooth" value="io.dcloud.feature.bluetooth.BluetoothFeature"/>
+        <feature name="Sqlite" value="io.dcloud.feature.sqlite.DataBaseFeature"/>
+        <feature name="Ad" value="io.dcloud.feature.ad.AdFlowFeatureImpl">
+            <module name="360" value="io.dcloud.feature.ad.juhe360.AD360Module"/>
+            <module name="csj" value="io.dcloud.feature.ad.csj.ADCsjModule"/>
+            <module name="gdt" value="io.dcloud.feature.ad.gdt.ADGdtModule"/>
+        </feature>
+	</features>
+
+	<services>
+		<service name="push" value="io.dcloud.feature.aps.APSFeatureImpl"/>
+		<service name="Statistic" value="io.dcloud.feature.statistics.StatisticsBootImpl"/>
+		<service name="Downloader" value="io.dcloud.net.DownloaderBootImpl"/>
+		<!--<service name="Maps" value="io.dcloud.js.map.MapInitImpl"/>-->
+	</services>
+</properties>

+ 32 - 0
app/src/main/assets/dcloud_uniplugins.json

@@ -0,0 +1,32 @@
+{
+  "nativePlugins": [
+    {
+      "plugins": [
+        {
+          "type": "module",
+          "name": "TestModule",
+          "class": "io.dcloud.uniplugin.TestModule"
+        }
+      ]
+    },
+    {
+      "plugins": [
+        {
+          "type": "component",
+          "name": "myText",
+          "class": "io.dcloud.uniplugin.TestText"
+        }
+      ]
+    },
+    {
+      "hooksClass": "",
+      "plugins": [
+        {
+          "type": "module",
+          "name": "DCloud-RichAlert",
+          "class": "uni.dcloud.io.uniplugin_richalert.RichAlertModule"
+        }
+      ]
+    }
+  ]
+}

二进制
app/src/main/res/drawable-xxhdpi/icon.png


二进制
app/src/main/res/drawable-xxhdpi/push.png


二进制
app/src/main/res/drawable-xxhdpi/splash.png


+ 3 - 0
app/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">HeartRateBand</string>
+</resources>

二进制
app/test.jks


+ 27 - 0
build.gradle

@@ -0,0 +1,27 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+    
+    repositories {
+        google()
+        jcenter()
+    }
+    dependencies {
+        classpath 'com.android.tools.build:gradle:4.1.1'
+        
+
+        // NOTE: Do not place your application dependencies here; they belong
+        // in the individual module build.gradle files
+    }
+}
+
+allprojects {
+    repositories {
+        google()
+        jcenter()
+    }
+}
+
+task clean(type: Delete) {
+    delete rootProject.buildDir
+}

二进制
core/.DS_Store


+ 1 - 0
core/.gitignore

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

+ 29 - 0
core/build.gradle

@@ -0,0 +1,29 @@
+apply plugin: 'com.android.library'
+
+
+android {
+    compileSdkVersion 28
+
+    defaultConfig {
+        minSdkVersion 16
+        targetSdkVersion 28
+    }
+
+    buildTypes {
+        release {
+            minifyEnabled false
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+    packagingOptions{
+        doNotStrip "*/*/libvad.dnn.so"
+        doNotStrip "*/*/libbd_easr_s1_merge_normal_20151216.dat.so"
+    }
+}
+
+
+
+dependencies {
+    api fileTree(include: ['*.jar'], dir: 'libs')
+    implementation 'androidx.appcompat:appcompat:1.0.2'
+}

+ 125 - 0
core/core.iml

@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module external.linked.project.id=":core" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$/.." external.system.id="GRADLE" type="JAVA_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="android-gradle" name="Android-Gradle">
+      <configuration>
+        <option name="GRADLE_PROJECT_PATH" value=":core" />
+        <option name="LAST_SUCCESSFUL_SYNC_AGP_VERSION" value="3.4.0" />
+        <option name="LAST_KNOWN_AGP_VERSION" value="3.4.0" />
+      </configuration>
+    </facet>
+    <facet type="android" name="Android">
+      <configuration>
+        <option name="SELECTED_BUILD_VARIANT" value="debug" />
+        <option name="ASSEMBLE_TASK_NAME" value="assembleDebug" />
+        <option name="COMPILE_JAVA_TASK_NAME" value="compileDebugSources" />
+        <afterSyncTasks>
+          <task>generateDebugSources</task>
+        </afterSyncTasks>
+        <option name="ALLOW_USER_CONFIGURATION" value="false" />
+        <option name="MANIFEST_FILE_RELATIVE_PATH" value="/src/main/AndroidManifest.xml" />
+        <option name="RES_FOLDER_RELATIVE_PATH" value="/src/main/res" />
+        <option name="RES_FOLDERS_RELATIVE_PATH" value="file://$MODULE_DIR$/src/main/res" />
+        <option name="TEST_RES_FOLDERS_RELATIVE_PATH" value="" />
+        <option name="ASSETS_FOLDER_RELATIVE_PATH" value="/src/main/assets" />
+        <option name="PROJECT_TYPE" value="1" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_7">
+    <output url="file://$MODULE_DIR$/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes" />
+    <output-test url="file://$MODULE_DIR$/build/intermediates/javac/debugUnitTest/compileDebugUnitTestJavaWithJavac/classes" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debug/compileDebugAidl/out" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/debug" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debug/compileDebugRenderscript/out" isTestSource="false" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/debug" type="java-resource" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/debug" type="java-resource" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/aidl_source_output_dir/debugAndroidTest/compileDebugAndroidTestAidl/out" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/renderscript_source_output_dir/debugAndroidTest/compileDebugAndroidTestRenderscript/out" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug" type="java-test-resource" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug" type="java-test-resource" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/build/generated/source/apt/test/debug" isTestSource="true" generated="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/debug/shaders" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTestDebug/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/testDebug/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/res" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/assets" type="java-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/aidl" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/rs" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/main/shaders" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/androidTest/shaders" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/res" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/resources" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/assets" type="java-test-resource" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/aidl" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/rs" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/src/test/shaders" isTestSource="true" />
+      <excludeFolder url="file://$MODULE_DIR$/build" />
+    </content>
+    <orderEntry type="jdk" jdkName="Android API 28 Platform" jdkType="Android SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" name="Gradle: __local_aars__:D.\AndroidProjects\asr_wakeup3\core\libs\bdasr_V3_20191017_81d0afc.jar:unspecified@jar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.collection:collection:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-common:2.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.arch.core:core-common:2.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.annotation:annotation:1.0.0@jar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.appcompat:appcompat:1.0.2@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.fragment:fragment:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable-animated:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-core-ui:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.legacy:legacy-support-core-utils:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.vectordrawable:vectordrawable:1.0.1@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.loader:loader:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.viewpager:viewpager:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.coordinatorlayout:coordinatorlayout:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.drawerlayout:drawerlayout:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.slidingpanelayout:slidingpanelayout:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.customview:customview:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.swiperefreshlayout:swiperefreshlayout:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.asynclayoutinflater:asynclayoutinflater:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.core:core:1.0.1@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.versionedparcelable:versionedparcelable:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.cursoradapter:cursoradapter:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-runtime:2.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.documentfile:documentfile:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.localbroadcastmanager:localbroadcastmanager:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.print:print:1.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-viewmodel:2.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata:2.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.lifecycle:lifecycle-livedata-core:2.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.arch.core:core-runtime:2.0.0@aar" level="project" />
+    <orderEntry type="library" name="Gradle: androidx.interpolator:interpolator:1.0.0@aar" level="project" />
+  </component>
+</module>

二进制
core/libs/bdasr_V3_20210628_cfe8c44.jar


+ 21 - 0
core/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

+ 12 - 0
core/src/main/AndroidManifest.xml

@@ -0,0 +1,12 @@
+<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.baidu.aip.asrwakeup3.core">
+<!--    <uses-permission android:name="android.permission.RECORD_AUDIO" />-->
+<!--    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />-->
+<!--    <uses-permission android:name="android.permission.INTERNET" />-->
+<!--    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />-->
+
+    <!-- 蓝牙录音使用,不需要可以去除 -->
+<!--    <uses-permission android:name="android.permission.BROADCAST_STICKY" />-->
+<!--    <uses-permission android:name="android.permission.BLUETOOTH" />-->
+<!--    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />-->
+<!--    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />-->
+</manifest>

+ 3 - 0
core/src/main/assets/README.txt

@@ -0,0 +1,3 @@
+百度语音识别SDK在demo程序中assets目录的文件解释说明:
+WakeUp.bin                               唤醒功能的唤醒词配置文件, 需要在百度语音开发平台中定义和导出 http://speech.baidu.com/wake
+baidu_speech_grammar.bsg                 自定义语义以及离线命令词识别共用的语法文件, 需要在百度语音开发平台中定义和导出 http://speech.baidu.com/asr

+ 1 - 0
core/src/main/assets/WakeUp.bin

@@ -0,0 +1 @@
+QahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõQahI@|qxÓ~cR]mRuZÓmRkAòõ

文件差异内容过多而无法显示
+ 0 - 0
core/src/main/assets/baidu_speech_grammar.bsg


+ 98 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/FileAudioInputStream.java

@@ -0,0 +1,98 @@
+package com.baidu.aip.asrwakeup3.core.inputstream;
+
+import android.util.Log;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.math.BigInteger;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Created by fujiayi on 2017/11/27.
+ * <p>
+ * 解决大文件的输入问题。
+ * 文件大时不能通过Infile参数一下子输入。
+ */
+
+public class FileAudioInputStream extends InputStream {
+
+    private InputStream in;
+
+    private long nextSleepTime = -1;
+
+    private long totalSleepMs = 0;
+
+    private static final String TAG = "FileAudioInputStream";
+
+    public FileAudioInputStream(String file) throws FileNotFoundException {
+        in = new FileInputStream(file);
+    }
+
+    public FileAudioInputStream(InputStream in) {
+        this.in = in;
+    }
+
+    @Override
+    public int read() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int read(byte[] buffer, int byteOffset, int byteCount) throws IOException {
+        int bytePerMs = 16000 * 2 / 1000;
+        int count = bytePerMs * 20; // 20ms 音频数据
+        if (byteCount < count) {
+            count = byteCount;
+        }
+        if (nextSleepTime > 0) {
+            try {
+                long sleepMs = nextSleepTime - System.currentTimeMillis();
+                if (sleepMs > 0) {
+                    Log.i(TAG, "will sleep " + sleepMs);
+                    Thread.sleep(sleepMs); // 每20ms的音频 ,比如等待20ms传输下一批
+                    totalSleepMs += sleepMs;
+                }
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        int r = in.read(buffer, byteOffset, count);
+
+        /*
+        if (r >= 0) {
+            Log.i("FileAudioInputStream", "Debug:" + System.currentTimeMillis() + ": " + md5(buffer, byteOffset, r));
+        } else {
+            Log.i("FileAudioInputStream", "Debug:" + System.currentTimeMillis() + ": return " + r);
+        }
+        */
+        nextSleepTime = System.currentTimeMillis() + r / bytePerMs;
+
+        // 如果是长语音,在r=-1的情况下,需要手动调用stop
+        return r;
+    }
+
+    @Override
+    public void close() throws IOException {
+        super.close();
+        Log.i(TAG, "time sleeped " + totalSleepMs);
+        if (null != in) {
+            in.close();
+        }
+    }
+
+    private String md5(byte[] buffer, int byteOffset, int byteCount) {
+        try {
+            MessageDigest digest = MessageDigest.getInstance("MD5");
+            digest.reset();
+            digest.update(buffer, byteOffset, byteCount);
+            BigInteger bigInt = new BigInteger(1, digest.digest());
+            return bigInt.toString(16);
+        } catch (NoSuchAlgorithmException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+}

+ 105 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/InFileStream.java

@@ -0,0 +1,105 @@
+package com.baidu.aip.asrwakeup3.core.inputstream;
+
+import android.app.Activity;
+import android.content.Context;
+
+import com.baidu.aip.asrwakeup3.core.util.MyLogger;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Created by fujiayi on 2017/6/20.
+ */
+
+public class InFileStream {
+
+    private static Context context;
+
+    private static final String TAG = "InFileStream";
+
+    private static volatile String filename;
+
+    private static volatile InputStream is;
+
+    // 以下3个setContext
+
+    /**
+     * 必须要先调用这个方法
+     * 如之后调用create16kStream,使用默认的app/src/main/assets/outfile.pcm作为输入
+     * 如之后调用createMyPipedInputStream, 见 InPipedStream
+     *
+     * @param context
+     */
+    public static void setContext(Context context) {
+        InFileStream.context = context;
+    }
+
+    /**
+     * 使用pcm文件作为输入
+     *
+     * @param context
+     * @param filename
+     */
+    public static void setContext(Context context, String filename) {
+        InFileStream.context = context;
+        InFileStream.filename = filename;
+    }
+
+    public static void setContext(Context context, InputStream is) {
+        InFileStream.context = context;
+        InFileStream.is = is;
+    }
+
+    public static Context getContext() {
+        return context;
+    }
+
+    public static void reset() {
+        filename = null;
+        is = null;
+    }
+
+
+    public static InputStream createMyPipedInputStream() {
+        return InPipedStream.createAndStart(context);
+    }
+
+    /**
+     * 默认使用必须要先调用setContext
+     * 默认从createFileStream中读取InputStream
+     *
+     * @return
+     */
+    public static InputStream create16kStream() {
+        if (is == null && filename == null) {
+            // 没有任何设置的话,从createFileStream中读取
+            return new FileAudioInputStream(createFileStream());
+        }
+
+        if (is != null) { // 默认为null,setInputStream调用后走这个逻辑
+            return new FileAudioInputStream(is);
+        } else if (filename != null) { //  默认为null, setFileName调用后走这个逻辑
+            try {
+                return new FileAudioInputStream(filename);
+            } catch (FileNotFoundException e) {
+                e.printStackTrace();
+            }
+        }
+
+        return null;
+    }
+
+    private static InputStream createFileStream() {
+        try {
+            // 这里抛异常表示没有调用 setContext方法
+            InputStream is = context.getAssets().open("outfile.pcm");
+            MyLogger.info(TAG, "create input stream ok " + is.available());
+            return is;
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+    }
+}

+ 82 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/InPipedStream.java

@@ -0,0 +1,82 @@
+package com.baidu.aip.asrwakeup3.core.inputstream;
+
+import android.content.Context;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+/**
+ * 本示例从app/src/main/assets/outfile.pcm作为byte[]的输入
+ * 生成PipedInputStream作为SDK里IN_FILE的参数
+ */
+public class InPipedStream {
+
+    private PipedInputStream pipedInputStream;
+    private PipedOutputStream pipedOutputStream;
+    private Context context;
+
+    private InPipedStream(Context context) {
+        pipedInputStream = new PipedInputStream();
+        pipedOutputStream = new PipedOutputStream();
+        this.context = context;
+    }
+
+    private void start() throws IOException {
+        /**  准备绑定 **/
+        pipedInputStream.connect(pipedOutputStream);
+
+        /** 准备文件 **/
+
+        /** 新线程中放入 20ms 音频数据,注意从新线程放入**/
+        Runnable run = new Runnable() {
+            @Override
+            public void run() {
+
+                try {
+                    final InputStream is = context.getAssets().open("outfile.pcm");
+                    /** 读取20ms的音频二进制数据 放入buffer 中**/
+                    int bytePerMs = 16000 * 2 / 1000;
+                    int count = bytePerMs * 20; // 20ms 音频数据
+                    int r = 0;
+                    byte[] buffer = new byte[count];
+                    do {
+                        r = is.read(buffer);
+                        int sleepTime = 0;
+                        if (r > 0) {
+                            pipedOutputStream.write(buffer, 0, count);
+                            sleepTime = r / bytePerMs;
+                        } else if (r == 0) {
+                            sleepTime = 100; // 这里数值按照自己情况而定
+                        }
+                        if (sleepTime > 0) {
+                            try {
+                                Thread.sleep(sleepTime);
+                            } catch (InterruptedException e) {
+                                e.printStackTrace();
+                            }
+                        }
+
+                    } while (r >= 0);
+                    is.close();
+                } catch (IOException e) {
+                    e.printStackTrace();
+                    throw new RuntimeException(e);
+                }
+            }
+        };
+        (new Thread(run)).start();
+    }
+
+    public static PipedInputStream createAndStart(Context context) {
+        InPipedStream obj = new InPipedStream(context);
+        try {
+            obj.start();
+        } catch (IOException e) {
+            e.printStackTrace();
+            throw new RuntimeException(e);
+        }
+        return obj.pipedInputStream;
+    }
+}

+ 100 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/inputstream/MyMicrophoneInputStream.java

@@ -0,0 +1,100 @@
+package com.baidu.aip.asrwakeup3.core.inputstream;
+
+import android.content.Context;
+import android.media.AudioFormat;
+import android.media.AudioRecord;
+import android.media.MediaRecorder;
+import android.util.Log;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Created by fujiayi on 2017/11/27.
+ */
+
+public class MyMicrophoneInputStream extends InputStream {
+    private static AudioRecord audioRecord;
+
+    private static MyMicrophoneInputStream is;
+
+    private boolean isStarted = false;
+
+    private static final String TAG = "MyMicrophoneInputStream";
+
+    public MyMicrophoneInputStream() {
+        if (audioRecord == null) {
+            int bufferSize = AudioRecord.getMinBufferSize(16000,
+                    AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT) * 16;
+            audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC,
+                    16000, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
+
+
+        }
+
+
+    }
+
+    public static MyMicrophoneInputStream getInstance() {
+        if (is == null) {
+            synchronized (MyMicrophoneInputStream.class) {
+                if (is == null) {
+                    is = new MyMicrophoneInputStream();
+                }
+            }
+        }
+        return is;
+    }
+
+    public void start() {
+        Log.i(TAG, " MyMicrophoneInputStream start recoding!");
+        try {
+            if (audioRecord == null
+                    || audioRecord.getState() != AudioRecord.STATE_INITIALIZED) {
+                throw new IllegalStateException(
+                        "startRecording() called on an uninitialized AudioRecord." + (audioRecord == null));
+            }
+
+            Context context = InFileStream.getContext();
+
+            audioRecord.startRecording();
+
+        } catch (Exception e) {
+            Log.e(TAG, e.getClass().getSimpleName(), e);
+        }
+        Log.i(TAG, " MyMicrophoneInputStream start recoding finished");
+    }
+
+    @Override
+    public int read() throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public int read(byte[] b, int off, int len) throws IOException {
+
+        if (!isStarted) {
+            start(); // 建议在CALLBACK_EVENT_ASR_READY事件中调用。
+            isStarted = true;
+        }
+        try {
+            int count = audioRecord.read(b, off, len);
+            return count;
+        } catch (Exception e) {
+            Log.e(TAG, e.getClass().getSimpleName(), e);
+            throw e;
+        }
+
+    }
+
+    // 建议在建议在CALLBACK_EVENT_ASR_EXIT事件中调用
+    @Override
+    public void close() throws IOException {
+        Log.i(TAG, " MyMicrophoneInputStream close");
+        if (audioRecord != null) {
+            audioRecord.stop();
+            // audioRecord.release(); 程序结束别忘记自行释放
+            isStarted = false;
+        }
+    }
+}

+ 283 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/ActivityMiniRecog.java

@@ -0,0 +1,283 @@
+package com.baidu.aip.asrwakeup3.core.mini;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.baidu.aip.asrwakeup3.core.R;
+import com.baidu.aip.asrwakeup3.core.util.AuthUtil;
+import com.baidu.speech.EventListener;
+import com.baidu.speech.EventManager;
+import com.baidu.speech.EventManagerFactory;
+import com.baidu.speech.asr.SpeechConstant;
+
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 集成文档: http://ai.baidu.com/docs#/ASR-Android-SDK/top 集成指南一节
+ * demo目录下doc_integration_DOCUMENT
+ * ASR-INTEGRATION-helloworld  ASR集成指南-集成到helloworld中 对应 ActivityMiniRecog
+ * ASR-INTEGRATION-TTS-DEMO ASR集成指南-集成到合成DEMO中 对应 ActivityRecog
+ */
+
+public class ActivityMiniRecog extends AppCompatActivity implements EventListener {
+    protected TextView txtLog;
+    protected TextView txtResult;
+    protected Button btn;
+    protected Button stopBtn;
+    private static String DESC_TEXT = "精简版识别,带有SDK唤醒运行的最少代码,仅仅展示如何调用,\n" +
+            "也可以用来反馈测试SDK输入参数及输出回调。\n" +
+            "本示例需要自行根据文档填写参数,可以使用之前识别示例中的日志中的参数。\n" +
+            "需要完整版请参见之前的识别示例。\n" +
+            "需要测试离线命令词识别功能可以将本类中的enableOffline改成true,首次测试离线命令词请联网使用。之后请说出“打电话给李四”";
+
+    private EventManager asr;
+
+    private boolean logTime = true;
+
+    protected boolean enableOffline = false; // 测试离线命令词,需要改成true
+
+    /**
+     * 基于SDK集成2.2 发送开始事件
+     * 点击开始按钮
+     * 测试参数填在这里
+     */
+    private void start() {
+        txtLog.setText("");
+        Map<String, Object> params = AuthUtil.getParam();
+        String event = null;
+        event = SpeechConstant.ASR_START; // 替换成测试的event
+
+        if (enableOffline) {
+            params.put(SpeechConstant.DECODER, 2);
+        }
+        // 基于SDK集成2.1 设置识别参数
+        params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);
+        // params.put(SpeechConstant.NLU, "enable");
+        // params.put(SpeechConstant.BDS_ASR_ENABLE_LONG_SPEECH, true);//长语音  优先级高于VAD_ENDPOINT_TIMEOUT
+        // params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音
+
+        // params.put(SpeechConstant.IN_FILE, "res:///com/baidu/android/voicedemo/16k_test.pcm");
+        // params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
+        // params.put(SpeechConstant.PID, 1537); // 中文输入法模型,有逗号
+
+        /* 语音自训练平台特有参数 */
+        // params.put(SpeechConstant.PID, 8002);
+        // 语音自训练平台特殊pid,8002:模型类似开放平台 1537  具体是8001还是8002,看自训练平台页面上的显示
+        // params.put(SpeechConstant.LMID,1068);
+        // 语音自训练平台已上线的模型ID,https://ai.baidu.com/smartasr/model
+        // 注意模型ID必须在你的appId所在的百度账号下
+        /* 语音自训练平台特有参数 */
+
+        /* 测试InputStream*/
+        // InFileStream.setContext(this);
+        // params.put(SpeechConstant.IN_FILE,
+        // "#com.baidu.aip.asrwakeup3.core.inputstream.InFileStream.createMyPipedInputStream()");
+
+        // 请先使用如‘在线识别’界面测试和生成识别参数。 params同ActivityRecog类中myRecognizer.start(params);
+        // 复制此段可以自动检测错误
+        (new AutoCheck(getApplicationContext(), new Handler() {
+            public void handleMessage(Message msg) {
+                if (msg.what == 100) {
+                    AutoCheck autoCheck = (AutoCheck) msg.obj;
+                    synchronized (autoCheck) {
+                        String message = autoCheck.obtainErrorMessage(); // autoCheck.obtainAllMessage();
+                        txtLog.append(message + "\n");
+                        ; // 可以用下面一行替代,在logcat中查看代码
+                        // Log.w("AutoCheckMessage", message);
+                    }
+                }
+            }
+        }, enableOffline)).checkAsr(params);
+        String json = null; // 可以替换成自己的json
+        json = new JSONObject(params).toString(); // 这里可以替换成你需要测试的json
+        asr.send(event, json, null, 0, 0);
+        printLog("输入参数:" + json);
+    }
+
+    /**
+     * 点击停止按钮
+     * 基于SDK集成4.1 发送停止事件
+     */
+    private void stop() {
+        printLog("停止识别:ASR_STOP");
+        asr.send(SpeechConstant.ASR_STOP, null, null, 0, 0); //
+    }
+
+
+    /**
+     * enableOffline设为true时,在onCreate中调用
+     * 基于SDK离线命令词1.4 加载离线资源(离线时使用)
+     */
+    private void loadOfflineEngine() {
+        Map<String, Object> params = new LinkedHashMap<String, Object>();
+        params.put(SpeechConstant.DECODER, 2);
+        params.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, "assets://baidu_speech_grammar.bsg");
+        asr.send(SpeechConstant.ASR_KWS_LOAD_ENGINE, new JSONObject(params).toString(), null, 0, 0);
+    }
+
+    /**
+     * enableOffline为true时,在onDestory中调用,与loadOfflineEngine对应
+     * 基于SDK集成5.1 卸载离线资源步骤(离线时使用)
+     */
+    private void unloadOfflineEngine() {
+        asr.send(SpeechConstant.ASR_KWS_UNLOAD_ENGINE, null, null, 0, 0); //
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.common_mini);
+        initView();
+        initPermission();
+        // 基于sdk集成1.1 初始化EventManager对象
+        asr = EventManagerFactory.create(this, "asr");
+        // 基于sdk集成1.3 注册自己的输出事件类
+        asr.registerListener(this); //  EventListener 中 onEvent方法
+        btn.setOnClickListener(new View.OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                start();
+            }
+        });
+        stopBtn.setOnClickListener(new View.OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                stop();
+            }
+        });
+        if (enableOffline) {
+            loadOfflineEngine(); // 测试离线命令词请开启, 测试 ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH 参数时开启
+        }
+    }
+
+    @Override
+    protected void onPause() {
+        super.onPause();
+        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
+        Log.i("ActivityMiniRecog", "On pause");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        // 基于SDK集成4.2 发送取消事件
+        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
+        if (enableOffline) {
+            unloadOfflineEngine(); // 测试离线命令词请开启, 测试 ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH 参数时开启
+        }
+
+        // 基于SDK集成5.2 退出事件管理器
+        // 必须与registerListener成对出现,否则可能造成内存泄露
+        asr.unregisterListener(this);
+    }
+
+    // 基于sdk集成1.2 自定义输出事件类 EventListener 回调方法
+    // 基于SDK集成3.1 开始回调事件
+    @Override
+    public void onEvent(String name, String params, byte[] data, int offset, int length) {
+        String logTxt = "name: " + name;
+
+        if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
+            // 识别相关的结果都在这里
+            if (params == null || params.isEmpty()) {
+                return;
+            }
+            if (params.contains("\"nlu_result\"")) {
+                // 一句话的语义解析结果
+                if (length > 0 && data.length > 0) {
+                    logTxt += ", 语义解析结果:" + new String(data, offset, length);
+                }
+            } else if (params.contains("\"partial_result\"")) {
+                // 一句话的临时识别结果
+                logTxt += ", 临时识别结果:" + params;
+            }  else if (params.contains("\"final_result\""))  {
+                // 一句话的最终识别结果
+                logTxt += ", 最终识别结果:" + params;
+            }  else {
+                // 一般这里不会运行
+                logTxt += " ;params :" + params;
+                if (data != null) {
+                    logTxt += " ;data length=" + data.length;
+                }
+            }
+        } else {
+            // 识别开始,结束,音量,音频数据回调
+            if (params != null && !params.isEmpty()){
+                logTxt += " ;params :" + params;
+            }
+            if (data != null) {
+                logTxt += " ;data length=" + data.length;
+            }
+        }
+
+
+        printLog(logTxt);
+    }
+
+    private void printLog(String text) {
+        if (logTime) {
+            text += "  ;time=" + System.currentTimeMillis();
+        }
+        text += "\n";
+        Log.i(getClass().getName(), text);
+        txtLog.append(text + "\n");
+    }
+
+
+    private void initView() {
+        txtResult = (TextView) findViewById(R.id.txtResult);
+        txtLog = (TextView) findViewById(R.id.txtLog);
+        btn = (Button) findViewById(R.id.btn);
+        stopBtn = (Button) findViewById(R.id.btn_stop);
+        txtLog.setText(DESC_TEXT + "\n");
+    }
+
+    /**
+     * android 6.0 以上需要动态申请权限
+     */
+    private void initPermission() {
+        String permissions[] = {Manifest.permission.RECORD_AUDIO,
+                Manifest.permission.ACCESS_NETWORK_STATE,
+                Manifest.permission.INTERNET,
+                Manifest.permission.WRITE_EXTERNAL_STORAGE
+        };
+
+        ArrayList<String> toApplyList = new ArrayList<String>();
+
+        for (String perm : permissions) {
+            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
+                toApplyList.add(perm);
+                // 进入到这里代表没有权限.
+
+            }
+        }
+        String tmpList[] = new String[toApplyList.size()];
+        if (!toApplyList.isEmpty()) {
+            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
+        }
+
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+        // 此处为android 6.0以上动态授权的回调,用户自行实现。
+    }
+
+}

+ 269 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/ActivityMiniUnit.java

@@ -0,0 +1,269 @@
+package com.baidu.aip.asrwakeup3.core.mini;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.baidu.aip.asrwakeup3.core.R;
+import com.baidu.aip.asrwakeup3.core.util.AuthUtil;
+import com.baidu.speech.EventListener;
+import com.baidu.speech.EventManager;
+import com.baidu.speech.EventManagerFactory;
+import com.baidu.speech.asr.SpeechConstant;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ *  集成文档: http://ai.baidu.com/docs#/ASR-Android-SDK/top 集成指南一节
+ *  demo目录下doc_integration_DOCUMENT
+ *      ASR-INTEGRATION-helloworld  ASR集成指南-集成到helloworld中 对应 ActivityMiniRecog
+ *      ASR-INTEGRATION-TTS-DEMO ASR集成指南-集成到合成DEMO中 对应 ActivityRecog
+ */
+
+public class ActivityMiniUnit extends AppCompatActivity implements EventListener {
+    protected TextView txtLog;
+    protected TextView txtResult;
+    protected Button btn;
+    protected Button stopBtn;
+    private static String DESC_TEXT =
+            "UNIT 2.0为自定义语义解析+多轮会话等功能。语音SDK使用时,仅省去一次http请求。(语音SDK调用Unit实际效果)=(语音识别后的文字+Unit http请求结果)\n\n"+
+            "精简版Unit,带有SDKUnit功能的最少代码,仅仅展示如何调用,\n" +
+            "结果返回‘我不知道应该怎么答复您。’表示测试成功。 \n" +
+            "上述句子测试成功后,Unit 2.0具体功能请通过Unit的QQ群,工单,论坛咨询。语音相关反馈方式不回复Unit相关问题。https://ai.baidu.com/unit/home";
+
+    private EventManager asr;
+
+    private boolean logTime = true;
+
+    protected boolean enableOffline = false; // 测试Unit 2.0 功能,必须一直联网
+
+    private String BOT_ID = "26435"; // 修改你自己的AppId AppKey AppSecret后,换成你自己的BOT_ID测试
+
+    /**
+     * 基于SDK集成2.2 发送开始事件
+     * 点击开始按钮
+     * 测试参数填在这里
+     */
+    private void start() {
+        txtLog.setText("");
+        Map<String, Object> params = AuthUtil.getParam();
+        String event = null;
+        event = SpeechConstant.ASR_START; // 替换成测试的event
+
+        if (enableOffline) {
+            params.put(SpeechConstant.DECODER, 2);
+        }
+        // 基于SDK集成2.1 设置识别参数
+        params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);
+        params.put(SpeechConstant.PID, 15374); // 或 19364 Unit  2.0 固定pid,仅支持中文普通话
+        // params.put(SpeechConstant.NLU, "enable");
+        // params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音
+        // params.put(SpeechConstant.IN_FILE, "res:///com/baidu/android/voicedemo/16k_test.pcm");
+        // params.put(SpeechConstant.VAD, SpeechConstant.VAD_DNN);
+
+        // 请先使用如‘在线识别’界面测试和生成识别参数。 params同ActivityRecog类中myRecognizer.start(params);
+        params.put(SpeechConstant.BOT_SESSION_LIST, unitParams());
+        // 复制此段可以自动检测错误
+        (new AutoCheck(getApplicationContext(), new Handler() {
+            public void handleMessage(Message msg) {
+                if (msg.what == 100) {
+                    AutoCheck autoCheck = (AutoCheck) msg.obj;
+                    synchronized (autoCheck) {
+                        String message = autoCheck.obtainErrorMessage(); // autoCheck.obtainAllMessage();
+                        txtLog.append(message + "\n");
+                        ; // 可以用下面一行替代,在logcat中查看代码
+                        // Log.w("AutoCheckMessage", message);
+                    }
+                }
+            }
+        },enableOffline)).checkAsr(params);
+        String json = null; // 可以替换成自己的json
+        json = new JSONObject(params).toString(); // 这里可以替换成你需要测试的json
+        asr.send(event, json, null, 0, 0);
+        printLog("输入参数:" + json);
+    }
+
+    /**
+     * 点击停止按钮
+     *  基于SDK集成4.1 发送停止事件
+     */
+    private void stop() {
+        printLog("停止识别:ASR_STOP");
+        asr.send(SpeechConstant.ASR_STOP, null, null, 0, 0); //
+    }
+
+
+    /**
+     * enableOffline设为true时,在onCreate中调用
+     * 基于SDK离线命令词1.4 加载离线资源(离线时使用)
+     */
+    private void loadOfflineEngine() {
+        Map<String, Object> params = new LinkedHashMap<String, Object>();
+        params.put(SpeechConstant.DECODER, 2);
+        params.put(SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH, "assets://baidu_speech_grammar.bsg");
+        asr.send(SpeechConstant.ASR_KWS_LOAD_ENGINE, new JSONObject(params).toString(), null, 0, 0);
+    }
+
+    /**
+     * enableOffline为true时,在onDestory中调用,与loadOfflineEngine对应
+     * 基于SDK集成5.1 卸载离线资源步骤(离线时使用)
+     */
+    private void unloadOfflineEngine() {
+        asr.send(SpeechConstant.ASR_KWS_UNLOAD_ENGINE, null, null, 0, 0); //
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.common_mini);
+        initView();
+        initPermission();
+        // 基于sdk集成1.1 初始化EventManager对象
+        asr = EventManagerFactory.create(this, "asr");
+        // 基于sdk集成1.3 注册自己的输出事件类
+        asr.registerListener(this); //  EventListener 中 onEvent方法
+        btn.setOnClickListener(new View.OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                start();
+            }
+        });
+        stopBtn.setOnClickListener(new View.OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                stop();
+            }
+        });
+        if (enableOffline) {
+            loadOfflineEngine(); // 测试离线命令词请开启, 测试 ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH 参数时开启
+        }
+    }
+
+    @Override
+    protected void onPause(){
+        super.onPause();
+        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
+        Log.i("ActivityMiniRecog","On pause");
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        // 基于SDK集成4.2 发送取消事件
+        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
+        if (enableOffline) {
+            unloadOfflineEngine(); // 测试离线命令词请开启, 测试 ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH 参数时开启
+        }
+
+        // 基于SDK集成5.2 退出事件管理器
+        // 必须与registerListener成对出现,否则可能造成内存泄露
+        asr.unregisterListener(this);
+    }
+
+    /**
+     * Unit 2.0具体功能请通过Unit的QQ群,工单,论坛咨询。语音相关反馈方式不回复Unit相关问题
+     * @return
+     */
+    private JSONArray unitParams() {
+        JSONArray json = new JSONArray();
+        try {
+            JSONObject bot = new JSONObject();
+            bot.put("bot_id",BOT_ID);
+            bot.put("bot_session_id","");
+            bot.put("bot_session","");
+            json.put(bot);
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return json;
+    }
+
+    // 基于sdk集成1.2 自定义输出事件类 EventListener 回调方法
+    // 基于SDK集成3.1 开始回调事件
+    @Override
+    public void onEvent(String name, String params, byte[] data, int offset, int length) {
+        String logTxt = "name: " + name;
+
+
+        if (params != null && !params.isEmpty()) {
+            logTxt += " ;params :" + params;
+        }
+        if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
+            if (params != null && params.contains("\"nlu_result\"")) {
+                if (length > 0 && data.length > 0) {
+                    logTxt += ", 语义解析结果:" + new String(data, offset, length);
+                }
+            }
+        } else if (data != null) {
+            logTxt += " ;data length=" + data.length;
+        }
+        printLog(logTxt);
+    }
+
+    private void printLog(String text) {
+        if (logTime) {
+            text += "  ;time=" + System.currentTimeMillis();
+        }
+        text += "\n";
+        Log.i(getClass().getName(), text);
+        txtLog.append(text + "\n");
+    }
+
+
+    private void initView() {
+        txtResult = (TextView) findViewById(R.id.txtResult);
+        txtLog = (TextView) findViewById(R.id.txtLog);
+        btn = (Button) findViewById(R.id.btn);
+        stopBtn = (Button) findViewById(R.id.btn_stop);
+        txtLog.setText(DESC_TEXT + "\n");
+    }
+
+    /**
+     * android 6.0 以上需要动态申请权限
+     */
+    private void initPermission() {
+        String permissions[] = {Manifest.permission.RECORD_AUDIO,
+                Manifest.permission.ACCESS_NETWORK_STATE,
+                Manifest.permission.INTERNET,
+                Manifest.permission.WRITE_EXTERNAL_STORAGE
+        };
+
+        ArrayList<String> toApplyList = new ArrayList<String>();
+
+        for (String perm : permissions) {
+            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
+                toApplyList.add(perm);
+                // 进入到这里代表没有权限.
+
+            }
+        }
+        String tmpList[] = new String[toApplyList.size()];
+        if (!toApplyList.isEmpty()) {
+            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
+        }
+
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+        // 此处为android 6.0以上动态授权的回调,用户自行实现。
+    }
+
+}

+ 166 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/ActivityMiniWakeUp.java

@@ -0,0 +1,166 @@
+package com.baidu.aip.asrwakeup3.core.mini;
+
+import android.Manifest;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.core.app.ActivityCompat;
+import androidx.core.content.ContextCompat;
+
+import com.baidu.aip.asrwakeup3.core.R;
+import com.baidu.aip.asrwakeup3.core.inputstream.InFileStream;
+import com.baidu.aip.asrwakeup3.core.util.AuthUtil;
+import com.baidu.speech.EventListener;
+import com.baidu.speech.EventManager;
+import com.baidu.speech.EventManagerFactory;
+import com.baidu.speech.asr.SpeechConstant;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Created by fujiayi on 2017/8/15.
+ */
+
+public class ActivityMiniWakeUp extends AppCompatActivity implements EventListener {
+    protected TextView txtLog;
+    protected TextView txtResult;
+    protected Button btn;
+    protected Button stopBtn;
+    private static String DESC_TEXT = "精简版唤醒,带有SDK唤醒运行的最少代码,仅仅展示如何调用,\n" +
+            "也可以用来反馈测试SDK输入参数及输出回调。\n" +
+            "本示例需要自行根据文档填写参数,可以使用之前唤醒示例中的日志中的参数。\n" +
+            "需要完整版请参见之前的唤醒示例。\n\n" +
+            "唤醒词是纯离线功能,需要获取正式授权文件(与离线命令词的正式授权文件是同一个)。 第一次联网使用唤醒词功能自动获取正式授权文件。之后可以断网测试\n" +
+            "请说“小度你好”或者 “百度一下”\n\n";
+
+    private EventManager wakeup;
+
+    private boolean logTime = true;
+
+    /**
+     * 测试参数填在这里
+     * 基于SDK唤醒词集成第2.1 设置唤醒的输入参数
+     */
+    private void start() {
+        txtLog.setText("");
+        // 基于SDK唤醒词集成第2.1 设置唤醒的输入参数
+        Map<String, Object> params = AuthUtil.getParam();
+        params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);
+        params.put(SpeechConstant.WP_WORDS_FILE, "assets:///WakeUp.bin");
+        // "assets:///WakeUp.bin" 表示WakeUp.bin文件定义在assets目录下
+        InFileStream.setContext(this);
+        String json = null; // 这里可以替换成你需要测试的json
+        json = new JSONObject(params).toString();
+        wakeup.send(SpeechConstant.WAKEUP_START, json, null, 0, 0);
+        printLog("输入参数:" + json);
+    }
+
+    // 基于SDK唤醒词集成第4.1 发送停止事件
+    private void stop() {
+        wakeup.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0); //
+    }
+
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.common_mini);
+        initView();
+        initPermission();
+        // 基于SDK唤醒词集成1.1 初始化EventManager
+        wakeup = EventManagerFactory.create(this, "wp");
+        // 基于SDK唤醒词集成1.3 注册输出事件
+        wakeup.registerListener(this); //  EventListener 中 onEvent方法
+        btn.setOnClickListener(new View.OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                start();
+            }
+        });
+        stopBtn.setOnClickListener(new View.OnClickListener() {
+
+            @Override
+            public void onClick(View v) {
+                stop();
+            }
+        });
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        wakeup.send(SpeechConstant.WAKEUP_STOP, "{}", null, 0, 0);
+    }
+
+    //  基于SDK唤醒词集成1.2 自定义输出事件类 EventListener  回调方法
+    // 基于SDK唤醒3.1 开始回调事件
+    @Override
+    public void onEvent(String name, String params, byte[] data, int offset, int length) {
+        String logTxt = "name: " + name;
+        if (params != null && !params.isEmpty()) {
+            logTxt += " ;params :" + params;
+        } else if (data != null) {
+            logTxt += " ;data length=" + data.length;
+        }
+        printLog(logTxt);
+    }
+
+    private void printLog(String text) {
+        if (logTime) {
+            text += "  ;time=" + System.currentTimeMillis();
+        }
+        text += "\n";
+        Log.i(getClass().getName(), text);
+        txtLog.append(text + "\n");
+    }
+
+
+    private void initView() {
+        txtResult = (TextView) findViewById(R.id.txtResult);
+        txtLog = (TextView) findViewById(R.id.txtLog);
+        btn = (Button) findViewById(R.id.btn);
+        stopBtn = (Button) findViewById(R.id.btn_stop);
+        txtLog.setText(DESC_TEXT + "\n");
+    }
+
+    /**
+     * android 6.0 以上需要动态申请权限
+     */
+    private void initPermission() {
+        String[] permissions = {Manifest.permission.RECORD_AUDIO,
+                Manifest.permission.ACCESS_NETWORK_STATE,
+                Manifest.permission.INTERNET,
+                Manifest.permission.WRITE_EXTERNAL_STORAGE
+        };
+
+        ArrayList<String> toApplyList = new ArrayList<String>();
+
+        for (String perm : permissions) {
+            if (PackageManager.PERMISSION_GRANTED != ContextCompat.checkSelfPermission(this, perm)) {
+                toApplyList.add(perm);
+                // 进入到这里代表没有权限.
+
+            }
+        }
+        String[] tmpList = new String[toApplyList.size()];
+        if (!toApplyList.isEmpty()) {
+            ActivityCompat.requestPermissions(this, toApplyList.toArray(tmpList), 123);
+        }
+
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+        // 此处为android 6.0以上动态授权的回调,用户自行实现。
+    }
+
+}

+ 472 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/mini/AutoCheck.java

@@ -0,0 +1,472 @@
+package com.baidu.aip.asrwakeup3.core.mini;
+
+import android.Manifest;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import androidx.core.content.ContextCompat;
+
+import com.baidu.speech.asr.SpeechConstant;
+import org.json.JSONObject;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+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;
+
+import javax.net.ssl.HttpsURLConnection;
+
+public class AutoCheck {
+    public static final boolean isOnlineLited = false; // 是否只需要是纯在线识别功能
+    private LinkedHashMap<String, Check> checks;
+
+    private Context context;
+    private Handler handler;
+
+    private boolean hasError;
+    private boolean enableOffline;
+    private boolean isFinished = false;
+
+    private String name;
+
+    private static final String TAG = "AutoCheck";
+
+    public AutoCheck(Context context, final Handler handler, boolean enableOffline) {
+        this.context = context;
+        checks = new LinkedHashMap<>();
+        this.handler = handler;
+        this.enableOffline = enableOffline;
+    }
+
+    public void checkAsr(final Map<String, Object> params) {
+        Thread t = new Thread(new Runnable() {
+            @Override
+            public void run() {
+                AutoCheck obj = checkAsrInternal(params);
+                name = "识别";
+                synchronized (obj) { // 偶发,同步线程信息
+                    isFinished = true;
+                    Message msg = handler.obtainMessage(100, obj);
+                    handler.sendMessage(msg);
+                }
+            }
+        });
+        t.start();
+    }
+
+    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;
+        config.withLogOnSuccess = true;
+        return formatString(config);
+    }
+
+    private 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");
+                Log.e("AutoCheck", sb.toString());
+                if (check.hasFix()) {
+                    sb.append("【修复方法】【").append(testName).append(" 】  ").append(check.getFixMessage()).append("\n");
+                }
+            } else if (config.withEachCheckInfo) {
+                sb.append("【无报错】【").append(testName).append(" 】  ").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("【" + name + "】集成自动排查工具: 恭喜没有检测到任何问题\n");
+        }
+        return sb.toString();
+    }
+
+    private AutoCheck checkAsrInternal(Map<String, Object> params) {
+        commonSetting(params);
+        checks.put("外部音频文件存在校验", new FileCheck(context, params, SpeechConstant.IN_FILE));
+        checks.put("离线命令词及本地语义bsg文件存在校验",
+                new FileCheck(context, params, SpeechConstant.ASR_OFFLINE_ENGINE_GRAMMER_FILE_PATH));
+        for (Map.Entry<String, Check> e : checks.entrySet()) {
+            Check check = e.getValue();
+            check.check();
+            if (check.hasError()) {
+                break;
+            }
+        }
+        return this;
+    }
+
+    private void commonSetting(Map<String, Object> params) {
+        checks.put("检查申请的Android权限", new PermissionCheck(context));
+        checks.put("检查so文件是否存在", new JniCheck(context));
+        AppInfoCheck infoCheck = null;
+        try {
+            infoCheck = new AppInfoCheck(context, params);
+            checks.put("检查AppId AppKey SecretKey", infoCheck);
+        } catch (PackageManager.NameNotFoundException e) {
+            e.printStackTrace();
+            Log.e(TAG, "检查AppId AppKey SecretKey 错误", e);
+            return;
+        }
+        if (enableOffline) {
+            checks.put("检查包名", new ApplicationIdCheck(context, infoCheck.appId));
+        }
+
+    }
+
+    private static class PrintConfig {
+        public boolean withEachCheckInfo = false;
+        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.RECORD_AUDIO,
+                    Manifest.permission.ACCESS_NETWORK_STATE,
+                    Manifest.permission.INTERNET,
+                    // Manifest.permission.WRITE_EXTERNAL_STORAGE,
+            };
+
+            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) {
+            this.context = context;
+            if (isOnlineLited) {
+                soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so"};
+            } else {
+                soNames = new String[]{"libBaiduSpeechSDK.so", "libvad.dnn.so",
+                        "libbd_easr_s1_merge_normal_20151216.dat.so", "libbdEASRAndroid.so",
+                        "libbdSpilWakeup.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) {
+                    set.add(file.getName());
+                }
+            }
+            // String debugMessage = "Jni目录内文件: " + set.toString();
+            // boolean isSuccess = true;
+            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 AppInfoCheck extends Check {
+        private String appId;
+        private String appKey;
+        private String secretKey;
+
+        public AppInfoCheck(Context context, Map<String, Object> params) throws PackageManager.NameNotFoundException {
+
+            if (params.get(SpeechConstant.APP_ID) != null) {
+                appId = params.get(SpeechConstant.APP_ID).toString();
+            }
+            if (params.get(SpeechConstant.APP_KEY) != null) {
+                appKey = params.get(SpeechConstant.APP_KEY).toString();
+            }
+
+            if (params.get(SpeechConstant.SECRET) != null) {
+                secretKey = params.get(SpeechConstant.SECRET).toString();
+            }
+        }
+
+
+        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;
+                }
+
+
+                try {
+                    checkOnline();
+                } catch (UnknownHostException e) {
+                    infoMessage = "无网络或者网络不连通,忽略检测 : " + e.getMessage();
+                } catch (Exception e) {
+                    errorMessage = e.getClass().getCanonicalName() + ":" + e.getMessage();
+                    fixMessage = " 重新检测appId, appKey, appSecret是否正确";
+                }
+            } while (false);
+        }
+
+        public void checkOnline() throws Exception {
+            String urlpath = "https://openapi.baidu.com/oauth/2.0/token?client_id="
+                    + appKey + "&client_secret=" + secretKey + "&grant_type=client_credentials";
+            Log.i("AutoCheck", "Url is " + urlpath);
+            URL url = new URL(urlpath);
+            HttpsURLConnection conn = (HttpsURLConnection) 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();
+            if (!res.contains("audio_voice_assistant_get")) {
+                errorMessage = "appid:" + appId + ",没有audio_voice_assistant_get 权限,请在网页上开通\"语音识别\"能力";
+                fixMessage = "secretKey";
+                return;
+            }
+            appendLogMessage("openapi return " + res);
+            JSONObject jsonObject = new JSONObject(res);
+            String error = jsonObject.optString("error");
+            if (error != null && !error.isEmpty()) {
+                errorMessage = "appkey secretKey 错误" + ", error:" + error + ", json is" + result;
+                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
+                return;
+            }
+            String token = jsonObject.getString("access_token");
+            if (token == null || !token.endsWith("-" + appId)) {
+                errorMessage = "appId 与 appkey及 appSecret 不一致。appId = " + appId + " ,token = " + token;
+                fixMessage = " 重新检测appId对应的 appKey, appSecret是否正确";
+            }
+        }
+    }
+
+    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 FileCheck extends Check {
+        private Map<String, Object> params;
+        private String key;
+        private Context context;
+        private boolean allowRes = false;
+        private boolean allowAssets = true;
+
+        public FileCheck(Context context, Map<String, Object> params, String key) {
+            this.context = context;
+            this.params = params;
+            this.key = key;
+            if (key.equals(SpeechConstant.IN_FILE)) {
+                allowRes = true;
+                allowAssets = false;
+            }
+        }
+
+        @Override
+        public void check() {
+            if (!params.containsKey(key)) {
+                return;
+            }
+            String value = params.get(key).toString();
+            if (allowAssets) {
+                int len = "assets".length();
+                int totalLen = len + ":///".length();
+                if (value.startsWith("assets")) {
+                    String filename = value.substring(totalLen);
+                    if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
+                        errorMessage = "参数:" + key + "格式错误:" + value;
+                        fixMessage = "修改成" + "assets:///sdcard/xxxx.yyy";
+                    }
+                    try {
+                        context.getAssets().open(filename);
+                    } catch (IOException e) {
+                        errorMessage = "assets 目录下,文件不存在:" + filename;
+                        fixMessage = "demo的assets目录是:src/main/assets";
+                        e.printStackTrace();
+                    }
+                    appendLogMessage("assets 检验完毕:" + filename);
+                }
+            }
+            if (allowRes) {
+                int len = "res".length();
+                int totalLen = len + ":///".length();
+                if (value.startsWith("res")) {
+                    String filename = value.substring(totalLen);
+                    if (!":///".equals(value.substring(len, totalLen)) || filename.isEmpty()) {
+                        errorMessage = "参数:" + key + "格式错误:" + value;
+                        fixMessage = "修改成" + "res:///com/baidu/android/voicedemo/16k_test.pcm";
+                    }
+                    InputStream is = getClass().getClassLoader().getResourceAsStream(filename);
+                    if (is == null) {
+                        errorMessage = "res,文件不存在:" + filename;
+                        fixMessage = "demo的res目录是:app/src/main/resources";
+                    } else {
+                        try {
+                            is.close();
+                        } catch (IOException e) {
+                            e.printStackTrace();
+                        }
+                    }
+                    appendLogMessage("res 检验完毕:" + filename);
+                }
+            }
+            if (value.startsWith("/")) {
+                if (!new File(value).canRead()) {
+                    errorMessage = "文件不存在:" + value;
+                    fixMessage = "请查看文件是否存在";
+                }
+                appendLogMessage("文件路径 检验完毕:" + value);
+            }
+        }
+    }
+
+    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();
+        }
+    }
+}
+

+ 24 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/IStatus.java

@@ -0,0 +1,24 @@
+package com.baidu.aip.asrwakeup3.core.recog;
+
+/**
+ * Created by fujiayi on 2017/6/14.
+ */
+
+public interface IStatus {
+
+    int STATUS_NONE = 2;
+
+    int STATUS_READY = 3;
+    int STATUS_SPEAKING = 4;
+    int STATUS_RECOGNITION = 5;
+
+    int STATUS_FINISHED = 6;
+    int STATUS_LONG_SPEECH_FINISHED = 7;
+    int STATUS_STOPPED = 10;
+
+    int STATUS_WAITING_READY = 8001;
+    int WHAT_MESSAGE_STATUS = 9001;
+
+    int STATUS_WAKEUP_SUCCESS = 7001;
+    int STATUS_WAKEUP_EXIT = 7003;
+}

+ 142 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/MyRecognizer.java

@@ -0,0 +1,142 @@
+package com.baidu.aip.asrwakeup3.core.recog;
+
+import android.content.Context;
+import com.baidu.aip.asrwakeup3.core.util.MyLogger;
+import com.baidu.speech.EventListener;
+import com.baidu.speech.EventManager;
+import com.baidu.speech.EventManagerFactory;
+import com.baidu.speech.asr.SpeechConstant;
+import org.json.JSONObject;
+import com.baidu.aip.asrwakeup3.core.recog.listener.IRecogListener;
+import com.baidu.aip.asrwakeup3.core.recog.listener.RecogEventAdapter;
+
+import java.util.Map;
+
+/**
+ * Created by fujiayi on 2017/6/13.
+ * EventManager内的方法如send 都可以在主线程中进行,SDK中做过处理
+ */
+
+public class MyRecognizer {
+    /**
+     * SDK 内部核心 EventManager 类
+     */
+    private EventManager asr;
+
+    // SDK 内部核心 事件回调类, 用于开发者写自己的识别回调逻辑
+    private EventListener eventListener;
+
+    // 是否加载离线资源
+    private static boolean isOfflineEngineLoaded = false;
+
+    // 未release前,只能new一个
+    private static volatile boolean isInited = false;
+
+    private static final String TAG = "MyRecognizer";
+
+    /**
+     * 初始化
+     *
+     * @param context
+     * @param recogListener 将EventListener结果做解析的DEMO回调。使用RecogEventAdapter 适配EventListener
+     */
+    public MyRecognizer(Context context, IRecogListener recogListener) {
+        this(context, new RecogEventAdapter(recogListener));
+    }
+
+    /**
+     * 初始化 提供 EventManagerFactory需要的Context和EventListener
+     *
+     * @param context
+     * @param eventListener 识别状态和结果回调
+     */
+    public MyRecognizer(Context context, EventListener eventListener) {
+        if (isInited) {
+            MyLogger.error(TAG, "还未调用release(),请勿新建一个新类");
+            throw new RuntimeException("还未调用release(),请勿新建一个新类");
+        }
+        isInited = true;
+        this.eventListener = eventListener;
+        // SDK集成步骤 初始化asr的EventManager示例,多次得到的类,只能选一个使用
+        asr = EventManagerFactory.create(context, "asr");
+        // SDK集成步骤 设置回调event, 识别引擎会回调这个类告知重要状态和识别结果
+        asr.registerListener(eventListener);
+    }
+
+
+    /**
+     * 离线命令词,在线不需要调用
+     *
+     * @param params 离线命令词加载参数,见文档“ASR_KWS_LOAD_ENGINE 输入事件参数”
+     */
+    public void loadOfflineEngine(Map<String, Object> params) {
+        String json = new JSONObject(params).toString();
+        MyLogger.info(TAG + ".Debug", "离线命令词初始化参数(反馈请带上此行日志):" + json);
+        // SDK集成步骤(可选)加载离线命令词(离线时使用)
+        asr.send(SpeechConstant.ASR_KWS_LOAD_ENGINE, json, null, 0, 0);
+        isOfflineEngineLoaded = true;
+     }
+
+    /**
+     * @param params
+     */
+    public void start(Map<String, Object> params) {
+        if (!isInited) {
+            throw new RuntimeException("release() was called");
+        }
+        // SDK集成步骤 拼接识别参数
+        String json = new JSONObject(params).toString();
+        MyLogger.info(TAG + ".Debug", "识别参数(反馈请带上此行日志)" + json);
+        asr.send(SpeechConstant.ASR_START, json, null, 0, 0);
+    }
+
+
+    /**
+     * 提前结束录音等待识别结果。
+     */
+    public void stop() {
+        MyLogger.info(TAG, "停止录音");
+        // SDK 集成步骤(可选)停止录音
+        if (!isInited) {
+            throw new RuntimeException("release() was called");
+        }
+        asr.send(SpeechConstant.ASR_STOP, "{}", null, 0, 0);
+    }
+
+    /**
+     * 取消本次识别,取消后将立即停止不会返回识别结果。
+     * cancel 与stop的区别是 cancel在stop的基础上,完全停止整个识别流程,
+     */
+    public void cancel() {
+        MyLogger.info(TAG, "取消识别");
+        if (!isInited) {
+            throw new RuntimeException("release() was called");
+        }
+        // SDK集成步骤 (可选) 取消本次识别
+        asr.send(SpeechConstant.ASR_CANCEL, "{}", null, 0, 0);
+    }
+
+    public void release() {
+        if (asr == null) {
+            return;
+        }
+        cancel();
+        if (isOfflineEngineLoaded) {
+            // SDK集成步骤 如果之前有调用过 加载离线命令词,这里要对应释放
+            asr.send(SpeechConstant.ASR_KWS_UNLOAD_ENGINE, null, null, 0, 0);
+            isOfflineEngineLoaded = false;
+        }
+        // SDK 集成步骤(可选),卸载listener
+        asr.unregisterListener(eventListener);
+        asr = null;
+        isInited = false;
+    }
+
+    public void setEventListener(IRecogListener recogListener) {
+        if (!isInited) {
+            throw new RuntimeException("release() was called");
+        }
+        this.eventListener = new RecogEventAdapter(recogListener);
+        asr.registerListener(eventListener);
+    }
+}

+ 134 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/RecogResult.java

@@ -0,0 +1,134 @@
+package com.baidu.aip.asrwakeup3.core.recog;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Created by fujiayi on 2017/6/24.
+ */
+public class RecogResult {
+    private static final int ERROR_NONE = 0;
+
+    private String origalJson;
+    private String[] resultsRecognition;
+    private String origalResult;
+    private String sn; // 日志id, 请求有问题请提问带上sn
+    private String desc;
+    private String resultType;
+    private int error = -1;
+    private int subError = -1;
+
+    public static RecogResult parseJson(String jsonStr) {
+        RecogResult result = new RecogResult();
+        result.setOrigalJson(jsonStr);
+        try {
+            JSONObject json = new JSONObject(jsonStr);
+            int error = json.optInt("error");
+            int subError = json.optInt("sub_error");
+            result.setError(error);
+            result.setDesc(json.optString("desc"));
+            result.setResultType(json.optString("result_type"));
+            result.setSubError(subError);
+            if (error == ERROR_NONE) {
+                result.setOrigalResult(json.getString("origin_result"));
+                JSONArray arr = json.optJSONArray("results_recognition");
+                if (arr != null) {
+                    int size = arr.length();
+                    String[] recogs = new String[size];
+                    for (int i = 0; i < size; i++) {
+                        recogs[i] = arr.getString(i);
+                    }
+                    result.setResultsRecognition(recogs);
+                }
+
+
+            }
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+
+    public boolean hasError() {
+        return error != ERROR_NONE;
+    }
+
+    public boolean isFinalResult() {
+        return "final_result".equals(resultType);
+    }
+
+
+    public boolean isPartialResult() {
+        return "partial_result".equals(resultType);
+    }
+
+    public boolean isNluResult() {
+        return "nlu_result".equals(resultType);
+    }
+
+    public String getOrigalJson() {
+        return origalJson;
+    }
+
+    public void setOrigalJson(String origalJson) {
+        this.origalJson = origalJson;
+    }
+
+    public String[] getResultsRecognition() {
+        return resultsRecognition;
+    }
+
+    public void setResultsRecognition(String[] resultsRecognition) {
+        this.resultsRecognition = resultsRecognition;
+    }
+
+    public String getSn() {
+        return sn;
+    }
+
+    public void setSn(String sn) {
+        this.sn = sn;
+    }
+
+    public int getError() {
+        return error;
+    }
+
+    public void setError(int error) {
+        this.error = error;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public String getOrigalResult() {
+        return origalResult;
+    }
+
+    public void setOrigalResult(String origalResult) {
+        this.origalResult = origalResult;
+    }
+
+    public String getResultType() {
+        return resultType;
+    }
+
+    public void setResultType(String resultType) {
+        this.resultType = resultType;
+    }
+
+    public int getSubError() {
+        return subError;
+    }
+
+    public void setSubError(int subError) {
+        this.subError = subError;
+    }
+}

+ 146 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/ChainRecogListener.java

@@ -0,0 +1,146 @@
+package com.baidu.aip.asrwakeup3.core.recog.listener;
+
+
+import com.baidu.aip.asrwakeup3.core.recog.RecogResult;
+
+import java.util.ArrayList;
+
+/**
+ * Created by fujiayi on 2017/10/18.
+ */
+
+public class ChainRecogListener implements IRecogListener {
+
+    private ArrayList<IRecogListener> listeners;
+
+    public ChainRecogListener() {
+        listeners = new ArrayList<IRecogListener>();
+    }
+
+    public void addListener(IRecogListener listener) {
+        listeners.add(listener);
+    }
+
+    /**
+     * ASR_START 输入事件调用后,引擎准备完毕
+     */
+    @Override
+    public void onAsrReady() {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrReady();
+        }
+    }
+
+    /**
+     * onAsrReady后检查到用户开始说话
+     */
+    @Override
+    public void onAsrBegin() {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrBegin();
+        }
+    }
+
+    /**
+     * 检查到用户开始说话停止,或者ASR_STOP 输入事件调用后,
+     */
+    @Override
+    public void onAsrEnd() {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrEnd();
+        }
+    }
+
+    /**
+     * onAsrBegin 后 随着用户的说话,返回的临时结果
+     *
+     * @param results     可能返回多个结果,请取第一个结果
+     * @param recogResult 完整的结果
+     */
+    @Override
+    public void onAsrPartialResult(String[] results, RecogResult recogResult) {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrPartialResult(results, recogResult);
+        }
+    }
+
+    /**
+     * 最终的识别结果
+     *
+     * @param results     可能返回多个结果,请取第一个结果
+     * @param recogResult 完整的结果
+     */
+    @Override
+    public void onAsrFinalResult(String[] results, RecogResult recogResult) {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrFinalResult(results, recogResult);
+        }
+    }
+
+    @Override
+    public void onAsrFinish(RecogResult recogResult) {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrFinish(recogResult);
+        }
+    }
+
+    @Override
+    public void onAsrFinishError(int errorCode, int subErrorCode, String descMessage,
+                                 RecogResult recogResult) {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrFinishError(errorCode, subErrorCode, descMessage, recogResult);
+        }
+    }
+
+    /**
+     * 长语音识别结束
+     */
+    @Override
+    public void onAsrLongFinish() {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrLongFinish();
+        }
+    }
+
+    @Override
+    public void onAsrVolume(int volumePercent, int volume) {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrVolume(volumePercent, volume);
+        }
+    }
+
+    @Override
+    public void onAsrAudio(byte[] data, int offset, int length) {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrAudio(data, offset, length);
+        }
+    }
+
+    @Override
+    public void onAsrExit() {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrExit();
+        }
+    }
+
+    @Override
+    public void onAsrOnlineNluResult(String nluResult) {
+        for (IRecogListener listener : listeners) {
+            listener.onAsrOnlineNluResult(nluResult);
+        }
+    }
+
+    @Override
+    public void onOfflineLoaded() {
+        for (IRecogListener listener : listeners) {
+            listener.onOfflineLoaded();
+        }
+    }
+
+    @Override
+    public void onOfflineUnLoaded() {
+        for (IRecogListener listener : listeners) {
+            listener.onOfflineUnLoaded();
+        }
+    }
+}

+ 113 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/IRecogListener.java

@@ -0,0 +1,113 @@
+package com.baidu.aip.asrwakeup3.core.recog.listener;
+
+import com.baidu.aip.asrwakeup3.core.recog.RecogResult;
+
+/**
+ * 与SDK中回调参数的对应关系定义在RecogEventAdapter类中
+ */
+
+public interface IRecogListener {
+
+    /**
+     * CALLBACK_EVENT_ASR_READY
+     * ASR_START 输入事件调用后,引擎准备完毕
+     */
+    void onAsrReady();
+
+    /**
+     * CALLBACK_EVENT_ASR_BEGIN
+     * onAsrReady后检查到用户开始说话
+     */
+    void onAsrBegin();
+
+    /**
+     * CALLBACK_EVENT_ASR_END
+     * 检查到用户开始说话停止,或者ASR_STOP 输入事件调用后,
+     */
+    void onAsrEnd();
+
+    /**
+     * CALLBACK_EVENT_ASR_PARTIAL resultType=partial_result
+     * onAsrBegin 后 随着用户的说话,返回的临时结果
+     *
+     * @param results     可能返回多个结果,请取第一个结果
+     * @param recogResult 完整的结果
+     */
+    void onAsrPartialResult(String[] results, RecogResult recogResult);
+
+    /**
+     * 语音的在线语义结果
+     *
+     * CALLBACK_EVENT_ASR_PARTIAL resultType=nlu_result
+     * @param nluResult
+     */
+    void onAsrOnlineNluResult(String nluResult);
+
+    /**
+     *  不开启长语音仅回调一次,长语音的每一句话都会回调一次
+     * CALLBACK_EVENT_ASR_PARTIAL resultType=final_result
+     * 最终的识别结果
+     *
+     * @param results     可能返回多个结果,请取第一个结果
+     * @param recogResult 完整的结果
+     */
+    void onAsrFinalResult(String[] results, RecogResult recogResult);
+
+    /**
+     * CALLBACK_EVENT_ASR_FINISH
+     * @param recogResult 结束识别
+     */
+    void onAsrFinish(RecogResult recogResult);
+
+    /**
+     * CALLBACK_EVENT_ASR_FINISH error!=0
+     *
+     * @param errorCode
+     * @param subErrorCode
+     * @param descMessage
+     * @param recogResult
+     */
+    void onAsrFinishError(int errorCode, int subErrorCode, String descMessage,
+                          RecogResult recogResult);
+
+    /**
+     * 长语音识别结束
+     */
+    void onAsrLongFinish();
+
+    /**
+     * CALLBACK_EVENT_ASR_VOLUME
+     * 音量回调
+     *
+     * @param volumePercent 音量的相对值,百分比,0-100
+     * @param volume 音量绝对值
+     */
+    void onAsrVolume(int volumePercent, int volume);
+
+    /**
+     * CALLBACK_EVENT_ASR_AUDIO
+     * @param data pcm格式,16bits 16000采样率
+     *
+     * @param offset
+     * @param length
+     */
+    void onAsrAudio(byte[] data, int offset, int length);
+
+    /**
+     * CALLBACK_EVENT_ASR_EXIT
+     * 引擎完成整个识别,空闲中
+     */
+    void onAsrExit();
+
+    /**
+     * CALLBACK_EVENT_ASR_LOADED
+     * 离线命令词资源加载成功
+     */
+    void onOfflineLoaded();
+
+    /**
+     * CALLBACK_EVENT_ASR_UNLOADED
+     * 离线命令词资源释放成功
+     */
+    void onOfflineUnLoaded();
+}

+ 165 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/MessageStatusRecogListener.java

@@ -0,0 +1,165 @@
+package com.baidu.aip.asrwakeup3.core.recog.listener;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import com.baidu.aip.asrwakeup3.core.recog.RecogResult;
+import com.baidu.speech.asr.SpeechConstant;
+
+/**
+ * Created by fujiayi on 2017/6/16.
+ */
+
+public class MessageStatusRecogListener extends StatusRecogListener {
+    private Handler handler;
+
+    private long speechEndTime = 0;
+
+    private boolean needTime = true;
+
+    private static final String TAG = "MesStatusRecogListener";
+
+    public MessageStatusRecogListener(Handler handler) {
+        this.handler = handler;
+    }
+
+
+    @Override
+    public void onAsrReady() {
+        super.onAsrReady();
+        speechEndTime = 0;
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_WAKEUP_READY, "引擎就绪,可以开始说话。");
+    }
+
+    @Override
+    public void onAsrBegin() {
+        super.onAsrBegin();
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_BEGIN, "检测到用户说话");
+    }
+
+    @Override
+    public void onAsrEnd() {
+        super.onAsrEnd();
+        speechEndTime = System.currentTimeMillis();
+        sendMessage("【asr.end事件】检测到用户说话结束");
+    }
+
+    @Override
+    public void onAsrPartialResult(String[] results, RecogResult recogResult) {
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL,
+                "临时识别结果,结果是“" + results[0] + "”;原始json:" + recogResult.getOrigalJson());
+        super.onAsrPartialResult(results, recogResult);
+    }
+
+    @Override
+    public void onAsrFinalResult(String[] results, RecogResult recogResult) {
+        super.onAsrFinalResult(results, recogResult);
+        String message = "识别结束,结果是”" + results[0] + "”";
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL,
+                message + ";原始json:" + recogResult.getOrigalJson());
+        if (speechEndTime > 0) {
+            long currentTime = System.currentTimeMillis();
+            long diffTime = currentTime - speechEndTime;
+            message += ";说话结束到识别结束耗时【" + diffTime + "ms】" + currentTime;
+
+        }
+        speechEndTime = 0;
+        sendMessage(message, status, true);
+    }
+
+    @Override
+    public void onAsrFinishError(int errorCode, int subErrorCode, String descMessage,
+                                 RecogResult recogResult) {
+        super.onAsrFinishError(errorCode, subErrorCode, descMessage, recogResult);
+        String message = "【asr.finish事件】识别错误, 错误码:" + errorCode + " ," + subErrorCode + " ; " + descMessage;
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL, message);
+        if (speechEndTime > 0) {
+            long diffTime = System.currentTimeMillis() - speechEndTime;
+            message += "。说话结束到识别结束耗时【" + diffTime + "ms】";
+        }
+        speechEndTime = 0;
+        sendMessage(message, status, true);
+        speechEndTime = 0;
+    }
+
+    @Override
+    public void onAsrOnlineNluResult(String nluResult) {
+        super.onAsrOnlineNluResult(nluResult);
+        if (!nluResult.isEmpty()) {
+            sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL, "原始语义识别结果json:" + nluResult);
+        }
+    }
+
+    @Override
+    public void onAsrFinish(RecogResult recogResult) {
+        super.onAsrFinish(recogResult);
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_FINISH, "识别一段话结束。如果是长语音的情况会继续识别下段话。");
+
+    }
+
+    /**
+     * 长语音识别结束
+     */
+    @Override
+    public void onAsrLongFinish() {
+        super.onAsrLongFinish();
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_LONG_SPEECH, "长语音识别结束。");
+    }
+
+
+    /**
+     * 使用离线命令词时,有该回调说明离线语法资源加载成功
+     */
+    @Override
+    public void onOfflineLoaded() {
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_LOADED, "离线资源加载成功。没有此回调可能离线语法功能不能使用。");
+    }
+
+    /**
+     * 使用离线命令词时,有该回调说明离线语法资源加载成功
+     */
+    @Override
+    public void onOfflineUnLoaded() {
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_UNLOADED, "离线资源卸载成功。");
+    }
+
+    @Override
+    public void onAsrExit() {
+        super.onAsrExit();
+        sendStatusMessage(SpeechConstant.CALLBACK_EVENT_ASR_EXIT, "识别引擎结束并空闲中");
+    }
+
+    private void sendStatusMessage(String eventName, String message) {
+        message = "[" + eventName + "]" + message;
+        sendMessage(message, status);
+    }
+
+    private void sendMessage(String message) {
+        sendMessage(message, WHAT_MESSAGE_STATUS);
+    }
+
+    private void sendMessage(String message, int what) {
+        sendMessage(message, what, false);
+    }
+
+
+    private void sendMessage(String message, int what, boolean highlight) {
+
+
+        if (needTime && what != STATUS_FINISHED) {
+            message += "  ;time=" + System.currentTimeMillis();
+        }
+        if (handler == null) {
+            Log.i(TAG, message);
+            return;
+        }
+        Message msg = Message.obtain();
+        msg.what = what;
+        msg.arg1 = status;
+        if (highlight) {
+            msg.arg2 = 1;
+        }
+        msg.obj = message + "\n";
+        handler.sendMessage(msg);
+    }
+}

+ 112 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/RecogEventAdapter.java

@@ -0,0 +1,112 @@
+package com.baidu.aip.asrwakeup3.core.recog.listener;
+
+import android.util.Log;
+import com.baidu.aip.asrwakeup3.core.util.MyLogger;
+import com.baidu.speech.EventListener;
+import com.baidu.speech.asr.SpeechConstant;
+import org.json.JSONException;
+import org.json.JSONObject;
+import com.baidu.aip.asrwakeup3.core.recog.RecogResult;
+
+/**
+ * Created by fujiayi on 2017/6/14.
+ */
+
+public class RecogEventAdapter implements EventListener {
+
+    private IRecogListener listener;
+
+    private static final String TAG = "RecogEventAdapter";
+
+    public RecogEventAdapter(IRecogListener listener) {
+        this.listener = listener;
+    }
+
+    // 基于DEMO集成3.1 开始回调事件
+    @Override
+    public void onEvent(String name, String params, byte[] data, int offset, int length) {
+        String currentJson = params;
+        String logMessage = "name:" + name + "; params:" + params;
+
+        // logcat 中 搜索RecogEventAdapter,即可以看见下面一行的日志
+        Log.i(TAG, logMessage);
+        if (false) { // 可以调试,不需要后续逻辑
+            return;
+        }
+        if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_LOADED)) {
+            listener.onOfflineLoaded();
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_UNLOADED)) {
+            listener.onOfflineUnLoaded();
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_READY)) {
+            // 引擎准备就绪,可以开始说话
+            listener.onAsrReady();
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_BEGIN)) {
+            // 检测到用户的已经开始说话
+            listener.onAsrBegin();
+
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_END)) {
+            // 检测到用户的已经停止说话
+            listener.onAsrEnd();
+
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_PARTIAL)) {
+            RecogResult recogResult = RecogResult.parseJson(params);
+            // 识别结果
+            String[] results = recogResult.getResultsRecognition();
+            if (recogResult.isFinalResult()) {
+                // 最终识别结果,长语音每一句话会回调一次
+                listener.onAsrFinalResult(results, recogResult);
+            } else if (recogResult.isPartialResult()) {
+                // 临时识别结果
+                listener.onAsrPartialResult(results, recogResult);
+            } else if (recogResult.isNluResult()) {
+                // 语义理解结果
+                listener.onAsrOnlineNluResult(new String(data, offset, length));
+            }
+
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_FINISH)) {
+            // 识别结束
+            RecogResult recogResult = RecogResult.parseJson(params);
+            if (recogResult.hasError()) {
+                int errorCode = recogResult.getError();
+                int subErrorCode = recogResult.getSubError();
+                MyLogger.error(TAG, "asr error:" + params);
+                listener.onAsrFinishError(errorCode, subErrorCode, recogResult.getDesc(), recogResult);
+            } else {
+                listener.onAsrFinish(recogResult);
+            }
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_LONG_SPEECH)) { // 长语音
+            listener.onAsrLongFinish(); // 长语音
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_EXIT)) {
+            listener.onAsrExit();
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_VOLUME)) {
+            // Logger.info(TAG, "asr volume event:" + params);
+            Volume vol = parseVolumeJson(params);
+            listener.onAsrVolume(vol.volumePercent, vol.volume);
+        } else if (name.equals(SpeechConstant.CALLBACK_EVENT_ASR_AUDIO)) {
+            if (data.length != length) {
+                MyLogger.error(TAG, "internal error: asr.audio callback data length is not equal to length param");
+            }
+            listener.onAsrAudio(data, offset, length);
+        }
+    }
+
+    private Volume parseVolumeJson(String jsonStr) {
+        Volume vol = new Volume();
+        vol.origalJson = jsonStr;
+        try {
+            JSONObject json = new JSONObject(jsonStr);
+            vol.volumePercent = json.getInt("volume-percent");
+            vol.volume = json.getInt("volume");
+        } catch (JSONException e) {
+            e.printStackTrace();
+        }
+        return vol;
+    }
+
+    private class Volume {
+        private int volumePercent = -1;
+        private int volume = -1;
+        private String origalJson;
+    }
+
+}

+ 110 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/recog/listener/StatusRecogListener.java

@@ -0,0 +1,110 @@
+package com.baidu.aip.asrwakeup3.core.recog.listener;
+
+/**
+ * Created by fujiayi on 2017/6/14.
+ */
+
+import android.util.Log;
+import com.baidu.aip.asrwakeup3.core.recog.IStatus;
+import com.baidu.aip.asrwakeup3.core.recog.RecogResult;
+
+/**
+ * 根据回调,判断asr引擎的状态
+ *
+ * 通常状态变化如下:
+ *
+ * STATUS_NONE 初始状态
+ * STATUS_READY 引擎准备完毕
+ * STATUS_SPEAKING 用户开始说话到用户说话完毕前
+ * STATUS_RECOGNITION 用户说话完毕后,识别结束前
+ * STATUS_FINISHED 获得最终识别结果
+ */
+public class StatusRecogListener implements IRecogListener, IStatus {
+
+    private static final String TAG = "StatusRecogListener";
+
+    /**
+     * 识别的引擎当前的状态
+     */
+    protected int status = STATUS_NONE;
+
+    @Override
+    public void onAsrReady() {
+        status = STATUS_READY;
+    }
+
+    @Override
+    public void onAsrBegin() {
+        status = STATUS_SPEAKING;
+    }
+
+    @Override
+    public void onAsrEnd() {
+        status = STATUS_RECOGNITION;
+    }
+
+    @Override
+    public void onAsrPartialResult(String[] results, RecogResult recogResult) {
+
+    }
+
+    @Override
+    public void onAsrFinalResult(String[] results, RecogResult recogResult) {
+        status = STATUS_FINISHED;
+    }
+
+    @Override
+    public void onAsrFinish(RecogResult recogResult) {
+        status = STATUS_FINISHED;
+    }
+
+
+    @Override
+    public void onAsrFinishError(int errorCode, int subErrorCode,  String descMessage,
+                                 RecogResult recogResult) {
+        status = STATUS_FINISHED;
+    }
+
+    @Override
+    public void onAsrLongFinish() {
+        status = STATUS_LONG_SPEECH_FINISHED;
+    }
+
+    @Override
+    public void onAsrVolume(int volumePercent, int volume) {
+        Log.i(TAG, "音量百分比" + volumePercent + " ; 音量" + volume);
+    }
+
+    @Override
+    public void onAsrAudio(byte[] data, int offset, int length) {
+        if (offset != 0 || data.length != length) {
+            byte[] actualData = new byte[length];
+            System.arraycopy(data, 0, actualData, 0, length);
+            data = actualData;
+        }
+
+        Log.i(TAG, "音频数据回调, length:" + data.length);
+    }
+
+    @Override
+    public void onAsrExit() {
+        status = STATUS_NONE;
+    }
+
+    @Override
+    public void onAsrOnlineNluResult(String nluResult) {
+        status = STATUS_FINISHED;
+    }
+
+    @Override
+    public void onOfflineLoaded() {
+
+    }
+
+    @Override
+    public void onOfflineUnLoaded() {
+
+    }
+
+
+}

+ 35 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/util/AuthUtil.java

@@ -0,0 +1,35 @@
+package com.baidu.aip.asrwakeup3.core.util;
+
+import com.baidu.speech.asr.SpeechConstant;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * 为方便说明 apiKey 简称ak,secretKey简称 sk
+ * ak sk为敏感信息,泄露后别人可使用ak sk 消耗你的调用次数,造成财产损失,请妥善保存
+ * 建议你将ak sk保存在自己服务端,通过接口请求获得。
+ * 如果暂时没有后端服务接口,建议将ak sk加密存储减少暴露风险。
+ **/
+public class AuthUtil {
+    public static   String   getAk(){
+        //todo 填入apiKey
+        return "N3gIyDw0AnQRXEUeGLxNCEDY";
+    }
+    public static   String   getSk(){
+        //todo 填入secretKey
+        return  "oWUSGAt7RFcfFZIs7Lk3Vk9fGan8xr6F";
+    }
+    public static   String getAppId(){
+        //todo 填入appId
+        return  "26721854";
+    }
+
+    public static Map<String, Object> getParam(){
+        Map<String, Object> params = new LinkedHashMap<String, Object>();
+        params.put(SpeechConstant.APP_ID, getAppId()); // 添加appId
+        params.put(SpeechConstant.APP_KEY, getAk()); // 添加apiKey
+        params.put(SpeechConstant.SECRET, getSk()); // 添加secretKey
+        return  params;
+    }
+}

+ 73 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/util/FileUtil.java

@@ -0,0 +1,73 @@
+package com.baidu.aip.asrwakeup3.core.util;
+
+import android.content.res.AssetManager;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Created by fujiayi on 2017/5/19.
+ */
+
+public class FileUtil {
+
+    public static boolean makeDir(String dirPath) {
+        File file = new File(dirPath);
+        if (!file.exists()) {
+            return file.mkdirs();
+        } else {
+            return true;
+        }
+    }
+
+    public static String getContentFromAssetsFile(AssetManager assets, String source) {
+        InputStream is = null;
+        FileOutputStream fos = null;
+        String result = "";
+        try {
+            is = assets.open(source);
+            int lenght = is.available();
+            byte[] buffer = new byte[lenght];
+            is.read(buffer);
+            result = new String(buffer, "utf8");
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    public static boolean copyFromAssets(AssetManager assets, String source, String dest,
+                                         boolean isCover) throws IOException {
+        File file = new File(dest);
+        boolean isCopyed = false;
+        if (isCover || (!isCover && !file.exists())) {
+            InputStream is = null;
+            FileOutputStream fos = null;
+            try {
+                is = assets.open(source);
+                String path = dest;
+                fos = new FileOutputStream(path);
+                byte[] buffer = new byte[1024];
+                int size = 0;
+                while ((size = is.read(buffer, 0, 1024)) >= 0) {
+                    fos.write(buffer, 0, size);
+                }
+                isCopyed = true;
+            } finally {
+                if (fos != null) {
+                    try {
+                        fos.close();
+                    } finally {
+                        if (is != null) {
+                            is.close();
+                        }
+                    }
+                }
+            }
+
+        }
+        return isCopyed;
+    }
+}

+ 59 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/util/MyLogger.java

@@ -0,0 +1,59 @@
+package com.baidu.aip.asrwakeup3.core.util;
+
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+
+/**
+ * 记录日志的时候, 顺带往handler记录一份
+ */
+
+public class MyLogger {
+    private static final String TAG = "MyLogger";
+
+    private static final String INFO = "INFO";
+
+    private static final String ERROR = "ERROR";
+
+    private static final boolean ENABLE = true;
+
+    private static Handler handler;
+
+    public static void info(String message) {
+        info(TAG, message);
+    }
+
+    public static void info(String tag, String message) {
+        log(INFO, tag, message);
+    }
+
+    public static void error(String message) {
+        error(TAG, message);
+    }
+
+    public static void error(String tag, String message) {
+        log(ERROR, tag, message);
+    }
+
+    public static void setHandler(Handler handler) {
+        MyLogger.handler = handler;
+    }
+
+    private static void log(String level, String tag, String message) {
+        if (!ENABLE) {
+            return;
+        }
+        if (level.equals(INFO)) {
+            Log.i(tag, message);
+
+        } else if (level.equals(ERROR)) {
+            Log.e(tag, message);
+        }
+        if (handler != null) {
+            Message msg = Message.obtain();
+            msg.obj = "[" + level + "]" + message + "\n";
+            handler.sendMessage(msg);
+        }
+    }
+}

+ 293 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/util/bluetooth/AndroidAudioManager.java

@@ -0,0 +1,293 @@
+package com.baidu.aip.asrwakeup3.core.util.bluetooth;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHeadset;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.media.AudioManager;
+import android.util.Log;
+
+import java.util.List;
+
+import static android.media.AudioManager.STREAM_VOICE_CALL;
+
+public class AndroidAudioManager {
+
+    private static volatile AndroidAudioManager instance;
+
+    private BluetoothAdapter mBluetoothAdapter;
+
+    private AudioManager mAudioManager;
+
+    private boolean mIsBluetoothHeadsetConnected;
+
+    private boolean mIsBluetoothHeadsetScoConnected;
+
+    private BluetoothReceiver mBluetoothReceiver;
+
+    private HeadsetReceiver mHeadsetReceiver;
+
+    private boolean mAudioFocused;
+
+    private Context mContext;
+    private BluetoothHeadset mBluetoothHeadset;
+
+    private AndroidAudioManager(Context context) {
+        mAudioManager = ((AudioManager) context.getSystemService(Context.AUDIO_SERVICE));
+        this.mContext = context.getApplicationContext();
+    }
+
+    public AudioManager getAudioManager() {
+        return mAudioManager;
+    }
+
+    public static AndroidAudioManager getInstance(Context context) {
+        if (instance == null) {
+            synchronized (AndroidAudioManager.class) {
+                if (instance == null) {
+                    instance = new AndroidAudioManager(context);
+                }
+            }
+        }
+        return instance;
+    }
+
+    public void startBluetooth() {
+        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
+        if (mBluetoothAdapter != null) {
+            Log.i("AndroidAudioManager", "[Audio Manager] [Bluetooth] Adapter found");
+            if (mAudioManager.isBluetoothScoAvailableOffCall()) {
+                Log.i("AndroidAudioManager", "[Audio Manager] [Bluetooth] SCO available off call, continue");
+            } else {
+                Log.w("AndroidAudioManager", "[Audio Manager] [Bluetooth] SCO not available off call !");
+            }
+            if (mBluetoothAdapter.isEnabled()) {
+                Log.i("AndroidAudioManager", "[Audio Manager] [Bluetooth] Adapter enabled");
+                mBluetoothReceiver = new BluetoothReceiver();
+                mIsBluetoothHeadsetConnected = false;
+                mIsBluetoothHeadsetScoConnected = false;
+
+                BluetoothProfile.ServiceListener bluetoothServiceListener =
+                        new BluetoothProfile.ServiceListener() {
+                            public void onServiceConnected(int profile, BluetoothProfile proxy) {
+                                if (profile == BluetoothProfile.HEADSET) {
+                                    Log.i("AndroidAudioManager",
+                                            "[Audio Manager] [Bluetooth] HEADSET profile connected");
+                                    mBluetoothHeadset = (BluetoothHeadset) proxy;
+
+                                    List<BluetoothDevice> devices =
+                                            mBluetoothHeadset.getConnectedDevices();
+                                    if (devices.size() > 0) {
+                                        Log.i("AndroidAudioManager",
+                                                "[Audio Manager] [Bluetooth] A device is already connected");
+                                        bluetoothHeadetConnectionChanged(true);
+                                    }
+
+                                    Log.i("AndroidAudioManager",
+                                            "[Audio Manager] [Bluetooth] Registering bluetooth receiver");
+
+                                    IntentFilter filter = new IntentFilter();
+                                    filter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
+                                    filter.addAction(
+                                            BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
+                                    filter.addAction(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
+                                    filter.addAction(
+                                            BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT);
+
+                                    Intent sticky =
+                                            mContext.registerReceiver(mBluetoothReceiver, filter);
+                                    int state =
+                                            sticky.getIntExtra(
+                                                    AudioManager.EXTRA_SCO_AUDIO_STATE,
+                                                    AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+                                    if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
+                                        Log.i("AndroidAudioManager",
+                                                "[Audio Manager] [Bluetooth] Bluetooth headset SCO connected");
+                                        bluetoothHeadetScoConnectionChanged(true);
+                                    } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
+                                        Log.i("AndroidAudioManager",
+                                                "[Audio Manager] [Bluetooth] Bluetooth headset SCO disconnected");
+                                        bluetoothHeadetScoConnectionChanged(false);
+                                    } else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) {
+                                        Log.i("AndroidAudioManager",
+                                                "[Audio Manager] [Bluetooth] Bluetooth headset SCO connecting");
+                                    } else if (state == AudioManager.SCO_AUDIO_STATE_ERROR) {
+                                        Log.i("AndroidAudioManager",
+                                                "[Audio Manager] [Bluetooth] Bluetooth headset SCO connection error");
+                                    } else {
+                                        Log.w("AndroidAudioManager",
+                                                "[Audio Manager] [Bluetooth] Bluetooth headset " +
+                                                        "unknown SCO state changed: "
+                                                        + state);
+                                    }
+                                }
+                            }
+
+                            public void onServiceDisconnected(int profile) {
+                                if (profile == BluetoothProfile.HEADSET) {
+                                    Log.i("AndroidAudioManager",
+                                            "[Audio Manager] [Bluetooth] HEADSET profile disconnected");
+                                    mBluetoothHeadset = null;
+                                    mIsBluetoothHeadsetConnected = false;
+                                    mIsBluetoothHeadsetScoConnected = false;
+                                }
+                            }
+                        };
+                mBluetoothAdapter.getProfileProxy(
+                        mContext, bluetoothServiceListener, BluetoothProfile.HEADSET);
+            }
+        }
+    }
+
+
+    // Bluetooth
+
+    public synchronized void bluetoothHeadetConnectionChanged(boolean connected) {
+        mIsBluetoothHeadsetConnected = connected;
+        mAudioManager.setBluetoothScoOn(connected);
+        mAudioManager.startBluetoothSco();
+        routeAudioToBluetooth();
+    }
+
+
+    public synchronized boolean isBluetoothHeadsetConnected() {
+        return mIsBluetoothHeadsetConnected;
+    }
+
+    public synchronized void bluetoothHeadetScoConnectionChanged(boolean connected) {
+        mIsBluetoothHeadsetScoConnected = connected;
+    }
+
+    public synchronized boolean isUsingBluetoothAudioRoute() {
+        return mIsBluetoothHeadsetScoConnected;
+    }
+
+    public synchronized void routeAudioToBluetooth() {
+        if (!isBluetoothHeadsetConnected()) {
+            Log.w("AndroidAudioManager", "[Audio Manager] [Bluetooth] No headset connected");
+            return;
+        }
+        if (mAudioManager.getMode() != AudioManager.MODE_IN_COMMUNICATION) {
+            Log.w("AndroidAudioManager",
+                    "[Audio Manager] [Bluetooth] Changing audio mode to MODE_IN_COMMUNICATION " +
+                            "and requesting STREAM_VOICE_CALL focus");
+            mAudioManager.setMode(AudioManager.MODE_IN_COMMUNICATION);
+            requestAudioFocus(STREAM_VOICE_CALL);
+        }
+        changeBluetoothSco(true);
+    }
+
+    private void requestAudioFocus(int stream) {
+        if (!mAudioFocused) {
+            int res =
+                    mAudioManager.requestAudioFocus(
+                            null, stream, AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE);
+            Log.d("AndroidAudioManager",
+                    "[Audio Manager] Audio focus requested: "
+                            + (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED
+                            ? "Granted"
+                            : "Denied"));
+            if (res == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+                mAudioFocused = true;
+            }
+        }
+    }
+
+    private synchronized void changeBluetoothSco(final boolean enable) {
+        // IT WILL TAKE A CERTAIN NUMBER OF CALLS TO EITHER START/STOP BLUETOOTH SCO FOR IT TO WORK
+        if (enable && mIsBluetoothHeadsetScoConnected) {
+            Log.i("AndroidAudioManager", "[Audio Manager] [Bluetooth] SCO already enabled, skipping");
+            return;
+        } else if (!enable && !mIsBluetoothHeadsetScoConnected) {
+            Log.i("AndroidAudioManager", "[Audio Manager] [Bluetooth] SCO already disabled, skipping");
+            return;
+        }
+
+        new Thread() {
+            @Override
+            public void run() {
+                boolean resultAcknoledged;
+                int retries = 0;
+                do {
+                    try {
+                        Thread.sleep(200);
+                    } catch (InterruptedException e) {
+                        Log.e("AndroidAudioManager", e.getMessage(), e);
+                    }
+
+                    synchronized (AndroidAudioManager.this) {
+                        if (enable) {
+                            Log.i("AndroidAudioManager",
+                                    "[Audio Manager] [Bluetooth] Starting SCO: try number "
+                                            + retries);
+                            mAudioManager.startBluetoothSco();
+                        } else {
+                            Log.i("AndroidAudioManager",
+                                    "[Audio Manager] [Bluetooth] Stopping SCO: try number "
+                                            + retries);
+                            mAudioManager.stopBluetoothSco();
+                        }
+                        resultAcknoledged = isUsingBluetoothAudioRoute() == enable;
+                        retries++;
+                    }
+                } while (!resultAcknoledged && retries < 10);
+            }
+        }.start();
+    }
+
+    public void destroy() {
+        if (mBluetoothAdapter != null && mBluetoothHeadset != null) {
+            Log.i("AndroidAudioManager", "[Audio Manager] [Bluetooth] Closing HEADSET profile proxy");
+            mBluetoothAdapter.closeProfileProxy(BluetoothProfile.HEADSET, mBluetoothHeadset);
+        }
+
+        Log.i("AndroidAudioManager", "[Audio Manager] [Bluetooth] Unegistering bluetooth receiver");
+        if (mBluetoothReceiver != null) {
+            mContext.unregisterReceiver(mBluetoothReceiver);
+        }
+        synchronized (AndroidAudioManager.class) {
+            mContext = null;
+            instance = null;
+        }
+    }
+
+
+    public void startSimpleBluetooth() {
+        mAudioManager.setBluetoothScoOn(true);
+        mAudioManager.startBluetoothSco();
+    }
+
+    public void destorySimpleBluetooth() {
+        mAudioManager.setBluetoothScoOn(false);
+        mAudioManager.stopBluetoothSco();
+    }
+
+    // HEADSET 插耳机的
+
+    public void enableHeadsetReceiver() {
+        mHeadsetReceiver = new HeadsetReceiver();
+
+        Log.i("AndroidAudioManager", "[Audio Manager] Registering headset receiver");
+        mContext.registerReceiver(
+                mHeadsetReceiver, new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
+        mContext.registerReceiver(
+                mHeadsetReceiver, new IntentFilter(AudioManager.ACTION_HEADSET_PLUG));
+    }
+
+    public void routeAudioToEarPiece() {
+        routeAudioToSpeakerHelper(false);
+    }
+
+    public void routeAudioToSpeakerHelper(boolean speakerOn) {
+        Log.w("AndroidAudioManager", "[Audio Manager] Routing audio to " + (speakerOn ? "speaker" : "earpiece"));
+        if (mIsBluetoothHeadsetScoConnected) {
+            Log.w("AndroidAudioManager", "[Audio Manager] [Bluetooth] Disabling bluetooth audio route");
+            changeBluetoothSco(false);
+        }
+
+        mAudioManager.setSpeakerphoneOn(speakerOn);
+    }
+}

+ 92 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/util/bluetooth/BluetoothReceiver.java

@@ -0,0 +1,92 @@
+package com.baidu.aip.asrwakeup3.core.util.bluetooth;
+
+import android.bluetooth.BluetoothHeadset;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.util.Log;
+
+public class BluetoothReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
+            int state =
+                    intent.getIntExtra(
+                            BluetoothHeadset.EXTRA_STATE, BluetoothHeadset.STATE_DISCONNECTED);
+            if (state == BluetoothHeadset.STATE_CONNECTED) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset connected");
+                AndroidAudioManager.getInstance(context).bluetoothHeadetConnectionChanged(true);
+            } else if (state == BluetoothHeadset.STATE_DISCONNECTED) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset disconnected");
+                AndroidAudioManager.getInstance(context).bluetoothHeadetConnectionChanged(false);
+            } else {
+                Log.w("BluetoothReceiver", "[Bluetooth] Bluetooth headset unknown state changed: " + state);
+            }
+        } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
+            int state =
+                    intent.getIntExtra(
+                            BluetoothHeadset.EXTRA_STATE,
+                            BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
+            if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset audio connected");
+                // AndroidAudioManager.getInstance(context).bluetoothHeadetAudioConnectionChanged(true);
+            } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset audio disconnected");
+                // AndroidAudioManager.getInstance(context).bluetoothHeadetAudioConnectionChanged(false);
+            } else {
+                Log.w("BluetoothReceiver", "[Bluetooth] Bluetooth headset unknown audio state changed: " + state);
+            }
+        } else if (action.equals(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED)) {
+            int state =
+                    intent.getIntExtra(
+                            AudioManager.EXTRA_SCO_AUDIO_STATE,
+                            AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
+            if (state == AudioManager.SCO_AUDIO_STATE_CONNECTED) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset SCO connected");
+                AndroidAudioManager.getInstance(context).bluetoothHeadetScoConnectionChanged(true);
+            } else if (state == AudioManager.SCO_AUDIO_STATE_DISCONNECTED) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset SCO disconnected");
+                AndroidAudioManager.getInstance(context).bluetoothHeadetScoConnectionChanged(false);
+            } else if (state == AudioManager.SCO_AUDIO_STATE_CONNECTING) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset SCO connecting");
+            } else if (state == AudioManager.SCO_AUDIO_STATE_ERROR) {
+                Log.i("BluetoothReceiver", "[Bluetooth] Bluetooth headset SCO connection error");
+            } else {
+                Log.w("BluetoothReceiver", "[Bluetooth] Bluetooth headset unknown SCO state changed: " + state);
+            }
+        } else if (action.equals(BluetoothHeadset.ACTION_VENDOR_SPECIFIC_HEADSET_EVENT)) {
+            String command =
+                    intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
+            int type =
+                    intent.getIntExtra(
+                            BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, -1);
+
+            String commandType;
+            switch (type) {
+                case BluetoothHeadset.AT_CMD_TYPE_ACTION:
+                    commandType = "AT Action";
+                    break;
+                case BluetoothHeadset.AT_CMD_TYPE_READ:
+                    commandType = "AT Read";
+                    break;
+                case BluetoothHeadset.AT_CMD_TYPE_TEST:
+                    commandType = "AT Test";
+                    break;
+                case BluetoothHeadset.AT_CMD_TYPE_SET:
+                    commandType = "AT Set";
+                    break;
+                case BluetoothHeadset.AT_CMD_TYPE_BASIC:
+                    commandType = "AT Basic";
+                    break;
+                default:
+                    commandType = "AT Unknown";
+                    break;
+            }
+            Log.i("BluetoothReceiver", "[Bluetooth] Vendor action " + commandType + " : " + command);
+        } else {
+            Log.w("BluetoothReceiver", "[Bluetooth] Bluetooth unknown action: " + action);
+        }
+    }
+}

+ 41 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/util/bluetooth/HeadsetReceiver.java

@@ -0,0 +1,41 @@
+package com.baidu.aip.asrwakeup3.core.util.bluetooth;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.util.Log;
+
+public class HeadsetReceiver extends BroadcastReceiver {
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        String action = intent.getAction();
+        if (action.equals(AudioManager.ACTION_HEADSET_PLUG)) {
+            // This happens when the user plugs a Jack headset to the device for example
+            int state = intent.getIntExtra("state", 0);
+            String name = intent.getStringExtra("name");
+            int hasMicrophone = intent.getIntExtra("microphone", 0);
+
+            if (state == 0) {
+                Log.i("HeadsetReceiver", "[Headset] Headset disconnected:" + name);
+            } else if (state == 1) {
+                Log.i("HeadsetReceiver", "[Headset] Headset connected:" + name);
+                if (hasMicrophone == 1) {
+                    Log.i("HeadsetReceiver", "[Headset] Headset " + name + " has a microphone");
+                }
+            } else {
+                Log.w("HeadsetReceiver", "[Headset] Unknown headset plugged state: " + state);
+            }
+
+            AndroidAudioManager.getInstance(context).routeAudioToEarPiece();
+            // LinphoneManager.getCallManager().refreshInCallActions();
+        } else if (action.equals(AudioManager.ACTION_AUDIO_BECOMING_NOISY)) {
+            // This happens when the user disconnect a headset, so we shouldn't play audio loudly
+            Log.i("HeadsetReceiver", "[Headset] Noisy state detected, most probably a headset has been disconnected");
+            AndroidAudioManager.getInstance(context).routeAudioToEarPiece();
+            // LinphoneManager.getCallManager().refreshInCallActions();
+        } else {
+            Log.w("HeadsetReceiver", "[Headset] Unknown action: " + action);
+        }
+    }
+}

+ 68 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/MyWakeup.java

@@ -0,0 +1,68 @@
+package com.baidu.aip.asrwakeup3.core.wakeup;
+
+import android.content.Context;
+import com.baidu.aip.asrwakeup3.core.util.MyLogger;
+import com.baidu.aip.asrwakeup3.core.wakeup.listener.IWakeupListener;
+import com.baidu.speech.EventListener;
+import com.baidu.speech.EventManager;
+import com.baidu.speech.EventManagerFactory;
+import com.baidu.speech.asr.SpeechConstant;
+import org.json.JSONObject;
+
+import java.util.Map;
+
+/**
+ * Created by fujiayi on 2017/6/20.
+ */
+
+public class MyWakeup {
+
+
+    private static boolean isInited = false;
+
+    private EventManager wp;
+    private EventListener eventListener;
+
+    private static final String TAG = "MyWakeup";
+
+    public MyWakeup(Context context, EventListener eventListener) {
+        if (isInited) {
+            MyLogger.error(TAG, "还未调用release(),请勿新建一个新类");
+            throw new RuntimeException("还未调用release(),请勿新建一个新类");
+        }
+        isInited = true;
+        this.eventListener = eventListener;
+        wp = EventManagerFactory.create(context, "wp");
+        wp.registerListener(eventListener);
+    }
+
+    public MyWakeup(Context context, IWakeupListener eventListener) {
+        this(context, new WakeupEventAdapter(eventListener));
+    }
+
+    public void start(Map<String, Object> params) {
+        String json = new JSONObject(params).toString();
+        MyLogger.info(TAG + ".Debug", "wakeup params(反馈请带上此行日志):" + json);
+        wp.send(SpeechConstant.WAKEUP_START, json, null, 0, 0);
+    }
+
+    public void stop() {
+        MyLogger.info(TAG, "唤醒结束");
+        wp.send(SpeechConstant.WAKEUP_STOP, null, null, 0, 0);
+    }
+
+    public void setEventListener(EventListener eventListener) {
+        this.eventListener = eventListener;
+    }
+
+    public void setEventListener(IWakeupListener eventListener) {
+        this.eventListener =  new WakeupEventAdapter(eventListener);
+    }
+
+    public void release() {
+        stop();
+        wp.unregisterListener(eventListener);
+        wp = null;
+        isInited = false;
+    }
+}

+ 91 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/WakeUpResult.java

@@ -0,0 +1,91 @@
+package com.baidu.aip.asrwakeup3.core.wakeup;
+
+import com.baidu.aip.asrwakeup3.core.util.MyLogger;
+import com.baidu.speech.asr.SpeechConstant;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+/**
+ * Created by fujiayi on 2017/6/24.
+ */
+public class WakeUpResult {
+    private String name;
+    private String origalJson;
+    private String word;
+    private String desc;
+    private int errorCode;
+
+    private static int ERROR_NONE = 0;
+
+    private static final String TAG = "WakeUpResult";
+
+    public boolean hasError() {
+        return errorCode != ERROR_NONE;
+    }
+
+    public String getOrigalJson() {
+        return origalJson;
+    }
+
+    public void setOrigalJson(String origalJson) {
+        this.origalJson = origalJson;
+    }
+
+    public String getWord() {
+        return word;
+    }
+
+    public void setWord(String word) {
+        this.word = word;
+    }
+
+    public String getDesc() {
+        return desc;
+    }
+
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    public int getErrorCode() {
+        return errorCode;
+    }
+
+    public void setErrorCode(int errorCode) {
+        this.errorCode = errorCode;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public static WakeUpResult parseJson(String name, String jsonStr) {
+        WakeUpResult result = new WakeUpResult();
+        result.setOrigalJson(jsonStr);
+        try {
+            JSONObject json = new JSONObject(jsonStr);
+            if (SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS.equals(name)) {
+                int error = json.optInt("errorCode");
+                result.setErrorCode(error);
+                result.setDesc(json.optString("errorDesc"));
+                if (!result.hasError()) {
+                    result.setWord(json.optString("word"));
+                }
+            } else {
+                int error = json.optInt("error");
+                result.setErrorCode(error);
+                result.setDesc(json.optString("desc"));
+            }
+
+        } catch (JSONException e) {
+            MyLogger.error(TAG, "Json parse error" + jsonStr);
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+}

+ 47 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/WakeupEventAdapter.java

@@ -0,0 +1,47 @@
+package com.baidu.aip.asrwakeup3.core.wakeup;
+
+import com.baidu.aip.asrwakeup3.core.util.MyLogger;
+import com.baidu.aip.asrwakeup3.core.wakeup.listener.IWakeupListener;
+import com.baidu.speech.EventListener;
+import com.baidu.speech.asr.SpeechConstant;
+
+/**
+ * Created by fujiayi on 2017/6/20.
+ */
+
+public class WakeupEventAdapter implements EventListener {
+    private IWakeupListener listener;
+
+    public WakeupEventAdapter(IWakeupListener listener) {
+        this.listener = listener;
+    }
+
+    private static final String TAG = "WakeupEventAdapter";
+  // 基于DEMO唤醒3.1 开始回调事件
+    @Override
+    public void onEvent(String name, String params, byte[] data, int offset, int length) {
+        // android studio日志Monitor 中搜索 WakeupEventAdapter即可看见下面一行的日志
+        MyLogger.info(TAG, "wakeup name:" + name + "; params:" + params);
+        if (SpeechConstant.CALLBACK_EVENT_WAKEUP_SUCCESS.equals(name)) { // 识别唤醒词成功
+            WakeUpResult result = WakeUpResult.parseJson(name, params);
+            int errorCode = result.getErrorCode();
+            if (result.hasError()) { // error不为0依旧有可能是异常情况
+                listener.onError(errorCode, "", result);
+            } else {
+                String word = result.getWord();
+                listener.onSuccess(word, result);
+
+            }
+        } else if (SpeechConstant.CALLBACK_EVENT_WAKEUP_ERROR.equals(name)) { // 识别唤醒词报错
+            WakeUpResult result = WakeUpResult.parseJson(name, params);
+            int errorCode = result.getErrorCode();
+            if (result.hasError()) {
+                listener.onError(errorCode, "", result);
+            }
+        } else if (SpeechConstant.CALLBACK_EVENT_WAKEUP_STOPED.equals(name)) { // 关闭唤醒词
+            listener.onStop();
+        } else if (SpeechConstant.CALLBACK_EVENT_WAKEUP_AUDIO.equals(name)) { // 音频回调
+            listener.onASrAudio(data, offset, length);
+        }
+    }
+}

+ 19 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/listener/IWakeupListener.java

@@ -0,0 +1,19 @@
+package com.baidu.aip.asrwakeup3.core.wakeup.listener;
+
+import com.baidu.aip.asrwakeup3.core.wakeup.WakeUpResult;
+
+/**
+ * Created by fujiayi on 2017/6/21.
+ */
+
+public interface IWakeupListener {
+
+
+    void onSuccess(String word, WakeUpResult result);
+
+    void onStop();
+
+    void onError(int errorCode, String errorMessge, WakeUpResult result);
+
+    void onASrAudio(byte[] data, int offset, int length);
+}

+ 26 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/listener/RecogWakeupListener.java

@@ -0,0 +1,26 @@
+package com.baidu.aip.asrwakeup3.core.wakeup.listener;
+
+import android.os.Handler;
+import com.baidu.aip.asrwakeup3.core.recog.IStatus;
+import com.baidu.aip.asrwakeup3.core.wakeup.WakeUpResult;
+
+/**
+ * Created by fujiayi on 2017/9/21.
+ */
+
+public class RecogWakeupListener extends SimpleWakeupListener implements IStatus {
+
+    private static final String TAG = "RecogWakeupListener";
+
+    private Handler handler;
+
+    public RecogWakeupListener(Handler handler) {
+        this.handler = handler;
+    }
+
+    @Override
+    public void onSuccess(String word, WakeUpResult result) {
+        super.onSuccess(word, result);
+        handler.sendMessage(handler.obtainMessage(STATUS_WAKEUP_SUCCESS));
+    }
+}

+ 35 - 0
core/src/main/java/com/baidu/aip/asrwakeup3/core/wakeup/listener/SimpleWakeupListener.java

@@ -0,0 +1,35 @@
+package com.baidu.aip.asrwakeup3.core.wakeup.listener;
+
+
+import com.baidu.aip.asrwakeup3.core.util.MyLogger;
+import com.baidu.aip.asrwakeup3.core.wakeup.WakeUpResult;
+
+/**
+ * Created by fujiayi on 2017/6/21.
+ */
+
+public class SimpleWakeupListener implements IWakeupListener {
+
+    private static final String TAG = "SimpleWakeupListener";
+
+    @Override
+    public void onSuccess(String word, WakeUpResult result) {
+        MyLogger.info(TAG, "唤醒成功,唤醒词:" + word);
+    }
+
+    @Override
+    public void onStop() {
+        MyLogger.info(TAG, "唤醒词识别结束:");
+    }
+
+    @Override
+    public void onError(int errorCode, String errorMessge, WakeUpResult result) {
+        MyLogger.info(TAG, "唤醒错误:" + errorCode + ";错误消息:" + errorMessge + "; 原始返回" + result.getOrigalJson());
+    }
+
+    @Override
+    public void onASrAudio(byte[] data, int offset, int length) {
+        MyLogger.error(TAG, "audio data: " + data.length);
+    }
+
+}

二进制
core/src/main/jniLibs/arm64-v8a/libBaiduSpeechSDK.so


二进制
core/src/main/jniLibs/arm64-v8a/libbdEASRAndroid.so


二进制
core/src/main/jniLibs/arm64-v8a/libbdSpilWakeup.so


二进制
core/src/main/jniLibs/arm64-v8a/libbd_easr_s1_merge_normal_20151216.dat.so


二进制
core/src/main/jniLibs/arm64-v8a/libvad.dnn.so


二进制
core/src/main/jniLibs/armeabi-v7a/libBaiduSpeechSDK.so


二进制
core/src/main/jniLibs/armeabi-v7a/libbdEASRAndroid.so


二进制
core/src/main/jniLibs/armeabi-v7a/libbdSpilWakeup.so


二进制
core/src/main/jniLibs/armeabi-v7a/libbd_easr_s1_merge_normal_20151216.dat.so


二进制
core/src/main/jniLibs/armeabi-v7a/libvad.dnn.so


二进制
core/src/main/jniLibs/armeabi/libBaiduSpeechSDK.so


部分文件因为文件数量过多而无法显示