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><{@link android.R.styleable#TextToSpeechEngine tts-engine}></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><manifest xmlns:android="..." 695 * package="<b>com.google.marvin.compass</b>"></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><manifest xmlns:android="..." 750 * package="<b>com.google.marvin.compass</b>"></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