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 dexé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.
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
Liens connexes et annexes