I am working on speechRecognizer module in android integrated with dialogflow. I have almost completed the integration and it's working fine but facing one issue. The issue is when TTS is speaking the response, before completing the speech mic gets enabled and it capture the response also instead of user utterance only.
So I want to know how can I get to know that TTS finished speaking the response? I have tried Google solution by using onUtteranceCompleted() method and also I have tried on setOnUtteranceProgressListener() method but doesn't seems to be working. I want to implement these methods in asynctask So that it can be done in background. How can I do that?
Here is the code that I have tried:
import android.Manifest;
import android.app.ProgressDialog;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.speech.RecognitionListener;
import android.speech.RecognizerIntent;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import ai.api.PartialResultsListener;
import ai.api.android.AIConfiguration;
import ai.api.android.GsonFactory;
import ai.api.model.AIError;
import ai.api.model.AIResponse;
import ai.api.ui.AIDialog;
import android.os.AsyncTask;
import com.google.gson.Gson;
public class VoiceActivity extends AppCompatActivity implements View.OnClickListener, TextToSpeech.OnInitListener, RecognitionListener {
private AIDialog.AIDialogListener resultsListener;
private static final int REQUEST_AUDIO_PERMISSIONS_ID = 33;
private Gson gson = GsonFactory.getGson();
private ListView wordList;
TextView txtmain;
private static final int VR_REQUEST = 999;
//Log tag for output information
private final String LOG_TAG = "SpeechRepeatActivity";//***enter your own tag here***
//variable for checking TTS engine data on user device
private int MY_DATA_CHECK_CODE = 0;
//Text To Speech instance
private TextToSpeech tts;
AIButton aiButton;
EditText time;
TextView matchcall;
// private SpeechRecognizer speech = null;
private Intent recognizerIntent;
TextView tvOutput ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_voice);
aiButton = (AIButton) findViewById(R.id.speech_btn);
txtmain =findViewById(R.id.txtmain);
wordList = findViewById(R.id.word_list);
tvOutput = findViewById(R.id.tvOutput);
time = (EditText) findViewById(R.id.in_time);
matchcall = (TextView) findViewById(R.id.matchcall);
final AsyncTaskRunner runner = new AsyncTaskRunner();
initService();
PackageManager packManager = getPackageManager();
List<ResolveInfo> intActivities = packManager.queryIntentActivities(new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
if (intActivities.size() != 0) {
//speech recognition is supported - detect user button clicks
aiButton.setOnClickListener(this);
//prepare the TTS to repeat chosen words
Intent checkTTSIntent = new Intent();
//check TTS data
checkTTSIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
//start the checking Intent - will retrieve result in onActivityResult
startActivityForResult(checkTTSIntent, MY_DATA_CHECK_CODE);
} else {
//speech recognition not supported, disable button and output message
aiButton.setEnabled(false);
Toast.makeText(this, "Oops - Speech recognition not supported!", Toast.LENGTH_LONG).show();
}
}
private void setAIButtonCallback(final AIButton aiButton) {
aiButton.setResultsListener(new AIButton.AIButtonListener() {
@Override
public void onResult(final AIResponse result) {
if (resultsListener != null) {
resultsListener.onResult(result);
Log.e(LOG_TAG,"onResult=="+result.getResult().getResolvedQuery());
final String speech = result.getResult().getFulfillment().getSpeech();
tvOutput.setText(speech);
}
}
@Override
public void onError(final AIError error) {
if (resultsListener != null) {
resultsListener.onError(error);
Log.e(LOG_TAG,"onError=="+error);
}
}
@Override
public void onCancelled() {
if (resultsListener != null) {
resultsListener.onCancelled();
Log.e(LOG_TAG,"onCancelled==");
}
}
});
aiButton.setPartialResultsListener(new PartialResultsListener() {
@Override
public void onPartialResults(final List<String> partialResults) {
final String result = partialResults.get(0);
if (!TextUtils.isEmpty(result)) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
txtmain.setText(result);
}
});
}
}
});
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.speech_btn) {
//listen for results
listenToSpeech();
}
}
@Override
public void onInit(int status) {
//if successful, set locale
if (status == TextToSpeech.SUCCESS)
tts.setLanguage(Locale.UK);//***choose your own locale here***
}
private void listenToSpeech() {
SpeechRecognizer speech = SpeechRecognizer.createSpeechRecognizer(this);
speech.setRecognitionListener(this);
recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE,"en");
recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,this.getPackageName());
recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE,this.getPackageName());
recognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);
//start listening
speech.startListening(recognizerIntent);
}
@Override
public void onResults(Bundle results) {
Log.i(LOG_TAG, "onResults");
ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
/* wordList.setAdapter(new ArrayAdapter<String>(this, R.layout.word, matches));
new Handler().post(new Runnable() {
@Override
public void run() {
wordList.performItemClick(
wordList.getChildAt(0),
0,
wordList.getAdapter().getItemId(0));
wordList.getChildAt(0).setBackgroundColor(0xFFD3D3D3);
}
});*/
String wordChosen = matches.get(0);
tvOutput.setText(wordChosen);
try {
AsyncTaskRunner runner = new AsyncTaskRunner();
runner.execute(wordChosen);
// aiButton.textRequest(text);
//runner.doInBackground(text);
} catch (Exception e) {
e.printStackTrace();
}
}
private class AsyncTaskRunner extends AsyncTask<String, AIResponse, AIResponse> {
private AIResponse resp;
ProgressDialog progressDialog;
@Override
protected AIResponse doInBackground(String... params) {
Log.e(LOG_TAG,"doInBackground=="+params[0]);
try {
resp = aiButton.textRequest(String.valueOf(params[0]));
}
catch (Exception e) {
e.printStackTrace();
}
return resp;
}
protected void onPostExecute(AIResponse result) {
Log.e(LOG_TAG,"onPostExecute== result=="+result.getResult().getFulfillment().getSpeech());
// execution of result of Long time consuming operation
findViewById(R.id.progBar).setVisibility(View.GONE);
txtmain.setText(result.getResult().getFulfillment().getSpeech());
String speech = result.getResult().getFulfillment().getSpeech();
tts.speak( speech, TextToSpeech.QUEUE_FLUSH, null);
Toast.makeText(VoiceActivity.this, speech, Toast.LENGTH_SHORT).show();
listenToSpeech();
}
@Override
protected void onPreExecute() {
Log.e(LOG_TAG,"onPreExecute==");
findViewById(R.id.progBar).setVisibility(View.VISIBLE);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//check speech recognition result
if (requestCode == VR_REQUEST && resultCode == RESULT_OK)
{
//store the returned word list as an ArrayList
ArrayList<String> suggestedWords = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
//set the retrieved list to display in the ListView using an ArrayAdapter
wordList.setAdapter(new ArrayAdapter<String>(this, R.layout.word, suggestedWords));
/*new Handler().post(new Runnable() {
@Override
public void run() {
wordList.performItemClick(
wordList.getChildAt(0),
0,
wordList.getAdapter().getItemId(0));
wordList.getChildAt(0).setBackgroundColor(0xFFD3D3D3);
}
});*/
tvOutput.setText(suggestedWords.get(0));
}
if (requestCode == MY_DATA_CHECK_CODE)
{
//we have the data - create a TTS instance
if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS)
tts = new TextToSpeech(this, this);
//data not installed, prompt the user to install it
else
{
//intent will take user to TTS download page in Google Play
Intent installTTSIntent = new Intent();
installTTSIntent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
startActivity(installTTSIntent);
}
}
//call superclass method
super.onActivityResult(requestCode, resultCode, data);
}
private void initService() {
final AIConfiguration config = new AIConfiguration("a73d5e88477e4926ae84af46f24e0aaa",
AIConfiguration.SupportedLanguages.English,
AIConfiguration.RecognitionEngine.Google);
aiButton.initialize(config);
setAIButtonCallback(aiButton);
Log.i(LOG_TAG, "initService:::::: ");
}
@Override
protected void onPause() {
super.onPause();
// speech.stopListening();
// use this method to disconnect from speech recognition service
new java.util.Timer().schedule(
new java.util.TimerTask() {
@Override
public void run() {
// your code here
}
},
5000
);
// Not destroying the SpeechRecognition object in onPause method would block other apps from using SpeechRecognition service
}
@Override
protected void onResume() {
super.onResume();
}
@Override
protected void onStop() {
Log.i(LOG_TAG, "stop");
super.onStop();
/* if (speech != null) {
speech.destroy();*/
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//}
@Override
protected void onStart() {
super.onStart();
checkAudioRecordPermission();
}
protected void checkAudioRecordPermission() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.RECORD_AUDIO)) {
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECORD_AUDIO},
REQUEST_AUDIO_PERMISSIONS_ID);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode) {
case REQUEST_AUDIO_PERMISSIONS_ID: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
}
return;
}
}
}
@Override
public void onReadyForSpeech(Bundle bundle) {
}
@Override
public void onBeginningOfSpeech() {
}
@Override
public void onRmsChanged(float v) {
}
@Override
public void onBufferReceived(byte[] bytes) {
}
@Override
public void onEndOfSpeech() {
//speech.stopListening();
}
@Override
public void onError(int errorcode) {
String errorMessage = getErrorText(errorcode);
Log.i(LOG_TAG, "FAILED " + errorMessage);
// speech.stopListening();
// listenToSpeech();
}
public String getErrorText(int errorCode) {
String message;
switch (errorCode) {
case SpeechRecognizer.ERROR_AUDIO:
message = "Audio recording error";
break;
case SpeechRecognizer.ERROR_CLIENT:
message = "Client side error";
break;
case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS:
message = "Insufficient permissions";
break;
case SpeechRecognizer.ERROR_NETWORK:
message = "Network error";
break;
case SpeechRecognizer.ERROR_NETWORK_TIMEOUT:
message = "Network timeout";
break;
case SpeechRecognizer.ERROR_NO_MATCH:
message = "No match";
break;
case SpeechRecognizer.ERROR_RECOGNIZER_BUSY:
message = "RecognitionService busy";
break;
case SpeechRecognizer.ERROR_SERVER:
message = "error from server";
break;
case SpeechRecognizer.ERROR_SPEECH_TIMEOUT:
message = "No speech input";
//speech.stopListening();
break;
default:
message = "Didn't understand, please try again.";
break;
}
return message;
}
@Override
public void onPartialResults(Bundle bundle) {
}
@Override
public void onEvent(int i, Bundle bundle) {
}
}