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