Implémentation Speech2Text et WebView

Elements explicatifs de l'implémentation du speech to text sur android en utilisant le composant webview.

Android possède une fonctionnalité de reconnaissance vocale qui n'est pas implémentée au composant webview. La fonctionnalité de reconnaissance vocale startSpeechToText(); utilise la description opérative Recognizerintent qui permet de d’exécuter une action de reconnaissance vocale, celle-ci est une class publique pourvu de plusieurs fonctionnalités intéressantes.

Pour débuter cet article on va configurer la reconnaissance vocale sur android en s'aidant des règlages du téléphone, dans la partie langue et saisie, changer clavier actuel ("keyboard settings") avec le langage que vous souhaitez configurer.

offline-setup-5-6

Puis sélectionnez la langue.

Une fois la langue sélectionnée , revenez au menu précédent et dans l'onglet "Voie" vous aurez le choix entre l'utilisation de la saisie vocale Online ou la saisie vocale Offline, (à savoir, la saisie vocale Hors ligne est moins optimisée que la saisie vocale en ligne).

Désomais on créé notre projet Android (supérieur à l'API 17), puis on se situe dans le fichier MainActivity.java . Je vais déclarer les variables d'import qui sont les suivantes :

package ctrlfagency.com;
/*
 * Author : Thibaut LOMBARD
 * 2016 - 2017
 * */
import java.util.ArrayList;
import java.util.Locale;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;

Vous pouvez constater que la saisie vocale est déclarée ainsi que le composant webview qui permettra par la suite d'ajouter une connection javascript entre un fichier html que j'aurai créé et le bouton de reconnaissance vocale.

On notera que j'ai placé la WebView dans la balise relativelayout de notre layout pour que le bouton Imagebutton ainsi que le textview puisse apparaître en premier plan entre les balises linearlayout.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="0dp"
    android:paddingLeft="0dp"
    android:paddingRight="0dp"
    android:paddingTop="0dp"
    tools:context="ctrlfagency.com.MainActivity"
    android:orientation="vertical" >

    <WebView
        android:id="@+id/activity_main_webview"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true" />

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:gravity="center"
        android:orientation="vertical">
       <ImageButton
            android:id="@+id/btn_mic"
            android:layout_width="80dp"
            android:layout_height="80dp"
            android:background="@null"
            android:scaleType="centerCrop"
            android:src="@drawable/microphone"
            android:layout_gravity="center_horizontal" />
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="Speech to text"
            android:textColor="#0089BF"
            android:textSize="15dp"
            android:textStyle="normal" />
    </LinearLayout>
</RelativeLayout>

Désormais créons un fichier html permettant d'executer une fonction javascript d'exemple afin de vérifier si l'intéraction entre l'application native android et le fichier html (placé dans le dossier /res/assets/ de notre projet) fonctionne correctement.

<!DOCTYPE html>
<!--
Author Thibaut LOMBARD
-->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script src="jquery.min.js"></script>
<title>Demonstration WebView et reconnaissance Vocale</title>
<style type="text/css">
	body {
    overflow-y:hidden;
    overflow-x:hidden;
		}
</style>
</head>
<body>
<INPUT Type="button" onclick="fonctionjs('texte chargé à partir du bouton');" value="Bouton de test">
<div id="afficheletexte"></div>
<script>
$(function(){
	// on fabrique la div map contenu dans la div mapid
	var heightt = (Number(screen.height));
	var widthh = (Number(screen.width));
	var textcontainer = $('#afficheletexte');
	textcontainer.html('<div id="contenu" style="<br> Dimensions de l\'écran : height: '+heightt+'px; width: '+widthh+'px;"> '+heightt+'px; width: '+widthh+'px; Chargé automatiquement au démarrage de l\'application.</div>');
});// fin de fonction document ready
    function fonctionjs(tag)
								{
								var tag = 	tag;
								var heightt = (Number(screen.height));
								var widthh = (Number(screen.width));
								var textcontainer = 	$('#afficheletexte');
								$('#afficheletexte').contents().remove();
								texteaafficher = '<div id="contenu" style="height: '+heightt+'px; width: '+widthh+'px;">Texte chargé dynamiquement :  '+tag+' <br>Dimensions de l\'écran'+heightt+'px; width: '+widthh+' Chargé dynamiquement à l\'aide de l\'api speechtotext</div>';
								textcontainer.html(texteaafficher);
								}
</script>
</body>
</html>

Afin d'afficher une page html, nous pouvons utiliser une WebView , le composant WebView permet l'affichage d'une page web locale ou externe à l'application , celle-ci s'effectue par les lignes suivantes.

// Déclare mWebView à activity_main (le layout)
        mWebView = (WebView) findViewById(R.id.activity_main_webview);

        // Configure la webview pour l'utilisation du javascript
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        //Les lignes suivantes ne sont pas forcément utiles
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setDomStorageEnabled(true);


        // Charge l'url
        mWebView.loadUrl("file:///android_asset/index.html");
        mWebView.setWebViewClient(new WebViewClient());

Par la suite j'ajoute les éléments de bases ainsi que les constantes permettant l'execution de la fonction startSpeechToText(); à l'aide de sa description opérative recognizerintent.

/**
     * Démarre l'opération Speech to Text (intent). 
     * Autorise l'ouverture d'une fenêtre popup (Google Speech Recognition API) permettant l'écoute des commandes vocales.
     * */
    private void startSpeechToText() {
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
                "Dîtes quelque chose...");
        try {
            startActivityForResult(intent, SPEECH_RECOGNITION_CODE);
        } catch (ActivityNotFoundException a) {
            Toast.makeText(getApplicationContext(),
                    "La reconnaissance vocale n'est pas activée sur votre appareil.",
                    Toast.LENGTH_SHORT).show();
        }
    }

