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