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