百度语音识别SDK嵌入开发文档
- 前提
本Demo将百度语音SDK(其中一部分功能)和自定义的UI对话框封装到一个module中,便于后续的SDK版本更新以及调用。
2.实施
1.新建项目(获取包名)
2.在百度AI平台创建应该获取API Key和Secre Key
官网地址:http://ai.baidu.com/tech/speech
(具体过程省略,都是正常的互联网网站可视化操作很简单)
3.下载SDK
4.使用步骤
一 项目组织结构图
二 导入步骤
(1)新建module,命名为baiduspeech
(2)在baiduspeech的AndroidMainfest.xml中添加以下代码
在官方demo的AndroidMainfest.xml中找到如下信息,然后复制到您自己的同名文件中。此处需要您复制1、权限2、官网申请的应用信息3、SDK的Service。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.why.project.baiduspeech">
<!-- ======================百度语音====================== -->
<!-- begin: baidu speech sdk 权限 -->
<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.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- end: baidu speech sdk 权限 -->
<application>
<!-- ======================百度语音========================== -->
<!-- 正式发布时,请替换成您自己的appId 本demo的appId会不定时下线 -->
<meta-data
android:name="com.baidu.speech.APP_ID"
android:value="11588936" />
<meta-data
android:name="com.baidu.speech.API_KEY"
android:value="XRF3IOf2tNGePzlv47cBnvF3" />
<meta-data
android:name="com.baidu.speech.SECRET_KEY"
android:value="diC8lQ7XDcGBKQ6FzCpvnan54F5CnMZI" />
<service
android:name="com.baidu.speech.VoiceRecognitionService"
android:exported="false" />
</application>
</manifest>
(3)复制jar文件
将官方demo中的app\libs\bdasr_V3_20180320_9066860.jar复制进您项目的同名目录
在build.gradle中确认是否含有以下红色标记的代码
(4)复制jni库的so文件
复制官方demo中 app\src\main\jniLibs 至项目的同名目录。
(5)在官方demo中找到下面的文件复制到项目中(按照下面的包名进行查找)
package com.why.project.baiduspeech.recognization;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
/**
* Created by fujiayi on 2017/6/16
*/
public class MessageStatusRecogListener extends StatusRecogListener {
private Handler handler;
private long speechEndTime;
private boolean needTime = true;
private static final String TAG = "MesStatusRecogListener";
public MessageStatusRecogListener(Handler handler) {
this.handler = handler;
}
@Override
public void onAsrReady() {
super.onAsrReady();
sendStatusMessage("引擎就绪,可以开始说话。");
}
@Override
public void onAsrBegin() {
super.onAsrBegin();
sendStatusMessage("检测到用户说话");
}
@Override
public void onAsrEnd() {
super.onAsrEnd();
speechEndTime = System.currentTimeMillis();
sendMessage("检测到用户说话结束");
}
@Override
public void onAsrPartialResult(String[] results, RecogResult recogResult) {
sendStatusMessage("临时识别结果,结果是“" + 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] + "”";//why 实际中可以去掉,不需要
String message = recogResult.getOrigalJson();//{"results_recognition":["什么什么"],"origin_result":{"corpus_no":6522034498058113957,"err_no":0,"result":{"word":["什么什么"]},"sn":"bfa8b286-ab0e-4f86-9209-1d36d38b1224","voice_energy":16191.7705078125},"error":0,"best_result":"什么什么","result_type":"final_result"}
sendStatusMessage(message + "“;原始json:" + recogResult.getOrigalJson());
if (speechEndTime > 0) {
long diffTime = System.currentTimeMillis() - speechEndTime;
//message += ";说话结束到识别结束耗时【" + diffTime + "ms】";// why 实际中可以去掉,不需要
}
speechEndTime = 0;
sendMessage(message, status, true);
}
@Override
public void onAsrFinishError(int errorCode, int subErrorCode, String errorMessage, String descMessage,
RecogResult recogResult) {
super.onAsrFinishError(errorCode, subErrorCode, errorMessage, descMessage, recogResult);
//String message = "识别错误, 错误码:" + errorCode + " ," + subErrorCode + " ; " + descMessage;// why 实际中可以去掉,不需要
String message = recogResult.getOrigalJson();//{"origin_result":{"sn":"","error":7,"desc":"No recognition result match","sub_error":7001},"error":7,"desc":"No recognition result match","sub_error":7001}
sendStatusMessage(message + ";错误消息:" + errorMessage + ";描述信息:" + descMessage);
if (speechEndTime > 0) {
long diffTime = System.currentTimeMillis() - speechEndTime;
//message += "。说话结束到识别结束耗时【" + diffTime + "ms】";// why实际中可以去掉,不需要
}
speechEndTime = 0;
sendMessage(message, status, true);
speechEndTime = 0;
}
@Override
public void onAsrOnlineNluResult(String nluResult) {
super.onAsrOnlineNluResult(nluResult);
if (!nluResult.isEmpty()) {
sendStatusMessage("原始语义识别结果json:" + nluResult);
}
}
@Override
public void onAsrFinish(RecogResult recogResult) {
super.onAsrFinish(recogResult);
sendStatusMessage("识别一段话结束。如果是长语音的情况会继续识别下段话。");
}
/**
* 长语音识别结束
*/
@Override
public void onAsrLongFinish() {
super.onAsrLongFinish();
sendStatusMessage("长语音识别结束。");
}
/**
* 使用离线命令词时,有该回调说明离线语法资源加载成功
*/
@Override
public void onOfflineLoaded() {
sendStatusMessage("【重要】asr.loaded:离线资源加载成功。没有此回调可能离线语法功能不能使用。");
}
/**
* 使用离线命令词时,有该回调说明离线语法资源加载成功
*/
@Override
public void onOfflineUnLoaded() {
sendStatusMessage(" 离线资源卸载成功。");
}
@Override
public void onAsrExit() {
super.onAsrExit();
sendStatusMessage("识别引擎结束并空闲中");
}
private void sendStatusMessage(String 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);
}
}
至此,百度语音SDK集成到baiduspeech中了,下一步就是在baiduspeech中创建UI对话框。
(7)创建底部对话框SpeechBottomSheetDialog
1、在baiduspeech的build.gradle中引用recyclerview【版本号和项目的appcompat保持一致】
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
defaultConfig {
minSdkVersion 16
targetSdkVersion 27
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:27.1.1'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//RecyclerView
compile "com.android.support:recyclerview-v7:27.1.1"
}
2、对话框类、列表适配器类、布局文件xml文件、图片资源、动画style样式等复制到baiduspeech中
3、这里主要标注下SpeechBottomSheetDialog.java中百度语音的相关代码
package com.why.project.baiduspeech.dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.DialogFragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.baidu.speech.asr.SpeechConstant;
import com.baidu.speech.utils.LogUtil;
import com.why.project.baiduspeech.R;
import com.why.project.baiduspeech.control.MyRecognizer;
import com.why.project.baiduspeech.recognization.IStatus;
import com.why.project.baiduspeech.recognization.MessageStatusRecogListener;
import com.why.project.baiduspeech.recognization.StatusRecogListener;
import com.why.project.baiduspeech.util.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Created by HaiyuKing
* Used 语音识别底部对话框
*/
public class SpeechBottomSheetDialog extends DialogFragment {
private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName();
private Context mContext;
/**View实例*/
private View myView;
private ImageView img_close;
private ProgressBar loadProgressBar;
private TextView tv_tishi;
private RecyclerView result_list;
private Button btn_start;
private ArrayList<String> resultWordList;
private SpeechResultAdapter speechResultAdapter;
private String BtnStartText = "按一下开始听音";
private String BtnStopText = "按一下结束听音";
private String BtnSearchingText = "正在识别";
private String TishiNoText = "没听清,请重说一遍";
/**识别控制器,使用MyRecognizer控制识别的流程*/
protected MyRecognizer myRecognizer;
/**控制UI按钮的状态*/
protected int status;
protected Handler handler;
public static SpeechBottomSheetDialog getInstance(Context mContext)
{
SpeechBottomSheetDialog speechBottomSheetDialog = new SpeechBottomSheetDialog();
speechBottomSheetDialog.mContext = mContext;
return speechBottomSheetDialog;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//设置背景为透明,并且没有标题
myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false);
return myView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
initHandler();//初始化handler
initRecog();//初始化语音
initViews();
initDatas();
initEvents();
}
/**
* 设置宽度和高度值,以及打开的动画效果
*/
@Override
public void onStart() {
super.onStart();
//设置对话框的宽高,必须在onStart中
DisplayMetrics metrics = new DisplayMetrics();
this.getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
Window window = this.getDialog().getWindow();
window.setLayout(metrics.widthPixels, this.getDialog().getWindow().getAttributes().height);
window.setGravity(Gravity.BOTTOM);//设置在底部
//打开的动画效果
//设置dialog的 进出 动画
getDialog().getWindow().setWindowAnimations(R.style.speechbottomsheetdialog_animation);
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
LogUtil.w(TAG,"{onDismiss}");
//当对话框消失的时候统一执行销毁语音功能
destroyRecog();//销毁语音
}
private void initViews() {
img_close = (ImageView) myView.findViewById(R.id.img_close);
loadProgressBar = (ProgressBar) myView.findViewById(R.id.loadProgressBar);
tv_tishi = (TextView) myView.findViewById(R.id.tv_tishi);
result_list = (RecyclerView) myView.findViewById(R.id.result_list);
btn_start = (Button) myView.findViewById(R.id.btn_start);
}
/**初始化数据*/
private void initDatas() {
resultWordList = new ArrayList<String>();
speechResultAdapter = null;
//设置布局管理器
LinearLayoutManager linerLayoutManager = new LinearLayoutManager(getActivity());
result_list.setLayoutManager(linerLayoutManager);
//可以设置为打开后自动识别语音
startRecog();
showProgress();
}
private void initEvents() {
//关闭图标的点击事件
img_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
//按钮的点击事件
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (status) {
case IStatus.STATUS_NONE: // 初始状态
startRecog();
status = IStatus.STATUS_WAITING_READY;
updateBtnTextByStatus();//更改按钮的文本
//显示加载区域
showProgress();
break;
case IStatus.STATUS_WAITING_READY: // 调用本类的start方法后,即输入START事件后,等待引擎准备完毕。
case IStatus.STATUS_READY: // 引擎准备完毕。
case IStatus.STATUS_SPEAKING:
case IStatus.STATUS_FINISHED: // 长语音情况
case IStatus.STATUS_RECOGNITION:
stopRecog();
status = IStatus.STATUS_STOPPED; // 引擎识别中
updateBtnTextByStatus();//更改按钮的文本
break;
case IStatus.STATUS_STOPPED: // 引擎识别中
cancelRecog();
status = IStatus.STATUS_NONE; // 识别结束,回到初始状态
updateBtnTextByStatus();//更改按钮的文本
break;
default:
break;
}
}
});
}
/**
* 显示加载进度区域,隐藏其他区域*/
private void showProgress(){
loadProgressBar.setVisibility(View.VISIBLE);
tv_tishi.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}
/**
* 显示文本提示区域,隐藏其他区域*/
private void showTishi(){
tv_tishi.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}
/**
* 显示语音结果区域,隐藏其他区域*/
private void showListView(){
result_list.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
tv_tishi.setVisibility(View.GONE);
}
//======================================语音相关代码==========================================
/**
* 初始化handler*/
private void initHandler(){
handler = new Handler() {
/*@param msg*/
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
handleMsg(msg);
}
};
Logger.setHandler(handler);
}
/**
* 在onCreate中调用。初始化识别控制类MyRecognizer
*/
protected void initRecog() {
StatusRecogListener listener = new MessageStatusRecogListener(handler);
myRecognizer = new MyRecognizer(mContext,listener);
status = IStatus.STATUS_NONE;//默认什么也没有做
}
/**
* 销毁时需要释放识别资源。
*/
protected void destroyRecog() {
myRecognizer.release();
Log.i(TAG, "destroyRecog");
}
/**
* 开始录音,点击“开始”按钮后调用。
*/
protected void startRecog() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);//是否保存音频
params.put(SpeechConstant.DISABLE_PUNCTUATION, false);//是否禁用标点符号,在选择输入法模型的前提下生效【不禁用的话,说完一段话,就自带标点符号】
params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);//暂时不知道什么意思
params.put(SpeechConstant.PID, 1536); // 普通话 search搜索模型,默认,适用于短句,无逗号,可以有语义
//params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音,建议搭配input输入法模型
myRecognizer.start(params);
}
/**
* 开始录音后,手动停止录音。SDK会识别在此过程中的录音。点击“停止”按钮后调用。
*/
private void stopRecog() {
myRecognizer.stop();
}
/**
* 开始录音后,取消这次录音。SDK会取消本次识别,回到原始状态。点击“取消”按钮后调用。
*/
private void cancelRecog() {
myRecognizer.cancel();
}
protected void handleMsg(Message msg) {
Log.e(TAG,"msg.what="+msg.what);
Log.e(TAG,"msg.obj.toString()="+msg.obj.toString());
Log.e(TAG,"msg.arg2="+msg.arg2);
switch (msg.what) { // 处理MessageStatusRecogListener中的状态回调
case IStatus.STATUS_FINISHED:
//识别结束时候的调用【判断显示结果列表区域还是提示区域】
if (msg.arg2 == 1) {
//解析json字符串
try {
JSONObject msgObj = new JSONObject(msg.obj.toString());
String error = msgObj.getString("error");
if(error.equals("0")){
//解析结果集合,展现列表
JSONObject origin_resultObj = msgObj.getJSONObject("origin_result");
JSONObject resultObj = origin_resultObj.getJSONObject("result");
JSONArray wordList = resultObj.getJSONArray("word");
initList(wordList);//初始化集合数据
showListView();
}else if(error.equals("7")){
tv_tishi.setText(TishiNoText);
showTishi();
}else{//应该根据不同的状态值,显示不同的提示
tv_tishi.setText(TishiNoText);
showTishi();
}
} catch (JSONException e) {
e.printStackTrace();
tv_tishi.setText(TishiNoText);
showTishi();
}
}else if(msg.arg2 == 0){//无网络的情况
//解析json字符串{"origin_result":{"sn":"","error":2,"desc":"Network is not available","sub_error":2100},"error":2,"desc":"Network is not available","sub_error":2100}
try {
JSONObject msgObj = new JSONObject(msg.obj.toString());
JSONObject origin_resultObj = msgObj.getJSONObject("origin_result");
String error = origin_resultObj.getString("error");
if(error.equals("2")){
//解析结果集合,展现列表
String desc = origin_resultObj.getString("desc");
Toast.makeText(mContext,desc,Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
status = msg.what;
updateBtnTextByStatus();
break;
case IStatus.STATUS_NONE:
case IStatus.STATUS_READY:
case IStatus.STATUS_SPEAKING:
case IStatus.STATUS_RECOGNITION:
status = msg.what;
updateBtnTextByStatus();
break;
default:
break;
}
}
/**更改按钮的文本*/
private void updateBtnTextByStatus() {
switch (status) {
case IStatus.STATUS_NONE:
btn_start.setText(BtnStartText);
btn_start.setEnabled(true);
break;
case IStatus.STATUS_WAITING_READY:
case IStatus.STATUS_READY:
case IStatus.STATUS_SPEAKING:
case IStatus.STATUS_RECOGNITION:
btn_start.setText(BtnStopText);
btn_start.setEnabled(true);
break;
case IStatus.STATUS_STOPPED:
btn_start.setText(BtnSearchingText);
btn_start.setEnabled(true);
break;
default:
break;
}
}
//========================================更改列表==========================
/**获取集合数据,并显示*/
private void initList(JSONArray wordList){
//先清空
if(resultWordList.size() > 0){
resultWordList.clear();
}
//再赋值
for(int i=0;i<wordList.length();i++){
String wordItem = "";
try {
wordItem = wordList.getString(i);
} catch (JSONException e) {
e.printStackTrace();
}
resultWordList.add(wordItem);
}
if(speechResultAdapter == null){
//设置适配器
speechResultAdapter = new SpeechResultAdapter(getActivity(), resultWordList);
result_list.setAdapter(speechResultAdapter);
//添加分割线
//设置添加删除动画
//调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
result_list.setSelected(true);
}else{
speechResultAdapter.notifyDataSetChanged();
}
speechResultAdapter.setOnItemClickLitener(new SpeechResultAdapter.OnItemClickLitener() {
@Override
public void onItemClick(int position) {
dismiss();
if(mOnResultListItemClickListener != null){
mOnResultListItemClickListener.onItemClick(resultWordList.get(position));
}
}
});
}
//=========================语音列表项的点击事件监听==============================
public static abstract interface OnResultListItemClickListener
{
//语音结果列表项的点击事件接口
public abstract void onItemClick(String title);
}
private OnResultListItemClickListener mOnResultListItemClickListener;
public void seOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener)
{
this.mOnResultListItemClickListener = mOnResultListItemClickListener;
}
}
4、如果想要使用长语音功能,请参考SpeechLongBottomSheetDialog.java文件
package com.why.project.baiduspeech.dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v4.app.DialogFragment;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.baidu.speech.asr.SpeechConstant;
import com.baidu.speech.utils.LogUtil;
import com.why.project.baiduspeech.R;
import com.why.project.baiduspeech.control.MyRecognizer;
import com.why.project.baiduspeech.recognization.IStatus;
import com.why.project.baiduspeech.recognization.MessageStatusRecogListener;
import com.why.project.baiduspeech.recognization.StatusRecogListener;
import com.why.project.baiduspeech.util.Logger;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Created by HaiyuKing
* Used 普通话 input输入法模型,适用于长句及长语音,有逗号分割,无语义【基本上和SpeechBottomSheetDialog代码相同】
*/
public class SpeechLongBottomSheetDialog extends DialogFragment {
private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName();
private Context mContext;
/**View实例*/
private View myView;
private ImageView img_close;
private ProgressBar loadProgressBar;
private TextView tv_tishi;
private RecyclerView result_list;
private Button btn_start;
private ArrayList<String> resultWordList;
private SpeechResultAdapter speechResultAdapter;
private String BtnStartText = "按一下开始听音";
private String BtnStopText = "按一下结束听音";
private String BtnSearchingText = "正在识别";
private String TishiNoText = "没听清,请重说一遍";
/**识别控制器,使用MyRecognizer控制识别的流程*/
protected MyRecognizer myRecognizer;
/**控制UI按钮的状态*/
protected int status;
protected Handler handler;
public static SpeechLongBottomSheetDialog getInstance(Context mContext)
{
SpeechLongBottomSheetDialog speechLongBottomSheetDialog = new SpeechLongBottomSheetDialog();
speechLongBottomSheetDialog.mContext = mContext;
return speechLongBottomSheetDialog;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//设置背景为透明,并且没有标题
myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false);
return myView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
initHandler();//初始化handler
initRecog();//初始化语音
initViews();
initDatas();
initEvents();
}
/**
* 设置宽度和高度值,以及打开的动画效果
*/
@Override
public void onStart() {
super.onStart();
//设置对话框的宽高,必须在onStart中
DisplayMetrics metrics = new DisplayMetrics();
this.getActivity().getWindowManager().getDefaultDisplay().getMetrics(metrics);
Window window = this.getDialog().getWindow();
window.setLayout(metrics.widthPixels, this.getDialog().getWindow().getAttributes().height);
window.setGravity(Gravity.BOTTOM);//设置在底部
//打开的动画效果
//设置dialog的 进出 动画
getDialog().getWindow().setWindowAnimations(R.style.speechbottomsheetdialog_animation);
}
@Override
public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
LogUtil.w(TAG,"{onDismiss}");
//当对话框消失的时候统一执行销毁语音功能
destroyRecog();//销毁语音
}
private void initViews() {
img_close = (ImageView) myView.findViewById(R.id.img_close);
loadProgressBar = (ProgressBar) myView.findViewById(R.id.loadProgressBar);
tv_tishi = (TextView) myView.findViewById(R.id.tv_tishi);
result_list = (RecyclerView) myView.findViewById(R.id.result_list);
btn_start = (Button) myView.findViewById(R.id.btn_start);
}
/**初始化数据*/
private void initDatas() {
resultWordList = new ArrayList<String>();
speechResultAdapter = null;
//设置布局管理器
LinearLayoutManager linerLayoutManager = new LinearLayoutManager(getActivity());
result_list.setLayoutManager(linerLayoutManager);
btn_start.setText(BtnStartText);//显示文字,和下面的二选一即可 why
//可以设置为打开后自动识别语音
/*startRecog();
showProgress();*/
}
private void initEvents() {
//关闭图标的点击事件
img_close.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
//按钮的点击事件
btn_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
switch (status) {
case IStatus.STATUS_NONE: // 初始状态
startRecog();
status = IStatus.STATUS_WAITING_READY;
updateBtnTextByStatus();//更改按钮的文本
//显示加载区域
showProgress();
break;
case IStatus.STATUS_WAITING_READY: // 调用本类的start方法后,即输入START事件后,等待引擎准备完毕。
case IStatus.STATUS_READY: // 引擎准备完毕。
case IStatus.STATUS_SPEAKING:
case IStatus.STATUS_FINISHED: // 长语音情况
case IStatus.STATUS_RECOGNITION:
stopRecog();
status = IStatus.STATUS_STOPPED; // 引擎识别中
updateBtnTextByStatus();//更改按钮的文本
//对于长语音来讲,需要手动执行代码,否则还得点击一次才能取消why
btn_start.callOnClick();
break;
case IStatus.STATUS_STOPPED: // 引擎识别中
cancelRecog();
hiddenAll();//隐藏加载区域why
status = IStatus.STATUS_NONE; // 识别结束,回到初始状态
updateBtnTextByStatus();//更改按钮的文本
break;
default:
break;
}
}
});
}
/**
* 显示加载进度区域,隐藏其他区域*/
private void showProgress(){
loadProgressBar.setVisibility(View.VISIBLE);
tv_tishi.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}
/**
* 显示文本提示区域,隐藏其他区域*/
private void showTishi(){
tv_tishi.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}
/**
* 显示语音结果区域,隐藏其他区域*/
private void showListView(){
result_list.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
tv_tishi.setVisibility(View.GONE);
}
/**隐藏所有的区域【主要用于长语音】why*/
private void hiddenAll(){
result_list.setVisibility(View.GONE);
loadProgressBar.setVisibility(View.GONE);
tv_tishi.setVisibility(View.GONE);
}
//======================================语音相关代码==========================================
/**
* 初始化handler*/
private void initHandler(){
handler = new Handler() {
/*@param msg*/
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
handleMsg(msg);
}
};
Logger.setHandler(handler);
}
/**
* 在onCreate中调用。初始化识别控制类MyRecognizer
*/
protected void initRecog() {
StatusRecogListener listener = new MessageStatusRecogListener(handler);
myRecognizer = new MyRecognizer(mContext,listener);
status = IStatus.STATUS_NONE;//默认什么也没有做
}
/**
* 销毁时需要释放识别资源。
*/
protected void destroyRecog() {
myRecognizer.release();
Log.i(TAG, "destroyRecog");
}
/**
* 开始录音,点击“开始”按钮后调用。
*/
protected void startRecog() {
Map<String, Object> params = new LinkedHashMap<String, Object>();
params.put(SpeechConstant.ACCEPT_AUDIO_DATA, false);//是否保存音频
params.put(SpeechConstant.DISABLE_PUNCTUATION, false);//是否禁用标点符号,在选择输入法模型的前提下生效【不禁用的话,说完一段话,就自带标点符号】
params.put(SpeechConstant.ACCEPT_AUDIO_VOLUME, false);//暂时不知道什么意思
//下面的1936和1537选择其中一个 why
//params.put(SpeechConstant.PID, 1936); // 普通话 far,远场模型,高级,适用于音源离麦克风较远(>1m)的录音,有逗号分隔,可以有语义
params.put(SpeechConstant.PID, 1537); // 普通话 input输入法模型,适用于长句及长语音,有逗号分割,无语义
params.put(SpeechConstant.VAD_ENDPOINT_TIMEOUT, 0); // 长语音,建议搭配input输入法模型
myRecognizer.start(params);
}
/**
* 开始录音后,手动停止录音。SDK会识别在此过程中的录音。点击“停止”按钮后调用。
*/
private void stopRecog() {
myRecognizer.stop();
}
/**
* 开始录音后,取消这次录音。SDK会取消本次识别,回到原始状态。点击“取消”按钮后调用。
*/
private void cancelRecog() {
myRecognizer.cancel();
}
protected void handleMsg(Message msg) {
switch (msg.what) { // 处理MessageStatusRecogListener中的状态回调
case IStatus.STATUS_FINISHED:
//识别结束时候的调用【判断显示结果列表区域还是提示区域】
if (msg.arg2 == 1) {
//解析json字符串
try {
JSONObject msgObj = new JSONObject(msg.obj.toString());
String error = msgObj.getString("error");
if(error.equals("0")){
//直接输入到文本框中 why
JSONArray recognitionObj = msgObj.getJSONArray("results_recognition");
String result = recognitionObj.getString(0);
if(mOnResultListItemClickListener != null){
mOnResultListItemClickListener.onItemClick(result);
}
}else if(error.equals("7")){
tv_tishi.setText(TishiNoText);
showTishi();
}else{//应该根据不同的状态值,显示不同的提示
tv_tishi.setText(TishiNoText);
showTishi();
}
} catch (JSONException e) {
e.printStackTrace();
tv_tishi.setText(TishiNoText);
showTishi();
}
}else if(msg.arg2 == 0){//无网络的情况
//解析json字符串{"origin_result":{"sn":"","error":2,"desc":"Network is not available","sub_error":2100},"error":2,"desc":"Network is not available","sub_error":2100}
try {
JSONObject msgObj = new JSONObject(msg.obj.toString());
JSONObject origin_resultObj = msgObj.getJSONObject("origin_result");
String error = origin_resultObj.getString("error");
if(error.equals("2")){
//解析结果集合,展现列表
String desc = origin_resultObj.getString("desc");
Toast.makeText(mContext,desc,Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
status = msg.what;
updateBtnTextByStatus();
break;
case IStatus.STATUS_NONE:
case IStatus.STATUS_READY:
case IStatus.STATUS_SPEAKING:
case IStatus.STATUS_RECOGNITION:
status = msg.what;
updateBtnTextByStatus();
break;
default:
break;
}
}
/**更改按钮的文本*/
private void updateBtnTextByStatus() {
switch (status) {
case IStatus.STATUS_NONE:
btn_start.setText(BtnStartText);
btn_start.setEnabled(true);
break;
case IStatus.STATUS_WAITING_READY:
case IStatus.STATUS_READY:
case IStatus.STATUS_SPEAKING:
case IStatus.STATUS_RECOGNITION:
btn_start.setText(BtnStopText);
btn_start.setEnabled(true);
break;
case IStatus.STATUS_STOPPED:
btn_start.setText(BtnSearchingText);
btn_start.setEnabled(true);
break;
default:
break;
}
}
//========================================更改列表==========================
/**获取集合数据,并显示*/
private void initList(JSONArray wordList){
//先清空
if(resultWordList.size() > 0){
resultWordList.clear();
}
//再赋值
for(int i=0;i<wordList.length();i++){
String wordItem = "";
try {
wordItem = wordList.getString(i);
} catch (JSONException e) {
e.printStackTrace();
}
resultWordList.add(wordItem);
}
if(speechResultAdapter == null){
//设置适配器
speechResultAdapter = new SpeechResultAdapter(getActivity(), resultWordList);
result_list.setAdapter(speechResultAdapter);
//添加分割线
//设置添加删除动画
//调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
result_list.setSelected(true);
}else{
speechResultAdapter.notifyDataSetChanged();
}
speechResultAdapter.setOnItemClickLitener(new SpeechResultAdapter.OnItemClickLitener() {
@Override
public void onItemClick(int position) {
dismiss();
if(mOnResultListItemClickListener != null){
mOnResultListItemClickListener.onItemClick(resultWordList.get(position));
}
}
});
}
//=========================语音列表项的点击事件监听==============================
public static abstract interface OnResultListItemClickListener
{
//语音结果列表项的点击事件接口
public abstract void onItemClick(String title);
}
private OnResultListItemClickListener mOnResultListItemClickListener;
public void seOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener)
{
this.mOnResultListItemClickListener = mOnResultListItemClickListener;
}
}
三 使用方法
因为要导入外部库文件,将项目中的modular变成libraries引用依赖,所以需要配置相应权限。
在Activity中调用
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.why.project.baiduspeechdemo.MainActivity">
<TextView
android:id="@+id/tv_result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.448"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.325"/>
<Button
android:id="@+id/btn_openSpeechDialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="32dp"
android:text="打开搜索模型语音识别对话框"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.419"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:id="@+id/btn_openSpeechLongDialog"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:text="打开input输入模型语音识别对话框"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.4"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_openSpeechDialog"/>
</android.support.constraint.ConstraintLayout>
activity_main.xml
package com.why.project.baiduspeechdemo;
import android.Manifest;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.tbruyelle.rxpermissions2.RxPermissions;
import com.why.project.baiduspeech.dialog.SpeechBottomSheetDialog;
import com.why.project.baiduspeech.dialog.SpeechLongBottomSheetDialog;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private Button mOpenSpeechDialogBtn;
private Button mOpenSpeechLongDialogBtn;
private TextView mResultTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
onePermission();
initViews();
initEvents();
}
private void initViews() {
mOpenSpeechDialogBtn = findViewById(R.id.btn_openSpeechDialog);
mOpenSpeechLongDialogBtn = findViewById(R.id.btn_openSpeechLongDialog);
mResultTv = findViewById(R.id.tv_result);
}
private void initEvents() {
mOpenSpeechDialogBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//打开百度语音对话框
SpeechBottomSheetDialog speechBottomSheetDialog = SpeechBottomSheetDialog.getInstance(MainActivity.this);
speechBottomSheetDialog.seOnResultListItemClickListener(new SpeechBottomSheetDialog.OnResultListItemClickListener() {
@Override
public void onItemClick(String title) {
//填充到输入框中
mResultTv.setText(title);
}
});
speechBottomSheetDialog.show(getSupportFragmentManager(), TAG);
}
});
mOpenSpeechLongDialogBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//打开百度语音对话框
SpeechLongBottomSheetDialog speechLongBottomSheetDialog = SpeechLongBottomSheetDialog.getInstance(MainActivity.this);
speechLongBottomSheetDialog.seOnResultListItemClickListener(new SpeechLongBottomSheetDialog.OnResultListItemClickListener() {
@Override
public void onItemClick(String title) {
//填充到输入框中
mResultTv.setText(mResultTv.getText()+title);
}
});
speechLongBottomSheetDialog.show(getSupportFragmentManager(), TAG);
}
});
}
/**只有一个运行时权限申请的情况*/
private void onePermission(){
RxPermissions rxPermissions = new RxPermissions(MainActivity.this); // where this is an Activity instance
rxPermissions.request(Manifest.permission.RECORD_AUDIO,
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.WRITE_EXTERNAL_STORAGE) //权限名称,多个权限之间逗号分隔开
.subscribe(new Consumer<Boolean>() {
@Override
public void accept(Boolean granted) throws Exception {
Log.e(TAG, "{accept}granted=" + granted);//执行顺序——1【多个权限的情况,只有所有的权限均允许的情况下granted==true】
if (granted) { // 在android 6.0之前会默认返回true
// 已经获取权限
} else {
// 未获取权限
Toast.makeText(MainActivity.this, "您没有授权该权限,请在设置中打开授权", Toast.LENGTH_SHORT).show();
}
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
}
}, new Action() {
@Override
public void run() throws Exception {
Log.e(TAG,"{run}");//执行顺序——2
}
});
}
}
参考资料
http://ai.baidu.com/tech/speech
集成指南
备注
1 引入外部项目时不要忘记需要在setting.gradle中配置