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