package com.maxwell.speechrecognition; import android.app.Activity; import android.app.Fragment; import android.app.FragmentManager; import android.content.Context; import android.content.Intent; import android.os.Build; import android.speech.RecognizerIntent; import android.speech.SpeechRecognizer; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; /** * Created by Maxwell on 13-Jan-18. */ public class SpeechRecognition { static final int MAX_RESULT_COUNT = 3; private Context context; private SpeechRecognizer speechRecognizer; private Intent recognizerIntent; private OnSpeechRecognitionListener onSpeechRecognitionListener; private SpeechRecognitionPermissions speechRecognitionPermissions; private OnSpeechRecognitionPermissionListener onSpeechRecognitionPermissionListener; private GoogleImeSpeechRecognition googleImeSpeechRecognition; private boolean enableOnlyOfflineRecognition = false; private boolean handlePermissions = true; private boolean useGoogleIme = false; private String googleImePrompt = "Speech Recognition"; /** * Initialize the SpeechRecognition class with the * application context instance requesting SpeechRecognition. * It is recommended you initialize this class in your Constructor * or in <strong>onCreate</strong> of your Activity. * * @param context the application context (cannot be null) * @see Context */ public SpeechRecognition(Context context){ this.context = context; initializeSpeechRecognitionParameters(); } /** * Sets the application context instance requesting SpeechRecognition * * @param context the application context (cannot be null) * @see Context */ public void setContext(@NonNull Context context){ this.context = context; initializeSpeechRecognitionParameters(); } /** * Sets the {@link OnSpeechRecognitionListener} that will receive the callbacks * for handling the SpeechRecognition responses or Errors. * * @param onSpeechRecognitionListener the listener that will receive the callbacks. * @see OnSpeechRecognitionListener */ public void setSpeechRecognitionListener(@NonNull OnSpeechRecognitionListener onSpeechRecognitionListener){ this.onSpeechRecognitionListener = onSpeechRecognitionListener; } /** * Sets the {@link OnSpeechRecognitionPermissionListener} that will receive the permission request callback. * This listener handles the onPermissionGranted and onPermissionDenied callbacks. * <strong>You must set this if {@link SpeechRecognition} is handling the permission request for you.</strong> * * @param onSpeechRecognitionPermissionListener the listener that will receive the permission callbacks. * @throws UnsupportedOperationException * @see OnSpeechRecognitionPermissionListener * @see #handleAudioPermissions(boolean) */ public void setSpeechRecognitionPermissionListener(@NonNull OnSpeechRecognitionPermissionListener onSpeechRecognitionPermissionListener){ if(!handlePermissions) throw new UnsupportedOperationException(context.getString(R.string.set_permission_listener_exception_text)); this.onSpeechRecognitionPermissionListener = onSpeechRecognitionPermissionListener; } public void setPreferredLanguage(){ throw new UnsupportedOperationException(); } /** * By default, {@link SpeechRecognition} requests for Audio permissions and returns the result * through {@link OnSpeechRecognitionPermissionListener} which is implemented in your Activity/Context. * You can decide to handle Audio permissions yourself by setting handleAudioPermissions to false. * * @param handlePermissions true or false whether to handle audio permissions yourself or not. * @see OnSpeechRecognitionPermissionListener * @see #setContext(Context) */ public void handleAudioPermissions(boolean handlePermissions){ this.handlePermissions = handlePermissions; } /** * Sets {@link SpeechRecognition} to use only it's offline recognition engine * This is disabled by default - meaning that either internet or offline recognition * engines may be used at anytime. <strong>It is highly recommended that this is set to false</strong> * * @param onlyOfflineRecognition true or false whether to use only offline recognition or not (false by default) * @since API level 23 */ public void useOnlyOfflineRecognition(boolean onlyOfflineRecognition){ this.enableOnlyOfflineRecognition = onlyOfflineRecognition; } /** * Checks if speech recognition is supported on the device. * If you try to use {@link SpeechRecognition} when your device does not support it, * {@link IllegalStateException} will be thrown */ public boolean isSpeechRecognitionAvailable(){ return SpeechRecognitionUtilities.isSpeechRecognitionEnabled(context); } /** * This allows {@link SpeechRecognition} to use Google Voice Ime. * <strong>Note: This prevents you from doing Continuous Recognition</strong> * You will be able to get the final text result only after you are done talking. * * @param useGoogleIme true or false whether to use GoogleVoiceIme or not (false by default) * @param prompt the text prompt to display on the GoogleVoiceIme dialog. * pass Null to use the default prompt. */ public void useGoogleImeRecognition(boolean useGoogleIme, @Nullable String prompt){ if(prompt != null) this.googleImePrompt = prompt; this.useGoogleIme = useGoogleIme; } public void startSpeechRecognition(){ /* * Set the SpeechRecognizerListener and SpeechRecognitionPermissionListener here * so that a later call to setSpeechRecognitionListener() or setSpeechRecognitionPermissionListener() * can still affect SpeechRecognition when you start listening */ checkProperties(); SpeechRecognitionListener speechRecognitionListener = new SpeechRecognitionListener( this.onSpeechRecognitionListener, context); if(!speechRecognitionPermissions.isPermissionGiven(context)){ if(!handlePermissions) throw new SecurityException(context.getString(R.string.security_exception_text)); speechRecognitionPermissions.setSpeechRecognitionPermissionListener(this.onSpeechRecognitionPermissionListener); speechRecognitionPermissions.requestPermissions(); }else{ /** * Trigger the OnSpeechRecognitionStarted() callback to notify client that * SpeechRecognition has started listening. * NOTE: do this here and not in {@link SpeechRecognitionListener} so that * GoogleIme can still notify via same Listener */ onSpeechRecognitionListener.OnSpeechRecognitionStarted(); if(useGoogleIme){ googleImeSpeechRecognition.setVoicePrompt(googleImePrompt); googleImeSpeechRecognition.setSpeechRecognitionListener(speechRecognitionListener); googleImeSpeechRecognition.startGoogleIme(); return; } speechRecognizer.setRecognitionListener(speechRecognitionListener); speechRecognizer.startListening(recognizerIntent); } } public void stopSpeechRecognition(){ onSpeechRecognitionListener.OnSpeechRecognitionStopped(); speechRecognizer.stopListening(); if(speechRecognizer != null) speechRecognizer.destroy(); //remove the fragments ((Activity)context).getFragmentManager().beginTransaction().remove(speechRecognitionPermissions).commit(); ((Activity)context).getFragmentManager().beginTransaction().remove(googleImeSpeechRecognition).commit(); } private void initializeGoogleVoiceImeParameters(){ googleImeSpeechRecognition = new GoogleImeSpeechRecognition(); ((Activity) context).getFragmentManager() .beginTransaction() .add(googleImeSpeechRecognition, SpeechRecognition.class.getSimpleName()) .commit(); } private void initializeSpeechRecognitionParameters(){ if(!isSpeechRecognitionAvailable()) throw new IllegalStateException(context.getString(R.string.speech_not_enabled_exception_text)); /* * Initialize the SpeechRecognitionPermissions and googleIme here * for lazy loading the fragments */ initializeGoogleVoiceImeParameters(); speechRecognitionPermissions = new SpeechRecognitionPermissions(); ((Activity) context).getFragmentManager() .beginTransaction() .add(speechRecognitionPermissions, SpeechRecognition.class.getSimpleName()) .commit(); /* *Initialize the SpeechRecognizer and set listener with onSpeechRecognizerListener implemented by client */ speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context); /* *Initialize the Speech recognition intent with default Language */ recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); recognizerIntent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, context.getPackageName()); recognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, MAX_RESULT_COUNT); recognizerIntent.putExtra(RecognizerIntent.EXTRA_PARTIAL_RESULTS, true); /* * Only offline recognition works from API level 23 */ if(enableOnlyOfflineRecognition){ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) recognizerIntent.putExtra(RecognizerIntent.EXTRA_PREFER_OFFLINE, true); } //TODO: Set preferred Speech recognition Language } private void checkProperties(){ if(onSpeechRecognitionListener == null) throw new NullPointerException(context.getString(R.string.speech_listener_null_exception_text)); if(handlePermissions){ if(onSpeechRecognitionPermissionListener == null) throw new NullPointerException(context.getString(R.string.permission_listener_null_exception_text)); } if(speechRecognitionPermissions == null) throw new NullPointerException(); if(speechRecognizer == null) throw new NullPointerException(); } }