TextToSpeech.java revision ad6df74ada7c478257425b746588f22eeec199a6
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.AudioManager;
26import android.net.Uri;
27import android.os.AsyncTask;
28import android.os.Bundle;
29import android.os.IBinder;
30import android.os.ParcelFileDescriptor;
31import android.os.RemoteException;
32import android.provider.Settings;
33import android.text.TextUtils;
34import android.util.Log;
35
36import java.io.File;
37import java.io.FileNotFoundException;
38import java.io.IOException;
39import java.util.ArrayList;
40import java.util.Collections;
41import java.util.HashMap;
42import java.util.HashSet;
43import java.util.List;
44import java.util.Locale;
45import java.util.Map;
46import java.util.MissingResourceException;
47import java.util.Set;
48import java.util.TreeSet;
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 identify an utterance in the
528         * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
529         * spoken, a file has been played back or a silence duration has elapsed.
530         *
531         * @see TextToSpeech#speak(String, int, HashMap)
532         * @see TextToSpeech#playEarcon(String, int, HashMap)
533         * @see TextToSpeech#synthesizeToFile(String, HashMap, String)
534         */
535        public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
536
537        /**
538         * Parameter key to specify the speech volume relative to the current stream type
539         * volume used when speaking text. Volume is specified as a float ranging from 0 to 1
540         * where 0 is silence, and 1 is the maximum volume (the default behavior).
541         *
542         * @see TextToSpeech#speak(String, int, HashMap)
543         * @see TextToSpeech#playEarcon(String, int, HashMap)
544         */
545        public static final String KEY_PARAM_VOLUME = "volume";
546
547        /**
548         * Parameter key to specify how the speech is panned from left to right when speaking text.
549         * Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
550         * 0 to center (the default behavior), and +1 to hard-right.
551         *
552         * @see TextToSpeech#speak(String, int, HashMap)
553         * @see TextToSpeech#playEarcon(String, int, HashMap)
554         */
555        public static final String KEY_PARAM_PAN = "pan";
556
557        /**
558         * Feature key for network synthesis. See {@link TextToSpeech#getFeatures(Locale)}
559         * for a description of how feature keys work. If set (and supported by the engine
560         * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must
561         * use network based synthesis.
562         *
563         * @see TextToSpeech#speak(String, int, java.util.HashMap)
564         * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
565         * @see TextToSpeech#getFeatures(java.util.Locale)
566         *
567         * @deprecated Starting from API level 20, to select network synthesis, call
568         * ({@link TextToSpeech#getVoices()}, find a suitable network voice
569         * ({@link Voice#getRequiresNetworkConnection()}) and pass it
570         * to {@link TextToSpeech#setVoice(Voice)}).
571         */
572        @Deprecated
573        public static final String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
574
575        /**
576         * Feature key for embedded synthesis. See {@link TextToSpeech#getFeatures(Locale)}
577         * for a description of how feature keys work. If set and supported by the engine
578         * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize
579         * text on-device (without making network requests).
580         *
581         * @see TextToSpeech#speak(String, int, java.util.HashMap)
582         * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
583         * @see TextToSpeech#getFeatures(java.util.Locale)
584
585         * @deprecated Starting from API level 20, to select embedded synthesis, call
586         * ({@link TextToSpeech#getVoices()}, find a suitable embedded voice
587         * ({@link Voice#getRequiresNetworkConnection()}) and pass it
588         * to {@link TextToSpeech#setVoice(Voice)}).
589         */
590        @Deprecated
591        public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
592
593        /**
594         * Parameter key to specify an audio session identifier (obtained from
595         * {@link AudioManager#allocateAudioSessionId()}) that will be used by the request audio
596         * output. It can be used to associate one of the {@link android.media.audiofx.AudioEffect}
597         * objects with the synthesis (or earcon) output.
598         *
599         * @see TextToSpeech#speak(String, int, HashMap)
600         * @see TextToSpeech#playEarcon(String, int, HashMap)
601         */
602        public static final String KEY_PARAM_SESSION_ID = "sessionId";
603
604        /**
605         * Feature key that indicates that the voice may need to download additional data to be fully
606         * functional. The download will be triggered by calling
607         * {@link TextToSpeech#setVoice(Voice)} or {@link TextToSpeech#setLanguage(Locale)}.
608         * Until download is complete, each synthesis request will either report
609         * {@link TextToSpeech#ERROR_NOT_INSTALLED_YET} error, or use a different voice to synthesize
610         * the request. This feature should NOT be used as a key of a request parameter.
611         *
612         * @see TextToSpeech#getFeatures(java.util.Locale)
613         * @see Voice#getFeatures()
614         */
615        public static final String KEY_FEATURE_NOT_INSTALLED = "notInstalled";
616
617        /**
618         * Feature key that indicate that a network timeout can be set for the request. If set and
619         * supported as per {@link TextToSpeech#getFeatures(Locale)} or {@link Voice#getFeatures()},
620         * it can be used as request parameter to set the maximum allowed time for a single
621         * request attempt, in milliseconds, before synthesis fails. When used as a key of
622         * a request parameter, its value should be a string with an integer value.
623         *
624         * @see TextToSpeech#getFeatures(java.util.Locale)
625         * @see Voice#getFeatures()
626         */
627        public static final String KEY_FEATURE_NETWORK_TIMEOUT_MS = "networkTimeoutMs";
628
629        /**
630         * Feature key that indicates that network request retries count can be set for the request.
631         * If set and supported as per {@link TextToSpeech#getFeatures(Locale)} or
632         * {@link Voice#getFeatures()}, it can be used as a request parameter to set the
633         * number of network request retries that are attempted in case of failure. When used as
634         * a key of a request parameter, its value should be a string with an integer value.
635         *
636         * @see TextToSpeech#getFeatures(java.util.Locale)
637         * @see Voice#getFeatures()
638         */
639        public static final String KEY_FEATURE_NETWORK_RETRIES_COUNT = "networkRetriesCount";
640    }
641
642    private final Context mContext;
643    private Connection mConnectingServiceConnection;
644    private Connection mServiceConnection;
645    private OnInitListener mInitListener;
646    // Written from an unspecified application thread, read from
647    // a binder thread.
648    private volatile UtteranceProgressListener mUtteranceProgressListener;
649    private final Object mStartLock = new Object();
650
651    private String mRequestedEngine;
652    // Whether to initialize this TTS object with the default engine,
653    // if the requested engine is not available. Valid only if mRequestedEngine
654    // is not null. Used only for testing, though potentially useful API wise
655    // too.
656    private final boolean mUseFallback;
657    private final Map<String, Uri> mEarcons;
658    private final Map<CharSequence, Uri> mUtterances;
659    private final Bundle mParams = new Bundle();
660    private final TtsEngines mEnginesHelper;
661    private volatile String mCurrentEngine = null;
662
663    /**
664     * The constructor for the TextToSpeech class, using the default TTS engine.
665     * This will also initialize the associated TextToSpeech engine if it isn't already running.
666     *
667     * @param context
668     *            The context this instance is running in.
669     * @param listener
670     *            The {@link TextToSpeech.OnInitListener} that will be called when the
671     *            TextToSpeech engine has initialized. In a case of a failure the listener
672     *            may be called immediately, before TextToSpeech instance is fully constructed.
673     */
674    public TextToSpeech(Context context, OnInitListener listener) {
675        this(context, listener, null);
676    }
677
678    /**
679     * The constructor for the TextToSpeech class, using the given TTS engine.
680     * This will also initialize the associated TextToSpeech engine if it isn't already running.
681     *
682     * @param context
683     *            The context this instance is running in.
684     * @param listener
685     *            The {@link TextToSpeech.OnInitListener} that will be called when the
686     *            TextToSpeech engine has initialized. In a case of a failure the listener
687     *            may be called immediately, before TextToSpeech instance is fully constructed.
688     * @param engine Package name of the TTS engine to use.
689     */
690    public TextToSpeech(Context context, OnInitListener listener, String engine) {
691        this(context, listener, engine, null, true);
692    }
693
694    /**
695     * Used by the framework to instantiate TextToSpeech objects with a supplied
696     * package name, instead of using {@link android.content.Context#getPackageName()}
697     *
698     * @hide
699     */
700    public TextToSpeech(Context context, OnInitListener listener, String engine,
701            String packageName, boolean useFallback) {
702        mContext = context;
703        mInitListener = listener;
704        mRequestedEngine = engine;
705        mUseFallback = useFallback;
706
707        mEarcons = new HashMap<String, Uri>();
708        mUtterances = new HashMap<CharSequence, Uri>();
709        mUtteranceProgressListener = null;
710
711        mEnginesHelper = new TtsEngines(mContext);
712        initTts();
713    }
714
715    private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method,
716            boolean onlyEstablishedConnection) {
717        return runAction(action, errorResult, method, false, onlyEstablishedConnection);
718    }
719
720    private <R> R runAction(Action<R> action, R errorResult, String method) {
721        return runAction(action, errorResult, method, true, true);
722    }
723
724    private <R> R runAction(Action<R> action, R errorResult, String method,
725            boolean reconnect, boolean onlyEstablishedConnection) {
726        synchronized (mStartLock) {
727            if (mServiceConnection == null) {
728                Log.w(TAG, method + " failed: not bound to TTS engine");
729                return errorResult;
730            }
731            return mServiceConnection.runAction(action, errorResult, method, reconnect,
732                    onlyEstablishedConnection);
733        }
734    }
735
736    private int initTts() {
737        // Step 1: Try connecting to the engine that was requested.
738        if (mRequestedEngine != null) {
739            if (mEnginesHelper.isEngineInstalled(mRequestedEngine)) {
740                if (connectToEngine(mRequestedEngine)) {
741                    mCurrentEngine = mRequestedEngine;
742                    return SUCCESS;
743                } else if (!mUseFallback) {
744                    mCurrentEngine = null;
745                    dispatchOnInit(ERROR);
746                    return ERROR;
747                }
748            } else if (!mUseFallback) {
749                Log.i(TAG, "Requested engine not installed: " + mRequestedEngine);
750                mCurrentEngine = null;
751                dispatchOnInit(ERROR);
752                return ERROR;
753            }
754        }
755
756        // Step 2: Try connecting to the user's default engine.
757        final String defaultEngine = getDefaultEngine();
758        if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) {
759            if (connectToEngine(defaultEngine)) {
760                mCurrentEngine = defaultEngine;
761                return SUCCESS;
762            }
763        }
764
765        // Step 3: Try connecting to the highest ranked engine in the
766        // system.
767        final String highestRanked = mEnginesHelper.getHighestRankedEngineName();
768        if (highestRanked != null && !highestRanked.equals(mRequestedEngine) &&
769                !highestRanked.equals(defaultEngine)) {
770            if (connectToEngine(highestRanked)) {
771                mCurrentEngine = highestRanked;
772                return SUCCESS;
773            }
774        }
775
776        // NOTE: The API currently does not allow the caller to query whether
777        // they are actually connected to any engine. This might fail for various
778        // reasons like if the user disables all her TTS engines.
779
780        mCurrentEngine = null;
781        dispatchOnInit(ERROR);
782        return ERROR;
783    }
784
785    private boolean connectToEngine(String engine) {
786        Connection connection = new Connection();
787        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
788        intent.setPackage(engine);
789        boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
790        if (!bound) {
791            Log.e(TAG, "Failed to bind to " + engine);
792            return false;
793        } else {
794            Log.i(TAG, "Sucessfully bound to " + engine);
795            mConnectingServiceConnection = connection;
796            return true;
797        }
798    }
799
800    private void dispatchOnInit(int result) {
801        synchronized (mStartLock) {
802            if (mInitListener != null) {
803                mInitListener.onInit(result);
804                mInitListener = null;
805            }
806        }
807    }
808
809    private IBinder getCallerIdentity() {
810        return mServiceConnection.getCallerIdentity();
811    }
812
813    /**
814     * Releases the resources used by the TextToSpeech engine.
815     * It is good practice for instance to call this method in the onDestroy() method of an Activity
816     * so the TextToSpeech engine can be cleanly stopped.
817     */
818    public void shutdown() {
819        // Special case, we are asked to shutdown connection that did finalize its connection.
820        synchronized (mStartLock) {
821            if (mConnectingServiceConnection != null) {
822                mContext.unbindService(mConnectingServiceConnection);
823                mConnectingServiceConnection = null;
824                return;
825            }
826        }
827
828        // Post connection case
829        runActionNoReconnect(new Action<Void>() {
830            @Override
831            public Void run(ITextToSpeechService service) throws RemoteException {
832                service.setCallback(getCallerIdentity(), null);
833                service.stop(getCallerIdentity());
834                mServiceConnection.disconnect();
835                // Context#unbindService does not result in a call to
836                // ServiceConnection#onServiceDisconnected. As a result, the
837                // service ends up being destroyed (if there are no other open
838                // connections to it) but the process lives on and the
839                // ServiceConnection continues to refer to the destroyed service.
840                //
841                // This leads to tons of log spam about SynthThread being dead.
842                mServiceConnection = null;
843                mCurrentEngine = null;
844                return null;
845            }
846        }, null, "shutdown", false);
847    }
848
849    /**
850     * Adds a mapping between a string of text and a sound resource in a
851     * package. After a call to this method, subsequent calls to
852     * {@link #speak(String, int, HashMap)} will play the specified sound resource
853     * if it is available, or synthesize the text it is missing.
854     *
855     * @param text
856     *            The string of text. Example: <code>"south_south_east"</code>
857     *
858     * @param packagename
859     *            Pass the packagename of the application that contains the
860     *            resource. If the resource is in your own application (this is
861     *            the most common case), then put the packagename of your
862     *            application here.<br/>
863     *            Example: <b>"com.google.marvin.compass"</b><br/>
864     *            The packagename can be found in the AndroidManifest.xml of
865     *            your application.
866     *            <p>
867     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
868     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
869     *            </p>
870     *
871     * @param resourceId
872     *            Example: <code>R.raw.south_south_east</code>
873     *
874     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
875     */
876    public int addSpeech(String text, String packagename, int resourceId) {
877        synchronized (mStartLock) {
878            mUtterances.put(text, makeResourceUri(packagename, resourceId));
879            return SUCCESS;
880        }
881    }
882
883    /**
884     * Adds a mapping between a CharSequence (may be spanned with TtsSpans) of text
885     * and a sound resource in a package. After a call to this method, subsequent calls to
886     * {@link #speak(String, int, HashMap)} will play the specified sound resource
887     * if it is available, or synthesize the text it is missing.
888     *
889     * @param text
890     *            The string of text. Example: <code>"south_south_east"</code>
891     *
892     * @param packagename
893     *            Pass the packagename of the application that contains the
894     *            resource. If the resource is in your own application (this is
895     *            the most common case), then put the packagename of your
896     *            application here.<br/>
897     *            Example: <b>"com.google.marvin.compass"</b><br/>
898     *            The packagename can be found in the AndroidManifest.xml of
899     *            your application.
900     *            <p>
901     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
902     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
903     *            </p>
904     *
905     * @param resourceId
906     *            Example: <code>R.raw.south_south_east</code>
907     *
908     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
909     */
910    public int addSpeech(CharSequence text, String packagename, int resourceId) {
911        synchronized (mStartLock) {
912            mUtterances.put(text, makeResourceUri(packagename, resourceId));
913            return SUCCESS;
914        }
915    }
916
917    /**
918     * Adds a mapping between a string of text and a sound file. Using this, it
919     * is possible to add custom pronounciations for a string of text.
920     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
921     * will play the specified sound resource if it is available, or synthesize the text it is
922     * missing.
923     *
924     * @param text
925     *            The string of text. Example: <code>"south_south_east"</code>
926     * @param filename
927     *            The full path to the sound file (for example:
928     *            "/sdcard/mysounds/hello.wav")
929     *
930     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
931     */
932    public int addSpeech(String text, String filename) {
933        synchronized (mStartLock) {
934            mUtterances.put(text, Uri.parse(filename));
935            return SUCCESS;
936        }
937    }
938
939    /**
940     * Adds a mapping between a CharSequence (may be spanned with TtsSpans and a sound file.
941     * Using this, it is possible to add custom pronounciations for a string of text.
942     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
943     * will play the specified sound resource if it is available, or synthesize the text it is
944     * missing.
945     *
946     * @param text
947     *            The string of text. Example: <code>"south_south_east"</code>
948     * @param filename
949     *            The full path to the sound file (for example:
950     *            "/sdcard/mysounds/hello.wav")
951     *
952     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
953     */
954    public int addSpeech(CharSequence text, String filename) {
955        synchronized (mStartLock) {
956            mUtterances.put(text, Uri.parse(filename));
957            return SUCCESS;
958        }
959    }
960
961
962    /**
963     * Adds a mapping between a string of text and a sound resource in a
964     * package. Use this to add custom earcons.
965     *
966     * @see #playEarcon(String, int, HashMap)
967     *
968     * @param earcon The name of the earcon.
969     *            Example: <code>"[tick]"</code><br/>
970     *
971     * @param packagename
972     *            the package name of the application that contains the
973     *            resource. This can for instance be the package name of your own application.
974     *            Example: <b>"com.google.marvin.compass"</b><br/>
975     *            The package name can be found in the AndroidManifest.xml of
976     *            the application containing the resource.
977     *            <p>
978     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
979     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
980     *            </p>
981     *
982     * @param resourceId
983     *            Example: <code>R.raw.tick_snd</code>
984     *
985     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
986     */
987    public int addEarcon(String earcon, String packagename, int resourceId) {
988        synchronized(mStartLock) {
989            mEarcons.put(earcon, makeResourceUri(packagename, resourceId));
990            return SUCCESS;
991        }
992    }
993
994    /**
995     * Adds a mapping between a string of text and a sound file.
996     * Use this to add custom earcons.
997     *
998     * @see #playEarcon(String, int, HashMap)
999     *
1000     * @param earcon
1001     *            The name of the earcon.
1002     *            Example: <code>"[tick]"</code>
1003     * @param filename
1004     *            The full path to the sound file (for example:
1005     *            "/sdcard/mysounds/tick.wav")
1006     *
1007     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
1008     */
1009    public int addEarcon(String earcon, String filename) {
1010        synchronized(mStartLock) {
1011            mEarcons.put(earcon, Uri.parse(filename));
1012            return SUCCESS;
1013        }
1014    }
1015
1016    private Uri makeResourceUri(String packageName, int resourceId) {
1017        return new Uri.Builder()
1018                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
1019                .encodedAuthority(packageName)
1020                .appendEncodedPath(String.valueOf(resourceId))
1021                .build();
1022    }
1023
1024    /**
1025     * Speaks the text using the specified queuing strategy and speech parameters, the text may
1026     * be spanned with TtsSpans.
1027     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1028     * requests and then returns. The synthesis might not have finished (or even started!) at the
1029     * time when this method returns. In order to reliably detect errors during synthesis,
1030     * we recommend setting an utterance progress listener (see
1031     * {@link #setOnUtteranceProgressListener}) and using the
1032     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1033     *
1034     * @param text The string of text to be spoken. No longer than
1035     *            {@link #getMaxSpeechInputLength()} characters.
1036     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1037     * @param params Parameters for the request. Can be null.
1038     *            Supported parameter names:
1039     *            {@link Engine#KEY_PARAM_STREAM},
1040     *            {@link Engine#KEY_PARAM_VOLUME},
1041     *            {@link Engine#KEY_PARAM_PAN}.
1042     *            Engine specific parameters may be passed in but the parameter keys
1043     *            must be prefixed by the name of the engine they are intended for. For example
1044     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1045     *            engine named "com.svox.pico" if it is being used.
1046     * @param utteranceId An unique identifier for this request.
1047     *
1048     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
1049     */
1050    public int speak(final CharSequence text,
1051                     final int queueMode,
1052                     final HashMap<String, String> params,
1053                     final String utteranceId) {
1054        return runAction(new Action<Integer>() {
1055            @Override
1056            public Integer run(ITextToSpeechService service) throws RemoteException {
1057                Uri utteranceUri = mUtterances.get(text);
1058                if (utteranceUri != null) {
1059                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
1060                            getParams(params), utteranceId);
1061                } else {
1062                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params),
1063                            utteranceId);
1064                }
1065            }
1066        }, ERROR, "speak");
1067    }
1068
1069    /**
1070     * Speaks the string using the specified queuing strategy and speech parameters.
1071     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1072     * requests and then returns. The synthesis might not have finished (or even started!) at the
1073     * time when this method returns. In order to reliably detect errors during synthesis,
1074     * we recommend setting an utterance progress listener (see
1075     * {@link #setOnUtteranceProgressListener}) and using the
1076     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1077     *
1078     * @param text The string of text to be spoken. No longer than
1079     *            {@link #getMaxSpeechInputLength()} characters.
1080     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1081     * @param params Parameters for the request. Can be null.
1082     *            Supported parameter names:
1083     *            {@link Engine#KEY_PARAM_STREAM},
1084     *            {@link Engine#KEY_PARAM_UTTERANCE_ID},
1085     *            {@link Engine#KEY_PARAM_VOLUME},
1086     *            {@link Engine#KEY_PARAM_PAN}.
1087     *            Engine specific parameters may be passed in but the parameter keys
1088     *            must be prefixed by the name of the engine they are intended for. For example
1089     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1090     *            engine named "com.svox.pico" if it is being used.
1091     *
1092     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
1093     * @deprecated As of API level 20, replaced by
1094     *         {@link #speak(CharSequence, int, HashMap, String)}.
1095     */
1096    @Deprecated
1097    public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
1098        return speak(text, queueMode, params,
1099                     params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1100    }
1101
1102    /**
1103     * Plays the earcon using the specified queueing mode and parameters.
1104     * The earcon must already have been added with {@link #addEarcon(String, String)} or
1105     * {@link #addEarcon(String, String, int)}.
1106     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1107     * requests and then returns. The synthesis might not have finished (or even started!) at the
1108     * time when this method returns. In order to reliably detect errors during synthesis,
1109     * we recommend setting an utterance progress listener (see
1110     * {@link #setOnUtteranceProgressListener}) and using the
1111     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1112     *
1113     * @param earcon The earcon that should be played
1114     * @param queueMode {@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     *            Engine specific parameters may be passed in but the parameter keys
1119     *            must be prefixed by the name of the engine they are intended for. For example
1120     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1121     *            engine named "com.svox.pico" if it is being used.
1122     *
1123     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
1124     */
1125    public int playEarcon(final String earcon, final int queueMode,
1126            final HashMap<String, String> params, final String utteranceId) {
1127        return runAction(new Action<Integer>() {
1128            @Override
1129            public Integer run(ITextToSpeechService service) throws RemoteException {
1130                Uri earconUri = mEarcons.get(earcon);
1131                if (earconUri == null) {
1132                    return ERROR;
1133                }
1134                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
1135                        getParams(params), utteranceId);
1136            }
1137        }, ERROR, "playEarcon");
1138    }
1139
1140    /**
1141     * Plays the earcon using the specified queueing mode and parameters.
1142     * The earcon must already have been added with {@link #addEarcon(String, String)} or
1143     * {@link #addEarcon(String, String, int)}.
1144     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1145     * requests and then returns. The synthesis might not have finished (or even started!) at the
1146     * time when this method returns. In order to reliably detect errors during synthesis,
1147     * we recommend setting an utterance progress listener (see
1148     * {@link #setOnUtteranceProgressListener}) and using the
1149     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1150     *
1151     * @param earcon The earcon that should be played
1152     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1153     * @param params Parameters for the request. Can be null.
1154     *            Supported parameter names:
1155     *            {@link Engine#KEY_PARAM_STREAM},
1156     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1157     *            Engine specific parameters may be passed in but the parameter keys
1158     *            must be prefixed by the name of the engine they are intended for. For example
1159     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1160     *            engine named "com.svox.pico" if it is being used.
1161     *
1162     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
1163     * @deprecated As of API level 20, replaced by
1164     *         {@link #playEarcon(String, int, HashMap, String)}.
1165     */
1166    @Deprecated
1167    public int playEarcon(final String earcon, final int queueMode,
1168            final HashMap<String, String> params) {
1169        return playEarcon(earcon, queueMode, params,
1170                          params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1171    }
1172
1173    /**
1174     * Plays silence for the specified amount of time using the specified
1175     * queue mode.
1176     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1177     * requests and then returns. The synthesis might not have finished (or even started!) at the
1178     * time when this method returns. In order to reliably detect errors during synthesis,
1179     * we recommend setting an utterance progress listener (see
1180     * {@link #setOnUtteranceProgressListener}) and using the
1181     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1182     *
1183     * @param durationInMs The duration of the silence.
1184     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1185     * @param params Parameters for the request. Can be null.
1186     *            Engine specific parameters may be passed in but the parameter keys
1187     *            must be prefixed by the name of the engine they are intended for. For example
1188     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1189     *            engine named "com.svox.pico" if it is being used.
1190     * @param utteranceId An unique identifier for this request.
1191     *
1192     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
1193     */
1194    public int playSilence(final long durationInMs, final int queueMode,
1195            final HashMap<String, String> params, final String utteranceId) {
1196        return runAction(new Action<Integer>() {
1197            @Override
1198            public Integer run(ITextToSpeechService service) throws RemoteException {
1199                return service.playSilence(getCallerIdentity(), durationInMs,
1200                                           queueMode, utteranceId);
1201            }
1202        }, ERROR, "playSilence");
1203    }
1204
1205    /**
1206     * Plays silence for the specified amount of time using the specified
1207     * queue mode.
1208     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1209     * requests and then returns. The synthesis might not have finished (or even started!) at the
1210     * time when this method returns. In order to reliably detect errors during synthesis,
1211     * we recommend setting an utterance progress listener (see
1212     * {@link #setOnUtteranceProgressListener}) and using the
1213     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1214     *
1215     * @param durationInMs The duration of the silence.
1216     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
1217     * @param params Parameters for the request. Can be null.
1218     *            Supported parameter names:
1219     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
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     *
1225     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
1226     * @deprecated As of API level 20, replaced by
1227     *         {@link #playSilence(long, int, HashMap, String)}.
1228     */
1229    @Deprecated
1230    public int playSilence(final long durationInMs, final int queueMode,
1231            final HashMap<String, String> params) {
1232        return playSilence(durationInMs, queueMode, params,
1233                           params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1234    }
1235
1236    /**
1237     * Queries the engine for the set of features it supports for a given locale.
1238     * Features can either be framework defined, e.g.
1239     * {@link TextToSpeech.Engine#KEY_FEATURE_NETWORK_SYNTHESIS} or engine specific.
1240     * Engine specific keys must be prefixed by the name of the engine they
1241     * are intended for. These keys can be used as parameters to
1242     * {@link TextToSpeech#speak(String, int, java.util.HashMap)} and
1243     * {@link TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)}.
1244     *
1245     * Features values are strings and their values must meet restrictions described in their
1246     * documentation.
1247     *
1248     * @param locale The locale to query features for.
1249     * @return Set instance. May return {@code null} on error.
1250     * @deprecated As of API level 20, please use voices. In order to query features of the voice,
1251     * call {@link #getVoices()} to retrieve the list of available voices and
1252     * {@link Voice#getFeatures()} to retrieve the set of features.
1253     */
1254    @Deprecated
1255    public Set<String> getFeatures(final Locale locale) {
1256        return runAction(new Action<Set<String>>() {
1257            @Override
1258            public Set<String> run(ITextToSpeechService service) throws RemoteException {
1259                String[] features = null;
1260                try {
1261                    features = service.getFeaturesForLanguage(
1262                        locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
1263                } catch(MissingResourceException e) {
1264                    Log.w(TAG, "Couldn't retrieve 3 letter ISO 639-2/T language and/or ISO 3166 " +
1265                            "country code for locale: " + locale, e);
1266                    return null;
1267                }
1268
1269                if (features != null) {
1270                    final Set<String> featureSet = new HashSet<String>();
1271                    Collections.addAll(featureSet, features);
1272                    return featureSet;
1273                }
1274                return null;
1275            }
1276        }, null, "getFeatures");
1277    }
1278
1279    /**
1280     * Checks whether the TTS engine is busy speaking. Note that a speech item is
1281     * considered complete once it's audio data has been sent to the audio mixer, or
1282     * written to a file. There might be a finite lag between this point, and when
1283     * the audio hardware completes playback.
1284     *
1285     * @return {@code true} if the TTS engine is speaking.
1286     */
1287    public boolean isSpeaking() {
1288        return runAction(new Action<Boolean>() {
1289            @Override
1290            public Boolean run(ITextToSpeechService service) throws RemoteException {
1291                return service.isSpeaking();
1292            }
1293        }, false, "isSpeaking");
1294    }
1295
1296    /**
1297     * Interrupts the current utterance (whether played or rendered to file) and discards other
1298     * utterances in the queue.
1299     *
1300     * @return {@link #ERROR} or {@link #SUCCESS}.
1301     */
1302    public int stop() {
1303        return runAction(new Action<Integer>() {
1304            @Override
1305            public Integer run(ITextToSpeechService service) throws RemoteException {
1306                return service.stop(getCallerIdentity());
1307            }
1308        }, ERROR, "stop");
1309    }
1310
1311    /**
1312     * Sets the speech rate.
1313     *
1314     * This has no effect on any pre-recorded speech.
1315     *
1316     * @param speechRate Speech rate. {@code 1.0} is the normal speech rate,
1317     *            lower values slow down the speech ({@code 0.5} is half the normal speech rate),
1318     *            greater values accelerate it ({@code 2.0} is twice the normal speech rate).
1319     *
1320     * @return {@link #ERROR} or {@link #SUCCESS}.
1321     */
1322    public int setSpeechRate(float speechRate) {
1323        if (speechRate > 0.0f) {
1324            int intRate = (int)(speechRate * 100);
1325            if (intRate > 0) {
1326                synchronized (mStartLock) {
1327                    mParams.putInt(Engine.KEY_PARAM_RATE, intRate);
1328                }
1329                return SUCCESS;
1330            }
1331        }
1332        return ERROR;
1333    }
1334
1335    /**
1336     * Sets the speech pitch for the TextToSpeech engine.
1337     *
1338     * This has no effect on any pre-recorded speech.
1339     *
1340     * @param pitch Speech pitch. {@code 1.0} is the normal pitch,
1341     *            lower values lower the tone of the synthesized voice,
1342     *            greater values increase it.
1343     *
1344     * @return {@link #ERROR} or {@link #SUCCESS}.
1345     */
1346    public int setPitch(float pitch) {
1347        if (pitch > 0.0f) {
1348            int intPitch = (int)(pitch * 100);
1349            if (intPitch > 0) {
1350                synchronized (mStartLock) {
1351                    mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch);
1352                }
1353                return SUCCESS;
1354            }
1355        }
1356        return ERROR;
1357    }
1358
1359    /**
1360     * @return the engine currently in use by this TextToSpeech instance.
1361     * @hide
1362     */
1363    public String getCurrentEngine() {
1364        return mCurrentEngine;
1365    }
1366
1367    /**
1368     * Returns a Locale instance describing the language currently being used as the default
1369     * Text-to-speech language.
1370     *
1371     * The locale object returned by this method is NOT a valid one. It has identical form to the
1372     * one in {@link #getLanguage()}. Please refer to {@link #getLanguage()} for more information.
1373     *
1374     * @return language, country (if any) and variant (if any) used by the client stored in a
1375     *     Locale instance, or {@code null} on error.
1376     * @deprecated As of API Level 20, use <code>getDefaultVoice().getLocale()</code> ({@link
1377     *   #getDefaultVoice()})
1378     */
1379    @Deprecated
1380    public Locale getDefaultLanguage() {
1381        return runAction(new Action<Locale>() {
1382            @Override
1383            public Locale run(ITextToSpeechService service) throws RemoteException {
1384                String[] defaultLanguage = service.getClientDefaultLanguage();
1385
1386                return new Locale(defaultLanguage[0], defaultLanguage[1], defaultLanguage[2]);
1387            }
1388        }, null, "getDefaultLanguage");
1389    }
1390
1391    /**
1392     * Sets the text-to-speech language.
1393     * The TTS engine will try to use the closest match to the specified
1394     * language as represented by the Locale, but there is no guarantee that the exact same Locale
1395     * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
1396     * before choosing the language to use for the next utterances.
1397     *
1398     * This method sets the current voice to the default one for the given Locale;
1399     * {@link #getVoice()} can be used to retrieve it.
1400     *
1401     * @param loc The locale describing the language to be used.
1402     *
1403     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1404     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1405     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1406     */
1407    public int setLanguage(final Locale loc) {
1408        return runAction(new Action<Integer>() {
1409            @Override
1410            public Integer run(ITextToSpeechService service) throws RemoteException {
1411                if (loc == null) {
1412                    return LANG_NOT_SUPPORTED;
1413                }
1414                String language = null, country = null;
1415                try {
1416                    language = loc.getISO3Language();
1417                } catch (MissingResourceException e) {
1418                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
1419                    return LANG_NOT_SUPPORTED;
1420                }
1421
1422                try {
1423                    country = loc.getISO3Country();
1424                } catch (MissingResourceException e) {
1425                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
1426                    return LANG_NOT_SUPPORTED;
1427                }
1428
1429                String variant = loc.getVariant();
1430
1431                // As of API level 20, setLanguage is implemented using setVoice.
1432                // (which, in the default implementation, will call loadLanguage on the service
1433                // interface).
1434
1435                // Sanitize locale using isLanguageAvailable.
1436                int result = service.isLanguageAvailable( language, country, variant);
1437                if (result >= LANG_AVAILABLE){
1438                    if (result < LANG_COUNTRY_VAR_AVAILABLE) {
1439                        variant = "";
1440                        if (result < LANG_COUNTRY_AVAILABLE) {
1441                            country = "";
1442                        }
1443                    }
1444                    // Get the default voice for the locale.
1445                    String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
1446                    if (TextUtils.isEmpty(voiceName)) {
1447                        Log.w(TAG, "Couldn't find the default voice for " + language + "/" +
1448                                country + "/" + variant);
1449                        return LANG_NOT_SUPPORTED;
1450                    }
1451
1452                    // Load it.
1453                    if (service.loadVoice(getCallerIdentity(), voiceName) == TextToSpeech.ERROR) {
1454                        return LANG_NOT_SUPPORTED;
1455                    }
1456
1457                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voiceName);
1458                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
1459                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
1460                    mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
1461                }
1462                return result;
1463            }
1464        }, LANG_NOT_SUPPORTED, "setLanguage");
1465    }
1466
1467    /**
1468     * Returns a Locale instance describing the language currently being used for synthesis
1469     * requests sent to the TextToSpeech engine.
1470     *
1471     * In Android 4.2 and before (API <= 17) this function returns the language that is currently
1472     * being used by the TTS engine. That is the last language set by this or any other
1473     * client by a {@link TextToSpeech#setLanguage} call to the same engine.
1474     *
1475     * In Android versions after 4.2 this function returns the language that is currently being
1476     * used for the synthesis requests sent from this client. That is the last language set
1477     * by a {@link TextToSpeech#setLanguage} call on this instance.
1478     *
1479     * If a voice is set (by {@link #setVoice(Voice)}), getLanguage will return the language of
1480     * the currently set voice.
1481     *
1482     * Please note that the Locale object returned by this method is NOT a valid Locale object. Its
1483     * language field contains a three-letter ISO 639-2/T code (where a proper Locale would use
1484     * a two-letter ISO 639-1 code), and the country field contains a three-letter ISO 3166 country
1485     * code (where a proper Locale would use a two-letter ISO 3166-1 code).
1486     *
1487     * @return language, country (if any) and variant (if any) used by the client stored in a
1488     *     Locale instance, or {@code null} on error.
1489     *
1490     * @deprecated As of API level 20, please use <code>getVoice().getLocale()</code>
1491     * ({@link #getVoice()}).
1492     */
1493    @Deprecated
1494    public Locale getLanguage() {
1495        return runAction(new Action<Locale>() {
1496            @Override
1497            public Locale run(ITextToSpeechService service) {
1498                /* No service call, but we're accessing mParams, hence need for
1499                   wrapping it as an Action instance */
1500                String lang = mParams.getString(Engine.KEY_PARAM_LANGUAGE, "");
1501                String country = mParams.getString(Engine.KEY_PARAM_COUNTRY, "");
1502                String variant = mParams.getString(Engine.KEY_PARAM_VARIANT, "");
1503                return new Locale(lang, country, variant);
1504            }
1505        }, null, "getLanguage");
1506    }
1507
1508    /**
1509     * Query the engine about the set of available languages.
1510     */
1511    public Set<Locale> getAvailableLanguages() {
1512        return runAction(new Action<Set<Locale>>() {
1513            @Override
1514            public Set<Locale> run(ITextToSpeechService service) throws RemoteException {
1515                List<Voice> voices = service.getVoices();
1516                if (voices != null) {
1517                    return new TreeSet<Locale>();
1518                }
1519                TreeSet<Locale> locales = new TreeSet<Locale>();
1520                for (Voice voice : voices) {
1521                    locales.add(voice.getLocale());
1522                }
1523                return locales;
1524            }
1525        }, null, "getAvailableLanguages");
1526    }
1527
1528    /**
1529     * Query the engine about the set of available voices.
1530     *
1531     * Each TTS Engine can expose multiple voices for each locale, each with a different set of
1532     * features.
1533     *
1534     * @see #setVoice(Voice)
1535     * @see Voice
1536     */
1537    public Set<Voice> getVoices() {
1538        return runAction(new Action<Set<Voice>>() {
1539            @Override
1540            public Set<Voice> run(ITextToSpeechService service) throws RemoteException {
1541                List<Voice> voices = service.getVoices();
1542                return (voices != null)  ? new TreeSet<Voice>(voices) : new TreeSet<Voice>();
1543            }
1544        }, null, "getVoices");
1545    }
1546
1547    /**
1548     * Sets the text-to-speech voice.
1549     *
1550     * @param voice One of objects returned by {@link #getVoices()}.
1551     *
1552     * @return {@link #ERROR} or {@link #SUCCESS}.
1553     *
1554     * @see #getVoices
1555     * @see Voice
1556     */
1557    public int setVoice(final Voice voice) {
1558        return runAction(new Action<Integer>() {
1559            @Override
1560            public Integer run(ITextToSpeechService service) throws RemoteException {
1561                int result = service.loadVoice(getCallerIdentity(), voice.getName());
1562                if (result == SUCCESS) {
1563                    mParams.putString(Engine.KEY_PARAM_VOICE_NAME, voice.getName());
1564
1565                    // Set the language/country/variant, so #getLanguage will return the voice
1566                    // locale when called.
1567                    String language = "";
1568                    try {
1569                        language = voice.getLocale().getISO3Language();
1570                    } catch (MissingResourceException e) {
1571                        Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " +
1572                                voice.getLocale(), e);
1573                    }
1574
1575                    String country = "";
1576                    try {
1577                        country = voice.getLocale().getISO3Country();
1578                    } catch (MissingResourceException e) {
1579                        Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " +
1580                                voice.getLocale(), e);
1581                    }
1582                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
1583                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
1584                    mParams.putString(Engine.KEY_PARAM_VARIANT, voice.getLocale().getVariant());
1585                }
1586                return result;
1587            }
1588        }, LANG_NOT_SUPPORTED, "setVoice");
1589    }
1590
1591    /**
1592     * Returns a Voice instance describing the voice currently being used for synthesis
1593     * requests sent to the TextToSpeech engine.
1594     *
1595     * @return Voice instance used by the client, or {@code null} if not set or on error.
1596     *
1597     * @see #getVoices
1598     * @see #setVoice
1599     * @see Voice
1600     */
1601    public Voice getVoice() {
1602        return runAction(new Action<Voice>() {
1603            @Override
1604            public Voice run(ITextToSpeechService service) throws RemoteException {
1605                String voiceName = mParams.getString(Engine.KEY_PARAM_VOICE_NAME, "");
1606                if (TextUtils.isEmpty(voiceName)) {
1607                    return null;
1608                }
1609                List<Voice> voices = service.getVoices();
1610                if (voices == null) {
1611                    return null;
1612                }
1613                for (Voice voice : voices) {
1614                    if (voice.getName().equals(voiceName)) {
1615                        return voice;
1616                    }
1617                }
1618                return null;
1619            }
1620        }, null, "getVoice");
1621    }
1622
1623    /**
1624     * Returns a Voice instance that's the default voice for the default Text-to-speech language.
1625     * @return The default voice instance for the default language, or {@code null} if not set or
1626     *     on error.
1627     */
1628    public Voice getDefaultVoice() {
1629        return runAction(new Action<Voice>() {
1630            @Override
1631            public Voice run(ITextToSpeechService service) throws RemoteException {
1632
1633                String[] defaultLanguage = service.getClientDefaultLanguage();
1634
1635                if (defaultLanguage == null || defaultLanguage.length == 0) {
1636                    Log.e(TAG, "service.getClientDefaultLanguage() returned empty array");
1637                    return null;
1638                }
1639                String language = defaultLanguage[0];
1640                String country = (defaultLanguage.length > 1) ? defaultLanguage[1] : "";
1641                String variant = (defaultLanguage.length > 2) ? defaultLanguage[2] : "";
1642
1643                // Sanitize the locale using isLanguageAvailable.
1644                int result = service.isLanguageAvailable(language, country, variant);
1645                if (result >= LANG_AVAILABLE){
1646                    if (result < LANG_COUNTRY_VAR_AVAILABLE) {
1647                        variant = "";
1648                        if (result < LANG_COUNTRY_AVAILABLE) {
1649                            country = "";
1650                        }
1651                    }
1652                } else {
1653                    // The default language is not supported.
1654                    return null;
1655                }
1656
1657                // Get the default voice name
1658                String voiceName = service.getDefaultVoiceNameFor(language, country, variant);
1659                if (TextUtils.isEmpty(voiceName)) {
1660                    return null;
1661                }
1662
1663                // Find it
1664                List<Voice> voices = service.getVoices();
1665                if (voices == null) {
1666                    return null;
1667                }
1668                for (Voice voice : voices) {
1669                    if (voice.getName().equals(voiceName)) {
1670                        return voice;
1671                    }
1672                }
1673                return null;
1674            }
1675        }, null, "getDefaultVoice");
1676    }
1677
1678
1679
1680    /**
1681     * Checks if the specified language as represented by the Locale is available and supported.
1682     *
1683     * @param loc The Locale describing the language to be used.
1684     *
1685     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1686     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1687     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1688     */
1689    public int isLanguageAvailable(final Locale loc) {
1690        return runAction(new Action<Integer>() {
1691            @Override
1692            public Integer run(ITextToSpeechService service) throws RemoteException {
1693                String language = null, country = null;
1694
1695                try {
1696                    language = loc.getISO3Language();
1697                } catch (MissingResourceException e) {
1698                    Log.w(TAG, "Couldn't retrieve ISO 639-2/T language code for locale: " + loc, e);
1699                    return LANG_NOT_SUPPORTED;
1700                }
1701
1702                try {
1703                    country = loc.getISO3Country();
1704                } catch (MissingResourceException e) {
1705                    Log.w(TAG, "Couldn't retrieve ISO 3166 country code for locale: " + loc, e);
1706                    return LANG_NOT_SUPPORTED;
1707                }
1708
1709                return service.isLanguageAvailable(language, country, loc.getVariant());
1710            }
1711        }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
1712    }
1713
1714    /**
1715     * Synthesizes the given text to a file using the specified parameters.
1716     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1717     * requests and then returns. The synthesis might not have finished (or even started!) at the
1718     * time when this method returns. In order to reliably detect errors during synthesis,
1719     * we recommend setting an utterance progress listener (see
1720     * {@link #setOnUtteranceProgressListener}).
1721     *
1722     * @param text The text that should be synthesized. No longer than
1723     *            {@link #getMaxSpeechInputLength()} characters.
1724     * @param params Parameters for the request. Can be null.
1725     *            Engine specific parameters may be passed in but the parameter keys
1726     *            must be prefixed by the name of the engine they are intended for. For example
1727     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1728     *            engine named "com.svox.pico" if it is being used.
1729     * @param filename Absolute file filename to write the generated audio data to.It should be
1730     *            something like "/sdcard/myappsounds/mysound.wav".
1731     * @param utteranceId An unique identifier for this request.
1732     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
1733     */
1734    public int synthesizeToFile(final CharSequence text, final HashMap<String, String> params,
1735            final String filename, final String utteranceId) {
1736        return runAction(new Action<Integer>() {
1737            @Override
1738            public Integer run(ITextToSpeechService service) throws RemoteException {
1739                ParcelFileDescriptor fileDescriptor;
1740                int returnValue;
1741                try {
1742                    File file = new File(filename);
1743                    if(file.exists() && !file.canWrite()) {
1744                        Log.e(TAG, "Can't write to " + filename);
1745                        return ERROR;
1746                    }
1747                    fileDescriptor = ParcelFileDescriptor.open(file,
1748                            ParcelFileDescriptor.MODE_WRITE_ONLY |
1749                            ParcelFileDescriptor.MODE_CREATE |
1750                            ParcelFileDescriptor.MODE_TRUNCATE);
1751                    returnValue = service.synthesizeToFileDescriptor(getCallerIdentity(), text,
1752                            fileDescriptor, getParams(params), utteranceId);
1753                    fileDescriptor.close();
1754                    return returnValue;
1755                } catch (FileNotFoundException e) {
1756                    Log.e(TAG, "Opening file " + filename + " failed", e);
1757                    return ERROR;
1758                } catch (IOException e) {
1759                    Log.e(TAG, "Closing file " + filename + " failed", e);
1760                    return ERROR;
1761                }
1762            }
1763        }, ERROR, "synthesizeToFile");
1764    }
1765
1766    /**
1767     * Synthesizes the given text to a file using the specified parameters.
1768     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1769     * requests and then returns. The synthesis might not have finished (or even started!) at the
1770     * time when this method returns. In order to reliably detect errors during synthesis,
1771     * we recommend setting an utterance progress listener (see
1772     * {@link #setOnUtteranceProgressListener}) and using the
1773     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
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     *            Supported parameter names:
1779     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1780     *            Engine specific parameters may be passed in but the parameter keys
1781     *            must be prefixed by the name of the engine they are intended for. For example
1782     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1783     *            engine named "com.svox.pico" if it is being used.
1784     * @param filename Absolute file filename to write the generated audio data to.It should be
1785     *            something like "/sdcard/myappsounds/mysound.wav".
1786     *
1787     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
1788     * @deprecated As of API level 20, replaced by
1789     *         {@link #synthesizeToFile(CharSequence, HashMap, String, String)}.
1790     */
1791    public int synthesizeToFile(final String text, final HashMap<String, String> params,
1792            final String filename) {
1793        return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID));
1794    }
1795
1796    private Bundle getParams(HashMap<String, String> params) {
1797        if (params != null && !params.isEmpty()) {
1798            Bundle bundle = new Bundle(mParams);
1799            copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
1800            copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID);
1801            copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
1802            copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
1803            copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
1804
1805            // Copy feature strings defined by the framework.
1806            copyStringParam(bundle, params, Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
1807            copyStringParam(bundle, params, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
1808            copyIntParam(bundle, params, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS);
1809            copyIntParam(bundle, params, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT);
1810
1811            // Copy over all parameters that start with the name of the
1812            // engine that we are currently connected to. The engine is
1813            // free to interpret them as it chooses.
1814            if (!TextUtils.isEmpty(mCurrentEngine)) {
1815                for (Map.Entry<String, String> entry : params.entrySet()) {
1816                    final String key = entry.getKey();
1817                    if (key != null && key.startsWith(mCurrentEngine)) {
1818                        bundle.putString(key, entry.getValue());
1819                    }
1820                }
1821            }
1822
1823            return bundle;
1824        } else {
1825            return mParams;
1826        }
1827    }
1828
1829    private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
1830        String value = params.get(key);
1831        if (value != null) {
1832            bundle.putString(key, value);
1833        }
1834    }
1835
1836    private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) {
1837        String valueString = params.get(key);
1838        if (!TextUtils.isEmpty(valueString)) {
1839            try {
1840                int value = Integer.parseInt(valueString);
1841                bundle.putInt(key, value);
1842            } catch (NumberFormatException ex) {
1843                // don't set the value in the bundle
1844            }
1845        }
1846    }
1847
1848    private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) {
1849        String valueString = params.get(key);
1850        if (!TextUtils.isEmpty(valueString)) {
1851            try {
1852                float value = Float.parseFloat(valueString);
1853                bundle.putFloat(key, value);
1854            } catch (NumberFormatException ex) {
1855                // don't set the value in the bundle
1856            }
1857        }
1858    }
1859
1860    /**
1861     * Sets the listener that will be notified when synthesis of an utterance completes.
1862     *
1863     * @param listener The listener to use.
1864     *
1865     * @return {@link #ERROR} or {@link #SUCCESS}.
1866     *
1867     * @deprecated Use {@link #setOnUtteranceProgressListener(UtteranceProgressListener)}
1868     *        instead.
1869     */
1870    @Deprecated
1871    public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
1872        mUtteranceProgressListener = UtteranceProgressListener.from(listener);
1873        return TextToSpeech.SUCCESS;
1874    }
1875
1876    /**
1877     * Sets the listener that will be notified of various events related to the
1878     * synthesis of a given utterance.
1879     *
1880     * See {@link UtteranceProgressListener} and
1881     * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}.
1882     *
1883     * @param listener the listener to use.
1884     * @return {@link #ERROR} or {@link #SUCCESS}
1885     */
1886    public int setOnUtteranceProgressListener(UtteranceProgressListener listener) {
1887        mUtteranceProgressListener = listener;
1888        return TextToSpeech.SUCCESS;
1889    }
1890
1891    /**
1892     * Sets the TTS engine to use.
1893     *
1894     * @deprecated This doesn't inform callers when the TTS engine has been
1895     *        initialized. {@link #TextToSpeech(Context, OnInitListener, String)}
1896     *        can be used with the appropriate engine name. Also, there is no
1897     *        guarantee that the engine specified will be loaded. If it isn't
1898     *        installed or disabled, the user / system wide defaults will apply.
1899     *
1900     * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico")
1901     *
1902     * @return {@link #ERROR} or {@link #SUCCESS}.
1903     */
1904    @Deprecated
1905    public int setEngineByPackageName(String enginePackageName) {
1906        mRequestedEngine = enginePackageName;
1907        return initTts();
1908    }
1909
1910    /**
1911     * Gets the package name of the default speech synthesis engine.
1912     *
1913     * @return Package name of the TTS engine that the user has chosen
1914     *        as their default.
1915     */
1916    public String getDefaultEngine() {
1917        return mEnginesHelper.getDefaultEngine();
1918    }
1919
1920    /**
1921     * Checks whether the user's settings should override settings requested
1922     * by the calling application. As of the Ice cream sandwich release,
1923     * user settings never forcibly override the app's settings.
1924     */
1925    @Deprecated
1926    public boolean areDefaultsEnforced() {
1927        return false;
1928    }
1929
1930    /**
1931     * Gets a list of all installed TTS engines.
1932     *
1933     * @return A list of engine info objects. The list can be empty, but never {@code null}.
1934     */
1935    public List<EngineInfo> getEngines() {
1936        return mEnginesHelper.getEngines();
1937    }
1938
1939    private class Connection implements ServiceConnection {
1940        private ITextToSpeechService mService;
1941
1942        private SetupConnectionAsyncTask mOnSetupConnectionAsyncTask;
1943
1944        private boolean mEstablished;
1945
1946        private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() {
1947            public void onStop(String utteranceId) throws RemoteException {
1948                // do nothing
1949            };
1950
1951            @Override
1952            public void onFallback(String utteranceId) throws RemoteException {
1953                // do nothing
1954            }
1955
1956            @Override
1957            public void onSuccess(String utteranceId) {
1958                UtteranceProgressListener listener = mUtteranceProgressListener;
1959                if (listener != null) {
1960                    listener.onDone(utteranceId);
1961                }
1962            }
1963
1964            @Override
1965            public void onError(String utteranceId, int errorCode) {
1966                UtteranceProgressListener listener = mUtteranceProgressListener;
1967                if (listener != null) {
1968                    listener.onError(utteranceId);
1969                }
1970            }
1971
1972            @Override
1973            public void onStart(String utteranceId) {
1974                UtteranceProgressListener listener = mUtteranceProgressListener;
1975                if (listener != null) {
1976                    listener.onStart(utteranceId);
1977                }
1978            }
1979        };
1980
1981        private class SetupConnectionAsyncTask extends AsyncTask<Void, Void, Integer> {
1982            private final ComponentName mName;
1983
1984            public SetupConnectionAsyncTask(ComponentName name) {
1985                mName = name;
1986            }
1987
1988            @Override
1989            protected Integer doInBackground(Void... params) {
1990                synchronized(mStartLock) {
1991                    if (isCancelled()) {
1992                        return null;
1993                    }
1994
1995                    try {
1996                        mService.setCallback(getCallerIdentity(), mCallback);
1997
1998                        if (mParams.getString(Engine.KEY_PARAM_LANGUAGE) == null) {
1999                            String[] defaultLanguage = mService.getClientDefaultLanguage();
2000                            mParams.putString(Engine.KEY_PARAM_LANGUAGE, defaultLanguage[0]);
2001                            mParams.putString(Engine.KEY_PARAM_COUNTRY, defaultLanguage[1]);
2002                            mParams.putString(Engine.KEY_PARAM_VARIANT, defaultLanguage[2]);
2003                        }
2004
2005                        Log.i(TAG, "Set up connection to " + mName);
2006                        return SUCCESS;
2007                    } catch (RemoteException re) {
2008                        Log.e(TAG, "Error connecting to service, setCallback() failed");
2009                        return ERROR;
2010                    }
2011                }
2012            }
2013
2014            @Override
2015            protected void onPostExecute(Integer result) {
2016                synchronized(mStartLock) {
2017                    if (mOnSetupConnectionAsyncTask == this) {
2018                        mOnSetupConnectionAsyncTask = null;
2019                    }
2020                    mEstablished = true;
2021                    dispatchOnInit(result);
2022                }
2023            }
2024        }
2025
2026        @Override
2027        public void onServiceConnected(ComponentName name, IBinder service) {
2028            synchronized(mStartLock) {
2029                mConnectingServiceConnection = null;
2030
2031                Log.i(TAG, "Connected to " + name);
2032
2033                if (mOnSetupConnectionAsyncTask != null) {
2034                    mOnSetupConnectionAsyncTask.cancel(false);
2035                }
2036
2037                mService = ITextToSpeechService.Stub.asInterface(service);
2038                mServiceConnection = Connection.this;
2039
2040                mEstablished = false;
2041                mOnSetupConnectionAsyncTask = new SetupConnectionAsyncTask(name);
2042                mOnSetupConnectionAsyncTask.execute();
2043            }
2044        }
2045
2046        public IBinder getCallerIdentity() {
2047            return mCallback;
2048        }
2049
2050        /**
2051         * Clear connection related fields and cancel mOnServiceConnectedAsyncTask if set.
2052         *
2053         * @return true if we cancel mOnSetupConnectionAsyncTask in progress.
2054         */
2055        private boolean clearServiceConnection() {
2056            synchronized(mStartLock) {
2057                boolean result = false;
2058                if (mOnSetupConnectionAsyncTask != null) {
2059                    result = mOnSetupConnectionAsyncTask.cancel(false);
2060                    mOnSetupConnectionAsyncTask = null;
2061                }
2062
2063                mService = null;
2064                // If this is the active connection, clear it
2065                if (mServiceConnection == this) {
2066                    mServiceConnection = null;
2067                }
2068                return result;
2069            }
2070        }
2071
2072        @Override
2073        public void onServiceDisconnected(ComponentName name) {
2074            Log.i(TAG, "Asked to disconnect from " + name);
2075            if (clearServiceConnection()) {
2076                /* We need to protect against a rare case where engine
2077                 * dies just after successful connection - and we process onServiceDisconnected
2078                 * before OnServiceConnectedAsyncTask.onPostExecute. onServiceDisconnected cancels
2079                 * OnServiceConnectedAsyncTask.onPostExecute and we don't call dispatchOnInit
2080                 * with ERROR as argument.
2081                 */
2082                dispatchOnInit(ERROR);
2083            }
2084        }
2085
2086        public void disconnect() {
2087            mContext.unbindService(this);
2088            clearServiceConnection();
2089        }
2090
2091        public boolean isEstablished() {
2092            return mService != null && mEstablished;
2093        }
2094
2095        public <R> R runAction(Action<R> action, R errorResult, String method,
2096                boolean reconnect, boolean onlyEstablishedConnection) {
2097            synchronized (mStartLock) {
2098                try {
2099                    if (mService == null) {
2100                        Log.w(TAG, method + " failed: not connected to TTS engine");
2101                        return errorResult;
2102                    }
2103                    if (onlyEstablishedConnection && !isEstablished()) {
2104                        Log.w(TAG, method + " failed: TTS engine connection not fully set up");
2105                        return errorResult;
2106                    }
2107                    return action.run(mService);
2108                } catch (RemoteException ex) {
2109                    Log.e(TAG, method + " failed", ex);
2110                    if (reconnect) {
2111                        disconnect();
2112                        initTts();
2113                    }
2114                    return errorResult;
2115                }
2116            }
2117        }
2118    }
2119
2120    private interface Action<R> {
2121        R run(ITextToSpeechService service) throws RemoteException;
2122    }
2123
2124    /**
2125     * Information about an installed text-to-speech engine.
2126     *
2127     * @see TextToSpeech#getEngines
2128     */
2129    public static class EngineInfo {
2130        /**
2131         * Engine package name..
2132         */
2133        public String name;
2134        /**
2135         * Localized label for the engine.
2136         */
2137        public String label;
2138        /**
2139         * Icon for the engine.
2140         */
2141        public int icon;
2142        /**
2143         * Whether this engine is a part of the system
2144         * image.
2145         *
2146         * @hide
2147         */
2148        public boolean system;
2149        /**
2150         * The priority the engine declares for the the intent filter
2151         * {@code android.intent.action.TTS_SERVICE}
2152         *
2153         * @hide
2154         */
2155        public int priority;
2156
2157        @Override
2158        public String toString() {
2159            return "EngineInfo{name=" + name + "}";
2160        }
2161
2162    }
2163
2164    /**
2165     * Limit of length of input string passed to speak and synthesizeToFile.
2166     *
2167     * @see #speak
2168     * @see #synthesizeToFile
2169     */
2170    public static int getMaxSpeechInputLength() {
2171        return 4000;
2172    }
2173}
2174