package ee.ioc.phon.android.speak.activity; import android.Manifest; import android.content.ComponentName; import android.os.Bundle; import android.preference.PreferenceManager; import android.speech.SpeechRecognizer; import android.support.v4.app.ActivityCompat; import android.widget.TextView; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import ee.ioc.phon.android.speak.Log; import ee.ioc.phon.android.speak.R; import ee.ioc.phon.android.speak.model.CallerInfo; import ee.ioc.phon.android.speak.utils.Utils; import ee.ioc.phon.android.speak.view.AbstractSpeechInputViewListener; import ee.ioc.phon.android.speak.view.SpeechInputView; import ee.ioc.phon.android.speechutils.Extras; import ee.ioc.phon.android.speechutils.TtsProvider; import ee.ioc.phon.android.speechutils.utils.PreferenceUtils; /** * <p>This activity responds to the following intent types:</p> * <ul> * <li>android.speech.action.RECOGNIZE_SPEECH</li> * <li>android.speech.action.WEB_SEARCH</li> * </ul> * <p>We have tried to implement the complete interface of RecognizerIntent as of API level 7 (v2.1).</p> * <p/> * <p>It records audio, transcribes it using a speech-to-text server * and returns the result as a non-empty list of Strings. * In case of <code>android.intent.action.MAIN</code>, * it submits the recorded/transcribed audio to a web search. * It never returns an error code, * all the errors are processed within this activity.</p> * <p/> * <p>This activity rewrites the error codes which originally come from the * speech recognizer service * to the RecognizerIntent result error codes. The RecognizerIntent error codes are the * following (with my interpretation after the colon):</p> * <p/> * <ul> * <li>RESULT_AUDIO_ERROR: recording of the audio fails</li> * <li>RESULT_NO_MATCH: everything worked great just no transcription was produced</li> * <li>RESULT_NETWORK_ERROR: cannot reach the recognizer server * <ul> * <li>Network is switched off on the device</li> * <li>The recognizer webservice URL does not exist in the internet</li> * </ul> * </li> * <li>RESULT_SERVER_ERROR: server was reached but it denied service for some reason, * or produced results in a wrong format (i.e. maybe it provides a different service)</li> * <li>RESULT_CLIENT_ERROR: generic client error * <ul> * <li>The URLs of the recognizer webservice and/or the grammar were malformed</li> * </ul> * </li> * </ul> * * @author Kaarel Kaljurand */ public class SpeechActionActivity extends AbstractRecognizerIntentActivity { private TextView mTvPrompt; private SpeechInputView mView; @Override void showError(String msg) { ((TextView) mView.findViewById(R.id.tvMessage)).setText(msg); } /** * <p>Only for developers, i.e. we are not going to localize these strings.</p> * TODO: fix */ @Override String[] getDetails() { String callingActivityClassName = null; String callingActivityPackageName = null; String pendingIntentTargetPackage = null; ComponentName callingActivity = getCallingActivity(); if (callingActivity != null) { callingActivityClassName = callingActivity.getClassName(); callingActivityPackageName = callingActivity.getPackageName(); } if (getExtraResultsPendingIntent() != null) { pendingIntentTargetPackage = getExtraResultsPendingIntent().getTargetPackage(); } List<String> info = new ArrayList<>(); info.add("ID: " + PreferenceUtils.getUniqueId(PreferenceManager.getDefaultSharedPreferences(this))); //info.add("User-Agent comment: " + getRecSessionBuilder().getUserAgentComment()); info.add("Calling activity class name: " + callingActivityClassName); info.add("Calling activity package name: " + callingActivityPackageName); info.add("Pending intent target package: " + pendingIntentTargetPackage); //info.add("Selected grammar: " + getRecSessionBuilder().getGrammarUrl()); //info.add("Selected target lang: " + getRecSessionBuilder().getGrammarTargetLang()); //info.add("Selected server: " + getRecSessionBuilder().getServerUrl()); info.add("Intent action: " + getIntent().getAction()); info.addAll(Utils.ppBundle(getExtras())); return info.toArray(new String[info.size()]); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // TODO: do not use the default dialog style when in multi window mode /* if (isInMultiWindowMode()) { setTheme(R.style.Theme_K6nele_NoActionBar); } */ setUpActivity(R.layout.activity_recognizer); mTvPrompt = (TextView) findViewById(R.id.tvPrompt); } @Override public void onRestart() { super.onRestart(); Log.i("onRestart"); } @Override public void onStart() { super.onStart(); Log.i("onStart"); } @Override public void onResume() { super.onResume(); Log.i("onResume"); // These steps are done in onResume, because when an activity that has been lanuched by Kõnele // finishes, then Kõnele might not necessarily go through onStart. clearAudioBuffer(); setUpExtras(); registerPrompt(mTvPrompt); setTvPrompt(); setUpSettingsButton(); mView = (SpeechInputView) findViewById(R.id.vVoiceImeView); CallerInfo callerInfo = new CallerInfo(getExtras(), getCallingActivity()); // TODO: do we need to send the ComponentName of the calling activity instead mView.init(R.array.keysActivity, callerInfo); mView.setListener(getSpeechInputViewListener()); String[] results = getExtras().getStringArray(Extras.EXTRA_RESULT_RESULTS); if (results == null) { if (hasVoicePrompt()) { sayVoicePrompt(new TtsProvider.Listener() { @Override public void onDone() { start(); } }); } else { start(); } } else { returnOrForwardMatches(Arrays.asList(results)); } } private void start() { if (isAutoStart()) { // TODO: test what happens if the view is started while TTS is running // and then started again when the TTS stops and calls onDone mView.post(new Runnable() { @Override public void run() { mView.start(); } }); } } @Override public void onStop() { super.onStop(); Log.i("onStop"); // We stop the service unless a configuration change causes onStop(), // i.e. the service is not stopped because of rotation, but is // stopped if BACK or HOME is pressed, or the Settings-activity is launched. if (!isChangingConfigurations()) { mView.cancel(); } stopTts(); } private SpeechInputView.SpeechInputViewListener getSpeechInputViewListener() { return new AbstractSpeechInputViewListener() { @Override public void onComboChange(String language, ComponentName service) { setRewriters(language, service); } @Override public void onFinalResult(List<String> results, Bundle bundle) { returnOrForwardMatches(results); } @Override public void onBufferReceived(byte[] buffer) { addToAudioBuffer(buffer); } @Override public void onError(int errorCode) { if (errorCode == SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS) { ActivityCompat.requestPermissions(SpeechActionActivity.this, new String[]{Manifest.permission.RECORD_AUDIO}, PERMISSION_REQUEST_RECORD_AUDIO); } else { setResultError(errorCode); } } @Override public void onStartListening() { stopTts(); } }; } }