TextToSpeech.java revision 76d9c3cb4f6f8c027959d2951e35a8b37762a1f3
1/*
2 * Copyright (C) 2009 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16package android.speech.tts;
17
18import android.speech.tts.ITts;
19import android.speech.tts.ITtsCallback;
20
21import android.annotation.SdkConstant;
22import android.annotation.SdkConstant.SdkConstantType;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.ServiceConnection;
27import android.media.AudioManager;
28import android.os.IBinder;
29import android.os.RemoteException;
30import android.util.Log;
31
32import java.util.HashMap;
33import java.util.Locale;
34
35/**
36 *
37 * Synthesizes speech from text for immediate playback or to create a sound file.
38 * <p>A TextToSpeech instance can only be used to synthesize text once it has completed its
39 * initialization. Implement the {@link TextToSpeech.OnInitListener} to be
40 * notified of the completion of the initialization.<br>
41 * When you are done using the TextToSpeech instance, call the {@link #shutdown()} method
42 * to release the native resources used by the TextToSpeech engine.
43 *
44 */
45public class TextToSpeech {
46
47    /**
48     * Denotes a successful operation.
49     */
50    public static final int SUCCESS                = 0;
51    /**
52     * Denotes a generic operation failure.
53     */
54    public static final int ERROR                  = -1;
55
56    /**
57     * Queue mode where all entries in the playback queue (media to be played
58     * and text to be synthesized) are dropped and replaced by the new entry.
59     */
60    public static final int QUEUE_FLUSH = 0;
61    /**
62     * Queue mode where the new entry is added at the end of the playback queue.
63     */
64    public static final int QUEUE_ADD = 1;
65
66
67    /**
68     * Denotes the language is available exactly as specified by the locale.
69     */
70    public static final int LANG_COUNTRY_VAR_AVAILABLE = 2;
71
72
73    /**
74     * Denotes the language is available for the language and country specified
75     * by the locale, but not the variant.
76     */
77    public static final int LANG_COUNTRY_AVAILABLE = 1;
78
79
80    /**
81     * Denotes the language is available for the language by the locale,
82     * but not the country and variant.
83     */
84    public static final int LANG_AVAILABLE = 0;
85
86    /**
87     * Denotes the language data is missing.
88     */
89    public static final int LANG_MISSING_DATA = -1;
90
91    /**
92     * Denotes the language is not supported.
93     */
94    public static final int LANG_NOT_SUPPORTED = -2;
95
96
97    /**
98     * Broadcast Action: The TextToSpeech synthesizer has completed processing
99     * of all the text in the speech queue.
100     */
101    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
102    public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
103            "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
104
105
106    /**
107     * Interface definition of a callback to be invoked indicating the completion of the
108     * TextToSpeech engine initialization.
109     */
110    public interface OnInitListener {
111        /**
112         * Called to signal the completion of the TextToSpeech engine initialization.
113         * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
114         */
115        public void onInit(int status);
116    }
117
118    /**
119     * Interface definition of a callback to be invoked indicating the TextToSpeech engine has
120     * completed synthesizing an utterance with an utterance ID set.
121     *
122     */
123    public interface OnUtteranceCompletedListener {
124        /**
125         * Called to signal the completion of the synthesis of the utterance that was identified
126         * with the string parameter. This identifier is the one originally passed in the
127         * parameter hashmap of the synthesis request in
128         * {@link TextToSpeech#speak(String, int, HashMap)} or
129         * {@link TextToSpeech#synthesizeToFile(String, HashMap, String)} with the
130         * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID} key.
131         * @param utteranceId the identifier of the utterance.
132         */
133        public void onUtteranceCompleted(String utteranceId);
134    }
135
136
137    /**
138     * Internal constants for the TextToSpeech functionality
139     *
140     */
141    public class Engine {
142        // default values for a TTS engine when settings are not found in the provider
143        /**
144         * {@hide}
145         */
146        public static final int DEFAULT_RATE = 100; // 1x
147        /**
148         * {@hide}
149         */
150        public static final int DEFAULT_PITCH = 100;// 1x
151        /**
152         * {@hide}
153         */
154        public static final int USE_DEFAULTS = 0; // false
155        /**
156         * {@hide}
157         */
158        public static final String DEFAULT_SYNTH = "com.svox.pico";
159
160        // default values for rendering
161        /**
162         * Default audio stream used when playing synthesized speech.
163         */
164        public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
165
166        // return codes for a TTS engine's check data activity
167        /**
168         * Indicates success when checking the installation status of the resources used by the
169         * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
170         */
171        public static final int CHECK_VOICE_DATA_PASS = 1;
172        /**
173         * Indicates failure when checking the installation status of the resources used by the
174         * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
175         */
176        public static final int CHECK_VOICE_DATA_FAIL = 0;
177        /**
178         * Indicates erroneous data when checking the installation status of the resources used by
179         * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
180         */
181        public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
182        /**
183         * Indicates missing resources when checking the installation status of the resources used
184         * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
185         */
186        public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
187        /**
188         * Indicates missing storage volume when checking the installation status of the resources
189         * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
190         */
191        public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
192
193        // intents to ask engine to install data or check its data
194        /**
195         * Activity Action: Triggers the platform TextToSpeech engine to
196         * start the activity that installs the resource files on the device
197         * that are required for TTS to be operational. Since the installation
198         * of the data can be interrupted or declined by the user, the application
199         * shouldn't expect successful installation upon return from that intent,
200         * and if need be, should check installation status with
201         * {@link #ACTION_CHECK_TTS_DATA}.
202         */
203        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
204        public static final String ACTION_INSTALL_TTS_DATA =
205                "android.speech.tts.engine.INSTALL_TTS_DATA";
206
207        /**
208         * Broadcast Action: broadcast to signal the completion of the installation of
209         * the data files used by the synthesis engine. Success or failure is indicated in the
210         * {@link #EXTRA_TTS_DATA_INSTALLED} extra.
211         */
212        @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
213        public static final String ACTION_TTS_DATA_INSTALLED =
214                "android.speech.tts.engine.TTS_DATA_INSTALLED";
215        /**
216         * Activity Action: Starts the activity from the platform TextToSpeech
217         * engine to verify the proper installation and availability of the
218         * resource files on the system. Upon completion, the activity will
219         * return one of the following codes:
220         * {@link #CHECK_VOICE_DATA_PASS},
221         * {@link #CHECK_VOICE_DATA_FAIL},
222         * {@link #CHECK_VOICE_DATA_BAD_DATA},
223         * {@link #CHECK_VOICE_DATA_MISSING_DATA}, or
224         * {@link #CHECK_VOICE_DATA_MISSING_VOLUME}.
225         * <p> Moreover, the data received in the activity result will contain the following
226         * fields:
227         * <ul>
228         *   <li>{@link #EXTRA_VOICE_DATA_ROOT_DIRECTORY} which
229         *       indicates the path to the location of the resource files,</li>
230         *   <li>{@link #EXTRA_VOICE_DATA_FILES} which contains
231         *       the list of all the resource files,</li>
232         *   <li>and {@link #EXTRA_VOICE_DATA_FILES_INFO} which
233         *       contains, for each resource file, the description of the language covered by
234         *       the file in the xxx-YYY format, where xxx is the 3-letter ISO language code,
235         *       and YYY is the 3-letter ISO country code.</li>
236         * </ul>
237         */
238        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
239        public static final String ACTION_CHECK_TTS_DATA =
240                "android.speech.tts.engine.CHECK_TTS_DATA";
241
242        // extras for a TTS engine's check data activity
243        /**
244         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
245         * the TextToSpeech engine specifies the path to its resources.
246         */
247        public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
248        /**
249         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
250         * the TextToSpeech engine specifies the file names of its resources under the
251         * resource path.
252         */
253        public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
254        /**
255         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
256         * the TextToSpeech engine specifies the locale associated with each resource file.
257         */
258        public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
259        /**
260         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
261         * the TextToSpeech engine returns an ArrayList<String> of all the available voices.
262         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
263         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
264         */
265        public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
266        /**
267         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
268         * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
269         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
270         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
271         */
272        public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
273        /**
274         * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
275         * caller indicates to the TextToSpeech engine which specific sets of voice data to
276         * check for by sending an ArrayList<String> of the voices that are of interest.
277         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
278         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
279         */
280        public static final String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor";
281
282        // extras for a TTS engine's data installation
283        /**
284         * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent.
285         * It indicates whether the data files for the synthesis engine were successfully
286         * installed. The installation was initiated with the  {@link #ACTION_INSTALL_TTS_DATA}
287         * intent. The possible values for this extra are
288         * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}.
289         */
290        public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
291
292        // keys for the parameters passed with speak commands. Hidden keys are used internally
293        // to maintain engine state for each TextToSpeech instance.
294        /**
295         * {@hide}
296         */
297        public static final String KEY_PARAM_RATE = "rate";
298        /**
299         * {@hide}
300         */
301        public static final String KEY_PARAM_LANGUAGE = "language";
302        /**
303         * {@hide}
304         */
305        public static final String KEY_PARAM_COUNTRY = "country";
306        /**
307         * {@hide}
308         */
309        public static final String KEY_PARAM_VARIANT = "variant";
310        /**
311         * {@hide}
312         */
313        public static final String KEY_PARAM_ENGINE = "engine";
314        /**
315         * Parameter key to specify the audio stream type to be used when speaking text
316         * or playing back a file.
317         * @see TextToSpeech#speak(String, int, HashMap)
318         * @see TextToSpeech#playEarcon(String, int, HashMap)
319         */
320        public static final String KEY_PARAM_STREAM = "streamType";
321        /**
322         * Parameter key to identify an utterance in the
323         * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
324         * spoken, a file has been played back or a silence duration has elapsed.
325         * @see TextToSpeech#speak(String, int, HashMap)
326         * @see TextToSpeech#playEarcon(String, int, HashMap)
327         * @see TextToSpeech#synthesizeToFile(String, HashMap, String)
328         */
329        public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
330
331        // key positions in the array of cached parameters
332        /**
333         * {@hide}
334         */
335        protected static final int PARAM_POSITION_RATE = 0;
336        /**
337         * {@hide}
338         */
339        protected static final int PARAM_POSITION_LANGUAGE = 2;
340        /**
341         * {@hide}
342         */
343        protected static final int PARAM_POSITION_COUNTRY = 4;
344        /**
345         * {@hide}
346         */
347        protected static final int PARAM_POSITION_VARIANT = 6;
348        /**
349         * {@hide}
350         */
351        protected static final int PARAM_POSITION_STREAM = 8;
352        /**
353         * {@hide}
354         */
355        protected static final int PARAM_POSITION_UTTERANCE_ID = 10;
356
357        /**
358         * {@hide}
359         */
360        protected static final int PARAM_POSITION_ENGINE = 12;
361
362        /**
363         * {@hide}
364         */
365        protected static final int NB_CACHED_PARAMS = 7;
366    }
367
368    /**
369     * Connection needed for the TTS.
370     */
371    private ServiceConnection mServiceConnection;
372
373    private ITts mITts = null;
374    private ITtsCallback mITtscallback = null;
375    private Context mContext = null;
376    private String mPackageName = "";
377    private OnInitListener mInitListener = null;
378    private boolean mStarted = false;
379    private final Object mStartLock = new Object();
380    /**
381     * Used to store the cached parameters sent along with each synthesis request to the
382     * TTS service.
383     */
384    private String[] mCachedParams;
385
386    /**
387     * The constructor for the TextToSpeech class.
388     * This will also initialize the associated TextToSpeech engine if it isn't already running.
389     *
390     * @param context
391     *            The context this instance is running in.
392     * @param listener
393     *            The {@link TextToSpeech.OnInitListener} that will be called when the
394     *            TextToSpeech engine has initialized.
395     */
396    public TextToSpeech(Context context, OnInitListener listener) {
397        mContext = context;
398        mPackageName = mContext.getPackageName();
399        mInitListener = listener;
400
401        mCachedParams = new String[2*Engine.NB_CACHED_PARAMS]; // store key and value
402        mCachedParams[Engine.PARAM_POSITION_RATE] = Engine.KEY_PARAM_RATE;
403        mCachedParams[Engine.PARAM_POSITION_LANGUAGE] = Engine.KEY_PARAM_LANGUAGE;
404        mCachedParams[Engine.PARAM_POSITION_COUNTRY] = Engine.KEY_PARAM_COUNTRY;
405        mCachedParams[Engine.PARAM_POSITION_VARIANT] = Engine.KEY_PARAM_VARIANT;
406        mCachedParams[Engine.PARAM_POSITION_STREAM] = Engine.KEY_PARAM_STREAM;
407        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID] = Engine.KEY_PARAM_UTTERANCE_ID;
408        mCachedParams[Engine.PARAM_POSITION_ENGINE] = Engine.KEY_PARAM_ENGINE;
409
410        mCachedParams[Engine.PARAM_POSITION_RATE + 1] =
411                String.valueOf(Engine.DEFAULT_RATE);
412        // initialize the language cached parameters with the current Locale
413        Locale defaultLoc = Locale.getDefault();
414        mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = defaultLoc.getISO3Language();
415        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = defaultLoc.getISO3Country();
416        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = defaultLoc.getVariant();
417        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
418                String.valueOf(Engine.DEFAULT_STREAM);
419        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = "";
420        mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = Engine.DEFAULT_SYNTH;
421
422        initTts();
423    }
424
425
426    private void initTts() {
427        mStarted = false;
428
429        // Initialize the TTS, run the callback after the binding is successful
430        mServiceConnection = new ServiceConnection() {
431            public void onServiceConnected(ComponentName name, IBinder service) {
432                synchronized(mStartLock) {
433                    mITts = ITts.Stub.asInterface(service);
434                    mStarted = true;
435                    if (mInitListener != null) {
436                        // TODO manage failures and missing resources
437                        mInitListener.onInit(SUCCESS);
438                    }
439                }
440            }
441
442            public void onServiceDisconnected(ComponentName name) {
443                synchronized(mStartLock) {
444                    mITts = null;
445                    mInitListener = null;
446                    mStarted = false;
447                }
448            }
449        };
450
451        Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
452        intent.addCategory("android.intent.category.TTS");
453        mContext.bindService(intent, mServiceConnection,
454                Context.BIND_AUTO_CREATE);
455        // TODO handle case where the binding works (should always work) but
456        //      the plugin fails
457    }
458
459
460    /**
461     * Releases the resources used by the TextToSpeech engine.
462     * It is good practice for instance to call this method in the onDestroy() method of an Activity
463     * so the TextToSpeech engine can be cleanly stopped.
464     */
465    public void shutdown() {
466        try {
467            mContext.unbindService(mServiceConnection);
468        } catch (IllegalArgumentException e) {
469            // Do nothing and fail silently since an error here indicates that
470            // binding never succeeded in the first place.
471        }
472    }
473
474
475    /**
476     * Adds a mapping between a string of text and a sound resource in a
477     * package. After a call to this method, subsequent calls to
478     * {@link #speak(String, int, HashMap)} will play the specified sound resource
479     * if it is available, or synthesize the text it is missing.
480     *
481     * @param text
482     *            The string of text. Example: <code>"south_south_east"</code>
483     *
484     * @param packagename
485     *            Pass the packagename of the application that contains the
486     *            resource. If the resource is in your own application (this is
487     *            the most common case), then put the packagename of your
488     *            application here.<br/>
489     *            Example: <b>"com.google.marvin.compass"</b><br/>
490     *            The packagename can be found in the AndroidManifest.xml of
491     *            your application.
492     *            <p>
493     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
494     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
495     *            </p>
496     *
497     * @param resourceId
498     *            Example: <code>R.raw.south_south_east</code>
499     *
500     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
501     */
502    public int addSpeech(String text, String packagename, int resourceId) {
503        synchronized(mStartLock) {
504            if (!mStarted) {
505                return ERROR;
506            }
507            try {
508                mITts.addSpeech(mPackageName, text, packagename, resourceId);
509                return SUCCESS;
510            } catch (RemoteException e) {
511                // TTS died; restart it.
512                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
513                e.printStackTrace();
514                mStarted = false;
515                initTts();
516            } catch (NullPointerException e) {
517                // TTS died; restart it.
518                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
519                e.printStackTrace();
520                mStarted = false;
521                initTts();
522            } catch (IllegalStateException e) {
523                // TTS died; restart it.
524                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
525                e.printStackTrace();
526                mStarted = false;
527                initTts();
528            }
529            return ERROR;
530        }
531    }
532
533
534    /**
535     * Adds a mapping between a string of text and a sound file. Using this, it
536     * is possible to add custom pronounciations for a string of text.
537     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
538     * will play the specified sound resource if it is available, or synthesize the text it is
539     * missing.
540     *
541     * @param text
542     *            The string of text. Example: <code>"south_south_east"</code>
543     * @param filename
544     *            The full path to the sound file (for example:
545     *            "/sdcard/mysounds/hello.wav")
546     *
547     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
548     */
549    public int addSpeech(String text, String filename) {
550        synchronized (mStartLock) {
551            if (!mStarted) {
552                return ERROR;
553            }
554            try {
555                mITts.addSpeechFile(mPackageName, text, filename);
556                return SUCCESS;
557            } catch (RemoteException e) {
558                // TTS died; restart it.
559                Log.e("TextToSpeech.java - addSpeech", "RemoteException");
560                e.printStackTrace();
561                mStarted = false;
562                initTts();
563            } catch (NullPointerException e) {
564                // TTS died; restart it.
565                Log.e("TextToSpeech.java - addSpeech", "NullPointerException");
566                e.printStackTrace();
567                mStarted = false;
568                initTts();
569            } catch (IllegalStateException e) {
570                // TTS died; restart it.
571                Log.e("TextToSpeech.java - addSpeech", "IllegalStateException");
572                e.printStackTrace();
573                mStarted = false;
574                initTts();
575            }
576            return ERROR;
577        }
578    }
579
580
581    /**
582     * Adds a mapping between a string of text and a sound resource in a
583     * package. Use this to add custom earcons.
584     *
585     * @see #playEarcon(String, int, HashMap)
586     *
587     * @param earcon The name of the earcon.
588     *            Example: <code>"[tick]"</code><br/>
589     *
590     * @param packagename
591     *            the package name of the application that contains the
592     *            resource. This can for instance be the package name of your own application.
593     *            Example: <b>"com.google.marvin.compass"</b><br/>
594     *            The package name can be found in the AndroidManifest.xml of
595     *            the application containing the resource.
596     *            <p>
597     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
598     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
599     *            </p>
600     *
601     * @param resourceId
602     *            Example: <code>R.raw.tick_snd</code>
603     *
604     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
605     */
606    public int addEarcon(String earcon, String packagename, int resourceId) {
607        synchronized(mStartLock) {
608            if (!mStarted) {
609                return ERROR;
610            }
611            try {
612                mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
613                return SUCCESS;
614            } catch (RemoteException e) {
615                // TTS died; restart it.
616                Log.e("TextToSpeech.java - addEarcon", "RemoteException");
617                e.printStackTrace();
618                mStarted = false;
619                initTts();
620            } catch (NullPointerException e) {
621                // TTS died; restart it.
622                Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
623                e.printStackTrace();
624                mStarted = false;
625                initTts();
626            } catch (IllegalStateException e) {
627                // TTS died; restart it.
628                Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
629                e.printStackTrace();
630                mStarted = false;
631                initTts();
632            }
633            return ERROR;
634        }
635    }
636
637
638    /**
639     * Adds a mapping between a string of text and a sound file.
640     * Use this to add custom earcons.
641     *
642     * @see #playEarcon(String, int, HashMap)
643     *
644     * @param earcon
645     *            The name of the earcon.
646     *            Example: <code>"[tick]"</code>
647     * @param filename
648     *            The full path to the sound file (for example:
649     *            "/sdcard/mysounds/tick.wav")
650     *
651     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
652     */
653    public int addEarcon(String earcon, String filename) {
654        synchronized (mStartLock) {
655            if (!mStarted) {
656                return ERROR;
657            }
658            try {
659                mITts.addEarconFile(mPackageName, earcon, filename);
660                return SUCCESS;
661            } catch (RemoteException e) {
662                // TTS died; restart it.
663                Log.e("TextToSpeech.java - addEarcon", "RemoteException");
664                e.printStackTrace();
665                mStarted = false;
666                initTts();
667            } catch (NullPointerException e) {
668                // TTS died; restart it.
669                Log.e("TextToSpeech.java - addEarcon", "NullPointerException");
670                e.printStackTrace();
671                mStarted = false;
672                initTts();
673            } catch (IllegalStateException e) {
674                // TTS died; restart it.
675                Log.e("TextToSpeech.java - addEarcon", "IllegalStateException");
676                e.printStackTrace();
677                mStarted = false;
678                initTts();
679            }
680            return ERROR;
681        }
682    }
683
684
685    /**
686     * Speaks the string using the specified queuing strategy and speech
687     * parameters.
688     *
689     * @param text
690     *            The string of text to be spoken.
691     * @param queueMode
692     *            The queuing strategy to use.
693     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
694     * @param params
695     *            The list of parameters to be used. Can be null if no parameters are given.
696     *            They are specified using a (key, value) pair, where the key can be
697     *            {@link Engine#KEY_PARAM_STREAM} or
698     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
699     *
700     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
701     */
702    public int speak(String text, int queueMode, HashMap<String,String> params)
703    {
704        synchronized (mStartLock) {
705            int result = ERROR;
706            Log.i("TTS received: ", text);
707            if (!mStarted) {
708                return result;
709            }
710            try {
711                if ((params != null) && (!params.isEmpty())) {
712                    String extra = params.get(Engine.KEY_PARAM_STREAM);
713                    if (extra != null) {
714                        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
715                    }
716                    extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
717                    if (extra != null) {
718                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
719                    }
720                    extra = params.get(Engine.KEY_PARAM_ENGINE);
721                    if (extra != null) {
722                        mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = extra;
723                    }
724                }
725                result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
726            } catch (RemoteException e) {
727                // TTS died; restart it.
728                Log.e("TextToSpeech.java - speak", "RemoteException");
729                e.printStackTrace();
730                mStarted = false;
731                initTts();
732            } catch (NullPointerException e) {
733                // TTS died; restart it.
734                Log.e("TextToSpeech.java - speak", "NullPointerException");
735                e.printStackTrace();
736                mStarted = false;
737                initTts();
738            } catch (IllegalStateException e) {
739                // TTS died; restart it.
740                Log.e("TextToSpeech.java - speak", "IllegalStateException");
741                e.printStackTrace();
742                mStarted = false;
743                initTts();
744            } finally {
745                resetCachedParams();
746                return result;
747            }
748        }
749    }
750
751
752    /**
753     * Plays the earcon using the specified queueing mode and parameters.
754     *
755     * @param earcon
756     *            The earcon that should be played
757     * @param queueMode
758     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
759     * @param params
760     *            The list of parameters to be used. Can be null if no parameters are given.
761     *            They are specified using a (key, value) pair, where the key can be
762     *            {@link Engine#KEY_PARAM_STREAM} or
763     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
764     *
765     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
766     */
767    public int playEarcon(String earcon, int queueMode,
768            HashMap<String,String> params) {
769        synchronized (mStartLock) {
770            int result = ERROR;
771            if (!mStarted) {
772                return result;
773            }
774            try {
775                if ((params != null) && (!params.isEmpty())) {
776                    String extra = params.get(Engine.KEY_PARAM_STREAM);
777                    if (extra != null) {
778                        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
779                    }
780                    extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
781                    if (extra != null) {
782                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
783                    }
784                }
785                result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
786            } catch (RemoteException e) {
787                // TTS died; restart it.
788                Log.e("TextToSpeech.java - playEarcon", "RemoteException");
789                e.printStackTrace();
790                mStarted = false;
791                initTts();
792            } catch (NullPointerException e) {
793                // TTS died; restart it.
794                Log.e("TextToSpeech.java - playEarcon", "NullPointerException");
795                e.printStackTrace();
796                mStarted = false;
797                initTts();
798            } catch (IllegalStateException e) {
799                // TTS died; restart it.
800                Log.e("TextToSpeech.java - playEarcon", "IllegalStateException");
801                e.printStackTrace();
802                mStarted = false;
803                initTts();
804            } finally {
805                resetCachedParams();
806                return result;
807            }
808        }
809    }
810
811    /**
812     * Plays silence for the specified amount of time using the specified
813     * queue mode.
814     *
815     * @param durationInMs
816     *            A long that indicates how long the silence should last.
817     * @param queueMode
818     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
819     * @param params
820     *            The list of parameters to be used. Can be null if no parameters are given.
821     *            They are specified using a (key, value) pair, where the key can be
822     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
823     *
824     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
825     */
826    public int playSilence(long durationInMs, int queueMode, HashMap<String,String> params) {
827        synchronized (mStartLock) {
828            int result = ERROR;
829            if (!mStarted) {
830                return result;
831            }
832            try {
833                if ((params != null) && (!params.isEmpty())) {
834                    String extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
835                    if (extra != null) {
836                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
837                    }
838                }
839                result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
840            } catch (RemoteException e) {
841                // TTS died; restart it.
842                Log.e("TextToSpeech.java - playSilence", "RemoteException");
843                e.printStackTrace();
844                mStarted = false;
845                initTts();
846            } catch (NullPointerException e) {
847                // TTS died; restart it.
848                Log.e("TextToSpeech.java - playSilence", "NullPointerException");
849                e.printStackTrace();
850                mStarted = false;
851                initTts();
852            } catch (IllegalStateException e) {
853                // TTS died; restart it.
854                Log.e("TextToSpeech.java - playSilence", "IllegalStateException");
855                e.printStackTrace();
856                mStarted = false;
857                initTts();
858            } finally {
859                return result;
860            }
861        }
862    }
863
864
865    /**
866     * Returns whether or not the TextToSpeech engine is busy speaking.
867     *
868     * @return Whether or not the TextToSpeech engine is busy speaking.
869     */
870    public boolean isSpeaking() {
871        synchronized (mStartLock) {
872            if (!mStarted) {
873                return false;
874            }
875            try {
876                return mITts.isSpeaking();
877            } catch (RemoteException e) {
878                // TTS died; restart it.
879                Log.e("TextToSpeech.java - isSpeaking", "RemoteException");
880                e.printStackTrace();
881                mStarted = false;
882                initTts();
883            } catch (NullPointerException e) {
884                // TTS died; restart it.
885                Log.e("TextToSpeech.java - isSpeaking", "NullPointerException");
886                e.printStackTrace();
887                mStarted = false;
888                initTts();
889            } catch (IllegalStateException e) {
890                // TTS died; restart it.
891                Log.e("TextToSpeech.java - isSpeaking", "IllegalStateException");
892                e.printStackTrace();
893                mStarted = false;
894                initTts();
895            }
896            return false;
897        }
898    }
899
900
901    /**
902     * Interrupts the current utterance (whether played or rendered to file) and discards other
903     * utterances in the queue.
904     *
905     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
906     */
907    public int stop() {
908        synchronized (mStartLock) {
909            int result = ERROR;
910            if (!mStarted) {
911                return result;
912            }
913            try {
914                result = mITts.stop(mPackageName);
915            } catch (RemoteException e) {
916                // TTS died; restart it.
917                Log.e("TextToSpeech.java - stop", "RemoteException");
918                e.printStackTrace();
919                mStarted = false;
920                initTts();
921            } catch (NullPointerException e) {
922                // TTS died; restart it.
923                Log.e("TextToSpeech.java - stop", "NullPointerException");
924                e.printStackTrace();
925                mStarted = false;
926                initTts();
927            } catch (IllegalStateException e) {
928                // TTS died; restart it.
929                Log.e("TextToSpeech.java - stop", "IllegalStateException");
930                e.printStackTrace();
931                mStarted = false;
932                initTts();
933            } finally {
934                return result;
935            }
936        }
937    }
938
939
940    /**
941     * Sets the speech rate for the TextToSpeech engine.
942     *
943     * This has no effect on any pre-recorded speech.
944     *
945     * @param speechRate
946     *            The speech rate for the TextToSpeech engine. 1 is the normal speed,
947     *            lower values slow down the speech (0.5 is half the normal speech rate),
948     *            greater values accelerate it (2 is twice the normal speech rate).
949     *
950     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
951     */
952    public int setSpeechRate(float speechRate) {
953        synchronized (mStartLock) {
954            int result = ERROR;
955            if (!mStarted) {
956                return result;
957            }
958            try {
959                if (speechRate > 0) {
960                    int rate = (int)(speechRate*100);
961                    mCachedParams[Engine.PARAM_POSITION_RATE + 1] = String.valueOf(rate);
962                    // the rate is not set here, instead it is cached so it will be associated
963                    // with all upcoming utterances.
964                    if (speechRate > 0.0f) {
965                        result = SUCCESS;
966                    } else {
967                        result = ERROR;
968                    }
969                }
970            } catch (NullPointerException e) {
971                // TTS died; restart it.
972                Log.e("TextToSpeech.java - setSpeechRate", "NullPointerException");
973                e.printStackTrace();
974                mStarted = false;
975                initTts();
976            } catch (IllegalStateException e) {
977                // TTS died; restart it.
978                Log.e("TextToSpeech.java - setSpeechRate", "IllegalStateException");
979                e.printStackTrace();
980                mStarted = false;
981                initTts();
982            } finally {
983                return result;
984            }
985        }
986    }
987
988
989    /**
990     * Sets the speech pitch for the TextToSpeech engine.
991     *
992     * This has no effect on any pre-recorded speech.
993     *
994     * @param pitch
995     *            The pitch for the TextToSpeech engine. 1 is the normal pitch,
996     *            lower values lower the tone of the synthesized voice,
997     *            greater values increase it.
998     *
999     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1000     */
1001    public int setPitch(float pitch) {
1002        synchronized (mStartLock) {
1003            int result = ERROR;
1004            if (!mStarted) {
1005                return result;
1006            }
1007            try {
1008                if (pitch > 0) {
1009                    result = mITts.setPitch(mPackageName, (int)(pitch*100));
1010                }
1011            } catch (RemoteException e) {
1012                // TTS died; restart it.
1013                Log.e("TextToSpeech.java - setPitch", "RemoteException");
1014                e.printStackTrace();
1015                mStarted = false;
1016                initTts();
1017            } catch (NullPointerException e) {
1018                // TTS died; restart it.
1019                Log.e("TextToSpeech.java - setPitch", "NullPointerException");
1020                e.printStackTrace();
1021                mStarted = false;
1022                initTts();
1023            } catch (IllegalStateException e) {
1024                // TTS died; restart it.
1025                Log.e("TextToSpeech.java - setPitch", "IllegalStateException");
1026                e.printStackTrace();
1027                mStarted = false;
1028                initTts();
1029            } finally {
1030                return result;
1031            }
1032        }
1033    }
1034
1035
1036    /**
1037     * Sets the language for the TextToSpeech engine.
1038     * The TextToSpeech engine will try to use the closest match to the specified
1039     * language as represented by the Locale, but there is no guarantee that the exact same Locale
1040     * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
1041     * before choosing the language to use for the next utterances.
1042     *
1043     * @param loc
1044     *            The locale describing the language to be used.
1045     *
1046     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1047     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1048     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1049     */
1050    public int setLanguage(Locale loc) {
1051        synchronized (mStartLock) {
1052            int result = LANG_NOT_SUPPORTED;
1053            if (!mStarted) {
1054                return result;
1055            }
1056            try {
1057                mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = loc.getISO3Language();
1058                mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = loc.getISO3Country();
1059                mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = loc.getVariant();
1060                // the language is not set here, instead it is cached so it will be associated
1061                // with all upcoming utterances. But we still need to report the language support,
1062                // which is achieved by calling isLanguageAvailable()
1063                result = mITts.isLanguageAvailable(
1064                        mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1],
1065                        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1],
1066                        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] );
1067            } catch (RemoteException e) {
1068                // TTS died; restart it.
1069                Log.e("TextToSpeech.java - setLanguage", "RemoteException");
1070                e.printStackTrace();
1071                mStarted = false;
1072                initTts();
1073            } catch (NullPointerException e) {
1074                // TTS died; restart it.
1075                Log.e("TextToSpeech.java - setLanguage", "NullPointerException");
1076                e.printStackTrace();
1077                mStarted = false;
1078                initTts();
1079            } catch (IllegalStateException e) {
1080                // TTS died; restart it.
1081                Log.e("TextToSpeech.java - setLanguage", "IllegalStateException");
1082                e.printStackTrace();
1083                mStarted = false;
1084                initTts();
1085            } finally {
1086                return result;
1087            }
1088        }
1089    }
1090
1091
1092    /**
1093     * Returns a Locale instance describing the language currently being used by the TextToSpeech
1094     * engine.
1095     * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
1096     *     instance, or null is the TextToSpeech engine has failed.
1097     */
1098    public Locale getLanguage() {
1099        synchronized (mStartLock) {
1100            if (!mStarted) {
1101                return null;
1102            }
1103            try {
1104                String[] locStrings = mITts.getLanguage();
1105                if ((locStrings != null) && (locStrings.length == 3)) {
1106                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
1107                } else {
1108                    return null;
1109                }
1110            } catch (RemoteException e) {
1111                // TTS died; restart it.
1112                Log.e("TextToSpeech.java - getLanguage", "RemoteException");
1113                e.printStackTrace();
1114                mStarted = false;
1115                initTts();
1116            } catch (NullPointerException e) {
1117                // TTS died; restart it.
1118                Log.e("TextToSpeech.java - getLanguage", "NullPointerException");
1119                e.printStackTrace();
1120                mStarted = false;
1121                initTts();
1122            } catch (IllegalStateException e) {
1123                // TTS died; restart it.
1124                Log.e("TextToSpeech.java - getLanguage", "IllegalStateException");
1125                e.printStackTrace();
1126                mStarted = false;
1127                initTts();
1128            }
1129            return null;
1130        }
1131    }
1132
1133    /**
1134     * Checks if the specified language as represented by the Locale is available and supported.
1135     *
1136     * @param loc
1137     *            The Locale describing the language to be used.
1138     *
1139     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1140     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1141     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1142     */
1143    public int isLanguageAvailable(Locale loc) {
1144        synchronized (mStartLock) {
1145            int result = LANG_NOT_SUPPORTED;
1146            if (!mStarted) {
1147                return result;
1148            }
1149            try {
1150                result = mITts.isLanguageAvailable(loc.getISO3Language(),
1151                        loc.getISO3Country(), loc.getVariant());
1152            } catch (RemoteException e) {
1153                // TTS died; restart it.
1154                Log.e("TextToSpeech.java - isLanguageAvailable", "RemoteException");
1155                e.printStackTrace();
1156                mStarted = false;
1157                initTts();
1158            } catch (NullPointerException e) {
1159                // TTS died; restart it.
1160                Log.e("TextToSpeech.java - isLanguageAvailable", "NullPointerException");
1161                e.printStackTrace();
1162                mStarted = false;
1163                initTts();
1164            } catch (IllegalStateException e) {
1165                // TTS died; restart it.
1166                Log.e("TextToSpeech.java - isLanguageAvailable", "IllegalStateException");
1167                e.printStackTrace();
1168                mStarted = false;
1169                initTts();
1170            } finally {
1171                return result;
1172            }
1173        }
1174    }
1175
1176
1177    /**
1178     * Synthesizes the given text to a file using the specified parameters.
1179     *
1180     * @param text
1181     *            The String of text that should be synthesized
1182     * @param params
1183     *            The list of parameters to be used. Can be null if no parameters are given.
1184     *            They are specified using a (key, value) pair, where the key can be
1185     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1186     * @param filename
1187     *            The string that gives the full output filename; it should be
1188     *            something like "/sdcard/myappsounds/mysound.wav".
1189     *
1190     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1191     */
1192    public int synthesizeToFile(String text, HashMap<String,String> params,
1193            String filename) {
1194        synchronized (mStartLock) {
1195            int result = ERROR;
1196            if (!mStarted) {
1197                return result;
1198            }
1199            try {
1200                if ((params != null) && (!params.isEmpty())) {
1201                    // no need to read the stream type here
1202                    String extra = params.get(Engine.KEY_PARAM_UTTERANCE_ID);
1203                    if (extra != null) {
1204                        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra;
1205                    }
1206                    extra = params.get(Engine.KEY_PARAM_ENGINE);
1207                    if (extra != null) {
1208                        mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = extra;
1209                    }
1210                }
1211                if (mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename)){
1212                    result = SUCCESS;
1213                }
1214            } catch (RemoteException e) {
1215                // TTS died; restart it.
1216                Log.e("TextToSpeech.java - synthesizeToFile", "RemoteException");
1217                e.printStackTrace();
1218                mStarted = false;
1219                initTts();
1220            } catch (NullPointerException e) {
1221                // TTS died; restart it.
1222                Log.e("TextToSpeech.java - synthesizeToFile", "NullPointerException");
1223                e.printStackTrace();
1224                mStarted = false;
1225                initTts();
1226            } catch (IllegalStateException e) {
1227                // TTS died; restart it.
1228                Log.e("TextToSpeech.java - synthesizeToFile", "IllegalStateException");
1229                e.printStackTrace();
1230                mStarted = false;
1231                initTts();
1232            } finally {
1233                resetCachedParams();
1234                return result;
1235            }
1236        }
1237    }
1238
1239
1240    /**
1241     * Convenience method to reset the cached parameters to the current default values
1242     * if they are not persistent between calls to the service.
1243     */
1244    private void resetCachedParams() {
1245        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
1246                String.valueOf(Engine.DEFAULT_STREAM);
1247        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID+ 1] = "";
1248    }
1249
1250    /**
1251     * Sets the OnUtteranceCompletedListener that will fire when an utterance completes.
1252     *
1253     * @param listener
1254     *            The OnUtteranceCompletedListener
1255     *
1256     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1257     */
1258    public int setOnUtteranceCompletedListener(
1259            final OnUtteranceCompletedListener listener) {
1260        synchronized (mStartLock) {
1261            int result = ERROR;
1262            if (!mStarted) {
1263                return result;
1264            }
1265            mITtscallback = new ITtsCallback.Stub() {
1266                public void utteranceCompleted(String utteranceId) throws RemoteException {
1267                    if (listener != null) {
1268                        listener.onUtteranceCompleted(utteranceId);
1269                    }
1270                }
1271            };
1272            try {
1273                result = mITts.registerCallback(mPackageName, mITtscallback);
1274            } catch (RemoteException e) {
1275                // TTS died; restart it.
1276                Log.e("TextToSpeech.java - registerCallback", "RemoteException");
1277                e.printStackTrace();
1278                mStarted = false;
1279                initTts();
1280            } catch (NullPointerException e) {
1281                // TTS died; restart it.
1282                Log.e("TextToSpeech.java - registerCallback", "NullPointerException");
1283                e.printStackTrace();
1284                mStarted = false;
1285                initTts();
1286            } catch (IllegalStateException e) {
1287                // TTS died; restart it.
1288                Log.e("TextToSpeech.java - registerCallback", "IllegalStateException");
1289                e.printStackTrace();
1290                mStarted = false;
1291                initTts();
1292            } finally {
1293                return result;
1294            }
1295        }
1296    }
1297
1298    /**
1299     * Sets the speech synthesis engine to be used by its packagename.
1300     *
1301     * @param enginePackageName
1302     *            The packagename for the synthesis engine (ie, "com.svox.pico")
1303     *
1304     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1305     */
1306    public int setEngineByPackageName(String enginePackageName) {
1307        synchronized (mStartLock) {
1308            int result = TextToSpeech.ERROR;
1309            if (!mStarted) {
1310                return result;
1311            }
1312            try {
1313                result = mITts.setEngineByPackageName(enginePackageName);
1314                if (result == TextToSpeech.SUCCESS){
1315                    mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName;
1316                }
1317            } catch (RemoteException e) {
1318                // TTS died; restart it.
1319                Log.e("TextToSpeech.java - setEngineByPackageName", "RemoteException");
1320                e.printStackTrace();
1321                mStarted = false;
1322                initTts();
1323            } catch (NullPointerException e) {
1324                // TTS died; restart it.
1325                Log.e("TextToSpeech.java - setEngineByPackageName", "NullPointerException");
1326                e.printStackTrace();
1327                mStarted = false;
1328                initTts();
1329            } catch (IllegalStateException e) {
1330                // TTS died; restart it.
1331                Log.e("TextToSpeech.java - setEngineByPackageName", "IllegalStateException");
1332                e.printStackTrace();
1333                mStarted = false;
1334                initTts();
1335            } finally {
1336                return result;
1337            }
1338        }
1339    }
1340
1341}
1342