From c3755cde41293067cc58133a62138e50c23fb847 Mon Sep 17 00:00:00 2001 From: Ciaran O'Reilly Date: Wed, 25 Nov 2020 22:27:04 +0100 Subject: [PATCH] Added SpeechActivity to handle incoming RecognitionIntents --- app/src/main/AndroidManifest.xml | 76 +++++- .../cat/oreilly/localstt/SpeechActivity.java | 248 ++++++++++++++++++ app/src/main/res/layout/speech_activity.xml | 24 ++ app/src/main/res/values/colors.xml | 6 + app/src/main/res/values/strings.xml | 1 + app/src/main/res/values/styles.xml | 11 + gradle.properties | 3 + 7 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/cat/oreilly/localstt/SpeechActivity.java create mode 100644 app/src/main/res/layout/speech_activity.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/styles.xml create mode 100644 gradle.properties diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b157c2b..9d27404 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,8 +8,82 @@ + android:label="@string/app_name" + android:launchMode="standard" + android:theme="@style/AppTheme"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + mRef; + + private SimpleMessageHandler(SpeechActivity c) { + mRef = new WeakReference<>(c); + } + + public void handleMessage(Message msg) { + SpeechActivity outerClass = mRef.get(); + if (outerClass != null) { + Bundle b = msg.getData(); + String msgAsString = b.getString(MSG); + switch (msg.what) { + case MSG_TOAST: + outerClass.toast(msgAsString); + break; + case MSG_RESULT_ERROR: + outerClass.showError(msgAsString); + break; + default: + break; + } + } + } + } + + protected static Message createMessage(int type, String str) { + Bundle b = new Bundle(); + b.putString(MSG, str); + Message msg = Message.obtain(); + msg.what = type; + msg.setData(b); + return msg; + } + + protected void toast(String message) { + Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); + } + + void showError(String msg) { + editText.setText(msg); + } + + @Override + protected void onCreate(final Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.speech_activity); + if (ContextCompat.checkSelfPermission(this, + Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) { + checkPermission(); + } + + editText = findViewById(R.id.text); + speechRecognizer = SpeechRecognizer.createSpeechRecognizer(this); + + speechRecognizer.setRecognitionListener(new RecognitionListener() { + @Override + public void onReadyForSpeech(Bundle bundle) { + + } + + @Override + public void onBeginningOfSpeech() { + editText.setText(""); + editText.setHint("Listening..."); + } + + @Override + public void onRmsChanged(float v) { + + } + + @Override + public void onBufferReceived(byte[] bytes) { + + } + + @Override + public void onEndOfSpeech() { + speechRecognizer.stopListening(); + } + + @Override + public void onError(int i) { + + } + + @Override + public void onResults(Bundle bundle) { + Log.i(TAG, "onResults"); + ArrayList results = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); + Log.i(TAG, results.get(0)); + editText.setText(results.get(0)); + returnResults(results); + } + + @Override + public void onPartialResults(Bundle bundle) { + Log.i(TAG, "onPartialResults"); + ArrayList data = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); + Log.i(TAG, data.get(0)); + editText.setText(data.get(0)); + } + + @Override + public void onEvent(int i, Bundle bundle) { + Log.d(TAG, bundle.toString()); + } + }); + + } + + @Override + public void onStart() { + super.onStart(); + Log.i(TAG, "onStart"); + final Intent speechRecognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); + speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, + RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); + speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault()); + speechRecognizer.startListening(speechRecognizerIntent); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + Log.i(TAG, "onDestroy"); + speechRecognizer.destroy(); + } + + private void checkPermission() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.RECORD_AUDIO }, + RecordAudioRequestCode); + } + } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, + @NonNull int[] grantResults) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults); + if (requestCode == RecordAudioRequestCode && grantResults.length > 0) { + if (grantResults[0] == PackageManager.PERMISSION_GRANTED) + Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show(); + } + } + + private void returnResults(ArrayList results) { + Handler handler = new SimpleMessageHandler(this); + + Intent incomingIntent = getIntent(); + Log.d(TAG, incomingIntent.toString()); + Bundle extras = incomingIntent.getExtras(); + if (extras == null) { + return; + } + Log.d(TAG, extras.toString()); + PendingIntent pendingIntent = getPendingIntent(extras); + if (pendingIntent == null) { + Log.d(TAG, "No pending intent, setting result intent."); + setResultIntent(handler, results); + } else { + Log.d(TAG, pendingIntent.toString()); + + Bundle bundle = extras.getBundle(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT_BUNDLE); + if (bundle == null) { + bundle = new Bundle(); + } + + Intent intent = new Intent(); + intent.putExtras(bundle); + // This is for Google Maps, YouTube, ... + // intent.putExtra(SearchManager.QUERY, result); + + // Display a toast with the transcription. + handler.sendMessage( + createMessage(MSG_TOAST, String.format(getString(R.string.toastForwardedMatches), results.get(0)))); + try { + Log.d(TAG, "Sending result via pendingIntent"); + pendingIntent.send(this, AppCompatActivity.RESULT_OK, intent); + } catch (PendingIntent.CanceledException e) { + Log.e(TAG, e.getMessage()); + handler.sendMessage(createMessage(MSG_TOAST, e.getMessage())); + } + } + + finish(); + } + + private void setResultIntent(final Handler handler, List matches) { + Intent intent = new Intent(); + intent.putStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS, new ArrayList<>(matches)); + setResult(Activity.RESULT_OK, intent); + } + + private PendingIntent getPendingIntent(Bundle extras) { + Parcelable extraResultsPendingIntentAsParceable = extras + .getParcelable(RecognizerIntent.EXTRA_RESULTS_PENDINGINTENT); + if (extraResultsPendingIntentAsParceable != null) { + // PendingIntent.readPendingIntentOrNullFromParcel(mExtraResultsPendingIntent); + if (extraResultsPendingIntentAsParceable instanceof PendingIntent) { + return (PendingIntent) extraResultsPendingIntentAsParceable; + } + } + return null; + } +} diff --git a/app/src/main/res/layout/speech_activity.xml b/app/src/main/res/layout/speech_activity.xml new file mode 100644 index 0000000..878943c --- /dev/null +++ b/app/src/main/res/layout/speech_activity.xml @@ -0,0 +1,24 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml new file mode 100644 index 0000000..030098f --- /dev/null +++ b/app/src/main/res/values/colors.xml @@ -0,0 +1,6 @@ + + + #6200EE + #3700B3 + #03DAC5 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e1cc34c..315c26b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -4,5 +4,6 @@ LocalSTT Kaldi/Vosk Recognizer Deepspeech Recognizer + Recognized: %1$s diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..5885930 --- /dev/null +++ b/app/src/main/res/values/styles.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..fdd4c96 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,3 @@ +android.enableD8=true +android.enableJetifier=true +android.useAndroidX=true