TextToSpeech.java revision 3786bc549891592335be0f454609d9c626d69ef4
1/*
2 * Copyright (C) 2009 The Android Open Source Project
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.annotation.SdkConstant;
19import android.annotation.SdkConstant.SdkConstantType;
20import android.content.ComponentName;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.media.AudioAttributes;
26import android.media.AudioManager;
27import android.net.Uri;
28import android.os.AsyncTask;
29import android.os.Bundle;
30import android.os.IBinder;
31import android.os.ParcelFileDescriptor;
32import android.os.RemoteException;
33import android.provider.Settings;
34import android.text.TextUtils;
35import android.util.Log;
36
37import java.io.File;
38import java.io.FileNotFoundException;
39import java.io.IOException;
40import java.util.ArrayList;
41import java.util.Collections;
42import java.util.HashMap;
43import java.util.HashSet;
44import java.util.List;
45import java.util.Locale;
46import java.util.Map;
47import java.util.MissingResourceException;
48import java.util.Set;
49
50/**
51 *
52 * Synthesizes speech from text for immediate playback or to create a sound file.
53 * <p>A TextToSpeech instance can only be used to synthesize text once it has completed its
54 * initialization. Implement the {@link TextToSpeech.OnInitListener} to be
55 * notified of the completion of the initialization.<br>
56 * When you are done using the TextToSpeech instance, call the {@link #shutdown()} method
57 * to release the native resources used by the TextToSpeech engine.
58 */
59public class TextToSpeech {
60
61    private static final String TAG = "TextToSpeech";
62
63    /**
64     * Denotes a successful operation.
65     */
66    public static final int SUCCESS = 0;
67    /**
68     * Denotes a generic operation failure.
69     */
70    public static final int ERROR = -1;
71
72    /**
73     * Denotes a stop requested by a client. It's used only on the service side of the API,
74     * client should never expect to see this result code.
75     */
76    public static final int STOPPED = -2;
77
78    /**
79     * Denotes a failure of a TTS engine to synthesize the given input.
80     */
81    public static final int ERROR_SYNTHESIS = -3;
82
83    /**
84     * Denotes a failure of a TTS service.
85     */
86    public static final int ERROR_SERVICE = -4;
87
88    /**
89     * Denotes a failure related to the output (audio device or a file).
90     */
91    public static final int ERROR_OUTPUT = -5;
92
93    /**
94     * Denotes a failure caused by a network connectivity problems.
95     */
96    public static final int ERROR_NETWORK = -6;
97
98    /**
99     * Denotes a failure caused by network timeout.
100     */
101    public static final int ERROR_NETWORK_TIMEOUT = -7;
102
103    /**
104     * Denotes a failure caused by an invalid request.
105     */
106    public static final int ERROR_INVALID_REQUEST = -8;
107
108    /**
109     * Denotes a failure caused by an unfinished download of the voice data.
110     * @see Engine#KEY_FEATURE_NOT_INSTALLED
111     */
112    public static final int ERROR_NOT_INSTALLED_YET = -9;
113
114    /**
115     * Queue mode where all entries in the playback queue (media to be played
116     * and text to be synthesized) are dropped and replaced by the new entry.
117     * Queues are flushed with respect to a given calling app. Entries in the queue
118     * from other callees are not discarded.
119     */
120    public static final int QUEUE_FLUSH = 0;
121    /**
122     * Queue mode where the new entry is added at the end of the playback queue.
123     */
124    public static final int QUEUE_ADD = 1;
125    /**
126     * Queue mode where the entire playback queue is purged. This is different
127     * from {@link #QUEUE_FLUSH} in that all entries are purged, not just entries
128     * from a given caller.
129     *
130     * @hide
131     */
132    static final int QUEUE_DESTROY = 2;
133
134    /**
135     * Denotes the language is available exactly as specified by the locale.
136     */
137    public static final int LANG_COUNTRY_VAR_AVAILABLE = 2;
138
139    /**
140     * Denotes the language is available for the language and country specified
141     * by the locale, but not the variant.
142     */
143    public static final int LANG_COUNTRY_AVAILABLE = 1;
144
145    /**
146     * Denotes the language is available for the language by the locale,
147     * but not the country and variant.
148     */
149    public static final int LANG_AVAILABLE = 0;
150
151    /**
152     * Denotes the language data is missing.
153     */
154    public static final int LANG_MISSING_DATA = -1;
155
156    /**
157     * Denotes the language is not supported.
158     */
159    public static final int LANG_NOT_SUPPORTED = -2;
160
161    /**
162     * Broadcast Action: The TextToSpeech synthesizer has completed processing
163     * of all the text in the speech queue.
164     *
165     * Note that this notifies callers when the <b>engine</b> has finished has
166     * processing text data. Audio playback might not have completed (or even started)
167     * at this point. If you wish to be notified when this happens, see
168     * {@link OnUtteranceCompletedListener}.
169     */
170    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
171    public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
172            "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
173
174    /**
175     * Interface definition of a callback to be invoked indicating the completion of the
176     * TextToSpeech engine initialization.
177     */
178    public interface OnInitListener {
179        /**
180         * Called to signal the completion of the TextToSpeech engine initialization.
181         *
182         * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
183         */
184        public void onInit(int status);
185    }
186
187    /**
188     * Listener that will be called when the TTS service has
189     * completed synthesizing an utterance. This is only called if the utterance
190     * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}).
191     *
192     * @deprecated Use {@link UtteranceProgressListener} instead.
193     */
194    @Deprecated
195    public interface OnUtteranceCompletedListener {
196        /**
197         * Called when an utterance has been synthesized.
198         *
199         * @param utteranceId the identifier of the utterance.
200         */
201        public void onUtteranceCompleted(String utteranceId);
202    }
203
204    /**
205     * Constants and parameter names for controlling text-to-speech. These include:
206     *
207     * <ul>
208     *     <li>
209     *         Intents to ask engine to install data or check its data and
210     *         extras for a TTS engine's check data activity.
211     *     </li>
212     *     <li>
213     *         Keys for the parameters passed with speak commands, e.g.
214     *         {@link Engine#KEY_PARAM_UTTERANCE_ID}, {@link Engine#KEY_PARAM_STREAM}.
215     *     </li>
216     *     <li>
217     *         A list of feature strings that engines might support, e.g
218     *         {@link Engine#KEY_FEATURE_NETWORK_SYNTHESIS}). These values may be passed in to
219     *         {@link TextToSpeech#speak} and {@link TextToSpeech#synthesizeToFile} to modify
220     *         engine behaviour. The engine can be queried for the set of features it supports
221     *         through {@link TextToSpeech#getFeatures(java.util.Locale)}.
222     *     </li>
223     * </ul>
224     */
225    public class Engine {
226
227        /**
228         * Default speech rate.
229         * @hide
230         */
231        public static final int DEFAULT_RATE = 100;
232
233        /**
234         * Default pitch.
235         * @hide
236         */
237        public static final int DEFAULT_PITCH = 100;
238
239        /**
240         * Default volume.
241         * @hide
242         */
243        public static final float DEFAULT_VOLUME = 1.0f;
244
245        /**
246         * Default pan (centered).
247         * @hide
248         */
249        public static final float DEFAULT_PAN = 0.0f;
250
251        /**
252         * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}.
253         * @hide
254         */
255        public static final int USE_DEFAULTS = 0; // false
256
257        /**
258         * Package name of the default TTS engine.
259         *
260         * @hide
261         * @deprecated No longer in use, the default engine is determined by
262         *         the sort order defined in {@link TtsEngines}. Note that
263         *         this doesn't "break" anything because there is no guarantee that
264         *         the engine specified below is installed on a given build, let
265         *         alone be the default.
266         */
267        @Deprecated
268        public static final String DEFAULT_ENGINE = "com.svox.pico";
269
270        /**
271         * Default audio stream used when playing synthesized speech.
272         */
273        public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
274
275        /**
276         * Indicates success when checking the installation status of the resources used by the
277         * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
278         */
279        public static final int CHECK_VOICE_DATA_PASS = 1;
280
281        /**
282         * Indicates failure when checking the installation status of the resources used by the
283         * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
284         */
285        public static final int CHECK_VOICE_DATA_FAIL = 0;
286
287        /**
288         * Indicates erroneous data when checking the installation status of the resources used by
289         * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
290         *
291         * @deprecated Use CHECK_VOICE_DATA_FAIL instead.
292         */
293        @Deprecated
294        public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
295
296        /**
297         * Indicates missing resources when checking the installation status of the resources used
298         * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
299         *
300         * @deprecated Use CHECK_VOICE_DATA_FAIL instead.
301         */
302        @Deprecated
303        public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
304
305        /**
306         * Indicates missing storage volume when checking the installation status of the resources
307         * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
308         *
309         * @deprecated Use CHECK_VOICE_DATA_FAIL instead.
310         */
311        @Deprecated
312        public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
313
314        /**
315         * Intent for starting a TTS service. Services that handle this intent must
316         * extend {@link TextToSpeechService}. Normal applications should not use this intent
317         * directly, instead they should talk to the TTS service using the the methods in this
318         * class.
319         */
320        @SdkConstant(SdkConstantType.SERVICE_ACTION)
321        public static final String INTENT_ACTION_TTS_SERVICE =
322                "android.intent.action.TTS_SERVICE";
323
324        /**
325         * Name under which a text to speech engine publishes information about itself.
326         * This meta-data should reference an XML resource containing a
327         * <code>&lt;{@link android.R.styleable#TextToSpeechEngine tts-engine}&gt;</code>
328         * tag.
329         */
330        public static final String SERVICE_META_DATA = "android.speech.tts";
331
332        // intents to ask engine to install data or check its data
333        /**
334         * Activity Action: Triggers the platform TextToSpeech engine to
335         * start the activity that installs the resource files on the device
336         * that are required for TTS to be operational. Since the installation
337         * of the data can be interrupted or declined by the user, the application
338         * shouldn't expect successful installation upon return from that intent,
339         * and if need be, should check installation status with
340         * {@link #ACTION_CHECK_TTS_DATA}.
341         */
342        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
343        public static final String ACTION_INSTALL_TTS_DATA =
344                "android.speech.tts.engine.INSTALL_TTS_DATA";
345
346        /**
347         * Broadcast Action: broadcast to signal the change in the list of available
348         * languages or/and their features.
349         */
350        @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
351        public static final String ACTION_TTS_DATA_INSTALLED =
352                "android.speech.tts.engine.TTS_DATA_INSTALLED";
353
354        /**
355         * Activity Action: Starts the activity from the platform TextToSpeech
356         * engine to verify the proper installation and availability of the
357         * resource files on the system. Upon completion, the activity will
358         * return one of the following codes:
359         * {@link #CHECK_VOICE_DATA_PASS},
360         * {@link #CHECK_VOICE_DATA_FAIL},
361         * <p> Moreover, the data received in the activity result will contain the following
362         * fields:
363         * <ul>
364         *   <li>{@link #EXTRA_AVAILABLE_VOICES} which contains an ArrayList<String> of all the
365         *   available voices. The format of each voice is: lang-COUNTRY-variant where COUNTRY and
366         *   variant are optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").</li>
367         *   <li>{@link #EXTRA_UNAVAILABLE_VOICES} which contains an ArrayList<String> of all the
368         *   unavailable voices (ones that user can install). The format of each voice is:
369         *   lang-COUNTRY-variant where COUNTRY and variant are optional (ie, "eng" or
370         *   "eng-USA" or "eng-USA-FEMALE").</li>
371         * </ul>
372         */
373        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
374        public static final String ACTION_CHECK_TTS_DATA =
375                "android.speech.tts.engine.CHECK_TTS_DATA";
376
377        /**
378         * Activity intent for getting some sample text to use for demonstrating TTS. Specific
379         * locale have to be requested by passing following extra parameters:
380         * <ul>
381         *   <li>language</li>
382         *   <li>country</li>
383         *   <li>variant</li>
384         * </ul>
385         *
386         * Upon completion, the activity result may contain the following fields:
387         * <ul>
388         *   <li>{@link #EXTRA_SAMPLE_TEXT} which contains an String with sample text.</li>
389         * </ul>
390         */
391        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
392        public static final String ACTION_GET_SAMPLE_TEXT =
393                "android.speech.tts.engine.GET_SAMPLE_TEXT";
394
395        /**
396         * Extra information received with the {@link #ACTION_GET_SAMPLE_TEXT} intent result where
397         * the TextToSpeech engine returns an String with sample text for requested voice
398         */
399        public static final String EXTRA_SAMPLE_TEXT = "sampleText";
400
401
402        // extras for a TTS engine's check data activity
403        /**
404         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
405         * the TextToSpeech engine returns an ArrayList<String> of all the available voices.
406         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
407         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
408         */
409        public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
410
411        /**
412         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
413         * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
414         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
415         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
416         */
417        public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
418
419        /**
420         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
421         * the TextToSpeech engine specifies the path to its resources.
422         *
423         * It may be used by language packages to find out where to put their data.
424         *
425         * @deprecated TTS engine implementation detail, this information has no use for
426         * text-to-speech API client.
427         */
428        @Deprecated
429        public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
430
431        /**
432         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
433         * the TextToSpeech engine specifies the file names of its resources under the
434         * resource path.
435         *
436         * @deprecated TTS engine implementation detail, this information has no use for
437         * text-to-speech API client.
438         */
439        @Deprecated
440        public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
441
442        /**
443         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent result where
444         * the TextToSpeech engine specifies the locale associated with each resource file.
445         *
446         * @deprecated TTS engine implementation detail, this information has no use for
447         * text-to-speech API client.
448         */
449        @Deprecated
450        public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
451
452        /**
453         * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
454         * caller indicates to the TextToSpeech engine which specific sets of voice data to
455         * check for by sending an ArrayList<String> of the voices that are of interest.
456         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
457         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
458         *
459         * @deprecated Redundant functionality, checking for existence of specific sets of voice
460         * data can be done on client side.
461         */
462        @Deprecated
463        public static final String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor";
464
465        // extras for a TTS engine's data installation
466        /**
467         * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent result.
468         * It indicates whether the data files for the synthesis engine were successfully
469         * installed. The installation was initiated with the  {@link #ACTION_INSTALL_TTS_DATA}
470         * intent. The possible values for this extra are
471         * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}.
472         *
473         * @deprecated No longer in use. If client ise interested in information about what
474         * changed, is should send ACTION_CHECK_TTS_DATA intent to discover available voices.
475         */
476        @Deprecated
477        public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
478
479        // keys for the parameters passed with speak commands. Hidden keys are used internally
480        // to maintain engine state for each TextToSpeech instance.
481        /**
482         * @hide
483         */
484        public static final String KEY_PARAM_RATE = "rate";
485
486        /**
487         * @hide
488         */
489        public static final String KEY_PARAM_VOICE_NAME = "voiceName";
490
491        /**
492         * @hide
493         */
494        public static final String KEY_PARAM_LANGUAGE = "language";
495
496        /**
497         * @hide
498         */
499        public static final String KEY_PARAM_COUNTRY = "country";
500
501        /**
502         * @hide
503         */
504        public static final String KEY_PARAM_VARIANT = "variant";
505
506        /**
507         * @hide
508         */
509        public static final String KEY_PARAM_ENGINE = "engine";
510
511        /**
512         * @hide
513         */
514        public static final String KEY_PARAM_PITCH = "pitch";
515
516        /**
517         * Parameter key to specify the audio stream type to be used when speaking text
518         * or playing back a file. The value should be one of the STREAM_ constants
519         * defined in {@link AudioManager}.
520         *
521         * @see TextToSpeech#speak(String, int, HashMap)
522         * @see TextToSpeech#playEarcon(String, int, HashMap)
523         */
524        public static final String KEY_PARAM_STREAM = "streamType";
525
526        /**
527         * Parameter key to specify the audio attributes to be used when
528         * speaking text or playing back a file. The value should be set
529         * using {@link TextToSpeech#setAudioAttributes(AudioAttributes)}.
530         *
531         * @see TextToSpeech#speak(String, int, HashMap)
532         * @see TextToSpeech#playEarcon(String, int, HashMap)
533         * @hide
534         */
535        public static final String KEY_PARAM_AUDIO_ATTRIBUTES = "audioAttributes";
536
537        /**
538         * Parameter key to identify an utterance in the
539         * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
540         * spoken, a file has been played back or a silence duration has elapsed.
541         *
542         * @see TextToSpeech#speak(String, int, HashMap)
543         * @see TextToSpeech#playEarcon(String, int, HashMap)
544         * @see TextToSpeech#synthesizeToFile(String, HashMap, String)
545         */
546        public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
547
548        /**
549         * Parameter key to specify the speech volume relative to the current stream type
550         * volume used when speaking text. Volume is specified as a float ranging from 0 to 1
551         * where 0 is silence, and 1 is the maximum volume (the default behavior).
552         *
553         * @see TextToSpeech#speak(String, int, HashMap)
554         * @see TextToSpeech#playEarcon(String, int, HashMap)
555         */
556        public static final String KEY_PARAM_VOLUME = "volume";
557
558        /**
559         * Parameter key to specify how the speech is panned from left to right when speaking text.
560         * Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
561         * 0 to center (the default behavior), and +1 to hard-right.
562         *
563         * @see TextToSpeech#speak(String, int, HashMap)
564         * @see TextToSpeech#playEarcon(String, int, HashMap)
565         */
566        public static final String KEY_PARAM_PAN = "pan";
567
568        /**
569         * Feature key for network synthesis. See {@link TextToSpeech#getFeatures(Locale)}
570         * for a description of how feature keys work. If set (and supported by the engine
571         * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must
572         * use network based synthesis.
573         *
574         * @see TextToSpeech#speak(String, int, java.util.HashMap)
575         * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
576         * @see TextToSpeech#getFeatures(java.util.Locale)
577         *
578         * @deprecated Starting from API level 20, to select network synthesis, call
579         * ({@link TextToSpeech#getVoices()}, find a suitable network voice
580         * ({@link Voice#isNetworkConnectionRequired()}) and pass it
581         * to {@link TextToSpeech#setVoice(Voice)}).
582         */
583        @Deprecated
584        public static final String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
585
586        /**
587         * Feature key for embedded synthesis. See {@link TextToSpeech#getFeatures(Locale)}
588         * for a description of how feature keys work. If set and supported by the engine
589         * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize
590         * text on-device (without making network requests).
591         *
592         * @see TextToSpeech#speak(String, int, java.util.HashMap)
593         * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
594         * @see TextToSpeech#getFeatures(java.util.Locale)
595
596         * @deprecated Starting from API level 20, to select embedded synthesis, call
597         * ({@link TextToSpeech#getVoices()}, find a suitable embedded voice
598         * ({@link Voice#isNetworkConnectionRequired()}) and pass it
599         * to {@link TextToSpeech#setVoice(Voice)}).
600         */
601        @Deprecated
602        public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
603
604        /**
605         * Parameter key to specify an audio session identifier (obtained from
606         * {@link AudioManager#generateAudioSessionId()}) that will be used by the request audio
607         * output. It can be used to associate one of the {@link android.media.audiofx.AudioEffect}
608         * objects with the synthesis (or earcon) output.
609         *
610         * @see TextToSpeech#speak(String, int, HashMap)
611         * @see TextToSpeech#playEarcon(String, int, HashMap)
612         */
613        public static final String KEY_PARAM_SESSION_ID = "sessionId";
614
615        /**
616         * Feature key that indicates that the voice may need to download additional data to be fully
617         * functional. The download will be triggered by calling
618         * {@link TextToSpeech#setVoice(Voice)} or {@link TextToSpeech#setLanguage(Locale)}.
619         * Until download is complete, each synthesis request will either report
620         * {@link TextToSpeech#ERROR_NOT_INSTALLED_YET} error, or use a different voice to synthesize
621         * the request. This feature should NOT be used as a key of a request parameter.
622         *
623         * @see TextToSpeech#getFeatures(java.util.Locale)
624         * @see Voice#getFeatures()
625         */
626        public static final String KEY_FEATURE_NOT_INSTALLED = "notInstalled";
627
628        /**
629         * Feature key that indicate that a network timeout can be set for the request. If set and
630         * supported as per {@link TextToSpeech#getFeatures(Locale)} or {@link Voice#getFeatures()},
631         * it can be used as request parameter to set the maximum allowed time for a single
632         * request attempt, in milliseconds, before synthesis fails. When used as a key of
633         * a request parameter, its value should be a string with an integer value.
634         *
635         * @see TextToSpeech#getFeatures(java.util.Locale)
636         * @see Voice#getFeatures()
637         */
638        public static final String KEY_FEATURE_NETWORK_TIMEOUT_MS = "networkTimeoutMs";
639
640        /**
641         * Feature key that indicates that network request retries count can be set for the request.
642         * If set and supported as per {@link TextToSpeech#getFeatures(Locale)} or
643         * {@link Voice#getFeatures()}, it can be used as a request parameter to set the
644         * number of network request retries that are attempted in case of failure. When used as
645         * a key of a request parameter, its value should be a string with an integer value.
646         *
647         * @see TextToSpeech#getFeatures(java.util.Locale)
648         * @see Voice#getFeatures()
649         */
650        public static final String KEY_FEATURE_NETWORK_RETRIES_COUNT = "networkRetriesCount";
651    }
652
653    private final Context mContext;
654    private Connection mConnectingServiceConnection;
655    private Connection mServiceConnection;
656    private OnInitListener mInitListener;
657    // Written from an unspecified application thread, read from
658    // a binder thread.
659    private volatile UtteranceProgressListener mUtteranceProgressListener;
660    private final Object mStartLock = new Object();
661
662    private String mRequestedEngine;
663    // Whether to initialize this TTS object with the default engine,
664    // if the requested engine is not available. Valid only if mRequestedEngine
665    // is not null. Used only for testing, though potentially useful API wise
666    // too.
667    private final boolean mUseFallback;
668    private final Map<String, Uri> mEarcons;
669    private final Map<CharSequence, Uri> mUtterances;
670    private final Bundle mParams = new Bundle();
671    private final TtsEngines mEnginesHelper;
672    private volatile String mCurrentEngine = null;
673
674    /**
675     * The constructor for the TextToSpeech class, using the default TTS engine.
676     * This will also initialize the associated TextToSpeech engine if it isn't already running.
677     *
678     * @param context
679     *            The context this instance is running in.
680     * @param listener
681     *            The {@link TextToSpeech.OnInitListener} that will be called when the
682     *            TextToSpeech engine has initialized. In a case of a failure the listener
683     *            may be called immediately, before TextToSpeech instance is fully constructed.
684     */
685    public TextToSpeech(Context context, OnInitListener listener) {
686        this(context, listener, null);
687    }
688
689    /**
690     * The constructor for the TextToSpeech class, using the given TTS engine.
691     * This will also initialize the associated TextToSpeech engine if it isn't already running.
692     *
693     * @param context
694     *            The context this instance is running in.
695     * @param listener
696     *            The {@link TextToSpeech.OnInitListener} that will be called when the
697     *            TextToSpeech engine has initialized. In a case of a failure the listener
698     *            may be called immediately, before TextToSpeech instance is fully constructed.
699     * @param engine Package name of the TTS engine to use.
700     */
701    public TextToSpeech(Context context, OnInitListener listener, String engine) {
702        this(context, listener, engine, null, true);
703    }
704
705    /**
706     * Used by the framework to instantiate TextToSpeech objects with a supplied
707     * package name, instead of using {@link android.content.Context#getPackageName()}
708     *
709     * @hide
710     */
711    public TextToSpeech(Context context, OnInitListener listener, String engine,
712            String packageName, boolean useFallback) {
713        mContext = context;
714        mInitListener = listener;
715        mRequestedEngine = engine;
716        mUseFallback = useFallback;
717
718        mEarcons = new HashMap<String, Uri>();
719        mUtterances = new HashMap<CharSequence, Uri>();
720        mUtteranceProgressListener = null;
721
722        mEnginesHelper = new TtsEngines(mContext);
723        initTts();
724    }
725
726    private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method,
727            boolean onlyEstablishedConnection) {
728        return runAction(action, errorResult, method, false, onlyEstablishedConnection);
729    }
730
731    private <R> R runAction(Action<R> action, R errorResult, String method) {
732        return runAction(action, errorResult, method, true, true);
733    }
734
735    private <R> R runAction(Action<R> action, R errorResult, String method,
736            boolean reconnect, boolean onlyEstablishedConnection) {
737        synchronized (mStartLock) {
738            if (mServiceConnection == null) {
739                Log.w(TAG, method + " failed: not bound to TTS engine");
740                return errorResult;
741            }
742            return mServiceConnection.runAction(action, errorResult, method, reconnect,
743                    onlyEstablishedConnection);
744        }
745    }
746
747    private int initTts() {
748        // Step 1: Try connecting to the engine that was requested.
749        if (mRequestedEngine != null) {
750            if (mEnginesHelper.isEngineInstalled(mRequestedEngine)) {
751                if (connectToEngine(mRequestedEngine)) {
752                    mCurrentEngine = mRequestedEngine;
753                    return SUCCESS;
754                } else if (!mUseFallback) {
755                    mCurrentEngine = null;
756                    dispatchOnInit(ERROR);
757                    return ERROR;
758                }
759            } else if (!mUseFallback) {
760                Log.i(TAG, "Requested engine not installed: " + mRequestedEngine);
761                mCurrentEngine = null;
762                dispatchOnInit(ERROR);
763                return ERROR;
764            }
765        }
766
767        // Step 2: Try connecting to the user's default engine.
768        final String defaultEngine = getDefaultEngine();
769        if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) {
770            if (connectToEngine(defaultEngine)) {
771                mCurrentEngine = defaultEngine;
772                return SUCCESS;
773            }
774        }
775
776        // Step 3: Try connecting to the highest ranked engine in the
777        // system.
778        final String highestRanked = mEnginesHelper.getHighestRankedEngineName();
779        if (highestRanked != null && !highestRanked.equals(mRequestedEngine) &&
780                !highestRanked.equals(defaultEngine)) {
781            if (connectToEngine(highestRanked)) {
782                mCurrentEngine = highestRanked;
783                return SUCCESS;
784            }
785        }
786
787        // NOTE: The API currently does not allow the caller to query whether
788        // they are actually connected to any engine. This might fail for various
789        // reasons like if the user disables all her TTS engines.
790
791        mCurrentEngine = null;
792        dispatchOnInit(ERROR);
793        return ERROR;
794    }
795
796    private boolean connectToEngine(String engine) {
797        Connection connection = new Connection();
798        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
799        intent.setPackage(engine);
800        boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
801        if (!bound) {
802            Log.e(TAG, "Failed to bind to " + engine);
803            return false;
804        } else {
805            Log.i(TAG, "Sucessfully bound to " + engine);
806            mConnectingServiceConnection = connection;
807            return true;
808        }
809    }
810
811    private void dispatchOnInit(int result) {
812        synchronized (mStartLock) {
813            if (mInitListener != null) {
814                mInitListener.onInit(result);
815                mInitListener = null;
816            }
817        }
818    }
819
820    private IBinder getCallerIdentity() {
821        return mServiceConnection.getCallerIdentity();
822    }
823
824    /**
825     * Releases the resources used by the TextToSpeech engine.
826     * It is good practice for instance to call this method in the onDestroy() method of an Activity
827     * so the TextToSpeech engine can be cleanly stopped.
828     */
829    public void shutdown() {
830        // Special case, we are asked to shutdown connection that did finalize its connection.
831        synchronized (mStartLock) {
832            if (mConnectingServiceConnection != null) {
833                mContext.unbindService(mConnectingServiceConnection);
834                mConnectingServiceConnection = null;
835                return;
836            }
837        }
838
839        // Post connection case
840        runActionNoReconnect(new Action<Void>() {
841            @Override
842            public Void run(ITextToSpeechService service) throws RemoteException {
843                service.setCallback(getCallerIdentity(), null);
844                service.stop(getCallerIdentity());
845                mServiceConnection.disconnect();
846                // Context#unbindService does not result in a call to
847                // ServiceConnection#onServiceDisconnected. As a result, the
848                // service ends up being destroyed (if there are no other open
849                // connections to it) but the process lives on and the
850                // ServiceConnection continues to refer to the destroyed service.
851                //
852                // This leads to tons of log spam about SynthThread being dead.
853                mServiceConnection = null;
854                mCurrentEngine = null;
855                return null;
856            }
857        }, null, "shutdown", false);
858    }
859
860    /**
861     * Adds a mapping between a string of text and a sound resource in a
862     * package. After a call to this method, subsequent calls to
863     * {@link #speak(String, int, HashMap)} will play the specified sound resource
864     * if it is available, or synthesize the text it is missing.
865     *
866     * @param text
867     *            The string of text. Example: <code>"south_south_east"</code>
868     *
869     * @param packagename
870     *            Pass the packagename of the application that contains the
871     *            resource. If the resource is in your own application (this is
872     *            the most common case), then put the packagename of your
873     *            application here.<br/>
874     *            Example: <b>"com.google.marvin.compass"</b><br/>
875     *            The packagename can be found in the AndroidManifest.xml of
876     *            your application.
877     *            <p>
878     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
879     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
880     *            </p>
881     *
882     * @param resourceId
883     *            Example: <code>R.raw.south_south_east</code>
884     *
885     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
886     */
887    public int addSpeech(String text, String packagename, int resourceId) {
888        synchronized (mStartLock) {
889            mUtterances.put(text, makeResourceUri(packagename, resourceId));
890            return SUCCESS;
891        }
892    }
893
894    /**
895     * Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text
896     * and a sound resource in a package. After a call to this method, subsequent calls to
897     * {@link #speak(String, int, HashMap)} will play the specified sound resource
898     * if it is available, or synthesize the text it is missing.
899     *
900     * @param text
901     *            The string of text. Example: <code>"south_south_east"</code>
902     *
903     * @param packagename
904     *            Pass the packagename of the application that contains the
905     *            resource. If the resource is in your own application (this is
906     *            the most common case), then put the packagename of your
907     *            application here.<br/>
908     *            Example: <b>"com.google.marvin.compass"</b><br/>
909     *            The packagename can be found in the AndroidManifest.xml of
910     *            your application.
911     *            <p>
912     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
913     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
914     *            </p>
915     *
916     * @param resourceId
917     *            Example: <code>R.raw.south_south_east</code>
918     *
919     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
920     */
921    public int addSpeech(CharSequence text, String packagename, int resourceId) {
922        synchronized (mStartLock) {
923            mUtterances.put(text, makeResourceUri(packagename, resourceId));
924            return SUCCESS;
925        }
926    }
927
928    /**
929     * Adds a mapping between a string of text and a sound file. Using this, it
930     * is possible to add custom pronounciations for a string of text.
931     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
932     * will play the specified sound resource if it is available, or synthesize the text it is
933     * missing.
934     *
935     * @param text
936     *            The string of text. Example: <code>"south_south_east"</code>
937     * @param filename
938     *            The full path to the sound file (for example:
939     *            "/sdcard/mysounds/hello.wav")
940     *
941     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
942     */
943    public int addSpeech(String text, String filename) {
944        synchronized (mStartLock) {
945            mUtterances.put(text, Uri.parse(filename));
946            return SUCCESS;
947        }
948    }
949
950    /**
951     * Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file.
952     * Using this, it is possible to add custom pronounciations for a string of text.
953     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
954     * will play the specified sound resource if it is available, or synthesize the text it is
955     * missing.
956     *
957     * @param text
958     *            The string of text. Example: <code>"south_south_east"</code>
959     * @param file
960     *            File object pointing to the sound file.
961     *
962     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
963     */
964    public int addSpeech(CharSequence text, File file) {
965        synchronized (mStartLock) {
966            mUtterances.put(text, Uri.fromFile(file));
967            return SUCCESS;
968        }
969    }
970
971    /**
972     * Adds a mapping between a string of text and a sound resource in a
973     * package. Use this to add custom earcons.
974     *
975     * @see #playEarcon(String, int, HashMap)
976     *
977     * @param earcon The name of the earcon.
978     *            Example: <code>"[tick]"</code><br/>
979     *
980     * @param packagename
981     *            the package name of the application that contains the
982     *            resource. This can for instance be the package name of your own application.
983     *            Example: <b>"com.google.marvin.compass"</b><br/>
984     *            The package name can be found in the AndroidManifest.xml of
985     *            the application containing the resource.
986     *            <p>
987     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
988     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
989     *            </p>
990     *
991     * @param resourceId
992     *            Example: <code>R.raw.tick_snd</code>
993     *
994     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
995     */
996    public int addEarcon(String earcon, String packagename, int resourceId) {
997        synchronized(mStartLock) {
998            mEarcons.put(earcon, makeResourceUri(packagename, resourceId));
999            return SUCCESS;
1000        }
1001    }
1002
1003    /**
1004     * Adds a mapping between a string of text and a sound file.
1005     * Use this to add custom earcons.
1006     *
1007     * @see #playEarcon(String, int, HashMap)
1008     *
1009     * @param earcon
1010     *            The name of the earcon.
1011     *            Example: <code>"[tick]"</code>
1012     * @param filename
1013     *            The full path to the sound file (for example:
1014     *            "/sdcard/mysounds/tick.wav")
1015     *
1016     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1017     *
1018     * @deprecated As of API level 20, replaced by
1019     *         {@link #addEarcon(String, File)}.
1020     */
1021    @Deprecated
1022    public int addEarcon(String earcon, String filename) {
1023        synchronized(mStartLock) {
1024            mEarcons.put(earcon, Uri.parse(filename));
1025            return SUCCESS;
1026        }
1027    }
1028
1029    /**
1030     * Adds a mapping between a string of text and a sound file.
1031     * Use this to add custom earcons.
1032     *
1033     * @see #playEarcon(String, int, HashMap)
1034     *
1035     * @param earcon
1036     *            The name of the earcon.
1037     *            Example: <code>"[tick]"</code>
1038     * @param file
1039     *            File object pointing to the sound file.
1040     *
1041     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1042     */
1043    public int addEarcon(String earcon, File file) {
1044        synchronized(mStartLock) {
1045            mEarcons.put(earcon, Uri.fromFile(file));
1046            return SUCCESS;
1047        }
1048    }
1049
1050    private Uri makeResourceUri(String packageName, int resourceId) {
1051        return new Uri.Builder()
1052                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
1053                .encodedAuthority(packageName)
1054                .appendEncodedPath(String.valueOf(resourceId))
1055                .build();
1056    }
1057
1058    /**
1059     * Speaks the text using the specified queuing strategy and speech parameters, the text may
1060     * be spanned with TtsSpans.
1061     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1062     * requests and then returns. The synthesis might not have finished (or even started!) at the
1063     * time when this method returns. In order to reliably detect errors during synthesis,
1064     * we recommend setting an utterance progress listener (see
1065     * {@link #setOnUtteranceProgressListener}) and using the
1066     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1067     *
1068     * @param text The string of text to be spoken. No longer than
1069     *            {@link #getMaxSpeechInputLength()} characters.
1070     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1071     * @param params Parameters for the request. Can be null.
1072     *            Supported parameter names:
1073     *            {@link Engine#KEY_PARAM_STREAM},
1074     *            {@link Engine#KEY_PARAM_VOLUME},
1075     *            {@link Engine#KEY_PARAM_PAN}.
1076     *            Engine specific parameters may be passed in but the parameter keys
1077     *            must be prefixed by the name of the engine they are intended for. For example
1078     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1079     *            engine named "com.svox.pico" if it is being used.
1080     * @param utteranceId An unique identifier for this request.
1081     *
1082     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
1083     */
1084    public int speak(final CharSequence text,
1085                     final int queueMode,
1086                     final Bundle params,
1087                     final String utteranceId) {
1088        return runAction(new Action<Integer>() {
1089            @Override
1090            public Integer run(ITextToSpeechService service) throws RemoteException {
1091                Uri utteranceUri = mUtterances.get(text);
1092                if (utteranceUri != null) {
1093                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
1094                            getParams(params), utteranceId);
1095                } else {
1096                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
1097                            utteranceId);
1098                }
1099            }
1100        }, ERROR, "speak");
1101    }
1102
1103    /**
1104     * Speaks the string using the specified queuing strategy and speech parameters.
1105     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1106     * requests and then returns. The synthesis might not have finished (or even started!) at the
1107     * time when this method returns. In order to reliably detect errors during synthesis,
1108     * we recommend setting an utterance progress listener (see
1109     * {@link #setOnUtteranceProgressListener}) and using the
1110     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1111     *
1112     * @param text The string of text to be spoken. No longer than
1113     *            {@link #getMaxSpeechInputLength()} characters.
1114     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1115     * @param params Parameters for the request. Can be null.
1116     *            Supported parameter names:
1117     *            {@link Engine#KEY_PARAM_STREAM},
1118     *            {@link Engine#KEY_PARAM_UTTERANCE_ID},
1119     *            {@link Engine#KEY_PARAM_VOLUME},
1120     *            {@link Engine#KEY_PARAM_PAN}.
1121     *            Engine specific parameters may be passed in but the parameter keys
1122     *            must be prefixed by the name of the engine they are intended for. For example
1123     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1124     *            engine named "com.svox.pico" if it is being used.
1125     *
1126     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
1127     * @deprecated As of API level 20, replaced by
1128     *         {@link #speak(CharSequence, int, Bundle, String)}.
1129     */
1130    @Deprecated
1131    public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
1132        return speak(text, queueMode, convertParamsHashMaptoBundle(params),
1133                     params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1134    }
1135
1136    /**
1137     * Plays the earcon using the specified queueing mode and parameters.
1138     * The earcon must already have been added with {@link #addEarcon(String, String)} or
1139     * {@link #addEarcon(String, String, int)}.
1140     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1141     * requests and then returns. The synthesis might not have finished (or even started!) at the
1142     * time when this method returns. In order to reliably detect errors during synthesis,
1143     * we recommend setting an utterance progress listener (see
1144     * {@link #setOnUtteranceProgressListener}) and using the
1145     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1146     *
1147     * @param earcon The earcon that should be played
1148     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1149     * @param params Parameters for the request. Can be null.
1150     *            Supported parameter names:
1151     *            {@link Engine#KEY_PARAM_STREAM},
1152     *            Engine specific parameters may be passed in but the parameter keys
1153     *            must be prefixed by the name of the engine they are intended for. For example
1154     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1155     *            engine named "com.svox.pico" if it is being used.
1156     *
1157     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
1158     */
1159    public int playEarcon(final String earcon, final int queueMode,
1160            final Bundle params, final String utteranceId) {
1161        return runAction(new Action<Integer>() {
1162            @Override
1163            public Integer run(ITextToSpeechService service) throws RemoteException {
1164                Uri earconUri = mEarcons.get(earcon);
1165                if (earconUri == null) {
1166                    return ERROR;
1167                }
1168                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
1169                        getParams(params), utteranceId);
1170            }
1171        }, ERROR, "playEarcon");
1172    }
1173
1174    /**
1175     * Plays the earcon using the specified queueing mode and parameters.
1176     * The earcon must already have been added with {@link #addEarcon(String, String)} or
1177     * {@link #addEarcon(String, String, int)}.
1178     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1179     * requests and then returns. The synthesis might not have finished (or even started!) at the
1180     * time when this method returns. In order to reliably detect errors during synthesis,
1181     * we recommend setting an utterance progress listener (see
1182     * {@link #setOnUtteranceProgressListener}) and using the
1183     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1184     *
1185     * @param earcon The earcon that should be played
1186     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1187     * @param params Parameters for the request. Can be null.
1188     *            Supported parameter names:
1189     *            {@link Engine#KEY_PARAM_STREAM},
1190     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1191     *            Engine specific parameters may be passed in but the parameter keys
1192     *            must be prefixed by the name of the engine they are intended for. For example
1193     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1194     *            engine named "com.svox.pico" if it is being used.
1195     *
1196     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
1197     * @deprecated As of API level 20, replaced by
1198     *         {@link #playEarcon(String, int, Bundle, String)}.
1199     */
1200    @Deprecated
1201    public int playEarcon(final String earcon, final int queueMode,
1202            final HashMap<String, String> params) {
1203        return playEarcon(earcon, queueMode, convertParamsHashMaptoBundle(params),
1204                          params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1205    }
1206
1207    /**
1208     * Plays silence for the specified amount of time using the specified
1209     * queue mode.
1210     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1211     * requests and then returns. The synthesis might not have finished (or even started!) at the
1212     * time when this method returns. In order to reliably detect errors during synthesis,
1213     * we recommend setting an utterance progress listener (see
1214     * {@link #setOnUtteranceProgressListener}) and using the
1215     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1216     *
1217     * @param durationInMs The duration of the silence.
1218     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1219     * @param params Parameters for the request. Can be null.
1220     *            Engine specific parameters may be passed in but the parameter keys
1221     *            must be prefixed by the name of the engine they are intended for. For example
1222     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1223     *            engine named "com.svox.pico" if it is being used.
1224     * @param utteranceId An unique identifier for this request.
1225     *
1226     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
1227     */
1228    public int playSilence(final long durationInMs, final int queueMode,
1229            final HashMap<String, String> params, final String utteranceId) {
1230        return runAction(new Action<Integer>() {
1231            @Override
1232            public Integer run(ITextToSpeechService service) throws RemoteException {
1233                return service.playSilence(getCallerIdentity(), durationInMs,
1234                                           queueMode, utteranceId);
1235            }
1236        }, ERROR, "playSilence");
1237    }
1238
1239    /**
1240     * Plays silence for the specified amount of time using the specified
1241     * queue mode.
1242     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1243     * requests and then returns. The synthesis might not have finished (or even started!) at the
1244     * time when this method returns. In order to reliably detect errors during synthesis,
1245     * we recommend setting an utterance progress listener (see
1246     * {@link #setOnUtteranceProgressListener}) and using the
1247     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1248     *
1249     * @param durationInMs The duration of the silence.
1250     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1251     * @param params Parameters for the request. Can be null.
1252     *            Supported parameter names:
1253     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1254     *            Engine specific parameters may be passed in but the parameter keys
1255     *            must be prefixed by the name of the engine they are intended for. For example
1256     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1257     *            engine named "com.svox.pico" if it is being used.
1258     *
1259     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
1260     * @deprecated As of API level 20, replaced by
1261     *         {@link #playSilence(long, int, HashMap, String)}.
1262     */
1263    @Deprecated
1264    public int playSilence(final long durationInMs, final int queueMode,
1265            final HashMap<String, String> params) {
1266        return playSilence(durationInMs, queueMode, params,
1267                           params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1268    }
1269
1270    /**
1271     * Queries the engine for the set of features it supports for a given locale.
1272     * Features can either be framework defined, e.g.
1273     * {@link TextToSpeech.Engine#KEY_FEATURE_NETWORK_SYNTHESIS} or engine specific.
1274     * Engine specific keys must be prefixed by the name of the engine they
1275     * are intended for. These keys can be used as parameters to
1276     * {@link TextToSpeech#speak(String, int, java.util.HashMap)} and
1277     * {@link TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)}.
1278     *
1279     * Features values are strings and their values must meet restrictions described in their
1280     * documentation.
1281     *
1282     * @param locale The locale to query features for.
1283     * @return Set instance. May return {@code null} on error.
1284     * @deprecated As of API level 20, please use voices. In order to query features of the voice,
1285     * call {@link #getVoices()} to retrieve the list of available voices and
1286     * {@link Voice#getFeatures()} to retrieve the set of features.
1287     */
1288    @Deprecated
1289    public Set<String> getFeatures(final Locale locale) {
1290        return runAction(new Action<Set<String>>() {
1291            @Override
1292            public Set<String> run(ITextToSpeechService service) throws RemoteException {
1293                String[] features = null;
1294                try {
1295                    features = service.getFeaturesForLanguage(
1296                        locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
1297                } catch(MissingResourceException e) {
1298                    Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " +
1299                            "country code for locale: " + locale, e);
1300                    return null;
1301                }
1302
1303                if (features != null) {
1304                    final Set<String> featureSet = new HashSet<String>();
1305                    Collections.addAll(featureSet, features);
1306                    return featureSet;
1307                }
1308                return null;
1309            }
1310        }, null, "getFeatures");
1311    }
1312
1313    /**
1314     * Checks whether the TTS engine is busy speaking. Note that a speech item is
1315     * considered complete once it's audio data has been sent to the audio mixer, or
1316     * written to a file. There might be a finite lag between this point, and when
1317     * the audio hardware completes playback.
1318     *
1319     * @return {@code true} if the TTS engine is speaking.
1320     */
1321    public boolean isSpeaking() {
1322        return runAction(new Action<Boolean>() {
1323            @Override
1324            public Boolean run(ITextToSpeechService service) throws RemoteException {
1325                return service.isSpeaking();
1326            }
1327        }, false, "isSpeaking");
1328    }
1329
1330    /**
1331     * Interrupts the current utterance (whether played or rendered to file) and discards other
1332     * utterances in the queue.
1333     *
1334     * @return {@link #ERROR} or {@link #SUCCESS}.
1335     */
1336    public int stop() {
1337        return runAction(new Action<Integer>() {
1338            @Override
1339            public Integer run(ITextToSpeechService service) throws RemoteException {
1340                return service.stop(getCallerIdentity());
1341            }
1342        }, ERROR, "stop");
1343    }
1344
1345    /**
1346     * Sets the speech rate.
1347     *
1348     * This has no effect on any pre-recorded speech.
1349     *
1350     * @param speechRate Speech rate. {@code 1.0} is the normal speech rate,
1351     *            lower values slow down the speech ({@code 0.5} is half the normal speech rate),
1352     *            greater values accelerate it ({@code 2.0} is twice the normal speech rate).
1353     *
1354     * @return {@link #ERROR} or {@link #SUCCESS}.
1355     */
1356    public int setSpeechRate(float speechRate) {
1357        if (speechRate > 0.0f) {
1358            int intRate = (int)(speechRate * 100);
1359            if (intRate > 0) {
1360                synchronized (mStartLock) {
1361                    mParams.putInt(Engine.KEY_PARAM_RATE, intRate);
1362                }
1363                return SUCCESS;
1364            }
1365        }
1366        return ERROR;
1367    }
1368
1369    /**
1370     * Sets the speech pitch for the TextToSpeech engine.
1371     *
1372     * This has no effect on any pre-recorded speech.
1373     *
1374     * @param pitch Speech pitch. {@code 1.0} is the normal pitch,
1375     *            lower values lower the tone of the synthesized voice,
1376     *            greater values increase it.
1377     *
1378     * @return {@link #ERROR} or {@link #SUCCESS}.
1379     */
1380    public int setPitch(float pitch) {
1381        if (pitch > 0.0f) {
1382            int intPitch = (int)(pitch * 100);
1383            if (intPitch > 0) {
1384                synchronized (mStartLock) {
1385                    mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch);
1386                }
1387                return SUCCESS;
1388            }
1389        }
1390        return ERROR;
1391    }
1392
1393    /**
1394     * Sets the audio attributes to be used when speaking text or playing
1395     * back a file.
1396     *
1397     * @param audioAttributes Valid AudioAttributes instance.
1398     *
1399     * @return {@link #ERROR} or {@link #SUCCESS}.
1400     */
1401    public int setAudioAttributes(AudioAttributes audioAttributes) {
1402        if (audioAttributes != null) {
1403            synchronized (mStartLock) {
1404                mParams.putParcelable(Engine.KEY_PARAM_AUDIO_ATTRIBUTES,
1405                    audioAttributes);
1406            }
1407            return SUCCESS;
1408        }
1409        return ERROR;
1410    }
1411
1412    /**
1413     * @return the engine currently in use by this TextToSpeech instance.
1414     * @hide
1415     */
1416    public String getCurrentEngine() {
1417        return mCurrentEngine;
1418    }
1419
1420    /**
1421     * Returns a Locale instance describing the language currently being used as the default
1422     * Text-to-speech language.
1423     *
1424     * The locale object returned by this method is NOT a valid one. It has identical form to the
1425     * one in {@link #getLanguage()}. Please refer to {@link #getLanguage()} for more information.
1426     *
1427     * @return language, country (if any) and variant (if any) used by the client stored in a
1428     *     Locale instance, or {@code null} on error.
1429     * @deprecated As of API Level 20, use <code>getDefaultVoice().getLocale()</code> ({@link
1430     *   #getDefaultVoice()})
1431     */
1432    @Deprecated
1433    public Locale getDefaultLanguage() {
1434        return runAction(new Action<Locale>() {
1435            @Override
1436            public Locale run(ITextToSpeechService service) throws RemoteException {
1437                String[] defaultLanguage = service.getClientDefaultLanguage();
1438
1439                return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
1440            }
1441        }, null, "getDefaultLanguage");
1442    }
1443
1444    /**
1445     * Sets the text-to-speech language.
1446     * The TTS engine will try to use the closest match to the specified
1447     * language as represented by the Locale, but there is no guarantee that the exact same Locale
1448     * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
1449     * before choosing the language to use for the next utterances.
1450     *
1451     * This method sets the current voice to the default one for the given Locale;
1452     * {@link #getVoice()} can be used to retrieve it.
1453     *
1454     * @param loc The locale describing the language to be used.
1455     *
1456     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1457     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1458     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1459     */
1460    public int setLanguage(final Locale loc) {
1461        return runAction(new Action<Integer>() {
1462            @Override
1463            public Integer run(ITextToSpeechService service) throws RemoteException {
1464                if (loc == null) {
1465                    return LANG_NOT_SUPPORTED;
1466                }
1467                String language = null, country = null;
1468                try {
1469                    language = loc.getISO3Language();
1470                } catch (MissingResourceException e) {
1471                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
1472                    return LANG_NOT_SUPPORTED;
1473                }
1474
1475                try {
1476                    country = loc.getISO3Country();
1477                } catch (MissingResourceException e) {
1478                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
1479                    return LANG_NOT_SUPPORTED;
1480                }
1481
1482                String variant = loc.getVariant();
1483
1484                // As of API level 20, setLanguage is implemented using setVoice.
1485                // (which, in the default implementation, will call loadLanguage on the service
1486                // interface).
1487
1488                // Sanitize locale using isLanguageAvailable.
1489                int result = service.isLanguageAvailable( language, country, variant);
1490                if (result >= LANG_AVAILABLE){
1491                    if (result < LANG_COUNTRY_VAR_AVAILABLE) {
1492                        variant = "";
1493                        if (result < LANG_COUNTRY_AVAILABLE) {
1494                            country = "";
1495                        }
1496                    }
1497                    // Get the default voice for the locale.
1498                    String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
1499                    if (TextUtils.isEmpty(voiceName)) {
1500                        Log.w(TAG, "Couldn't find the default voice for " + language + "/" +
1501                                country + "/" + variant);
1502                        return LANG_NOT_SUPPORTED;
1503                    }
1504
1505                    // Load it.
1506                    if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
1507                        return LANG_NOT_SUPPORTED;
1508                    }
1509
1510                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
1511                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
1512                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
1513                    mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
1514                }
1515                return result;
1516            }
1517        }, LANG_NOT_SUPPORTED, "setLanguage");
1518    }
1519
1520    /**
1521     * Returns a Locale instance describing the language currently being used for synthesis
1522     * requests sent to the TextToSpeech engine.
1523     *
1524     * In Android 4.2 and before (API <= 17) this function returns the language that is currently
1525     * being used by the TTS engine. That is the last language set by this or any other
1526     * client by a {@link TextToSpeech#setLanguage} call to the same engine.
1527     *
1528     * In Android versions after 4.2 this function returns the language that is currently being
1529     * used for the synthesis requests sent from this client. That is the last language set
1530     * by a {@link TextToSpeech#setLanguage} call on this instance.
1531     *
1532     * If a voice is set (by {@link #setVoice(Voice)}), getLanguage will return the language of
1533     * the currently set voice.
1534     *
1535     * Please note that the Locale object returned by this method is NOT a valid Locale object. Its
1536     * language field contains a three-letter ISO 639-2/T code (where a proper Locale would use
1537     * a two-letter ISO 639-1 code), and the country field contains a three-letter ISO 3166 country
1538     * code (where a proper Locale would use a two-letter ISO 3166-1 code).
1539     *
1540     * @return language, country (if any) and variant (if any) used by the client stored in a
1541     *     Locale instance, or {@code null} on error.
1542     *
1543     * @deprecated As of API level 20, please use <code>getVoice().getLocale()</code>
1544     * ({@link #getVoice()}).
1545     */
1546    @Deprecated
1547    public Locale getLanguage() {
1548        return runAction(new Action<Locale>() {
1549            @Override
1550            public Locale run(ITextToSpeechService service) {
1551                /* No service call, but we're accessing mParams, hence need for
1552                   wrapping it as an Action instance */
1553                String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
1554                String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
1555                String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
1556                return new Locale(lang, country, variant);
1557            }
1558        }, null, "getLanguage");
1559    }
1560
1561    /**
1562     * Query the engine about the set of available languages.
1563     */
1564    public Set<Locale> getAvailableLanguages() {
1565        return runAction(new Action<Set<Locale>>() {
1566            @Override
1567            public Set<Locale> run(ITextToSpeechService service) throws RemoteException {
1568                List<Voice> voices = service.getVoices();
1569                if (voices == null) {
1570                    return new HashSet<Locale>();
1571                }
1572                HashSet<Locale> locales = new HashSet<Locale>();
1573                for (Voice voice : voices) {
1574                    locales.add(voice.getLocale());
1575                }
1576                return locales;
1577            }
1578        }, null, "getAvailableLanguages");
1579    }
1580
1581    /**
1582     * Query the engine about the set of available voices.
1583     *
1584     * Each TTS Engine can expose multiple voices for each locale, each with a different set of
1585     * features.
1586     *
1587     * @see #setVoice(Voice)
1588     * @see Voice
1589     */
1590    public Set<Voice> getVoices() {
1591        return runAction(new Action<Set<Voice>>() {
1592            @Override
1593            public Set<Voice> run(ITextToSpeechService service) throws RemoteException {
1594                List<Voice> voices = service.getVoices();
1595                return (voices != null)  ? new HashSet<Voice>(voices) : new HashSet<Voice>();
1596            }
1597        }, null, "getVoices");
1598    }
1599
1600    /**
1601     * Sets the text-to-speech voice.
1602     *
1603     * @param voice One of objects returned by {@link #getVoices()}.
1604     *
1605     * @return {@link #ERROR} or {@link #SUCCESS}.
1606     *
1607     * @see #getVoices
1608     * @see Voice
1609     */
1610    public int setVoice(final Voice voice) {
1611        return runAction(new Action<Integer>() {
1612            @Override
1613            public Integer run(ITextToSpeechService service) throws RemoteException {
1614                int result = service.loadVoice(getCallerIdentity(), voice.getName());
1615                if (result == SUCCESS) {
1616                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
1617
1618                    // Set the language/country/variant, so #getLanguage will return the voice
1619                    // locale when called.
1620                    String language = "";
1621                    try {
1622                        language = voice.getLocale().getISO3Language();
1623                    } catch (MissingResourceException e) {
1624                        Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
1625                                voice.getLocale(), e);
1626                    }
1627
1628                    String country = "";
1629                    try {
1630                        country = voice.getLocale().getISO3Country();
1631                    } catch (MissingResourceException e) {
1632                        Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
1633                                voice.getLocale(), e);
1634                    }
1635                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
1636                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
1637                    mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
1638                }
1639                return result;
1640            }
1641        }, LANG_NOT_SUPPORTED, "setVoice");
1642    }
1643
1644    /**
1645     * Returns a Voice instance describing the voice currently being used for synthesis
1646     * requests sent to the TextToSpeech engine.
1647     *
1648     * @return Voice instance used by the client, or {@code null} if not set or on error.
1649     *
1650     * @see #getVoices
1651     * @see #setVoice
1652     * @see Voice
1653     */
1654    public Voice getVoice() {
1655        return runAction(new Action<Voice>() {
1656            @Override
1657            public Voice run(ITextToSpeechService service) throws RemoteException {
1658                String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
1659                if (TextUtils.isEmpty(voiceName)) {
1660                    return null;
1661                }
1662                List<Voice> voices = service.getVoices();
1663                if (voices == null) {
1664                    return null;
1665                }
1666                for (Voice voice : voices) {
1667                    if (voice.getName().equals(voiceName)) {
1668                        return voice;
1669                    }
1670                }
1671                return null;
1672            }
1673        }, null, "getVoice");
1674    }
1675
1676    /**
1677     * Returns a Voice instance that's the default voice for the default Text-to-speech language.
1678     * @return The default voice instance for the default language, or {@code null} if not set or
1679     *     on error.
1680     */
1681    public Voice getDefaultVoice() {
1682        return runAction(new Action<Voice>() {
1683            @Override
1684            public Voice run(ITextToSpeechService service) throws RemoteException {
1685
1686                String[] defaultLanguage = service.getClientDefaultLanguage();
1687
1688                if (defaultLanguage == null || defaultLanguage.length == 0) {
1689                    Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
1690                    return null;
1691                }
1692                String language = defaultLanguage[0];
1693                String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
1694                String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
1695
1696                // Sanitize the locale using isLanguageAvailable.
1697                int result = service.isLanguageAvailable(language, country, variant);
1698                if (result >= LANG_AVAILABLE){
1699                    if (result < LANG_COUNTRY_VAR_AVAILABLE) {
1700                        variant = "";
1701                        if (result < LANG_COUNTRY_AVAILABLE) {
1702                            country = "";
1703                        }
1704                    }
1705                } else {
1706                    // The default language is not supported.
1707                    return null;
1708                }
1709
1710                // Get the default voice name
1711                String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
1712                if (TextUtils.isEmpty(voiceName)) {
1713                    return null;
1714                }
1715
1716                // Find it
1717                List<Voice> voices = service.getVoices();
1718                if (voices == null) {
1719                    return null;
1720                }
1721                for (Voice voice : voices) {
1722                    if (voice.getName().equals(voiceName)) {
1723                        return voice;
1724                    }
1725                }
1726                return null;
1727            }
1728        }, null, "getDefaultVoice");
1729    }
1730
1731
1732
1733    /**
1734     * Checks if the specified language as represented by the Locale is available and supported.
1735     *
1736     * @param loc The Locale describing the language to be used.
1737     *
1738     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1739     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1740     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1741     */
1742    public int isLanguageAvailable(final Locale loc) {
1743        return runAction(new Action<Integer>() {
1744            @Override
1745            public Integer run(ITextToSpeechService service) throws RemoteException {
1746                String language = null, country = null;
1747
1748                try {
1749                    language = loc.getISO3Language();
1750                } catch (MissingResourceException e) {
1751                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
1752                    return LANG_NOT_SUPPORTED;
1753                }
1754
1755                try {
1756                    country = loc.getISO3Country();
1757                } catch (MissingResourceException e) {
1758                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
1759                    return LANG_NOT_SUPPORTED;
1760                }
1761
1762                return service.isLanguageAvailable(language, country, loc.getVariant());
1763            }
1764        }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
1765    }
1766
1767    /**
1768     * Synthesizes the given text to a file using the specified parameters.
1769     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1770     * requests and then returns. The synthesis might not have finished (or even started!) at the
1771     * time when this method returns. In order to reliably detect errors during synthesis,
1772     * we recommend setting an utterance progress listener (see
1773     * {@link #setOnUtteranceProgressListener}).
1774     *
1775     * @param text The text that should be synthesized. No longer than
1776     *            {@link #getMaxSpeechInputLength()} characters.
1777     * @param params Parameters for the request. Can be null.
1778     *            Engine specific parameters may be passed in but the parameter keys
1779     *            must be prefixed by the name of the engine they are intended for. For example
1780     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1781     *            engine named "com.svox.pico" if it is being used.
1782     * @param file File to write the generated audio data to.
1783     * @param utteranceId An unique identifier for this request.
1784     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
1785     */
1786    public int synthesizeToFile(final CharSequence text, final Bundle params,
1787            final File file, final String utteranceId) {
1788        return runAction(new Action<Integer>() {
1789            @Override
1790            public Integer run(ITextToSpeechService service) throws RemoteException {
1791                ParcelFileDescriptor fileDescriptor;
1792                int returnValue;
1793                try {
1794                    if(file.exists() && !file.canWrite()) {
1795                        Log.e(TAG, "Can't write to " + file);
1796                        return ERROR;
1797                    }
1798                    fileDescriptor = ParcelFileDescriptor.open(file,
1799                            ParcelFileDescriptor.MODE_WRITE_ONLY |
1800                            ParcelFileDescriptor.MODE_CREATE |
1801                            ParcelFileDescriptor.MODE_TRUNCATE);
1802                    returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
1803                            fileDescriptor, getParams(params), utteranceId);
1804                    fileDescriptor.close();
1805                    return returnValue;
1806                } catch (FileNotFoundException e) {
1807                    Log.e(TAG, "Opening file " + file + " failed", e);
1808                    return ERROR;
1809                } catch (IOException e) {
1810                    Log.e(TAG, "Closing file " + file + " failed", e);
1811                    return ERROR;
1812                }
1813            }
1814        }, ERROR, "synthesizeToFile");
1815    }
1816
1817    /**
1818     * Synthesizes the given text to a file using the specified parameters.
1819     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1820     * requests and then returns. The synthesis might not have finished (or even started!) at the
1821     * time when this method returns. In order to reliably detect errors during synthesis,
1822     * we recommend setting an utterance progress listener (see
1823     * {@link #setOnUtteranceProgressListener}) and using the
1824     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1825     *
1826     * @param text The text that should be synthesized. No longer than
1827     *            {@link #getMaxSpeechInputLength()} characters.
1828     * @param params Parameters for the request. Can be null.
1829     *            Supported parameter names:
1830     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1831     *            Engine specific parameters may be passed in but the parameter keys
1832     *            must be prefixed by the name of the engine they are intended for. For example
1833     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1834     *            engine named "com.svox.pico" if it is being used.
1835     * @param filename Absolute file filename to write the generated audio data to.It should be
1836     *            something like "/sdcard/myappsounds/mysound.wav".
1837     *
1838     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
1839     * @deprecated As of API level 20, replaced by
1840     *         {@link #synthesizeToFile(CharSequence, Bundle, File, String)}.
1841     */
1842    @Deprecated
1843    public int synthesizeToFile(final String text, final HashMap<String, String> params,
1844            final String filename) {
1845        return synthesizeToFile(text, convertParamsHashMaptoBundle(params),
1846                new File(filename), params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1847    }
1848
1849    private Bundle convertParamsHashMaptoBundle(HashMap<String, String> params) {
1850        if (params != null && !params.isEmpty()) {
1851            Bundle bundle = new Bundle();
1852            copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
1853            copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID);
1854            copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
1855            copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
1856            copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
1857
1858            // Copy feature strings defined by the framework.
1859            copyStringParam(bundle, params, Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
1860            copyStringParam(bundle, params, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
1861            copyIntParam(bundle, params, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS);
1862            copyIntParam(bundle, params, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT);
1863
1864            // Copy over all parameters that start with the name of the
1865            // engine that we are currently connected to. The engine is
1866            // free to interpret them as it chooses.
1867            if (!TextUtils.isEmpty(mCurrentEngine)) {
1868                for (Map.Entry<String, String> entry : params.entrySet()) {
1869                    final String key = entry.getKey();
1870                    if (key != null && key.startsWith(mCurrentEngine)) {
1871                        bundle.putString(key, entry.getValue());
1872                    }
1873                }
1874            }
1875
1876            return bundle;
1877        }
1878        return null;
1879    }
1880
1881    private Bundle getParams(Bundle params) {
1882        if (params != null && !params.isEmpty()) {
1883            Bundle bundle = new Bundle(mParams);
1884            bundle.putAll(params);
1885
1886            verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_STREAM);
1887            verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_SESSION_ID);
1888            verifyStringBundleParam(bundle, Engine.KEY_PARAM_UTTERANCE_ID);
1889            verifyFloatBundleParam(bundle, Engine.KEY_PARAM_VOLUME);
1890            verifyFloatBundleParam(bundle, Engine.KEY_PARAM_PAN);
1891
1892            // Copy feature strings defined by the framework.
1893            verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
1894            verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
1895            verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS);
1896            verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT);
1897
1898            return bundle;
1899        } else {
1900            return mParams;
1901        }
1902    }
1903
1904    private static boolean verifyIntegerBundleParam(Bundle bundle, String key) {
1905        if (bundle.containsKey(key)) {
1906            if (!(bundle.get(key) instanceof Integer ||
1907                    bundle.get(key) instanceof Long)) {
1908                bundle.remove(key);
1909                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
1910                        + " with invalid type. Should be an Integer or a Long");
1911                return false;
1912            }
1913        }
1914        return true;
1915    }
1916
1917    private static boolean verifyStringBundleParam(Bundle bundle, String key) {
1918        if (bundle.containsKey(key)) {
1919            if (!(bundle.get(key) instanceof String)) {
1920                bundle.remove(key);
1921                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
1922                        + " with invalid type. Should be a String");
1923                return false;
1924            }
1925        }
1926        return true;
1927    }
1928
1929    private static boolean verifyBooleanBundleParam(Bundle bundle, String key) {
1930        if (bundle.containsKey(key)) {
1931            if (!(bundle.get(key) instanceof Boolean ||
1932                    bundle.get(key) instanceof String)) {
1933                bundle.remove(key);
1934                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
1935                        + " with invalid type. Should be a Boolean or String");
1936                return false;
1937            }
1938        }
1939        return true;
1940    }
1941
1942
1943    private static boolean verifyFloatBundleParam(Bundle bundle, String key) {
1944        if (bundle.containsKey(key)) {
1945            if (!(bundle.get(key) instanceof Float ||
1946                    bundle.get(key) instanceof Double)) {
1947                bundle.remove(key);
1948                Log.w(TAG, "Synthesis request paramter " + key + " containst value "
1949                        + " with invalid type. Should be a Float or a Double");
1950                return false;
1951            }
1952        }
1953        return true;
1954    }
1955
1956    private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
1957        String value = params.get(key);
1958        if (value != null) {
1959            bundle.putString(key, value);
1960        }
1961    }
1962
1963    private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) {
1964        String valueString = params.get(key);
1965        if (!TextUtils.isEmpty(valueString)) {
1966            try {
1967                int value = Integer.parseInt(valueString);
1968                bundle.putInt(key, value);
1969            } catch (NumberFormatException ex) {
1970                // don't set the value in the bundle
1971            }
1972        }
1973    }
1974
1975    private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) {
1976        String valueString = params.get(key);
1977        if (!TextUtils.isEmpty(valueString)) {
1978            try {
1979                float value = Float.parseFloat(valueString);
1980                bundle.putFloat(key, value);
1981            } catch (NumberFormatException ex) {
1982                // don't set the value in the bundle
1983            }
1984        }
1985    }
1986
1987    /**
1988     * Sets the listener that will be notified when synthesis of an utterance completes.
1989     *
1990     * @param listener The listener to use.
1991     *
1992     * @return {@link #ERROR} or {@link #SUCCESS}.
1993     *
1994     * @deprecated Use {@link #setOnUtteranceProgressListener(UtteranceProgressListener)}
1995     *        instead.
1996     */
1997    @Deprecated
1998    public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
1999        mUtteranceProgressListener = UtteranceProgressListener.from(listener);
2000        return TextToSpeech.SUCCESS;
2001    }
2002
2003    /**
2004     * Sets the listener that will be notified of various events related to the
2005     * synthesis of a given utterance.
2006     *
2007     * See {@link UtteranceProgressListener} and
2008     * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}.
2009     *
2010     * @param listener the listener to use.
2011     * @return {@link #ERROR} or {@link #SUCCESS}
2012     */
2013    public int setOnUtteranceProgressListener(UtteranceProgressListener listener) {
2014        mUtteranceProgressListener = listener;
2015        return TextToSpeech.SUCCESS;
2016    }
2017
2018    /**
2019     * Sets the TTS engine to use.
2020     *
2021     * @deprecated This doesn't inform callers when the TTS engine has been
2022     *        initialized. {@link #TextToSpeech(Context, OnInitListener, String)}
2023     *        can be used with the appropriate engine name. Also, there is no
2024     *        guarantee that the engine specified will be loaded. If it isn't
2025     *        installed or disabled, the user / system wide defaults will apply.
2026     *
2027     * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico")
2028     *
2029     * @return {@link #ERROR} or {@link #SUCCESS}.
2030     */
2031    @Deprecated
2032    public int setEngineByPackageName(String enginePackageName) {
2033        mRequestedEngine = enginePackageName;
2034        return initTts();
2035    }
2036
2037    /**
2038     * Gets the package name of the default speech synthesis engine.
2039     *
2040     * @return Package name of the TTS engine that the user has chosen
2041     *        as their default.
2042     */
2043    public String getDefaultEngine() {
2044        return mEnginesHelper.getDefaultEngine();
2045    }
2046
2047    /**
2048     * Checks whether the user's settings should override settings requested
2049     * by the calling application. As of the Ice cream sandwich release,
2050     * user settings never forcibly override the app's settings.
2051     */
2052    @Deprecated
2053    public boolean areDefaultsEnforced() {
2054        return false;
2055    }
2056
2057    /**
2058     * Gets a list of all installed TTS engines.
2059     *
2060     * @return A list of engine info objects. The list can be empty, but never {@code null}.
2061     */
2062    public List<EngineInfo> getEngines() {
2063        return mEnginesHelper.getEngines();
2064    }
2065
2066    private class Connection implements ServiceConnection {
2067        private ITextToSpeechService mService;
2068
2069        private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask;
2070
2071        private boolean mEstablished;
2072
2073        private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() {
2074            public void onStop(String utteranceId) throws RemoteException {
2075                UtteranceProgressListener listener = mUtteranceProgressListener;
2076                if (listener != null) {
2077                    listener.onDone(utteranceId);
2078                }
2079            };
2080
2081            @Override
2082            public void onSuccess(String utteranceId) {
2083                UtteranceProgressListener listener = mUtteranceProgressListener;
2084                if (listener != null) {
2085                    listener.onDone(utteranceId);
2086                }
2087            }
2088
2089            @Override
2090            public void onError(String utteranceId, int errorCode) {
2091                UtteranceProgressListener listener = mUtteranceProgressListener;
2092                if (listener != null) {
2093                    listener.onError(utteranceId);
2094                }
2095            }
2096
2097            @Override
2098            public void onStart(String utteranceId) {
2099                UtteranceProgressListener listener = mUtteranceProgressListener;
2100                if (listener != null) {
2101                    listener.onStart(utteranceId);
2102                }
2103            }
2104        };
2105
2106        private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> {
2107            private final ComponentName mName;
2108
2109            public SetupConnectionAsyncTask(ComponentName name) {
2110                mName = name;
2111            }
2112
2113            @Override
2114            protected Integer doInBackground(Void... params) {
2115                synchronized(mStartLock) {
2116                    if (isCancelled()) {
2117                        return null;
2118                    }
2119
2120                    try {
2121                        mService.setCallback(getCallerIdentity(), mCallback);
2122
2123                        if (mParams.getString(Engine.KEY_PARAM_LANGUAGE) == null) {
2124                            String[] defaultLanguage = mService.getClientDefaultLanguage();
2125                            mParams.putString(Engine.KEY_PARAM_LANGUAGE, defaultLanguage[0]);
2126                            mParams.putString(Engine.KEY_PARAM_COUNTRY, defaultLanguage[1]);
2127                            mParams.putString(Engine.KEY_PARAM_VARIANT, defaultLanguage[2]);
2128
2129                            // Get the default voice for the locale.
2130                            String defaultVoiceName = mService.getDefaultVoiceNameFor(
2131                                defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
2132                            mParams.putString(Engine.KEY_PARAM_VOICE_NAME, defaultVoiceName);
2133                        }
2134
2135                        Log.i(TAG, "Set up connection to " + mName);
2136                        return SUCCESS;
2137                    } catch (RemoteException re) {
2138                        Log.e(TAG, "Error connecting to service, setCallback() failed");
2139                        return ERROR;
2140                    }
2141                }
2142            }
2143
2144            @Override
2145            protected void onPostExecute(Integer result) {
2146                synchronized(mStartLock) {
2147                    if (mOnSetupConnectionAsyncTask == this) {
2148                        mOnSetupConnectionAsyncTask = null;
2149                    }
2150                    mEstablished = true;
2151                    dispatchOnInit(result);
2152                }
2153            }
2154        }
2155
2156        @Override
2157        public void onServiceConnected(ComponentName name, IBinder service) {
2158            synchronized(mStartLock) {
2159                mConnectingServiceConnection = null;
2160
2161                Log.i(TAG, "Connected to " + name);
2162
2163                if (mOnSetupConnectionAsyncTask != null) {
2164                    mOnSetupConnectionAsyncTask.cancel(false);
2165                }
2166
2167                mService = ITextToSpeechService.Stub.asInterface(service);
2168                mServiceConnection = Connection.this;
2169
2170                mEstablished = false;
2171                mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name);
2172                mOnSetupConnectionAsyncTask.execute();
2173            }
2174        }
2175
2176        public IBinder getCallerIdentity() {
2177            return mCallback;
2178        }
2179
2180        /**
2181         * Clear connection related fields and cancel mOnServiceConnectedAsyncTask if set.
2182         *
2183         * @return true if we cancel mOnSetupConnectionAsyncTask in progress.
2184         */
2185        private boolean clearServiceConnection() {
2186            synchronized(mStartLock) {
2187                boolean result = false;
2188                if (mOnSetupConnectionAsyncTask != null) {
2189                    result = mOnSetupConnectionAsyncTask.cancel(false);
2190                    mOnSetupConnectionAsyncTask = null;
2191                }
2192
2193                mService = null;
2194                // If this is the active connection, clear it
2195                if (mServiceConnection == this) {
2196                    mServiceConnection = null;
2197                }
2198                return result;
2199            }
2200        }
2201
2202        @Override
2203        public void onServiceDisconnected(ComponentName name) {
2204            Log.i(TAG, "Asked to disconnect from " + name);
2205            if (clearServiceConnection()) {
2206                /* We need to protect against a rare case where engine
2207                 * dies just after successful connection - and we process onServiceDisconnected
2208                 * before OnServiceConnectedAsyncTask.onPostExecute. onServiceDisconnected cancels
2209                 * OnServiceConnectedAsyncTask.onPostExecute and we don't call dispatchOnInit
2210                 * with ERROR as argument.
2211                 */
2212                dispatchOnInit(ERROR);
2213            }
2214        }
2215
2216        public void disconnect() {
2217            mContext.unbindService(this);
2218            clearServiceConnection();
2219        }
2220
2221        public boolean isEstablished() {
2222            return mService != null && mEstablished;
2223        }
2224
2225        public <R> R runAction(Action<R> action, R errorResult, String method,
2226                boolean reconnect, boolean onlyEstablishedConnection) {
2227            synchronized (mStartLock) {
2228                try {
2229                    if (mService == null) {
2230                        Log.w(TAG, method + " failed: not connected to TTS engine");
2231                        return errorResult;
2232                    }
2233                    if (onlyEstablishedConnection && !isEstablished()) {
2234                        Log.w(TAG, method + " failed: TTS engine connection not fully set up");
2235                        return errorResult;
2236                    }
2237                    return action.run(mService);
2238                } catch (RemoteException ex) {
2239                    Log.e(TAG, method + " failed", ex);
2240                    if (reconnect) {
2241                        disconnect();
2242                        initTts();
2243                    }
2244                    return errorResult;
2245                }
2246            }
2247        }
2248    }
2249
2250    private interface Action<R> {
2251        R run(ITextToSpeechService service) throws RemoteException;
2252    }
2253
2254    /**
2255     * Information about an installed text-to-speech engine.
2256     *
2257     * @see TextToSpeech#getEngines
2258     */
2259    public static class EngineInfo {
2260        /**
2261         * Engine package name..
2262         */
2263        public String name;
2264        /**
2265         * Localized label for the engine.
2266         */
2267        public String label;
2268        /**
2269         * Icon for the engine.
2270         */
2271        public int icon;
2272        /**
2273         * Whether this engine is a part of the system
2274         * image.
2275         *
2276         * @hide
2277         */
2278        public boolean system;
2279        /**
2280         * The priority the engine declares for the the intent filter
2281         * {@code android.intent.action.TTS_SERVICE}
2282         *
2283         * @hide
2284         */
2285        public int priority;
2286
2287        @Override
2288        public String toString() {
2289            return "EngineInfo{name=" + name + "}";
2290        }
2291
2292    }
2293
2294    /**
2295     * Limit of length of input string passed to speak and synthesizeToFile.
2296     *
2297     * @see #speak
2298     * @see #synthesizeToFile
2299     */
2300    public static int getMaxSpeechInputLength() {
2301        return 4000;
2302    }
2303}
2304