/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package my.home.lehome.view; import android.animation.AnimatorSet; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageButton; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.baidu.voicerecognition.android.VoiceRecognitionClient; import com.baidu.voicerecognition.android.VoiceRecognitionClient.VoiceClientStatusChangeListener; import com.baidu.voicerecognition.android.VoiceRecognitionConfig; import com.skyfishjy.library.RippleBackground; import java.lang.ref.WeakReference; import java.util.List; import my.home.common.util.UIUtil; import my.home.lehome.R; import my.home.lehome.util.Constants; public class SpeechDialog extends Dialog { public static final String TAG = "SpeechDialog"; public class State { public static final int IDLE = 0; public static final int LISTENING = 1; public static final int PROCESSING = 2; public static final int FINISHED = 3; public static final int CANCELED = 4; public static final int ERROR = 5; } public class Msg { public static final int START = 0; public static final int END = 1; public static final int FINISH = 2; public static final int HIDE = 3; public static final int CANCEL = 4; public static final int ERROR = 5; } private static int CUR_STATE = State.IDLE; private static boolean AUTO_START = true; private static boolean AUTO_HIDE = true; private final Handler mHandler = new MyHandler(this); private Handler mMainThreadHandler; private VoiceRecognitionClient mASREngine; private SpeechDialogListener mSpeechDialogListener; private MyVoiceRecogListener mVoiceRecogListener = new MyVoiceRecogListener(); private TextView mStatusTextView; private TextView mReleaseTextView; private ProgressBar mVolumnProgressBar; private View mSpeechContentView; private ImageButton mSpeechImageButton; private final WeakReference<Activity> mContextActivity; private RippleBackground mRippleBg; private boolean mUseBluetooth = false; private boolean isMusicPlaying = false; // private List<String> mResult = null; private static final int POWER_UPDATE_INTERVAL = 50; private static final int SHORTEST_SHOWN_TIME = 500; private static final int DIALOG_HINT_DELAY = 1000; /** * 音量更新任务 */ private Runnable mUpdateVolume = new Runnable() { public void run() { if (CUR_STATE == State.LISTENING) { long vol = mASREngine.getCurrentDBLevelMeter(); // Log.i(TAG, String.valueOf(vol)); mVolumnProgressBar.setProgress((int) vol); // set vol mMainThreadHandler.removeCallbacks(mUpdateVolume); mMainThreadHandler.postDelayed(mUpdateVolume, POWER_UPDATE_INTERVAL); } } }; public SpeechDialog(Activity context) { super(context, R.style.SpeechDialog); LayoutInflater inflater = context.getLayoutInflater(); View contentView = inflater.inflate(R.layout.speech_dialog, null); setContentView(contentView); mStatusTextView = (TextView) contentView.findViewById(R.id.status_textview); mVolumnProgressBar = (ProgressBar) contentView.findViewById(R.id.volumn_progressbar); mReleaseTextView = (TextView) contentView.findViewById(R.id.speech_release_cancel_textview); mSpeechContentView = contentView.findViewById(R.id.speeching_content); mRippleBg = (RippleBackground) contentView.findViewById(R.id.speech_rippleBackground); mSpeechImageButton = (ImageButton) contentView.findViewById(R.id.speech_imageButton); mSpeechImageButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finishListening(); } }); mContextActivity = new WeakReference<Activity>(context); } private static class MyHandler extends Handler { private final WeakReference<SpeechDialog> mSpeechDialogReference; public MyHandler(SpeechDialog dialog) { mSpeechDialogReference = new WeakReference<SpeechDialog>(dialog); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.d(TAG, msg.toString()); SpeechDialog dialog = mSpeechDialogReference.get(); if (dialog == null) { return; } switch (msg.what) { case Msg.START: dialog.processStartMsg(msg); break; case Msg.END: dialog.processEndMsg(msg); break; case Msg.CANCEL: dialog.processCancelMsg(msg); break; case Msg.FINISH: dialog.processFinishMsg(msg); break; case Msg.HIDE: dialog.processHideMsg(msg); break; case Msg.ERROR: dialog.processErrorMsg(msg); break; default: break; } } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); } ; @Override public void onStart() { initViewVisible(); CUR_STATE = State.IDLE; if (AUTO_START) { sendMsg(Msg.START, SHORTEST_SHOWN_TIME); } super.onStart(); } public static SpeechDialog getInstance(Activity activity) { SpeechDialog dialog = new SpeechDialog(activity); return dialog; } private void initViewVisible() { mSpeechContentView.setVisibility(View.VISIBLE); mVolumnProgressBar.setVisibility(View.INVISIBLE); mStatusTextView.setVisibility(View.VISIBLE); mReleaseTextView.setVisibility(View.INVISIBLE); mSpeechImageButton.setVisibility(View.INVISIBLE); mStatusTextView.setText(R.string.speech_initializing); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_VOLUME_UP: finishListening(); return true; } return super.onKeyDown(keyCode, event); } @Override public void show() { super.show(); } @Override public void cancel() { sendMsg(Msg.CANCEL); super.cancel(); } @Override public void dismiss() { super.dismiss(); if (mSpeechDialogListener != null) { mSpeechDialogListener.onDissmiss(CUR_STATE); } VoiceRecognitionClient.releaseInstance(); } /* * User must call setup() before showing the Speech Dialog. */ public void setup(Context contect, SpeechDialogListener listener) { mASREngine = VoiceRecognitionClient.getInstance(contect); mASREngine.setTokenApis(Constants.BAIDUVOICE_API_KEY, Constants.BAIDUVOICE_SECRET_KEY); mMainThreadHandler = new Handler(); mSpeechDialogListener = listener; } public void setHintText(String content) { mStatusTextView.setText(content); } private void sendMsg(int what) { mHandler.obtainMessage(what).sendToTarget(); } private void sendMsg(int what, Object obj) { mHandler.obtainMessage(what, obj).sendToTarget(); } private void sendMsg(int what, long delay) { Message m = mHandler.obtainMessage(what); mHandler.sendMessageDelayed(m, delay); } private void sendMsg(int what, int arg1, int arg2) { Message m = mHandler.obtainMessage(what); m.arg1 = arg1; m.arg2 = arg2; m.sendToTarget(); } /* * Start Speech Message */ protected void processStartMsg(Message msg) { switch (CUR_STATE) { case State.LISTENING: break; case State.PROCESSING: break; case State.CANCELED: break; case State.FINISHED: break; case State.ERROR: break; case State.IDLE: // mResult = null; AnimatorSet animatorSet = UIUtil.getShowViewScaleAnimatorSet(mSpeechImageButton, 400); mSpeechImageButton.setVisibility(View.VISIBLE); animatorSet.start(); if (isMusicPlaying(mContextActivity.get())) { ToggleMusicPlay(mContextActivity.get()); isMusicPlaying = true; } mStatusTextView.setText(R.string.speech_speaking); CUR_STATE = State.LISTENING; if (!startListening()) { sendMsg(Msg.ERROR); } break; default: break; } } /* * Stop Speech, Start Recognize. */ protected void processEndMsg(Message msg) { switch (CUR_STATE) { case State.LISTENING: CUR_STATE = State.PROCESSING; mVolumnProgressBar.setVisibility(View.INVISIBLE); // mSpeechContentView.setVisibility(View.INVISIBLE); mRippleBg.startRippleAnimation(); mStatusTextView.setText(R.string.speech_processing); break; case State.PROCESSING: break; case State.FINISHED: break; case State.ERROR: break; case State.IDLE: break; case State.CANCELED: break; default: break; } } protected void processCancelMsg(Message msg) { switch (CUR_STATE) { case State.CANCELED: break; case State.LISTENING: case State.PROCESSING: CUR_STATE = State.CANCELED; // mVolumnProgressBar.setIndeterminate(false); mRippleBg.stopRippleAnimation(); mASREngine.stopVoiceRecognition(); if (AUTO_HIDE) { sendMsg(Msg.HIDE); } break; case State.FINISHED: break; case State.ERROR: break; case State.IDLE: mStatusTextView.setText(R.string.speech_too_short); // mVolumnProgressBar.setIndeterminate(false); mRippleBg.stopRippleAnimation(); CUR_STATE = State.CANCELED; if (AUTO_HIDE) { sendMsg(Msg.HIDE, DIALOG_HINT_DELAY); } break; default: break; } } protected void processFinishMsg(Message msg) { switch (CUR_STATE) { case State.CANCELED: break; case State.LISTENING: break; case State.PROCESSING: CUR_STATE = State.FINISHED; // mVolumnProgressBar.setIndeterminate(false); mRippleBg.stopRippleAnimation(); List<String> result = (List<String>) msg.obj; parseResult(result); if (AUTO_HIDE) { sendMsg(Msg.HIDE); } break; case State.FINISHED: break; case State.ERROR: break; case State.IDLE: break; default: break; } } protected void processErrorMsg(Message msg) { switch (CUR_STATE) { case State.CANCELED: break; case State.LISTENING: case State.PROCESSING: // mVolumnProgressBar.setIndeterminate(false); mRippleBg.stopRippleAnimation(); int errType = msg.arg1; int errCode = msg.arg2; mStatusTextView.setText(errorCodeToString(errType, errCode)); CUR_STATE = State.ERROR; if (AUTO_HIDE) { sendMsg(Msg.HIDE, DIALOG_HINT_DELAY); } break; case State.FINISHED: break; case State.ERROR: break; case State.IDLE: break; default: break; } } protected void processHideMsg(Message msg) { switch (CUR_STATE) { case State.CANCELED: case State.FINISHED: case State.ERROR: // CUR_STATE = State.IDLE; if (isMusicPlaying && !isMusicPlaying(mContextActivity.get())) ToggleMusicPlay(mContextActivity.get()); if (this != null && this.isShowing()) this.dismiss(); break; case State.LISTENING: break; case State.PROCESSING: break; case State.IDLE: break; default: break; } } public int currentState() { return CUR_STATE; } public void setReleaseCancelVisible(boolean visible) { if (CUR_STATE == State.LISTENING) { if (visible) { mReleaseTextView.setVisibility(View.VISIBLE); mSpeechContentView.setVisibility(View.INVISIBLE); // mStatusTextView.setVisibility(View.INVISIBLE); // mVolumnProgressBar.setVisibility(View.INVISIBLE); } else { mReleaseTextView.setVisibility(View.INVISIBLE); mSpeechContentView.setVisibility(View.VISIBLE); // mStatusTextView.setVisibility(View.VISIBLE); // mVolumnProgressBar.setVisibility(View.VISIBLE); } } } /** * 重写用于处理语音识别回调的监听器 */ class MyVoiceRecogListener implements VoiceClientStatusChangeListener { @Override public void onClientStatusChange(int status, Object obj) { switch (status) { // 语音识别实际开始,这是真正开始识别的时间点,需在界面提示用户说话。 case VoiceRecognitionClient.CLIENT_STATUS_START_RECORDING: mStatusTextView.setText(R.string.speech_speaking); mVolumnProgressBar.setVisibility(View.VISIBLE); mMainThreadHandler.removeCallbacks(mUpdateVolume); mMainThreadHandler.postDelayed(mUpdateVolume, POWER_UPDATE_INTERVAL); break; case VoiceRecognitionClient.CLIENT_STATUS_SPEECH_START: // 检测到语音起点 break; // 已经检测到语音终点,等待网络返回 case VoiceRecognitionClient.CLIENT_STATUS_SPEECH_END: sendMsg(Msg.END); break; // 语音识别完成,显示obj中的结果 case VoiceRecognitionClient.CLIENT_STATUS_FINISH: if (obj != null && obj instanceof List) { List results = (List) obj; sendMsg(Msg.FINISH, results); } break; // 用户取消 case VoiceRecognitionClient.CLIENT_STATUS_USER_CANCELED: sendMsg(Msg.CANCEL); break; default: break; } } @Override public void onError(int errorType, int errorCode) { sendMsg(Msg.ERROR, errorType, errorCode); } @Override public void onNetworkStatusChange(int status, Object obj) { } } public boolean finishListening() { if (CUR_STATE == State.IDLE) { sendMsg(Msg.CANCEL); } else if (CUR_STATE == State.LISTENING) { mASREngine.speakFinish(); } return true; } public boolean startListening() { VoiceRecognitionConfig config = new VoiceRecognitionConfig(); config.setProp(VoiceRecognitionConfig.PROP_SEARCH); config.setLanguage(VoiceRecognitionConfig.LANGUAGE_CHINESE); // config.enableNLU(); config.setUseBlueTooth(ismUseBluetooth()); config.enableVoicePower(true); // 音量反馈。 config.enableBeginSoundEffect(R.raw.bdspeech_recognition_start); // 设置识别开始提示音 config.enableEndSoundEffect(R.raw.bdspeech_speech_end); // 设置识别结束提示音 // config.setSampleRate(VoiceRecognitionConfig.SAMPLE_RATE_8K); // 设置采样率 // 下面发起识别 int code = mASREngine.startVoiceRecognition(mVoiceRecogListener, config); if (code != VoiceRecognitionClient.START_WORK_RESULT_WORKING) { Activity activity = mContextActivity.get(); if (activity != null) Toast.makeText(activity, activity.getString(R.string.speech_start_faild, code), Toast.LENGTH_LONG).show(); } return code == VoiceRecognitionClient.START_WORK_RESULT_WORKING; } public boolean cancelListening() { sendMsg(Msg.CANCEL); return true; } private void parseResult(List<String> results) { if (mSpeechDialogListener != null) { mSpeechDialogListener.onResult(results); } } public interface SpeechDialogListener { void onResult(List<String> results); void onDissmiss(int state); } /* * Error code to String */ public String errorCodeToString(int type, int code) { switch (type) { case VoiceRecognitionClient.ERROR_CLIENT: switch (code) { case VoiceRecognitionClient.ERROR_CLIENT_JNI_EXCEPTION: return "ERROR_CLIENT_JNI_EXCEPTION"; case VoiceRecognitionClient.ERROR_CLIENT_NO_SPEECH: return "ERROR_CLIENT_NO_SPEECH"; case VoiceRecognitionClient.ERROR_CLIENT_TOO_SHORT: return "ERROR_CLIENT_TOO_SHORT"; case VoiceRecognitionClient.ERROR_CLIENT_UNKNOWN: return "ERROR_CLIENT_UNKNOWN"; case VoiceRecognitionClient.ERROR_CLIENT_WHOLE_PROCESS_TIMEOUT: return "ERROR_CLIENT_WHOLE_PROCESS_TIMEOUT"; default: break; } break; case VoiceRecognitionClient.ERROR_NETWORK: switch (code) { case VoiceRecognitionClient.ERROR_NETWORK_UNUSABLE: return "ERROR_NETWORK_UNUSABLE"; case VoiceRecognitionClient.ERROR_NETWORK_CONNECT_ERROR: return "ERROR_NETWORK_CONNECT_ERROR"; case VoiceRecognitionClient.ERROR_NETWORK_PARSE_ERROR: return "ERROR_NETWORK_PARSE_ERROR"; default: break; } break; case VoiceRecognitionClient.ERROR_RECORDER: switch (code) { case VoiceRecognitionClient.ERROR_RECORDER_UNAVAILABLE: return "ERROR_RECORDER_UNAVAILABLE"; case VoiceRecognitionClient.ERROR_RECORDER_INTERCEPTED: return "ERROR_RECORDER_INTERCEPTED"; default: break; } break; case VoiceRecognitionClient.ERROR_SERVER: switch (code) { case VoiceRecognitionClient.ERROR_SERVER_BACKEND_ERROR: return "ERROR_SERVER_BACKEND_ERROR"; case VoiceRecognitionClient.ERROR_SERVER_PARAMETER_ERROR: return "ERROR_CLIENT_JNI_EXCEPTION"; case VoiceRecognitionClient.ERROR_SERVER_RECOGNITION_ERROR: return "ERROR_SERVER_RECOGNITION_ERROR"; case VoiceRecognitionClient.ERROR_SERVER_INVALID_APP_NAME: return "ERROR_SERVER_INVALID_APP_NAME"; case VoiceRecognitionClient.ERROR_SERVER_SPEECH_QUALITY_ERROR: return "ERROR_SERVER_SPEECH_QUALITY_ERROR"; default: break; } break; default: break; } return "UNKNOWN ERROR TYPE OR CODE"; } public boolean isMusicPlaying(Context context) { AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); return audioManager.isMusicActive(); } public void ToggleMusicPlay(Context context) { Intent intent = new Intent("com.android.music.musicservicecommand"); intent.putExtra("command", "togglepause"); context.sendBroadcast(intent); } public boolean ismUseBluetooth() { return mUseBluetooth; } public void setmUseBluetooth(boolean mUseBluetooth) { this.mUseBluetooth = mUseBluetooth; } }