版权声明:本文为HaiyuKing原创文章,转载请注明出处!
前言
本Demo将百度语音SDK(其中一部分功能)和自定义的UI对话框封装到一个module中,便于后续的SDK版本更新以及调用。
本Demo使用的百度语音SDK版本是audiobd_speech_sdk_asr_v3.0.7.3_bdasr_20180313_726f26e。
本Demo中使用的appkey已失效,请自行创建应用,使用新的appkey。
效果图
前提
(1)新建项目(获取包名)
(2)在百度AI开发平台上创建应用,获取API Key及Secret Key
1、成为开发者
参考《接入指南》
2、创建应用
2.1、点击百度AI开放平台导航右侧的控制台,选择需要使用的AI服务项【这里选择语音技术】。
2.2、创建应用
2.3、填写应用信息
2.4、创建成功
2.5、应用列表
(3)下载SDK
3.1、管理应用
3.2、下载SDK
代码分析
普通话 search搜索模型:参考SpeechBottomSheetDialog.java类
普通话 input输入法模型,适用于长句及长语音,有逗号分割,无语义:参考SpeechLongBottomSheetDialog.java类
注意:关于语音识别状态维护,API调用的代码,是自己根据官网demo的理解进行整理的,可能有所偏颇,仅供参考。【希望官网demo可以添加百度APP的语音对话框效果就好了】
使用步骤
一、项目组织结构图
注意事项:
1、 导入类文件后需要change包名以及重新import R文件路径
2、 Values目录下的文件(strings.xml、dimens.xml、colors.xml等),如果项目中存在,则复制里面的内容,不要整个覆盖
二、导入步骤
(1)新建module,命名为BaiduSpeech
(2)在baiduspeech的AndroidManifest.xml中添加以下代码
从官方demo的AndroidManifest.xml中找到如下信息,然后复制到您自己的同名文件中。此处需要您复制1、权限2、官网申请的应用信息3、SDK的Service。
注意:此时标签那里是红色错误的标记,暂时不用管,导入jar、so文件后编译下就正常了。
(3)复制jar 文件
将官方demo中的app\libs\bdasr_V3_20180320_9066860.jar复制进您项目的同名目录。
在build.gradle中确认是否含有以下红色标记的代码
(4)复制jni库的so文件
复制官方demo中 app\src\main\jniLibs 至项目的同名目录。
这个时候编译下,就会发现AndroidManifest.xml文件的标签那里正常了。
(5)在官方demo中找到下面的文件复制到项目中(按照下面的包名进行查找)【注意,复制过来后,需要重新import 相关类】
(6)修改MessageStatusRecogListener.java文件【根据实际情况进行修改】
packagecom.why.project.baiduspeech.recognization;importandroid.os.Handler;importandroid.os.Message;importandroid.util.Log;/*** Created by fujiayi on 2017/6/16.*/
public class MessageStatusRecogListener extendsStatusRecogListener {privateHandler handler;private longspeechEndTime;private boolean needTime = true;private static final String TAG = "MesStatusRecogListener";publicMessageStatusRecogListener(Handler handler) {this.handler =handler;
}
@Overridepublic voidonAsrReady() {super.onAsrReady();
sendStatusMessage("引擎就绪,可以开始说话。");
}
@Overridepublic voidonAsrBegin() {super.onAsrBegin();
sendStatusMessage("检测到用户说话");
}
@Overridepublic voidonAsrEnd() {super.onAsrEnd();
speechEndTime=System.currentTimeMillis();
sendMessage("检测到用户说话结束");
}
@Overridepublic voidonAsrPartialResult(String[] results, RecogResult recogResult) {
sendStatusMessage("临时识别结果,结果是“" + results[0] + "”;原始json:" +recogResult.getOrigalJson());super.onAsrPartialResult(results, recogResult);
}
@Overridepublic voidonAsrFinalResult(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);
}
@Overridepublic void onAsrFinishError(int errorCode, intsubErrorCode, 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;
}
@Overridepublic voidonAsrOnlineNluResult(String nluResult) {super.onAsrOnlineNluResult(nluResult);if (!nluResult.isEmpty()) {
sendStatusMessage("原始语义识别结果json:" +nluResult);
}
}
@Overridepublic voidonAsrFinish(RecogResult recogResult) {super.onAsrFinish(recogResult);
sendStatusMessage("识别一段话结束。如果是长语音的情况会继续识别下段话。");
}/*** 长语音识别结束*/@Overridepublic voidonAsrLongFinish() {super.onAsrLongFinish();
sendStatusMessage("长语音识别结束。");
}/*** 使用离线命令词时,有该回调说明离线语法资源加载成功*/@Overridepublic voidonOfflineLoaded() {
sendStatusMessage("【重要】asr.loaded:离线资源加载成功。没有此回调可能离线语法功能不能使用。");
}/*** 使用离线命令词时,有该回调说明离线语法资源加载成功*/@Overridepublic voidonOfflineUnLoaded() {
sendStatusMessage(" 离线资源卸载成功。");
}
@Overridepublic voidonAsrExit() {super.onAsrExit();
sendStatusMessage("识别引擎结束并空闲中");
}private voidsendStatusMessage(String message) {
sendMessage(message, status);
}private voidsendMessage(String message) {
sendMessage(message, WHAT_MESSAGE_STATUS);
}private void sendMessage(String message, intwhat) {
sendMessage(message, what,false);
}private void sendMessage(String message, int what, booleanhighlight) {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【根据实际情况自行修改UI布局】
1、在baiduspeech的build.gradle中引用recyclerview【版本号和项目的appcompat保持一致】【因为demo中用到了】
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中百度语音的相关代码
packagecom.why.project.baiduspeech.dialog;importandroid.content.Context;importandroid.content.DialogInterface;importandroid.graphics.drawable.ColorDrawable;importandroid.os.Bundle;importandroid.os.Handler;importandroid.os.Message;importandroid.support.v4.app.DialogFragment;importandroid.support.v7.widget.LinearLayoutManager;importandroid.support.v7.widget.RecyclerView;importandroid.util.DisplayMetrics;importandroid.util.Log;importandroid.view.Gravity;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.view.ViewGroup;importandroid.view.Window;importandroid.widget.Button;importandroid.widget.ImageView;importandroid.widget.ProgressBar;importandroid.widget.TextView;importandroid.widget.Toast;importcom.baidu.speech.asr.SpeechConstant;importcom.baidu.speech.utils.LogUtil;importcom.why.project.baiduspeech.R;importcom.why.project.baiduspeech.control.MyRecognizer;importcom.why.project.baiduspeech.recognization.IStatus;importcom.why.project.baiduspeech.recognization.MessageStatusRecogListener;importcom.why.project.baiduspeech.recognization.StatusRecogListener;importcom.why.project.baiduspeech.util.Logger;importorg.json.JSONArray;importorg.json.JSONException;importorg.json.JSONObject;importjava.util.ArrayList;importjava.util.LinkedHashMap;importjava.util.Map;/*** Created by HaiyuKing
* Used 语音识别底部对话框*/
public class SpeechBottomSheetDialog extendsDialogFragment {private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName();privateContext mContext;/**View实例*/
privateView myView;privateImageView img_close;privateProgressBar loadProgressBar;privateTextView tv_tishi;privateRecyclerView result_list;privateButton btn_start;private ArrayListresultWordList;privateSpeechResultAdapter speechResultAdapter;private String BtnStartText = "按一下开始听音";private String BtnStopText = "按一下结束听音";private String BtnSearchingText = "正在识别";private String TishiNoText = "没听清,请重说一遍";/**识别控制器,使用MyRecognizer控制识别的流程*/
protectedMyRecognizer myRecognizer;/**控制UI按钮的状态*/
protected intstatus;protectedHandler handler;public staticSpeechBottomSheetDialog getInstance(Context mContext)
{
SpeechBottomSheetDialog speechBottomSheetDialog= newSpeechBottomSheetDialog();
speechBottomSheetDialog.mContext=mContext;returnspeechBottomSheetDialog;
}publicView onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//设置背景为透明,并且没有标题
myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false);returnmyView;
}
@Overridepublic voidonActivityCreated(Bundle savedInstanceState) {//TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
initHandler();//初始化handler
initRecog();//初始化语音
initViews();
initDatas();
initEvents();
}/*** 设置宽度和高度值,以及打开的动画效果*/@Overridepublic voidonStart() {super.onStart();//设置对话框的宽高,必须在onStart中
DisplayMetrics metrics = newDisplayMetrics();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);
}
@Overridepublic voidonDismiss(DialogInterface dialog) {super.onDismiss(dialog);
LogUtil.w(TAG,"{onDismiss}");//当对话框消失的时候统一执行销毁语音功能
destroyRecog();//销毁语音
}private voidinitViews() {
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 voidinitDatas() {
resultWordList= new ArrayList();
speechResultAdapter= null;//设置布局管理器
LinearLayoutManager linerLayoutManager = newLinearLayoutManager(getActivity());
result_list.setLayoutManager(linerLayoutManager);//可以设置为打开后自动识别语音startRecog();
showProgress();
}private voidinitEvents() {//关闭图标的点击事件
img_close.setOnClickListener(newView.OnClickListener() {
@Overridepublic voidonClick(View v) {
dismiss();
}
});//按钮的点击事件
btn_start.setOnClickListener(newView.OnClickListener() {
@Overridepublic voidonClick(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: //引擎准备完毕。
caseIStatus.STATUS_SPEAKING:case IStatus.STATUS_FINISHED: //长语音情况
caseIStatus.STATUS_RECOGNITION:
stopRecog();
status= IStatus.STATUS_STOPPED; //引擎识别中
updateBtnTextByStatus();//更改按钮的文本
break;case IStatus.STATUS_STOPPED: //引擎识别中
cancelRecog();
status= IStatus.STATUS_NONE; //识别结束,回到初始状态
updateBtnTextByStatus();//更改按钮的文本
break;default:break;
}
}
});
}/*** 显示加载进度区域,隐藏其他区域*/
private voidshowProgress(){
loadProgressBar.setVisibility(View.VISIBLE);
tv_tishi.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}/*** 显示文本提示区域,隐藏其他区域*/
private voidshowTishi(){
tv_tishi.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}/*** 显示语音结果区域,隐藏其他区域*/
private voidshowListView(){
result_list.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
tv_tishi.setVisibility(View.GONE);
}//======================================语音相关代码==========================================
/*** 初始化handler*/
private voidinitHandler(){
handler= newHandler() {/*@param msg*/@Overridepublic voidhandleMessage(Message msg) {super.handleMessage(msg);
handleMsg(msg);
}
};
Logger.setHandler(handler);
}/*** 在onCreate中调用。初始化识别控制类MyRecognizer*/
protected voidinitRecog() {
StatusRecogListener listener= newMessageStatusRecogListener(handler);
myRecognizer= newMyRecognizer(mContext,listener);
status= IStatus.STATUS_NONE;//默认什么也没有做
}/*** 销毁时需要释放识别资源。*/
protected voiddestroyRecog() {
myRecognizer.release();
Log.i(TAG,"destroyRecog");
}/*** 开始录音,点击“开始”按钮后调用。*/
protected voidstartRecog() {
Map params = new LinkedHashMap();
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 voidstopRecog() {
myRecognizer.stop();
}/*** 开始录音后,取消这次录音。SDK会取消本次识别,回到原始状态。点击“取消”按钮后调用。*/
private voidcancelRecog() {
myRecognizer.cancel();
}protected voidhandleMsg(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中的状态回调
caseIStatus.STATUS_FINISHED://识别结束时候的调用【判断显示结果列表区域还是提示区域】
if (msg.arg2 == 1) {//解析json字符串
try{
JSONObject msgObj= newJSONObject(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= newJSONObject(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;caseIStatus.STATUS_NONE:caseIStatus.STATUS_READY:caseIStatus.STATUS_SPEAKING:caseIStatus.STATUS_RECOGNITION:
status=msg.what;
updateBtnTextByStatus();break;default:break;
}
}/**更改按钮的文本*/
private voidupdateBtnTextByStatus() {switch(status) {caseIStatus.STATUS_NONE:
btn_start.setText(BtnStartText);
btn_start.setEnabled(true);break;caseIStatus.STATUS_WAITING_READY:caseIStatus.STATUS_READY:caseIStatus.STATUS_SPEAKING:caseIStatus.STATUS_RECOGNITION:
btn_start.setText(BtnStopText);
btn_start.setEnabled(true);break;caseIStatus.STATUS_STOPPED:
btn_start.setText(BtnSearchingText);
btn_start.setEnabled(true);break;default:break;
}
}//========================================更改列表==========================
/**获取集合数据,并显示*/
private voidinitList(JSONArray wordList){//先清空
if(resultWordList.size() > 0){
resultWordList.clear();
}//再赋值
for(int i=0;i
String wordItem= "";try{
wordItem=wordList.getString(i);
}catch(JSONException e) {
e.printStackTrace();
}
resultWordList.add(wordItem);
}if(speechResultAdapter == null){//设置适配器
speechResultAdapter = newSpeechResultAdapter(getActivity(), resultWordList);
result_list.setAdapter(speechResultAdapter);//添加分割线//设置添加删除动画//调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
result_list.setSelected(true);
}else{
speechResultAdapter.notifyDataSetChanged();
}
speechResultAdapter.setOnItemClickLitener(newSpeechResultAdapter.OnItemClickLitener() {
@Overridepublic void onItemClick(intposition) {
dismiss();if(mOnResultListItemClickListener != null){
mOnResultListItemClickListener.onItemClick(resultWordList.get(position));
}
}
});
}//=========================语音列表项的点击事件监听==============================
public static abstract interfaceOnResultListItemClickListener
{//语音结果列表项的点击事件接口
public abstract voidonItemClick(String title);
}privateOnResultListItemClickListener mOnResultListItemClickListener;public voidseOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener)
{this.mOnResultListItemClickListener =mOnResultListItemClickListener;
}
}
4、如果想要使用长语音功能,请参考SpeechLongBottomSheetDialog.java文件
packagecom.why.project.baiduspeech.dialog;importandroid.content.Context;importandroid.content.DialogInterface;importandroid.graphics.drawable.ColorDrawable;importandroid.os.Bundle;importandroid.os.Handler;importandroid.os.Message;importandroid.support.v4.app.DialogFragment;importandroid.support.v7.widget.LinearLayoutManager;importandroid.support.v7.widget.RecyclerView;importandroid.util.DisplayMetrics;importandroid.util.Log;importandroid.view.Gravity;importandroid.view.LayoutInflater;importandroid.view.View;importandroid.view.ViewGroup;importandroid.view.Window;importandroid.widget.Button;importandroid.widget.ImageView;importandroid.widget.ProgressBar;importandroid.widget.TextView;importandroid.widget.Toast;importcom.baidu.speech.asr.SpeechConstant;importcom.baidu.speech.utils.LogUtil;importcom.why.project.baiduspeech.R;importcom.why.project.baiduspeech.control.MyRecognizer;importcom.why.project.baiduspeech.recognization.IStatus;importcom.why.project.baiduspeech.recognization.MessageStatusRecogListener;importcom.why.project.baiduspeech.recognization.StatusRecogListener;importcom.why.project.baiduspeech.util.Logger;importorg.json.JSONArray;importorg.json.JSONException;importorg.json.JSONObject;importjava.util.ArrayList;importjava.util.LinkedHashMap;importjava.util.Map;/*** Created by HaiyuKing
* Used 普通话 input输入法模型,适用于长句及长语音,有逗号分割,无语义【基本上和SpeechBottomSheetDialog代码相同】*/
public class SpeechLongBottomSheetDialog extendsDialogFragment {private static final String TAG = SpeechBottomSheetDialog.class.getSimpleName();privateContext mContext;/**View实例*/
privateView myView;privateImageView img_close;privateProgressBar loadProgressBar;privateTextView tv_tishi;privateRecyclerView result_list;privateButton btn_start;private ArrayListresultWordList;privateSpeechResultAdapter speechResultAdapter;private String BtnStartText = "按一下开始听音";private String BtnStopText = "按一下结束听音";private String BtnSearchingText = "正在识别";private String TishiNoText = "没听清,请重说一遍";/**识别控制器,使用MyRecognizer控制识别的流程*/
protectedMyRecognizer myRecognizer;/**控制UI按钮的状态*/
protected intstatus;protectedHandler handler;public staticSpeechLongBottomSheetDialog getInstance(Context mContext)
{
SpeechLongBottomSheetDialog speechLongBottomSheetDialog= newSpeechLongBottomSheetDialog();
speechLongBottomSheetDialog.mContext=mContext;returnspeechLongBottomSheetDialog;
}publicView onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
getDialog().getWindow().setBackgroundDrawable(new ColorDrawable(0));//设置背景为透明,并且没有标题
myView = inflater.inflate(R.layout.dialog_bottomsheet_speech, container, false);returnmyView;
}
@Overridepublic voidonActivityCreated(Bundle savedInstanceState) {//TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
initHandler();//初始化handler
initRecog();//初始化语音
initViews();
initDatas();
initEvents();
}/*** 设置宽度和高度值,以及打开的动画效果*/@Overridepublic voidonStart() {super.onStart();//设置对话框的宽高,必须在onStart中
DisplayMetrics metrics = newDisplayMetrics();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);
}
@Overridepublic voidonDismiss(DialogInterface dialog) {super.onDismiss(dialog);
LogUtil.w(TAG,"{onDismiss}");//当对话框消失的时候统一执行销毁语音功能
destroyRecog();//销毁语音
}private voidinitViews() {
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 voidinitDatas() {
resultWordList= new ArrayList();
speechResultAdapter= null;//设置布局管理器
LinearLayoutManager linerLayoutManager = newLinearLayoutManager(getActivity());
result_list.setLayoutManager(linerLayoutManager);
btn_start.setText(BtnStartText);//显示文字,和下面的二选一即可 why//可以设置为打开后自动识别语音
/*startRecog();
showProgress();*/}private voidinitEvents() {//关闭图标的点击事件
img_close.setOnClickListener(newView.OnClickListener() {
@Overridepublic voidonClick(View v) {
dismiss();
}
});//按钮的点击事件
btn_start.setOnClickListener(newView.OnClickListener() {
@Overridepublic voidonClick(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: //引擎准备完毕。
caseIStatus.STATUS_SPEAKING:case IStatus.STATUS_FINISHED: //长语音情况
caseIStatus.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 voidshowProgress(){
loadProgressBar.setVisibility(View.VISIBLE);
tv_tishi.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}/*** 显示文本提示区域,隐藏其他区域*/
private voidshowTishi(){
tv_tishi.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
result_list.setVisibility(View.GONE);
}/*** 显示语音结果区域,隐藏其他区域*/
private voidshowListView(){
result_list.setVisibility(View.VISIBLE);
loadProgressBar.setVisibility(View.GONE);
tv_tishi.setVisibility(View.GONE);
}/**隐藏所有的区域【主要用于长语音】why*/
private voidhiddenAll(){
result_list.setVisibility(View.GONE);
loadProgressBar.setVisibility(View.GONE);
tv_tishi.setVisibility(View.GONE);
}//======================================语音相关代码==========================================
/*** 初始化handler*/
private voidinitHandler(){
handler= newHandler() {/*@param msg*/@Overridepublic voidhandleMessage(Message msg) {super.handleMessage(msg);
handleMsg(msg);
}
};
Logger.setHandler(handler);
}/*** 在onCreate中调用。初始化识别控制类MyRecognizer*/
protected voidinitRecog() {
StatusRecogListener listener= newMessageStatusRecogListener(handler);
myRecognizer= newMyRecognizer(mContext,listener);
status= IStatus.STATUS_NONE;//默认什么也没有做
}/*** 销毁时需要释放识别资源。*/
protected voiddestroyRecog() {
myRecognizer.release();
Log.i(TAG,"destroyRecog");
}/*** 开始录音,点击“开始”按钮后调用。*/
protected voidstartRecog() {
Map params = new LinkedHashMap();
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 voidstopRecog() {
myRecognizer.stop();
}/*** 开始录音后,取消这次录音。SDK会取消本次识别,回到原始状态。点击“取消”按钮后调用。*/
private voidcancelRecog() {
myRecognizer.cancel();
}protected voidhandleMsg(Message msg) {switch (msg.what) { //处理MessageStatusRecogListener中的状态回调
caseIStatus.STATUS_FINISHED://识别结束时候的调用【判断显示结果列表区域还是提示区域】
if (msg.arg2 == 1) {//解析json字符串
try{
JSONObject msgObj= newJSONObject(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= newJSONObject(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;caseIStatus.STATUS_NONE:caseIStatus.STATUS_READY:caseIStatus.STATUS_SPEAKING:caseIStatus.STATUS_RECOGNITION:
status=msg.what;
updateBtnTextByStatus();break;default:break;
}
}/**更改按钮的文本*/
private voidupdateBtnTextByStatus() {switch(status) {caseIStatus.STATUS_NONE:
btn_start.setText(BtnStartText);
btn_start.setEnabled(true);break;caseIStatus.STATUS_WAITING_READY:caseIStatus.STATUS_READY:caseIStatus.STATUS_SPEAKING:caseIStatus.STATUS_RECOGNITION:
btn_start.setText(BtnStopText);
btn_start.setEnabled(true);break;caseIStatus.STATUS_STOPPED:
btn_start.setText(BtnSearchingText);
btn_start.setEnabled(true);break;default:break;
}
}//========================================更改列表==========================
/**获取集合数据,并显示*/
private voidinitList(JSONArray wordList){//先清空
if(resultWordList.size() > 0){
resultWordList.clear();
}//再赋值
for(int i=0;i
String wordItem= "";try{
wordItem=wordList.getString(i);
}catch(JSONException e) {
e.printStackTrace();
}
resultWordList.add(wordItem);
}if(speechResultAdapter == null){//设置适配器
speechResultAdapter = newSpeechResultAdapter(getActivity(), resultWordList);
result_list.setAdapter(speechResultAdapter);//添加分割线//设置添加删除动画//调用ListView的setSelected(!ListView.isSelected())方法,这样就能及时刷新布局
result_list.setSelected(true);
}else{
speechResultAdapter.notifyDataSetChanged();
}
speechResultAdapter.setOnItemClickLitener(newSpeechResultAdapter.OnItemClickLitener() {
@Overridepublic void onItemClick(intposition) {
dismiss();if(mOnResultListItemClickListener != null){
mOnResultListItemClickListener.onItemClick(resultWordList.get(position));
}
}
});
}//=========================语音列表项的点击事件监听==============================
public static abstract interfaceOnResultListItemClickListener
{//语音结果列表项的点击事件接口
public abstract voidonItemClick(String title);
}privateOnResultListItemClickListener mOnResultListItemClickListener;public voidseOnResultListItemClickListener(OnResultListItemClickListener mOnResultListItemClickListener)
{this.mOnResultListItemClickListener =mOnResultListItemClickListener;
}
}
三、使用方法
(1)因为需要使用到运行时权限,所以参考《Android6.0运行时权限(基于RxPermission开源库)》在APP的build.gradle中引入第三方库
(2)在APP的build.gradle中引入baiduspeech
(3)在Activity中调用
activity_main.xml
packagecom.why.project.baiduspeechdemo;importandroid.Manifest;importandroid.os.Bundle;importandroid.support.v7.app.AppCompatActivity;importandroid.util.Log;importandroid.view.View;importandroid.widget.Button;importandroid.widget.TextView;importandroid.widget.Toast;importcom.tbruyelle.rxpermissions2.RxPermissions;importcom.why.project.baiduspeech.dialog.SpeechBottomSheetDialog;importcom.why.project.baiduspeech.dialog.SpeechLongBottomSheetDialog;importio.reactivex.functions.Action;importio.reactivex.functions.Consumer;public class MainActivity extendsAppCompatActivity {private static final String TAG = MainActivity.class.getSimpleName();privateButton mOpenSpeechDialogBtn;privateButton mOpenSpeechLongDialogBtn;privateTextView mResultTv;
@Overrideprotected voidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
onePermission();
initViews();
initEvents();
}private voidinitViews() {
mOpenSpeechDialogBtn=findViewById(R.id.btn_openSpeechDialog);
mOpenSpeechLongDialogBtn=findViewById(R.id.btn_openSpeechLongDialog);
mResultTv=findViewById(R.id.tv_result);
}private voidinitEvents() {
mOpenSpeechDialogBtn.setOnClickListener(newView.OnClickListener() {
@Overridepublic voidonClick(View view) {//打开百度语音对话框
SpeechBottomSheetDialog speechBottomSheetDialog = SpeechBottomSheetDialog.getInstance(MainActivity.this);
speechBottomSheetDialog.seOnResultListItemClickListener(newSpeechBottomSheetDialog.OnResultListItemClickListener() {
@Overridepublic voidonItemClick(String title) {//填充到输入框中mResultTv.setText(title);
}
});
speechBottomSheetDialog.show(getSupportFragmentManager(), TAG);
}
});
mOpenSpeechLongDialogBtn.setOnClickListener(newView.OnClickListener() {
@Overridepublic voidonClick(View view) {//打开百度语音对话框
SpeechLongBottomSheetDialog speechLongBottomSheetDialog = SpeechLongBottomSheetDialog.getInstance(MainActivity.this);
speechLongBottomSheetDialog.seOnResultListItemClickListener(newSpeechLongBottomSheetDialog.OnResultListItemClickListener() {
@Overridepublic voidonItemClick(String title) {//填充到输入框中
mResultTv.setText(mResultTv.getText()+title);
}
});
speechLongBottomSheetDialog.show(getSupportFragmentManager(), TAG);
}
});
}/**只有一个运行时权限申请的情况*/
private voidonePermission(){
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() {
@Overridepublic void accept(Boolean granted) throwsException {
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() {
@Overridepublic void accept(Throwable throwable) throwsException {
Log.e(TAG,"{accept}");//可能是授权异常的情况下的处理
}
},newAction() {
@Overridepublic void run() throwsException {
Log.e(TAG,"{run}");//执行顺序——2
}
});
}
}
混淆配置
#=====================百度语音混淆=====================
-keep class com.baidu.speech.**{*;}
参考资料
项目demo下载地址