TextToSpeech.java revision f288a64f5bed290d162c1a6fd54bea7c0ba14042
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16package android.speech.tts;
17
18import android.annotation.SdkConstant;
19import android.annotation.SdkConstant.SdkConstantType;
20import android.content.ComponentName;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.ServiceConnection;
25import android.media.AudioManager;
26import android.net.Uri;
27import android.os.Bundle;
28import android.os.IBinder;
29import android.os.RemoteException;
30import android.provider.Settings;
31import android.text.TextUtils;
32import android.util.Log;
33
34import java.util.Collections;
35import java.util.HashMap;
36import java.util.HashSet;
37import java.util.List;
38import java.util.Locale;
39import java.util.Map;
40import java.util.Set;
41
42/**
43 *
44 * Synthesizes speech from text for immediate playback or to create a sound file.
45 * <p>A TextToSpeech instance can only be used to synthesize text once it has completed its
46 * initialization. Implement the {@link TextToSpeech.OnInitListener} to be
47 * notified of the completion of the initialization.<br>
48 * When you are done using the TextToSpeech instance, call the {@link #shutdown()} method
49 * to release the native resources used by the TextToSpeech engine.
50 *
51 */
52public class TextToSpeech {
53
54    private static final String TAG = "TextToSpeech";
55
56    /**
57     * Denotes a successful operation.
58     */
59    public static final int SUCCESS = 0;
60    /**
61     * Denotes a generic operation failure.
62     */
63    public static final int ERROR = -1;
64
65    /**
66     * Queue mode where all entries in the playback queue (media to be played
67     * and text to be synthesized) are dropped and replaced by the new entry.
68     * Queues are flushed with respect to a given calling app. Entries in the queue
69     * from other callees are not discarded.
70     */
71    public static final int QUEUE_FLUSH = 0;
72    /**
73     * Queue mode where the new entry is added at the end of the playback queue.
74     */
75    public static final int QUEUE_ADD = 1;
76    /**
77     * Queue mode where the entire playback queue is purged. This is different
78     * from {@link #QUEUE_FLUSH} in that all entries are purged, not just entries
79     * from a given caller.
80     *
81     * @hide
82     */
83    static final int QUEUE_DESTROY = 2;
84
85    /**
86     * Denotes the language is available exactly as specified by the locale.
87     */
88    public static final int LANG_COUNTRY_VAR_AVAILABLE = 2;
89
90    /**
91     * Denotes the language is available for the language and country specified
92     * by the locale, but not the variant.
93     */
94    public static final int LANG_COUNTRY_AVAILABLE = 1;
95
96    /**
97     * Denotes the language is available for the language by the locale,
98     * but not the country and variant.
99     */
100    public static final int LANG_AVAILABLE = 0;
101
102    /**
103     * Denotes the language data is missing.
104     */
105    public static final int LANG_MISSING_DATA = -1;
106
107    /**
108     * Denotes the language is not supported.
109     */
110    public static final int LANG_NOT_SUPPORTED = -2;
111
112    /**
113     * Broadcast Action: The TextToSpeech synthesizer has completed processing
114     * of all the text in the speech queue.
115     *
116     * Note that this notifies callers when the <b>engine</b> has finished has
117     * processing text data. Audio playback might not have completed (or even started)
118     * at this point. If you wish to be notified when this happens, see
119     * {@link OnUtteranceCompletedListener}.
120     */
121    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
122    public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
123            "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
124
125    /**
126     * Interface definition of a callback to be invoked indicating the completion of the
127     * TextToSpeech engine initialization.
128     */
129    public interface OnInitListener {
130        /**
131         * Called to signal the completion of the TextToSpeech engine initialization.
132         *
133         * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
134         */
135        public void onInit(int status);
136    }
137
138    /**
139     * Listener that will be called when the TTS service has
140     * completed synthesizing an utterance. This is only called if the utterance
141     * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}).
142     */
143    public interface OnUtteranceCompletedListener {
144        /**
145         * Called when an utterance has been synthesized.
146         *
147         * @param utteranceId the identifier of the utterance.
148         */
149        public void onUtteranceCompleted(String utteranceId);
150    }
151
152    /**
153     * Constants and parameter names for controlling text-to-speech. These include:
154     *
155     * <ul>
156     *     <li>
157     *         Intents to ask engine to install data or check its data and
158     *         extras for a TTS engine's check data activity.
159     *     </li>
160     *     <li>
161     *         Keys for the parameters passed with speak commands, e.g.
162     *         {@link Engine#KEY_PARAM_UTTERANCE_ID}, {@link Engine#KEY_PARAM_STREAM}.
163     *     </li>
164     *     <li>
165     *         A list of feature strings that engines might support, e.g
166     *         {@link Engine#KEY_FEATURE_NETWORK_SYNTHESIS}). These values may be passed in to
167     *         {@link TextToSpeech#speak} and {@link TextToSpeech#synthesizeToFile} to modify
168     *         engine behaviour. The engine can be queried for the set of features it supports
169     *         through {@link TextToSpeech#getFeatures(java.util.Locale)}.
170     *     </li>
171     * </ul>
172     */
173    public class Engine {
174
175        /**
176         * Default speech rate.
177         * @hide
178         */
179        public static final int DEFAULT_RATE = 100;
180
181        /**
182         * Default pitch.
183         * @hide
184         */
185        public static final int DEFAULT_PITCH = 100;
186
187        /**
188         * Default volume.
189         * @hide
190         */
191        public static final float DEFAULT_VOLUME = 1.0f;
192
193        /**
194         * Default pan (centered).
195         * @hide
196         */
197        public static final float DEFAULT_PAN = 0.0f;
198
199        /**
200         * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}.
201         * @hide
202         */
203        public static final int USE_DEFAULTS = 0; // false
204
205        /**
206         * Limit of length of input string passed to speak/synthesizeToFile.
207         * Larger strings will be rejected.
208         */
209        public static final int MAX_SPEECH_STRING_LENGTH = 4000;
210
211        /**
212         * Package name of the default TTS engine.
213         *
214         * @hide
215         * @deprecated No longer in use, the default engine is determined by
216         *         the sort order defined in {@link TtsEngines}. Note that
217         *         this doesn't "break" anything because there is no guarantee that
218         *         the engine specified below is installed on a given build, let
219         *         alone be the default.
220         */
221        @Deprecated
222        public static final String DEFAULT_ENGINE = "com.svox.pico";
223
224        /**
225         * Default audio stream used when playing synthesized speech.
226         */
227        public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
228
229        /**
230         * Indicates success when checking the installation status of the resources used by the
231         * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
232         */
233        public static final int CHECK_VOICE_DATA_PASS = 1;
234
235        /**
236         * Indicates failure when checking the installation status of the resources used by the
237         * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
238         */
239        public static final int CHECK_VOICE_DATA_FAIL = 0;
240
241        /**
242         * Indicates erroneous data when checking the installation status of the resources used by
243         * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
244         */
245        public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
246
247        /**
248         * Indicates missing resources when checking the installation status of the resources used
249         * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
250         */
251        public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
252
253        /**
254         * Indicates missing storage volume when checking the installation status of the resources
255         * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
256         */
257        public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
258
259        /**
260         * Intent for starting a TTS service. Services that handle this intent must
261         * extend {@link TextToSpeechService}. Normal applications should not use this intent
262         * directly, instead they should talk to the TTS service using the the methods in this
263         * class.
264         */
265        @SdkConstant(SdkConstantType.SERVICE_ACTION)
266        public static final String INTENT_ACTION_TTS_SERVICE =
267                "android.intent.action.TTS_SERVICE";
268
269        /**
270         * Name under which a text to speech engine publishes information about itself.
271         * This meta-data should reference an XML resource containing a
272         * <code>&lt;{@link android.R.styleable#TextToSpeechEngine tts-engine}&gt;</code>
273         * tag.
274         */
275        public static final String SERVICE_META_DATA = "android.speech.tts";
276
277        // intents to ask engine to install data or check its data
278        /**
279         * Activity Action: Triggers the platform TextToSpeech engine to
280         * start the activity that installs the resource files on the device
281         * that are required for TTS to be operational. Since the installation
282         * of the data can be interrupted or declined by the user, the application
283         * shouldn't expect successful installation upon return from that intent,
284         * and if need be, should check installation status with
285         * {@link #ACTION_CHECK_TTS_DATA}.
286         */
287        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
288        public static final String ACTION_INSTALL_TTS_DATA =
289                "android.speech.tts.engine.INSTALL_TTS_DATA";
290
291        /**
292         * Broadcast Action: broadcast to signal the completion of the installation of
293         * the data files used by the synthesis engine. Success or failure is indicated in the
294         * {@link #EXTRA_TTS_DATA_INSTALLED} extra.
295         */
296        @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
297        public static final String ACTION_TTS_DATA_INSTALLED =
298                "android.speech.tts.engine.TTS_DATA_INSTALLED";
299
300        /**
301         * Activity Action: Starts the activity from the platform TextToSpeech
302         * engine to verify the proper installation and availability of the
303         * resource files on the system. Upon completion, the activity will
304         * return one of the following codes:
305         * {@link #CHECK_VOICE_DATA_PASS},
306         * {@link #CHECK_VOICE_DATA_FAIL},
307         * {@link #CHECK_VOICE_DATA_BAD_DATA},
308         * {@link #CHECK_VOICE_DATA_MISSING_DATA}, or
309         * {@link #CHECK_VOICE_DATA_MISSING_VOLUME}.
310         * <p> Moreover, the data received in the activity result will contain the following
311         * fields:
312         * <ul>
313         *   <li>{@link #EXTRA_VOICE_DATA_ROOT_DIRECTORY} which
314         *       indicates the path to the location of the resource files,</li>
315         *   <li>{@link #EXTRA_VOICE_DATA_FILES} which contains
316         *       the list of all the resource files,</li>
317         *   <li>and {@link #EXTRA_VOICE_DATA_FILES_INFO} which
318         *       contains, for each resource file, the description of the language covered by
319         *       the file in the xxx-YYY format, where xxx is the 3-letter ISO language code,
320         *       and YYY is the 3-letter ISO country code.</li>
321         * </ul>
322         */
323        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
324        public static final String ACTION_CHECK_TTS_DATA =
325                "android.speech.tts.engine.CHECK_TTS_DATA";
326
327        /**
328         * Activity intent for getting some sample text to use for demonstrating TTS.
329         *
330         * @hide This intent was used by engines written against the old API.
331         * Not sure if it should be exposed.
332         */
333        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
334        public static final String ACTION_GET_SAMPLE_TEXT =
335                "android.speech.tts.engine.GET_SAMPLE_TEXT";
336
337        // extras for a TTS engine's check data activity
338        /**
339         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
340         * the TextToSpeech engine specifies the path to its resources.
341         */
342        public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
343
344        /**
345         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
346         * the TextToSpeech engine specifies the file names of its resources under the
347         * resource path.
348         */
349        public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
350
351        /**
352         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
353         * the TextToSpeech engine specifies the locale associated with each resource file.
354         */
355        public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
356
357        /**
358         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
359         * the TextToSpeech engine returns an ArrayList<String> of all the available voices.
360         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
361         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
362         */
363        public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
364
365        /**
366         * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
367         * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
368         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
369         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
370         */
371        public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
372
373        /**
374         * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
375         * caller indicates to the TextToSpeech engine which specific sets of voice data to
376         * check for by sending an ArrayList<String> of the voices that are of interest.
377         * The format of each voice is: lang-COUNTRY-variant where COUNTRY and variant are
378         * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
379         */
380        public static final String EXTRA_CHECK_VOICE_DATA_FOR = "checkVoiceDataFor";
381
382        // extras for a TTS engine's data installation
383        /**
384         * Extra information received with the {@link #ACTION_TTS_DATA_INSTALLED} intent.
385         * It indicates whether the data files for the synthesis engine were successfully
386         * installed. The installation was initiated with the  {@link #ACTION_INSTALL_TTS_DATA}
387         * intent. The possible values for this extra are
388         * {@link TextToSpeech#SUCCESS} and {@link TextToSpeech#ERROR}.
389         */
390        public static final String EXTRA_TTS_DATA_INSTALLED = "dataInstalled";
391
392        // keys for the parameters passed with speak commands. Hidden keys are used internally
393        // to maintain engine state for each TextToSpeech instance.
394        /**
395         * @hide
396         */
397        public static final String KEY_PARAM_RATE = "rate";
398
399        /**
400         * @hide
401         */
402        public static final String KEY_PARAM_LANGUAGE = "language";
403
404        /**
405         * @hide
406         */
407        public static final String KEY_PARAM_COUNTRY = "country";
408
409        /**
410         * @hide
411         */
412        public static final String KEY_PARAM_VARIANT = "variant";
413
414        /**
415         * @hide
416         */
417        public static final String KEY_PARAM_ENGINE = "engine";
418
419        /**
420         * @hide
421         */
422        public static final String KEY_PARAM_PITCH = "pitch";
423
424        /**
425         * Parameter key to specify the audio stream type to be used when speaking text
426         * or playing back a file. The value should be one of the STREAM_ constants
427         * defined in {@link AudioManager}.
428         *
429         * @see TextToSpeech#speak(String, int, HashMap)
430         * @see TextToSpeech#playEarcon(String, int, HashMap)
431         */
432        public static final String KEY_PARAM_STREAM = "streamType";
433
434        /**
435         * Parameter key to identify an utterance in the
436         * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
437         * spoken, a file has been played back or a silence duration has elapsed.
438         *
439         * @see TextToSpeech#speak(String, int, HashMap)
440         * @see TextToSpeech#playEarcon(String, int, HashMap)
441         * @see TextToSpeech#synthesizeToFile(String, HashMap, String)
442         */
443        public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
444
445        /**
446         * Parameter key to specify the speech volume relative to the current stream type
447         * volume used when speaking text. Volume is specified as a float ranging from 0 to 1
448         * where 0 is silence, and 1 is the maximum volume (the default behavior).
449         *
450         * @see TextToSpeech#speak(String, int, HashMap)
451         * @see TextToSpeech#playEarcon(String, int, HashMap)
452         */
453        public static final String KEY_PARAM_VOLUME = "volume";
454
455        /**
456         * Parameter key to specify how the speech is panned from left to right when speaking text.
457         * Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
458         * 0 to center (the default behavior), and +1 to hard-right.
459         *
460         * @see TextToSpeech#speak(String, int, HashMap)
461         * @see TextToSpeech#playEarcon(String, int, HashMap)
462         */
463        public static final String KEY_PARAM_PAN = "pan";
464
465        /**
466         * Feature key for network synthesis. See {@link TextToSpeech#getFeatures(Locale)}
467         * for a description of how feature keys work. If set (and supported by the engine
468         * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must
469         * use network based synthesis.
470         *
471         * @see TextToSpeech#speak(String, int, java.util.HashMap)
472         * @see TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)
473         * @see TextToSpeech#getFeatures(java.util.Locale)
474         */
475        public static final String KEY_FEATURE_NETWORK_SYNTHESIS = "networkTts";
476
477        /**
478         * Feature key for embedded synthesis. See {@link TextToSpeech#getFeatures(Locale)}
479         * for a description of how feature keys work. If set and supported by the engine
480         * as per {@link TextToSpeech#getFeatures(Locale)}, the engine must synthesize
481         * text on-device (without making network requests).
482         */
483        public static final String KEY_FEATURE_EMBEDDED_SYNTHESIS = "embeddedTts";
484    }
485
486    private final Context mContext;
487    private Connection mServiceConnection;
488    private OnInitListener mInitListener;
489    // Written from an unspecified application thread, read from
490    // a binder thread.
491    private volatile UtteranceProgressListener mUtteranceProgressListener;
492    private final Object mStartLock = new Object();
493
494    private String mRequestedEngine;
495    // Whether to initialize this TTS object with the default engine,
496    // if the requested engine is not available. Valid only if mRequestedEngine
497    // is not null. Used only for testing, though potentially useful API wise
498    // too.
499    private final boolean mUseFallback;
500    private final Map<String, Uri> mEarcons;
501    private final Map<String, Uri> mUtterances;
502    private final Bundle mParams = new Bundle();
503    private final TtsEngines mEnginesHelper;
504    private final String mPackageName;
505    private volatile String mCurrentEngine = null;
506
507    /**
508     * The constructor for the TextToSpeech class, using the default TTS engine.
509     * This will also initialize the associated TextToSpeech engine if it isn't already running.
510     *
511     * @param context
512     *            The context this instance is running in.
513     * @param listener
514     *            The {@link TextToSpeech.OnInitListener} that will be called when the
515     *            TextToSpeech engine has initialized.
516     */
517    public TextToSpeech(Context context, OnInitListener listener) {
518        this(context, listener, null);
519    }
520
521    /**
522     * The constructor for the TextToSpeech class, using the given TTS engine.
523     * This will also initialize the associated TextToSpeech engine if it isn't already running.
524     *
525     * @param context
526     *            The context this instance is running in.
527     * @param listener
528     *            The {@link TextToSpeech.OnInitListener} that will be called when the
529     *            TextToSpeech engine has initialized.
530     * @param engine Package name of the TTS engine to use.
531     */
532    public TextToSpeech(Context context, OnInitListener listener, String engine) {
533        this(context, listener, engine, null, true);
534    }
535
536    /**
537     * Used by the framework to instantiate TextToSpeech objects with a supplied
538     * package name, instead of using {@link android.content.Context#getPackageName()}
539     *
540     * @hide
541     */
542    public TextToSpeech(Context context, OnInitListener listener, String engine,
543            String packageName, boolean useFallback) {
544        mContext = context;
545        mInitListener = listener;
546        mRequestedEngine = engine;
547        mUseFallback = useFallback;
548
549        mEarcons = new HashMap<String, Uri>();
550        mUtterances = new HashMap<String, Uri>();
551        mUtteranceProgressListener = null;
552
553        mEnginesHelper = new TtsEngines(mContext);
554        if (packageName != null) {
555            mPackageName = packageName;
556        } else {
557            mPackageName = mContext.getPackageName();
558        }
559        initTts();
560    }
561
562    private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) {
563        return runAction(action, errorResult, method, false);
564    }
565
566    private <R> R runAction(Action<R> action, R errorResult, String method) {
567        return runAction(action, errorResult, method, true);
568    }
569
570    private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
571        synchronized (mStartLock) {
572            if (mServiceConnection == null) {
573                Log.w(TAG, method + " failed: not bound to TTS engine");
574                return errorResult;
575            }
576            return mServiceConnection.runAction(action, errorResult, method, reconnect);
577        }
578    }
579
580    private int initTts() {
581        // Step 1: Try connecting to the engine that was requested.
582        if (mRequestedEngine != null) {
583            if (mEnginesHelper.isEngineInstalled(mRequestedEngine)) {
584                if (connectToEngine(mRequestedEngine)) {
585                    mCurrentEngine = mRequestedEngine;
586                    return SUCCESS;
587                } else if (!mUseFallback) {
588                    mCurrentEngine = null;
589                    dispatchOnInit(ERROR);
590                    return ERROR;
591                }
592            } else if (!mUseFallback) {
593                Log.i(TAG, "Requested engine not installed: " + mRequestedEngine);
594                mCurrentEngine = null;
595                dispatchOnInit(ERROR);
596                return ERROR;
597            }
598        }
599
600        // Step 2: Try connecting to the user's default engine.
601        final String defaultEngine = getDefaultEngine();
602        if (defaultEngine != null && !defaultEngine.equals(mRequestedEngine)) {
603            if (connectToEngine(defaultEngine)) {
604                mCurrentEngine = defaultEngine;
605                return SUCCESS;
606            }
607        }
608
609        // Step 3: Try connecting to the highest ranked engine in the
610        // system.
611        final String highestRanked = mEnginesHelper.getHighestRankedEngineName();
612        if (highestRanked != null && !highestRanked.equals(mRequestedEngine) &&
613                !highestRanked.equals(defaultEngine)) {
614            if (connectToEngine(highestRanked)) {
615                mCurrentEngine = highestRanked;
616                return SUCCESS;
617            }
618        }
619
620        // NOTE: The API currently does not allow the caller to query whether
621        // they are actually connected to any engine. This might fail for various
622        // reasons like if the user disables all her TTS engines.
623
624        mCurrentEngine = null;
625        dispatchOnInit(ERROR);
626        return ERROR;
627    }
628
629    private boolean connectToEngine(String engine) {
630        Connection connection = new Connection();
631        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
632        intent.setPackage(engine);
633        boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
634        if (!bound) {
635            Log.e(TAG, "Failed to bind to " + engine);
636            return false;
637        } else {
638            Log.i(TAG, "Sucessfully bound to " + engine);
639            return true;
640        }
641    }
642
643    private void dispatchOnInit(int result) {
644        synchronized (mStartLock) {
645            if (mInitListener != null) {
646                mInitListener.onInit(result);
647                mInitListener = null;
648            }
649        }
650    }
651
652    private IBinder getCallerIdentity() {
653        return mServiceConnection.getCallerIdentity();
654    }
655
656    /**
657     * Releases the resources used by the TextToSpeech engine.
658     * It is good practice for instance to call this method in the onDestroy() method of an Activity
659     * so the TextToSpeech engine can be cleanly stopped.
660     */
661    public void shutdown() {
662        runActionNoReconnect(new Action<Void>() {
663            @Override
664            public Void run(ITextToSpeechService service) throws RemoteException {
665                service.setCallback(getCallerIdentity(), null);
666                service.stop(getCallerIdentity());
667                mServiceConnection.disconnect();
668                // Context#unbindService does not result in a call to
669                // ServiceConnection#onServiceDisconnected. As a result, the
670                // service ends up being destroyed (if there are no other open
671                // connections to it) but the process lives on and the
672                // ServiceConnection continues to refer to the destroyed service.
673                //
674                // This leads to tons of log spam about SynthThread being dead.
675                mServiceConnection = null;
676                mCurrentEngine = null;
677                return null;
678            }
679        }, null, "shutdown");
680    }
681
682    /**
683     * Adds a mapping between a string of text and a sound resource in a
684     * package. After a call to this method, subsequent calls to
685     * {@link #speak(String, int, HashMap)} will play the specified sound resource
686     * if it is available, or synthesize the text it is missing.
687     *
688     * @param text
689     *            The string of text. Example: <code>"south_south_east"</code>
690     *
691     * @param packagename
692     *            Pass the packagename of the application that contains the
693     *            resource. If the resource is in your own application (this is
694     *            the most common case), then put the packagename of your
695     *            application here.<br/>
696     *            Example: <b>"com.google.marvin.compass"</b><br/>
697     *            The packagename can be found in the AndroidManifest.xml of
698     *            your application.
699     *            <p>
700     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
701     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
702     *            </p>
703     *
704     * @param resourceId
705     *            Example: <code>R.raw.south_south_east</code>
706     *
707     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
708     */
709    public int addSpeech(String text, String packagename, int resourceId) {
710        synchronized (mStartLock) {
711            mUtterances.put(text, makeResourceUri(packagename, resourceId));
712            return SUCCESS;
713        }
714    }
715
716    /**
717     * Adds a mapping between a string of text and a sound file. Using this, it
718     * is possible to add custom pronounciations for a string of text.
719     * After a call to this method, subsequent calls to {@link #speak(String, int, HashMap)}
720     * will play the specified sound resource if it is available, or synthesize the text it is
721     * missing.
722     *
723     * @param text
724     *            The string of text. Example: <code>"south_south_east"</code>
725     * @param filename
726     *            The full path to the sound file (for example:
727     *            "/sdcard/mysounds/hello.wav")
728     *
729     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
730     */
731    public int addSpeech(String text, String filename) {
732        synchronized (mStartLock) {
733            mUtterances.put(text, Uri.parse(filename));
734            return SUCCESS;
735        }
736    }
737
738
739    /**
740     * Adds a mapping between a string of text and a sound resource in a
741     * package. Use this to add custom earcons.
742     *
743     * @see #playEarcon(String, int, HashMap)
744     *
745     * @param earcon The name of the earcon.
746     *            Example: <code>"[tick]"</code><br/>
747     *
748     * @param packagename
749     *            the package name of the application that contains the
750     *            resource. This can for instance be the package name of your own application.
751     *            Example: <b>"com.google.marvin.compass"</b><br/>
752     *            The package name can be found in the AndroidManifest.xml of
753     *            the application containing the resource.
754     *            <p>
755     *            <code>&lt;manifest xmlns:android=&quot;...&quot;
756     *      package=&quot;<b>com.google.marvin.compass</b>&quot;&gt;</code>
757     *            </p>
758     *
759     * @param resourceId
760     *            Example: <code>R.raw.tick_snd</code>
761     *
762     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
763     */
764    public int addEarcon(String earcon, String packagename, int resourceId) {
765        synchronized(mStartLock) {
766            mEarcons.put(earcon, makeResourceUri(packagename, resourceId));
767            return SUCCESS;
768        }
769    }
770
771    /**
772     * Adds a mapping between a string of text and a sound file.
773     * Use this to add custom earcons.
774     *
775     * @see #playEarcon(String, int, HashMap)
776     *
777     * @param earcon
778     *            The name of the earcon.
779     *            Example: <code>"[tick]"</code>
780     * @param filename
781     *            The full path to the sound file (for example:
782     *            "/sdcard/mysounds/tick.wav")
783     *
784     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
785     */
786    public int addEarcon(String earcon, String filename) {
787        synchronized(mStartLock) {
788            mEarcons.put(earcon, Uri.parse(filename));
789            return SUCCESS;
790        }
791    }
792
793    private Uri makeResourceUri(String packageName, int resourceId) {
794        return new Uri.Builder()
795                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
796                .encodedAuthority(packageName)
797                .appendEncodedPath(String.valueOf(resourceId))
798                .build();
799    }
800
801    /**
802     * Speaks the string using the specified queuing strategy and speech parameters.
803     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
804     * requests and then returns. The synthesis might not have finished (or even started!) at the
805     * time when this method returns. In order to reliably detect errors during synthesis,
806     * we recommend setting an utterance progress listener (see
807     * {@link #setOnUtteranceProgressListener}) and using the
808     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
809     *
810     * @param text The string of text to be spoken. No longer than
811     *            {@link Engine#MAX_SPEECH_STRING_LENGTH} characters.
812     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
813     * @param params Parameters for the request. Can be null.
814     *            Supported parameter names:
815     *            {@link Engine#KEY_PARAM_STREAM},
816     *            {@link Engine#KEY_PARAM_UTTERANCE_ID},
817     *            {@link Engine#KEY_PARAM_VOLUME},
818     *            {@link Engine#KEY_PARAM_PAN}.
819     *            Engine specific parameters may be passed in but the parameter keys
820     *            must be prefixed by the name of the engine they are intended for. For example
821     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
822     *            engine named "com.svox.pico" if it is being used.
823     *
824     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the speak operation.
825     */
826    public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
827        return runAction(new Action<Integer>() {
828            @Override
829            public Integer run(ITextToSpeechService service) throws RemoteException {
830                Uri utteranceUri = mUtterances.get(text);
831                if (utteranceUri != null) {
832                    return service.playAudio(getCallerIdentity(), utteranceUri, queueMode,
833                            getParams(params));
834                } else {
835                    return service.speak(getCallerIdentity(), text, queueMode, getParams(params));
836                }
837            }
838        }, ERROR, "speak");
839    }
840
841    /**
842     * Plays the earcon using the specified queueing mode and parameters.
843     * The earcon must already have been added with {@link #addEarcon(String, String)} or
844     * {@link #addEarcon(String, String, int)}.
845     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
846     * requests and then returns. The synthesis might not have finished (or even started!) at the
847     * time when this method returns. In order to reliably detect errors during synthesis,
848     * we recommend setting an utterance progress listener (see
849     * {@link #setOnUtteranceProgressListener}) and using the
850     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
851     *
852     * @param earcon The earcon that should be played
853     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
854     * @param params Parameters for the request. Can be null.
855     *            Supported parameter names:
856     *            {@link Engine#KEY_PARAM_STREAM},
857     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
858     *            Engine specific parameters may be passed in but the parameter keys
859     *            must be prefixed by the name of the engine they are intended for. For example
860     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
861     *            engine named "com.svox.pico" if it is being used.
862     *
863     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playEarcon operation.
864     */
865    public int playEarcon(final String earcon, final int queueMode,
866            final HashMap<String, String> params) {
867        return runAction(new Action<Integer>() {
868            @Override
869            public Integer run(ITextToSpeechService service) throws RemoteException {
870                Uri earconUri = mEarcons.get(earcon);
871                if (earconUri == null) {
872                    return ERROR;
873                }
874                return service.playAudio(getCallerIdentity(), earconUri, queueMode,
875                        getParams(params));
876            }
877        }, ERROR, "playEarcon");
878    }
879
880    /**
881     * Plays silence for the specified amount of time using the specified
882     * queue mode.
883     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
884     * requests and then returns. The synthesis might not have finished (or even started!) at the
885     * time when this method returns. In order to reliably detect errors during synthesis,
886     * we recommend setting an utterance progress listener (see
887     * {@link #setOnUtteranceProgressListener}) and using the
888     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
889     *
890     * @param durationInMs The duration of the silence.
891     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
892     * @param params Parameters for the request. Can be null.
893     *            Supported parameter names:
894     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
895     *            Engine specific parameters may be passed in but the parameter keys
896     *            must be prefixed by the name of the engine they are intended for. For example
897     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
898     *            engine named "com.svox.pico" if it is being used.
899     *
900     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the playSilence operation.
901     */
902    public int playSilence(final long durationInMs, final int queueMode,
903            final HashMap<String, String> params) {
904        return runAction(new Action<Integer>() {
905            @Override
906            public Integer run(ITextToSpeechService service) throws RemoteException {
907                return service.playSilence(getCallerIdentity(), durationInMs, queueMode,
908                        getParams(params));
909            }
910        }, ERROR, "playSilence");
911    }
912
913    /**
914     * Queries the engine for the set of features it supports for a given locale.
915     * Features can either be framework defined, e.g.
916     * {@link TextToSpeech.Engine#KEY_FEATURE_NETWORK_SYNTHESIS} or engine specific.
917     * Engine specific keys must be prefixed by the name of the engine they
918     * are intended for. These keys can be used as parameters to
919     * {@link TextToSpeech#speak(String, int, java.util.HashMap)} and
920     * {@link TextToSpeech#synthesizeToFile(String, java.util.HashMap, String)}.
921     *
922     * Features are boolean flags, and their values in the synthesis parameters
923     * must be behave as per {@link Boolean#parseBoolean(String)}.
924     *
925     * @param locale The locale to query features for.
926     */
927    public Set<String> getFeatures(final Locale locale) {
928        return runAction(new Action<Set<String>>() {
929            @Override
930            public Set<String> run(ITextToSpeechService service) throws RemoteException {
931                String[] features = service.getFeaturesForLanguage(
932                        locale.getISO3Language(), locale.getISO3Country(), locale.getVariant());
933                if (features != null) {
934                    final Set<String> featureSet = new HashSet<String>();
935                    Collections.addAll(featureSet, features);
936                    return featureSet;
937                }
938                return null;
939            }
940        }, null, "getFeatures");
941    }
942
943    /**
944     * Checks whether the TTS engine is busy speaking. Note that a speech item is
945     * considered complete once it's audio data has been sent to the audio mixer, or
946     * written to a file. There might be a finite lag between this point, and when
947     * the audio hardware completes playback.
948     *
949     * @return {@code true} if the TTS engine is speaking.
950     */
951    public boolean isSpeaking() {
952        return runAction(new Action<Boolean>() {
953            @Override
954            public Boolean run(ITextToSpeechService service) throws RemoteException {
955                return service.isSpeaking();
956            }
957        }, false, "isSpeaking");
958    }
959
960    /**
961     * Interrupts the current utterance (whether played or rendered to file) and discards other
962     * utterances in the queue.
963     *
964     * @return {@link #ERROR} or {@link #SUCCESS}.
965     */
966    public int stop() {
967        return runAction(new Action<Integer>() {
968            @Override
969            public Integer run(ITextToSpeechService service) throws RemoteException {
970                return service.stop(getCallerIdentity());
971            }
972        }, ERROR, "stop");
973    }
974
975    /**
976     * Sets the speech rate.
977     *
978     * This has no effect on any pre-recorded speech.
979     *
980     * @param speechRate Speech rate. {@code 1.0} is the normal speech rate,
981     *            lower values slow down the speech ({@code 0.5} is half the normal speech rate),
982     *            greater values accelerate it ({@code 2.0} is twice the normal speech rate).
983     *
984     * @return {@link #ERROR} or {@link #SUCCESS}.
985     */
986    public int setSpeechRate(float speechRate) {
987        if (speechRate > 0.0f) {
988            int intRate = (int)(speechRate * 100);
989            if (intRate > 0) {
990                synchronized (mStartLock) {
991                    mParams.putInt(Engine.KEY_PARAM_RATE, intRate);
992                }
993                return SUCCESS;
994            }
995        }
996        return ERROR;
997    }
998
999    /**
1000     * Sets the speech pitch for the TextToSpeech engine.
1001     *
1002     * This has no effect on any pre-recorded speech.
1003     *
1004     * @param pitch Speech pitch. {@code 1.0} is the normal pitch,
1005     *            lower values lower the tone of the synthesized voice,
1006     *            greater values increase it.
1007     *
1008     * @return {@link #ERROR} or {@link #SUCCESS}.
1009     */
1010    public int setPitch(float pitch) {
1011        if (pitch > 0.0f) {
1012            int intPitch = (int)(pitch * 100);
1013            if (intPitch > 0) {
1014                synchronized (mStartLock) {
1015                    mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch);
1016                }
1017                return SUCCESS;
1018            }
1019        }
1020        return ERROR;
1021    }
1022
1023    /**
1024     * @return the engine currently in use by this TextToSpeech instance.
1025     * @hide
1026     */
1027    public String getCurrentEngine() {
1028        return mCurrentEngine;
1029    }
1030
1031    /**
1032     * Sets the text-to-speech language.
1033     * The TTS engine will try to use the closest match to the specified
1034     * language as represented by the Locale, but there is no guarantee that the exact same Locale
1035     * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
1036     * before choosing the language to use for the next utterances.
1037     *
1038     * @param loc The locale describing the language to be used.
1039     *
1040     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1041     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1042     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1043     */
1044    public int setLanguage(final Locale loc) {
1045        return runAction(new Action<Integer>() {
1046            @Override
1047            public Integer run(ITextToSpeechService service) throws RemoteException {
1048                if (loc == null) {
1049                    return LANG_NOT_SUPPORTED;
1050                }
1051                String language = loc.getISO3Language();
1052                String country = loc.getISO3Country();
1053                String variant = loc.getVariant();
1054                // Check if the language, country, variant are available, and cache
1055                // the available parts.
1056                // Note that the language is not actually set here, instead it is cached so it
1057                // will be associated with all upcoming utterances.
1058                int result = service.loadLanguage(language, country, variant);
1059                if (result >= LANG_AVAILABLE){
1060                    if (result < LANG_COUNTRY_VAR_AVAILABLE) {
1061                        variant = "";
1062                        if (result < LANG_COUNTRY_AVAILABLE) {
1063                            country = "";
1064                        }
1065                    }
1066                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
1067                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
1068                    mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
1069                }
1070                return result;
1071            }
1072        }, LANG_NOT_SUPPORTED, "setLanguage");
1073    }
1074
1075    /**
1076     * Returns a Locale instance describing the language currently being used by the TextToSpeech
1077     * engine.
1078     *
1079     * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
1080     *     instance, or {@code null} on error.
1081     */
1082    public Locale getLanguage() {
1083        return runAction(new Action<Locale>() {
1084            @Override
1085            public Locale run(ITextToSpeechService service) throws RemoteException {
1086                String[] locStrings = service.getLanguage();
1087                if (locStrings != null && locStrings.length == 3) {
1088                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
1089                }
1090                return null;
1091            }
1092        }, null, "getLanguage");
1093    }
1094
1095    /**
1096     * Checks if the specified language as represented by the Locale is available and supported.
1097     *
1098     * @param loc The Locale describing the language to be used.
1099     *
1100     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
1101     *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
1102     *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
1103     */
1104    public int isLanguageAvailable(final Locale loc) {
1105        return runAction(new Action<Integer>() {
1106            @Override
1107            public Integer run(ITextToSpeechService service) throws RemoteException {
1108                return service.isLanguageAvailable(loc.getISO3Language(),
1109                        loc.getISO3Country(), loc.getVariant());
1110            }
1111        }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
1112    }
1113
1114    /**
1115     * Synthesizes the given text to a file using the specified parameters.
1116     * This method is asynchronous, i.e. the method just adds the request to the queue of TTS
1117     * requests and then returns. The synthesis might not have finished (or even started!) at the
1118     * time when this method returns. In order to reliably detect errors during synthesis,
1119     * we recommend setting an utterance progress listener (see
1120     * {@link #setOnUtteranceProgressListener}) and using the
1121     * {@link Engine#KEY_PARAM_UTTERANCE_ID} parameter.
1122     *
1123     * @param text The text that should be synthesized. No longer than
1124     *            {@link Engine#MAX_SPEECH_STRING_LENGTH} characters.
1125     * @param params Parameters for the request. Can be null.
1126     *            Supported parameter names:
1127     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
1128     *            Engine specific parameters may be passed in but the parameter keys
1129     *            must be prefixed by the name of the engine they are intended for. For example
1130     *            the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the
1131     *            engine named "com.svox.pico" if it is being used.
1132     * @param filename Absolute file filename to write the generated audio data to.It should be
1133     *            something like "/sdcard/myappsounds/mysound.wav".
1134     *
1135     * @return {@link #ERROR} or {@link #SUCCESS} of <b>queuing</b> the synthesizeToFile operation.
1136     */
1137    public int synthesizeToFile(final String text, final HashMap<String, String> params,
1138            final String filename) {
1139        return runAction(new Action<Integer>() {
1140            @Override
1141            public Integer run(ITextToSpeechService service) throws RemoteException {
1142                return service.synthesizeToFile(getCallerIdentity(), text, filename,
1143                        getParams(params));
1144            }
1145        }, ERROR, "synthesizeToFile");
1146    }
1147
1148    private Bundle getParams(HashMap<String, String> params) {
1149        if (params != null && !params.isEmpty()) {
1150            Bundle bundle = new Bundle(mParams);
1151            copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
1152            copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
1153            copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
1154            copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
1155
1156            // Copy feature strings defined by the framework.
1157            copyStringParam(bundle, params, Engine.KEY_FEATURE_NETWORK_SYNTHESIS);
1158            copyStringParam(bundle, params, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
1159
1160            // Copy over all parameters that start with the name of the
1161            // engine that we are currently connected to. The engine is
1162            // free to interpret them as it chooses.
1163            if (!TextUtils.isEmpty(mCurrentEngine)) {
1164                for (Map.Entry<String, String> entry : params.entrySet()) {
1165                    final String key = entry.getKey();
1166                    if (key != null && key.startsWith(mCurrentEngine)) {
1167                        bundle.putString(key, entry.getValue());
1168                    }
1169                }
1170            }
1171
1172            return bundle;
1173        } else {
1174            return mParams;
1175        }
1176    }
1177
1178    private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
1179        String value = params.get(key);
1180        if (value != null) {
1181            bundle.putString(key, value);
1182        }
1183    }
1184
1185    private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) {
1186        String valueString = params.get(key);
1187        if (!TextUtils.isEmpty(valueString)) {
1188            try {
1189                int value = Integer.parseInt(valueString);
1190                bundle.putInt(key, value);
1191            } catch (NumberFormatException ex) {
1192                // don't set the value in the bundle
1193            }
1194        }
1195    }
1196
1197    private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) {
1198        String valueString = params.get(key);
1199        if (!TextUtils.isEmpty(valueString)) {
1200            try {
1201                float value = Float.parseFloat(valueString);
1202                bundle.putFloat(key, value);
1203            } catch (NumberFormatException ex) {
1204                // don't set the value in the bundle
1205            }
1206        }
1207    }
1208
1209    /**
1210     * Sets the listener that will be notified when synthesis of an utterance completes.
1211     *
1212     * @param listener The listener to use.
1213     *
1214     * @return {@link #ERROR} or {@link #SUCCESS}.
1215     *
1216     * @deprecated Use {@link #setOnUtteranceProgressListener(UtteranceProgressListener)}
1217     *        instead.
1218     */
1219    @Deprecated
1220    public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
1221        mUtteranceProgressListener = UtteranceProgressListener.from(listener);
1222        return TextToSpeech.SUCCESS;
1223    }
1224
1225    /**
1226     * Sets the listener that will be notified of various events related to the
1227     * synthesis of a given utterance.
1228     *
1229     * See {@link UtteranceProgressListener} and
1230     * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}.
1231     *
1232     * @param listener the listener to use.
1233     * @return {@link #ERROR} or {@link #SUCCESS}
1234     */
1235    public int setOnUtteranceProgressListener(UtteranceProgressListener listener) {
1236        mUtteranceProgressListener = listener;
1237        return TextToSpeech.SUCCESS;
1238    }
1239
1240    /**
1241     * Sets the TTS engine to use.
1242     *
1243     * @deprecated This doesn't inform callers when the TTS engine has been
1244     *        initialized. {@link #TextToSpeech(Context, OnInitListener, String)}
1245     *        can be used with the appropriate engine name. Also, there is no
1246     *        guarantee that the engine specified will be loaded. If it isn't
1247     *        installed or disabled, the user / system wide defaults will apply.
1248     *
1249     * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico")
1250     *
1251     * @return {@link #ERROR} or {@link #SUCCESS}.
1252     */
1253    @Deprecated
1254    public int setEngineByPackageName(String enginePackageName) {
1255        mRequestedEngine = enginePackageName;
1256        return initTts();
1257    }
1258
1259    /**
1260     * Gets the package name of the default speech synthesis engine.
1261     *
1262     * @return Package name of the TTS engine that the user has chosen
1263     *        as their default.
1264     */
1265    public String getDefaultEngine() {
1266        return mEnginesHelper.getDefaultEngine();
1267    }
1268
1269    /**
1270     * Checks whether the user's settings should override settings requested
1271     * by the calling application. As of the Ice cream sandwich release,
1272     * user settings never forcibly override the app's settings.
1273     */
1274    public boolean areDefaultsEnforced() {
1275        return false;
1276    }
1277
1278    /**
1279     * Gets a list of all installed TTS engines.
1280     *
1281     * @return A list of engine info objects. The list can be empty, but never {@code null}.
1282     */
1283    public List<EngineInfo> getEngines() {
1284        return mEnginesHelper.getEngines();
1285    }
1286
1287
1288    private class Connection implements ServiceConnection {
1289        private ITextToSpeechService mService;
1290        private final ITextToSpeechCallback.Stub mCallback = new ITextToSpeechCallback.Stub() {
1291            @Override
1292            public void onDone(String utteranceId) {
1293                UtteranceProgressListener listener = mUtteranceProgressListener;
1294                if (listener != null) {
1295                    listener.onDone(utteranceId);
1296                }
1297            }
1298
1299            @Override
1300            public void onError(String utteranceId) {
1301                UtteranceProgressListener listener = mUtteranceProgressListener;
1302                if (listener != null) {
1303                    listener.onError(utteranceId);
1304                }
1305            }
1306
1307            @Override
1308            public void onStart(String utteranceId) {
1309                UtteranceProgressListener listener = mUtteranceProgressListener;
1310                if (listener != null) {
1311                    listener.onStart(utteranceId);
1312                }
1313            }
1314        };
1315
1316        @Override
1317        public void onServiceConnected(ComponentName name, IBinder service) {
1318            Log.i(TAG, "Connected to " + name);
1319            synchronized(mStartLock) {
1320                if (mServiceConnection != null) {
1321                    // Disconnect any previous service connection
1322                    mServiceConnection.disconnect();
1323                }
1324                mServiceConnection = this;
1325                mService = ITextToSpeechService.Stub.asInterface(service);
1326                try {
1327                    mService.setCallback(getCallerIdentity(), mCallback);
1328                    dispatchOnInit(SUCCESS);
1329                } catch (RemoteException re) {
1330                    Log.e(TAG, "Error connecting to service, setCallback() failed");
1331                    dispatchOnInit(ERROR);
1332                }
1333            }
1334        }
1335
1336        public IBinder getCallerIdentity() {
1337            return mCallback;
1338        }
1339
1340        @Override
1341        public void onServiceDisconnected(ComponentName name) {
1342            synchronized(mStartLock) {
1343                mService = null;
1344                // If this is the active connection, clear it
1345                if (mServiceConnection == this) {
1346                    mServiceConnection = null;
1347                }
1348            }
1349        }
1350
1351        public void disconnect() {
1352            mContext.unbindService(this);
1353
1354            synchronized (mStartLock) {
1355                mService = null;
1356                // If this is the active connection, clear it
1357                if (mServiceConnection == this) {
1358                    mServiceConnection = null;
1359                }
1360
1361            }
1362        }
1363
1364        public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
1365            synchronized (mStartLock) {
1366                try {
1367                    if (mService == null) {
1368                        Log.w(TAG, method + " failed: not connected to TTS engine");
1369                        return errorResult;
1370                    }
1371                    return action.run(mService);
1372                } catch (RemoteException ex) {
1373                    Log.e(TAG, method + " failed", ex);
1374                    if (reconnect) {
1375                        disconnect();
1376                        initTts();
1377                    }
1378                    return errorResult;
1379                }
1380            }
1381        }
1382    }
1383
1384    private interface Action<R> {
1385        R run(ITextToSpeechService service) throws RemoteException;
1386    }
1387
1388    /**
1389     * Information about an installed text-to-speech engine.
1390     *
1391     * @see TextToSpeech#getEngines
1392     */
1393    public static class EngineInfo {
1394        /**
1395         * Engine package name..
1396         */
1397        public String name;
1398        /**
1399         * Localized label for the engine.
1400         */
1401        public String label;
1402        /**
1403         * Icon for the engine.
1404         */
1405        public int icon;
1406        /**
1407         * Whether this engine is a part of the system
1408         * image.
1409         *
1410         * @hide
1411         */
1412        public boolean system;
1413        /**
1414         * The priority the engine declares for the the intent filter
1415         * {@code android.intent.action.TTS_SERVICE}
1416         *
1417         * @hide
1418         */
1419        public int priority;
1420
1421        @Override
1422        public String toString() {
1423            return "EngineInfo{name=" + name + "}";
1424        }
1425
1426    }
1427
1428}
1429