pbakondy / cordova-plugin-speechrecognition

:microphone: Cordova Plugin for Speech Recognition
MIT License
197 stars 117 forks source link

Is there a way to detect if results are partial or final? #63

Open joshglazer opened 6 years ago

joshglazer commented 6 years ago

When getting back results from the recognition listener, is there a way to tell if the results are partial or final? I am using this plugin for an iOS app.

AugusDogus commented 6 years ago

Yes, there appears to be a way. I remember it showing in the log when testing in Xcode.

joshglazer commented 6 years ago

Thanks for the tip @AugusDogus . I see it in the Xcode logs, but is it available in the typescript/javascript side of things?

pagrawl3 commented 6 years ago

^bump, this would be really useful to have on the JS side.

pstadler1990 commented 2 years ago

There is a simple way if you slightly modify the SpeechRecognition.java file (for Android)

@@ -10,6 +10,7 @@

 import org.json.JSONArray;
 import org.json.JSONException;
+import org.json.JSONObject;

 import android.app.Activity;
 import android.content.Context;
@@ -31,6 +32,7 @@
 import java.util.List;
 import java.util.Locale;

+
 public class SpeechRecognition extends CordovaPlugin {

   private static final String LOG_TAG = "SpeechRecognition";
@@ -292,13 +294,23 @@
     public void onPartialResults(Bundle bundle) {
       ArrayList<String> matches = bundle.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
       Log.d(LOG_TAG, "SpeechRecognitionListener partialResults: " + matches);
+
+      JSONObject jsonObject = new JSONObject();
       JSONArray matchesJSON = new JSONArray(matches);
       try {
         if (matches != null
                 && matches.size() > 0
-                        && !mLastPartialResults.equals(matchesJSON)) {
+                && !mLastPartialResults.equals(matchesJSON)) {
           mLastPartialResults = matchesJSON;
-          PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, matchesJSON);
+
+          try {
+            jsonObject.put("isPartial", true);
+            jsonObject.put("results", matchesJSON);
+          } catch (JSONException e) {
+            e.printStackTrace();
+          }
+
+          PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, jsonObject);
           pluginResult.setKeepCallback(true);
           callbackContext.sendPluginResult(pluginResult);
         }
@@ -318,8 +330,14 @@
       ArrayList<String> matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
       Log.d(LOG_TAG, "SpeechRecognitionListener results: " + matches);
       try {
+        JSONObject jsonObject = new JSONObject();
+
         JSONArray jsonMatches = new JSONArray(matches);
-        callbackContext.success(jsonMatches);
+
+        jsonObject.put("isPartial", false);
+        jsonObject.put("results", jsonMatches);
+
+        callbackContext.success(jsonObject);
       } catch (Exception e) {
         e.printStackTrace();
         callbackContext.error(e.getMessage());

We just wrap the jsonMatches resp. matchesJSON (consistency?) in a JSONObject and provide a new boolean field isPartial.

In the frontend we can now simply access the partial resp. final results:

SpeechRecognition.startListening({
          language: "de-DE",
          matches: 1,
          prompt: "Sagen Sie jetzt etwas",
          showPartial: true,
          showPopup: false,
        }).subscribe((matches) => {
          // Example result: { "isPartial": false, "results": ["hallo Welt wie geht es dir"] }
          if (matches) {
            const isPartial = matches['isPartial']
            const results = matches['results']
            if (isPartial) {
             // do something with the partial results, e.g., show a custom popup with the text
            } else {
              // do something with the final results, e.g., place them in your database or whatever
            }
          }
        }, (onerror) => {
          // error handling
        })
      }

It may not be the most elegant solution, but it does work easily.