J'ajuste par la suite de l'objet activity onActivityResult() la fonction permettant de faire intéragir le composant webview et le bouton de reconnaissance vocale.

/**
     * Callback pour l'activity de reconnaissance vocale
     * */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            case SPEECH_RECOGNITION_CODE: {
                if (resultCode == RESULT_OK && null != data) {

                    ArrayList<String> result = data
                            .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                    String text = result.get(0);
                    mWebView.loadUrl("javascript:fonctionjs('"+text+"');");
                }
                break;
            }

        }
    }

Voila le code source complet de MainActivity.java.

package ctrlfagency.com;
/*
 * Author : Thibaut LOMBARD
 * 2016 - 2017
 * */

import java.util.ArrayList;
import java.util.Locale;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.os.Bundle;
import android.speech.RecognizerIntent;
import android.view.View;
import android.widget.ImageButton;
import android.widget.Toast;
import android.view.View;
import android.webkit.WebSettings;
import android.webkit.WebView;

public class MainActivity extends Activity {

    private final int SPEECH_RECOGNITION_CODE = 1;

    private ImageButton btnMicrophone;
    private WebView mWebView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Déclare mWebView à activity_main (le layout)
        mWebView = (WebView) findViewById(R.id.activity_main_webview);

        // Configure la webview pour l'utilisation du javascript
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);
        //Les lignes suivantes ne sont pas forcément utiles
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webSettings.setDomStorageEnabled(true);


        // Charge l'url
        mWebView.loadUrl("file:///android_asset/index.html");
        mWebView.setWebViewClient(new WebViewClient());
        btnMicrophone = (ImageButton) findViewById(R.id.btn_mic);

        btnMicrophone.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                startSpeechToText();
            }
        });
    }

    private class WebViewClient extends android.webkit.WebViewClient
    {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url)
        {
            return super.shouldOverrideUrlLoading(view, url);
        }
    }
    /**
     * Démarre l'opération Speech to Text (intent). 
     * Autorise l'ouverture d'une fenêtre popup (Google Speech Recognition API) permettant l'écoute des commandes vocales.
     * */
    private void startSpeechToText() {
        Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
                RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
        intent.putExtra(RecognizerIntent.EXTRA_PROMPT,
                "Dîtes quelque chose...");
        try {
            startActivityForResult(intent, SPEECH_RECOGNITION_CODE);
        } catch (ActivityNotFoundException a) {
            Toast.makeText(getApplicationContext(),
                    "La reconnaissance vocale n'est pas activée sur votre appareil.",
                    Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * Callback pour l'activity de reconnaissance vocale
     * */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            case SPEECH_RECOGNITION_CODE: {
                if (resultCode == RESULT_OK && null != data) {

                    ArrayList<String> result = data
                            .getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
                    String text = result.get(0);
                    mWebView.loadUrl("javascript:fonctionjs('"+text+"');");
                }
                break;
            }

        }
    }
}

ScreenShot : Composant WebView et Speech to text

screenvocalr

Liens connexes et annexes