/* * Copyright 2011 Greg Milette and Adam Stroud * * 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 root.gast.playground.speech; import java.util.Arrays; import java.util.List; import java.util.Locale; import org.apache.commons.codec.language.Soundex; import root.gast.playground.R; import root.gast.playground.pref.PreferenceHelper; import root.gast.playground.pref.SummarizingEditPreferences; import root.gast.playground.speech.activation.SpeechActivatorTagWriter; import root.gast.playground.speech.visitor.MatchesTargetVisitor; import root.gast.playground.speech.visitor.MatchesTargetVisitorSoundex; import root.gast.playground.speech.visitor.MatchesTargetVisitorStem; import root.gast.playground.speech.visitor.PartialMatchTargetVisitor; import root.gast.playground.speech.visitor.SpeechResultVisitor; import root.gast.playground.util.DialogGenerator; import root.gast.speech.LanguageDetailsChecker; import root.gast.speech.OnLanguageDetailsListener; import root.gast.speech.RecognizerIntentFactory; import root.gast.speech.SpeechRecognizingActivity; import root.gast.speech.activation.ClapperActivator; import root.gast.speech.activation.MovementActivator; import root.gast.speech.activation.SpeechActivationListener; import root.gast.speech.activation.SpeechActivator; import root.gast.speech.activation.SpeechActivatorFactory; import root.gast.speech.activation.WordActivator; import root.gast.speech.text.index.Stemmer; import android.app.Activity; import android.app.PendingIntent; import android.content.DialogInterface; import android.content.Intent; import android.os.Build; import android.os.Bundle; import android.speech.RecognizerIntent; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import android.widget.ViewSwitcher; /** * helps the user test speech recognition with all its settings and use cases interactively * @author Greg Milette <<a href="mailto:gregorym@gmail.com">gregorym@gmail.com</a>> */ public class SpeechRecognitionPlay extends SpeechRecognizingActivity implements SpeechActivationListener { private static final String TAG = "SpeechRecognitionPlay"; private ListView log; private EditText whatYouAreTryingToSay; private TextView resultsSummary; private Button speak; private Button stopSpeaking; private PreferenceHelper preferences; private List<String> presets; private List<String> activationMethod; private SpeechActivator speechActivator; private ViewSwitcher startStopButton; private boolean isListeningForActivation = false; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.speechrecognition); hookButtons(); init(); } private void init() { preferences = new PreferenceHelper(getResources().getString(R.string.pref_speech_key), this.getApplicationContext()); isListeningForActivation = false; } private void hookButtons() { final SpeechRecognitionPlay finalContext = this; speechActivator = null; speak = (Button)findViewById(R.id.btn_speak); speak.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivator(); } }); stopSpeaking = (Button)findViewById(R.id.btn_stop_speak); stopSpeaking.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { stopActivator(); } }); startStopButton = (ViewSwitcher)findViewById(R.id.vf_start_speak_flipper); log = (ListView)findViewById(R.id.lv_resultlog); whatYouAreTryingToSay = (EditText)findViewById(R.id.et_speech_target); resultsSummary = (TextView)findViewById(R.id.tv_speechResultsSummary); //set speech target selector. what you are trying to say String [] defaultTargets = getResources().getStringArray(R.array.default_speech_targets); presets = Arrays.asList(defaultTargets); ImageButton bt = (ImageButton)findViewById(R.id.bt_speechTargets); bt.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DialogGenerator.makeSelectListDialog(getResources().getString(R.string.speechTargetPresetHeading), SpeechRecognitionPlay.this, presets, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { whatYouAreTryingToSay.setText(presets.get(which)); resetSpeechResult(); } }, DialogGenerator.DO_NOTHING).show(); } }); //set activation method selector String [] activationMethods = getResources().getStringArray(R.array.speech_activation_types); activationMethod = Arrays.asList(activationMethods); ImageButton btActivate = (ImageButton)findViewById(R.id.bt_speechActivation); btActivate.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { DialogGenerator.makeSelectListDialog(getResources().getString(R.string.speechActivationHeading), SpeechRecognitionPlay.this, activationMethod, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { //stop the activator before doing something new stopActivator(); String whichMethod = activationMethod.get(which); Log.d(TAG, "selected activation method: " + whichMethod); speechActivator = SpeechActivatorFactory.createSpeechActivator( SpeechRecognitionPlay.this, finalContext, whichMethod); resetSpeakButtonText(); } }, DialogGenerator.DO_NOTHING).show(); } }); } //handle the various activation methods private void restartActivator() { if (isListeningForActivation) { isListeningForActivation = false; startActivator(); } } private void startActivator() { if (isListeningForActivation) { //only activate once return; } if (speechActivator == null) { Log.d(TAG, "activator is null"); startRecognition(); } else { isListeningForActivation = true; speechActivator.detectActivation(); startStopButton.showNext(); resetSpeakButtonText(); } } private void stopActivator() { if (speechActivator != null) { // first stop speechActivator.stop(); // then reset button startStopButton.showPrevious(); } isListeningForActivation = false; } private void resetSpeakButtonText() { setStartButton(); setStopButton(); } private void setStartButton() { String prefix = getResources().getString(R.string.speech_activation_btn_prefix); String label = SpeechActivatorFactory.getLabel(this, speechActivator); if (speechActivator == null) { stopSpeaking.setText(this.getResources().getString(R.string.btn_speak_label)); } else { speak.setText(prefix + " " + label); } } private void setStopButton() { if (speechActivator == null) { stopSpeaking.setText(this.getResources().getString(R.string.btn_speak_label)); } else if (speechActivator instanceof WordActivator) { stopSpeaking.setText("Stop listinging for hello"); } else if ((speechActivator instanceof MovementActivator)) { stopSpeaking.setText("Stop detecting movement"); } else if ((speechActivator instanceof ClapperActivator)) { stopSpeaking.setText("Stop listening for clap"); } else { stopSpeaking.setText("Other listening happening"); } } @Override public void activated(boolean success) { Log.d(TAG, "activated..."); //app could indicated to stop, but an activation //could still occur. possibly ignore it if (!isListeningForActivation) { Log.d(TAG, "listening stopped"); //don't do multiple activations return; } // first stop the activator so no activations occur if (speechActivator != null) { //still more may occur, do I need a boolean speechActivator.stop(); } startRecognition(); } //now do some recognition based on the set preferences private void startRecognition() { Log.d(TAG, "starting recognition"); Intent targetIntent = readRecognizerIntentFromPreferences(); boolean direct = preferences.getBoolean(SpeechRecognitionPlay.this, R.string.pref_direct, R.string.pref_direct_default); if (direct) { //put up toast since there is not dialog Toast.makeText(SpeechRecognitionPlay.this, "Started recognition", Toast.LENGTH_SHORT).show(); recognizeDirectly(targetIntent); } else { recognize(targetIntent); } resetSpeakButtonText(); } /** * display the results */ @Override public void receiveWhatWasHeard(List<String> heard, float [] confidenceScores) { String matchPreference = getMatchIndexingType(); //Note: use a visitor pattern to process the heard strings and determine //what to put in the final column and how to highlight the heard strings //in the output log MatchesTargetVisitor matches = null; if (matchPreference.equals(getResources().getString(R.string.pref_match_stem))) { matches = new MatchesTargetVisitor(whatYouAreTryingToSay.getText().toString()); } else if (matchPreference.equals(getResources().getString(R.string.pref_match_phonetic))) { matches = new MatchesTargetVisitorStem(whatYouAreTryingToSay.getText().toString()); } else { matches = new MatchesTargetVisitorSoundex(whatYouAreTryingToSay.getText().toString()); } //depends on "indexing algorithm" SpeechResultVisitor partial = new PartialMatchTargetVisitor(whatYouAreTryingToSay.getText().toString(), matches); List<? extends SpeechResultVisitor> visitors = Arrays.asList( matches, partial ); //need to execute this so that the speech result works int i = 0; for (String heardPossibility : heard) { matches.mark(heardPossibility, i, null); i++; } SpeechResultAdapter adapter = new SpeechResultAdapter(this, R.layout.speechresult_listitem, heard, confidenceScores, visitors); log.setAdapter(adapter); setSpeechResult(matches); //possibly restart restartActivator(); } private String getMatchIndexingType() { String matchPreference = preferences.getString(getResources().getString(R.string.pref_match), getResources().getString(R.string.pref_match_default)); return matchPreference; } //menu handling public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.speech_menu, menu); return true; } public boolean onOptionsItemSelected (MenuItem item) { if (item.getItemId() == R.id.m_speechparam) { //launch preferences activity Intent i = new Intent(this, SummarizingEditPreferences.class); i.putExtra(SummarizingEditPreferences.WHICH_PREFERENCES_INTENT, R.xml.speech_preferences); String preferenceName = getResources().getString(R.string.pref_speech_key); i.putExtra(SummarizingEditPreferences.WHICH_PREFERENCES_NAME_INTENT, preferenceName); startActivity(i); } else if (item.getItemId() == R.id.m_speech_language_details) { OnLanguageDetailsListener andThen = new OnLanguageDetailsListener() { @Override public void onLanguageDetailsReceived( LanguageDetailsChecker data) { String languagesSupportedDescription = data.toString(); DialogGenerator.createInfoDialog(SpeechRecognitionPlay.this, getResources().getString(R.string.speech_data_check_result_title), languagesSupportedDescription).show(); } }; Intent detailsIntent = new Intent(RecognizerIntent.ACTION_GET_LANGUAGE_DETAILS); sendOrderedBroadcast( detailsIntent, null, new LanguageDetailsChecker(andThen), null, Activity.RESULT_OK, null, null); } else if (item.getItemId() == R.id.m_setlanguage) { OnLanguageDetailsListener andThenSetLanguage = new OnLanguageDetailsListener() { @Override public void onLanguageDetailsReceived( LanguageDetailsChecker data) { final List<String> langs = data.getSupportedLanguages(); DialogGenerator.makeSelectListDialog(getResources().getString(R.string.d_select_language), SpeechRecognitionPlay.this, langs, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String setLanguagePreferenceTo = langs.get(which); preferences.setString(getResources().getString(R.string.pref_language), setLanguagePreferenceTo); } }).show(); } }; Intent details = new Intent(RecognizerIntent.ACTION_GET_LANGUAGE_DETAILS); sendOrderedBroadcast( details, null, new LanguageDetailsChecker(andThenSetLanguage), null, Activity.RESULT_OK, null, null); } else if (item.getItemId() == R.id.m_checkmenu_languageavailable) { //show all the locales, and pic one to check final List<Locale> localesUsed = Arrays.asList(Locale.getAvailableLocales()); DialogGenerator.makeSelectListDialog(getResources().getString(R.string.d_select_language), this, localesUsed, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { Locale loc = localesUsed.get(which); Toast.makeText(SpeechRecognitionPlay.this, "Checking language support for: " + loc, Toast.LENGTH_SHORT).show(); checkForLanguage(loc); } }).show(); } else if (item.getItemId() == R.id.m_speech_compute_index) { final View frameLayout = getLayoutInflater().inflate(R.layout.enterworddialog, null); final EditText dialogtext = (EditText)frameLayout.findViewById(R.id.et_dialog_text_input); String hint = whatYouAreTryingToSay.getText().toString(); if ( (hint != null) && (hint.length() > 0) ) { hint = hint.split("\\s")[0]; } dialogtext.setText(hint); //reply with the word's index //show an edit box DialogGenerator.createFrameDialog(this, "Enter word to index", frameLayout, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { String input = dialogtext.getText().toString(); String soundex = new Soundex().encode(input); String stem = Stemmer.stem(input); StringBuilder message = new StringBuilder(); message.append("input: ").append(input).append("\n"); message.append("soundex: ").append(soundex).append("\n"); message.append("stem: ").append(stem); DialogGenerator.createInfoDialog( SpeechRecognitionPlay.this, getResources().getString(R.string.d_info), message.toString()).show(); } }).show(); } else if (item.getItemId() == R.id.m_write_speech_activation_tag) { Log.d(TAG, "start write activation"); Intent doTag = new Intent(this, SpeechActivatorTagWriter.class); startActivity(doTag); } else { throw new RuntimeException("unknown menu selection"); } return true; } /** * create the {@link RecognizerIntent} based on the many preferences */ private Intent readRecognizerIntentFromPreferences() { Intent intentToSend; //web search handling boolean isWebSearchAction = preferences.getBoolean(this, R.string.pref_websearch, R.string.pref_websearch_default); boolean isHandsFreeAction = preferences.getBoolean(this, R.string.pref_handsfree, R.string.pref_handsfree_default); if (isWebSearchAction) { intentToSend = RecognizerIntentFactory.getWebSearchRecognizeIntent(); final boolean ADD_ORIGIN = true; if (ADD_ORIGIN && Build.VERSION.SDK_INT >= 14) { intentToSend.putExtra(RecognizerIntent.EXTRA_ORIGIN, true); } } else { if (isHandsFreeAction && Build.VERSION.SDK_INT >= 16) { intentToSend = RecognizerIntentFactory.getHandsFreeRecognizeIntent(); } else { intentToSend = RecognizerIntentFactory.getBlankRecognizeIntent(); } } //language model boolean isFreeFormModel = preferences.getBoolean(this, R.string.pref_languagemodel, R.string.pref_languagemodel_default); if (isFreeFormModel) { intentToSend.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); } else { intentToSend.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); } //common extras String language = preferences.getString(getResources().getString(R.string.pref_language), getResources().getString(R.string.pref_language_default)); intentToSend.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language); String prompt = getResources().getString(R.string.speech_prompt) + ": " + whatYouAreTryingToSay.getText().toString(); intentToSend.putExtra(RecognizerIntent.EXTRA_PROMPT, prompt); intentToSend.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, preferences.getInt(this, R.string.pref_maxresults, R.string.pref_maxresults_default)); intentToSend.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, preferences.getBoolean(this, R.string.pref_partial, R.string.pref_partial_default)); setIfValueSpecified(RecognizerIntent.EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS, R.string.pref_complete_silence, R.string.pref_complete_silence_default, intentToSend); setIfValueSpecified(RecognizerIntent.EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS, R.string.pref_minimum_input_length, R.string.pref_minimum_input_length_default, intentToSend); setIfValueSpecified(RecognizerIntent.EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS, R.string.pref_possibly_complete_silence_length, R.string.pref_possibly_complete_silence_length_default, intentToSend); //pendingIntent handling boolean doPending = preferences.getBoolean(this, R.string.pref_withpendingintent, R.string.pref_withpendingintent); if (doPending) { Intent pendingIntentSource = new Intent(this, SpeechRecognitionResultsActivity.class); PendingIntent pi = PendingIntent.getActivity(this, 0, pendingIntentSource, 0); Bundle extraInfoBundle = new Bundle(); // pass in what you are trying to say so the results activity can // show it extraInfoBundle .putString( SpeechRecognitionResultsActivity. WHAT_YOU_ARE_TRYING_TO_SAY_INTENT_INPUT, whatYouAreTryingToSay.getText().toString()); // set the variables in the intent this is sending intentToSend.putExtra(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT, pi); intentToSend.putExtra( RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE, extraInfoBundle); } Log.d(TAG, "sending recognizer intent: " + intentToSend.getExtras().toString()); return intentToSend; } /** * utility method */ private void setIfValueSpecified( String bundlePart, int resource, int resourceDefault, Intent intent) { long unsetValue = Long.valueOf(getResources().getString(R.string.UNSET)); long value = preferences.getLong(this, resource, resourceDefault); if (value != unsetValue) { intent.putExtra(bundlePart, value); } } private void resetSpeechResult() { resultsSummary.setText(""); } private void setSpeechResult(MatchesTargetVisitor matches) { String result = ""; if (matches.isMatch()) { result = result + " matched at " + matches.getMatchRank() + " (" + getMatchIndexingType() + ")"; } else { result = result + " no match"; } Log.d(TAG, "result: " + result); resultsSummary.setText(result); } //super class methods @Override public void speechNotAvailable() { String message = getResources().getString(R.string.speechNotAvailableMessage); DialogGenerator.createErrorDialog(this, getResources().getString(R.string.d_error), message, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { finish(); } }).show(); } @Override protected void directSpeechNotAvailable() { } @Override protected void languageCheckResult(String languageToUse) { if (languageToUse != null) { DialogGenerator.createInfoDialog(this, getResources().getString(R.string.d_info), languageToUse + " is available").show(); } else { DialogGenerator.createInfoDialog(this, getResources().getString(R.string.d_info), "language not available").show(); } } @Override protected void recognitionFailure(int errorCode) { //do not report an error } @Override protected void onPause() { super.onPause(); if (speechActivator != null) { stopActivator(); } } @Override protected void onResume() { super.onResume(); if (speechActivator != null) { startActivator(); } } @Override protected void onDestroy() { stopActivator(); super.onDestroy(); } }