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