TextToSpeechService.java revision 992ea1553c3ae6766505f084061c5ef2321229b7
1b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/* 2b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Copyright (C) 2011 The Android Open Source Project 3b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 4b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * use this file except in compliance with the License. You may obtain a copy of 6014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * the License at 7b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 8014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * http://www.apache.org/licenses/LICENSE-2.0 9014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * 10014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Unless required by applicable law or agreed to in writing, software 11b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * License for the specific language governing permissions and limitations under 14b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * the License. 15b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 16b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochpackage android.speech.tts; 17b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 18b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.app.Service; 19b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.content.Intent; 20b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.media.AudioAttributes; 21b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.media.AudioSystem; 22b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.net.Uri; 23b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.Binder; 24b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.Bundle; 25b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.Handler; 26b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.HandlerThread; 27b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.IBinder; 28b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.Looper; 29b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.Message; 30b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.MessageQueue; 31b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.ParcelFileDescriptor; 32b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.RemoteCallbackList; 33b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.os.RemoteException; 34b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.provider.Settings; 35b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.speech.tts.TextToSpeech.Engine; 36b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.text.TextUtils; 37b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport android.util.Log; 38b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 39014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdochimport java.io.FileOutputStream; 40b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport java.io.IOException; 41b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport java.util.ArrayList; 42b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport java.util.HashMap; 43b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport java.util.List; 44b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport java.util.Locale; 45b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport java.util.MissingResourceException; 46b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochimport java.util.Set; 47b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 48b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 49b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch/** 50b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Abstract base class for TTS engine implementations. The following methods 51b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * need to be implemented: 52b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <ul> 53b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onIsLanguageAvailable}</li> 54b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onLoadLanguage}</li> 55b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onGetLanguage}</li> 56b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onSynthesizeText}</li> 57b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onStop}</li> 58b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * </ul> 59b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * The first three deal primarily with language management, and are used to 60b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * query the engine for it's support for a given language and indicate to it 61b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * that requests in a given language are imminent. 62b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 63b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onSynthesizeText} is central to the engine implementation. The 64b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * implementation should synthesize text as per the request parameters and 65b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * return synthesized data via the supplied callback. This class and its helpers 66b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * will then consume that data, which might mean queuing it for playback or writing 67b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * it to a file or similar. All calls to this method will be on a single thread, 68b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * which will be different from the main thread of the service. Synthesis must be 69b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * synchronous which means the engine must NOT hold on to the callback or call any 70b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * methods on it after the method returns. 71b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 72b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onStop} tells the engine that it should stop 73bcf72ee8e3b26f1d0726869c7ddb3921c68b09a8Ben Murdoch * all ongoing synthesis, if any. Any pending data from the current synthesis 74b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * will be discarded. 75b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 76b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onGetLanguage} is not required as of JELLYBEAN_MR2 (API 18) and later, it is only 77b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * called on earlier versions of Android. 78b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 79b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * API Level 20 adds support for Voice objects. Voices are an abstraction that allow the TTS 80014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * service to expose multiple backends for a single locale. Each one of them can have a different 81b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * features set. In order to fully take advantage of voices, an engine should implement 82b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * the following methods: 83b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <ul> 84b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onGetVoices()}</li> 85b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onIsValidVoiceName(String)}</li> 86b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onLoadVoice(String)}</li> 87b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * <li>{@link #onGetDefaultVoiceNameFor(String, String, String)}</li> 88b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * </ul> 89b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * The first three methods are siblings of the {@link #onGetLanguage}, 90b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onIsLanguageAvailable} and {@link #onLoadLanguage} methods. The last one, 91b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onGetDefaultVoiceNameFor(String, String, String)} is a link between locale and voice 92b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * based methods. Since API level 21 {@link TextToSpeech#setLanguage} is implemented by 93b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * calling {@link TextToSpeech#setVoice} with the voice returned by 94b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onGetDefaultVoiceNameFor(String, String, String)}. 95b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 96b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If the client uses a voice instead of a locale, {@link SynthesisRequest} will contain the 97b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * requested voice name. 98b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 9913e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch * The default implementations of Voice-related methods implement them using the 100b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * pre-existing locale-based implementation. 101b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 102b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdochpublic abstract class TextToSpeechService extends Service { 103b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 104b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private static final boolean DBG = false; 105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private static final String TAG = "TextToSpeechService"; 106b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 107b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private static final String SYNTH_THREAD_NAME = "SynthThread"; 108014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 10913e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch private SynthHandler mSynthHandler; 110b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // A thread and it's associated handler for playing back any audio 111b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // associated with this TTS engine. Will handle all requests except synthesis 112b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // to file requests, which occur on the synthesis thread. 113b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private AudioPlaybackHandler mAudioPlaybackHandler; 114b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private TtsEngines mEngineHelper; 115b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 116b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private CallbackMap mCallbacks; 117b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private String mPackageName; 118b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 119b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final Object mVoicesInfoLock = new Object(); 120b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 121014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch @Override 122b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void onCreate() { 123b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (DBG) Log.d(TAG, "onCreate()"); 124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super.onCreate(); 125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SynthThread synthThread = new SynthThread(); 127b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synthThread.start(); 128b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mSynthHandler = new SynthHandler(synthThread.getLooper()); 129b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 130b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioPlaybackHandler = new AudioPlaybackHandler(); 131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioPlaybackHandler.start(); 132b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 133b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mEngineHelper = new TtsEngines(this); 134b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 135b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallbacks = new CallbackMap(); 136b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 137b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mPackageName = getApplicationInfo().packageName; 138b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String[] defaultLocale = getSettingsLocale(); 140b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 141b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Load default language 142b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch onLoadLanguage(defaultLocale[0], defaultLocale[1], defaultLocale[2]); 143b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 144b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 145b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 146b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void onDestroy() { 147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (DBG) Log.d(TAG, "onDestroy()"); 148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 149958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier // Tell the synthesizer to stop 150014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch mSynthHandler.quit(); 151014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch // Tell the audio playback thread to stop. 152014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch mAudioPlaybackHandler.quit(); 153958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier // Unregister all callbacks. 154958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier mCallbacks.kill(); 155958fae7ec3f466955f8e5b50fa5b8d38b9e91675Emily Bernier 156b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super.onDestroy(); 157b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 158b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 159b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 160b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Checks whether the engine supports a given language. 161b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 162b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Can be called on multiple threads. 163b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 164b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Its return values HAVE to be consistent with onLoadLanguage. 165b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 166b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param lang ISO-3 language code. 167b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param country ISO-3 country code. May be empty or null. 168b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param variant Language variant. May be empty or null. 169b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return Code indicating the support status for the locale. 170b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * One of {@link TextToSpeech#LANG_AVAILABLE}, 171b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE}, 172b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE}, 173b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_MISSING_DATA} 174b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_NOT_SUPPORTED}. 175b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 176b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected abstract int onIsLanguageAvailable(String lang, String country, String variant); 177b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 178b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 179b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Returns the language, country and variant currently being used by the TTS engine. 180b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 181b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * This method will be called only on Android 4.2 and before (API <= 17). In later versions 182b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * this method is not called by the Android TTS framework. 183b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 184b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Can be called on multiple threads. 185b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 186b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return A 3-element array, containing language (ISO 3-letter code), 187b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * country (ISO 3-letter code) and variant used by the engine. 188b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * The country and variant may be {@code ""}. If country is empty, then variant must 189b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * be empty too. 190b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @see Locale#getISO3Language() 191b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @see Locale#getISO3Country() 192b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @see Locale#getVariant() 193b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 194b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected abstract String[] onGetLanguage(); 195b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 196b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 197b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Notifies the engine that it should load a speech synthesis language. There is no guarantee 198b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * that this method is always called before the language is used for synthesis. It is merely 199b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * a hint to the engine that it will probably get some synthesis requests for this language 200b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * at some point in the future. 201b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 202b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Can be called on multiple threads. 203b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * In <= Android 4.2 (<= API 17) can be called on main and service binder threads. 204b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * In > Android 4.2 (> API 17) can be called on main and synthesis threads. 205b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 206b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param lang ISO-3 language code. 207b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param country ISO-3 country code. May be empty or null. 208b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param variant Language variant. May be empty or null. 209b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return Code indicating the support status for the locale. 210b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * One of {@link TextToSpeech#LANG_AVAILABLE}, 211b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE}, 212b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE}, 213b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_MISSING_DATA} 214b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#LANG_NOT_SUPPORTED}. 215b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 216b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected abstract int onLoadLanguage(String lang, String country, String variant); 217b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 218b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 219b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Notifies the service that it should stop any in-progress speech synthesis. 2203b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch * This method can be called even if no speech synthesis is currently in progress. 2213b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch * 2223b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch * Can be called on multiple threads, but not on the synthesis thread. 2233b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch */ 2243b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch protected abstract void onStop(); 2253b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch 2263b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch /** 227b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Tells the service to synthesize speech from the given text. This method 228b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * should block until the synthesis is finished. Used for requests from V1 229b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * clients ({@link android.speech.tts.TextToSpeech}). Called on the synthesis 230b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * thread. 231b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 232b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param request The synthesis request. 233b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param callback The callback that the engine must use to make data 234b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * available for playback or for writing to a file. 235b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 236b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected abstract void onSynthesizeText(SynthesisRequest request, 237b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SynthesisCallback callback); 238b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 239b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 240b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Queries the service for a set of features supported for a given language. 241b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 242b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Can be called on multiple threads. 243b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 244b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param lang ISO-3 language code. 245b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param country ISO-3 country code. May be empty or null. 246b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param variant Language variant. May be empty or null. 247b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return A list of features supported for the given language. 248b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 249b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected Set<String> onGetFeaturesForLanguage(String lang, String country, String variant) { 250b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return null; 251b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 252b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 253b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private int getExpectedLanguageAvailableStatus(Locale locale) { 254b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int expectedStatus = TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE; 255b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (locale.getVariant().isEmpty()) { 256b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (locale.getCountry().isEmpty()) { 257b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch expectedStatus = TextToSpeech.LANG_AVAILABLE; 258b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 259b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch expectedStatus = TextToSpeech.LANG_COUNTRY_AVAILABLE; 260b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 261b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 262b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return expectedStatus; 263b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 264b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 2653b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch /** 2663b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch * Queries the service for a set of supported voices. 267b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 268b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Can be called on multiple threads. 269b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 270b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * The default implementation tries to enumerate all available locales, pass them to 271b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onIsLanguageAvailable(String, String, String)} and create Voice instances (using 272b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * the locale's BCP-47 language tag as the voice name) for the ones that are supported. 273b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Note, that this implementation is suitable only for engines that don't have multiple voices 274b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * for a single locale. Also, this implementation won't work with Locales not listed in the 275b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * set returned by the {@link Locale#getAvailableLocales()} method. 276b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 277b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return A list of voices supported. 278b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 279b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public List<Voice> onGetVoices() { 280b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Enumerate all locales and check if they are available 281014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch ArrayList<Voice> voices = new ArrayList<Voice>(); 282b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch for (Locale locale : Locale.getAvailableLocales()) { 283014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch int expectedStatus = getExpectedLanguageAvailableStatus(locale); 284b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch try { 285b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int localeStatus = onIsLanguageAvailable(locale.getISO3Language(), 286b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch locale.getISO3Country(), locale.getVariant()); 287b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (localeStatus != expectedStatus) { 288b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch continue; 289b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 290b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } catch (MissingResourceException e) { 291b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Ignore locale without iso 3 codes 292b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch continue; 293b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 294b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Set<String> features = onGetFeaturesForLanguage(locale.getISO3Language(), 295b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch locale.getISO3Country(), locale.getVariant()); 296b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String voiceName = onGetDefaultVoiceNameFor(locale.getISO3Language(), 297b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch locale.getISO3Country(), locale.getVariant()); 298b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch voices.add(new Voice(voiceName, locale, Voice.QUALITY_NORMAL, 299b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Voice.LATENCY_NORMAL, false, features)); 300b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 301014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return voices; 302014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 303b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 304014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch /** 305014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Return a name of the default voice for a given locale. 306b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 307b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * This method provides a mapping between locales and available voices. This method is 308b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * used in {@link TextToSpeech#setLanguage}, which calls this method and then calls 309b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech#setVoice} with the voice returned by this method. 310b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 311b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Also, it's used by {@link TextToSpeech#getDefaultVoice()} to find a default voice for 312b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * the default locale. 313b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 314b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param lang ISO-3 language code. 315b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param country ISO-3 country code. May be empty or null. 316b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param variant Language variant. May be empty or null. 317b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 318b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return A name of the default voice for a given locale. 319014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch */ 320014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public String onGetDefaultVoiceNameFor(String lang, String country, String variant) { 321b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int localeStatus = onIsLanguageAvailable(lang, country, variant); 322b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Locale iso3Locale = null; 323b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch switch (localeStatus) { 324b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch case TextToSpeech.LANG_AVAILABLE: 325b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch iso3Locale = new Locale(lang); 326b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch break; 327b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch case TextToSpeech.LANG_COUNTRY_AVAILABLE: 328b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch iso3Locale = new Locale(lang, country); 329b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch break; 330b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch case TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE: 331b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch iso3Locale = new Locale(lang, country, variant); 332b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch break; 333b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch default: 334b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return null; 335b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 336b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Locale properLocale = TtsEngines.normalizeTTSLocale(iso3Locale); 337b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String voiceName = properLocale.toLanguageTag(); 338b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (onIsValidVoiceName(voiceName) == TextToSpeech.SUCCESS) { 339b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return voiceName; 340b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 341b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return null; 342b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 343b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 344b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 345b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 346b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Notifies the engine that it should load a speech synthesis voice. There is no guarantee 347b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * that this method is always called before the voice is used for synthesis. It is merely 348b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * a hint to the engine that it will probably get some synthesis requests for this voice 349b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * at some point in the future. 350b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 351b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Will be called only on synthesis thread. 352b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 353b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * The default implementation creates a Locale from the voice name (by interpreting the name as 354b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * a BCP-47 tag for the locale), and passes it to 355b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link #onLoadLanguage(String, String, String)}. 356014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * 357b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @param voiceName Name of the voice. 358b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}. 359b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 360014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public int onLoadVoice(String voiceName) { 361b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Locale locale = Locale.forLanguageTag(voiceName); 362b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (locale == null) { 363b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 364b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 365b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int expectedStatus = getExpectedLanguageAvailableStatus(locale); 366b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch try { 367b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int localeStatus = onIsLanguageAvailable(locale.getISO3Language(), 368014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch locale.getISO3Country(), locale.getVariant()); 369b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (localeStatus != expectedStatus) { 370b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 371b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 372014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch onLoadLanguage(locale.getISO3Language(), 373b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch locale.getISO3Country(), locale.getVariant()); 374b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.SUCCESS; 375b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } catch (MissingResourceException e) { 376b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 377b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 378014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 379b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 380b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 381b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Checks whether the engine supports a voice with a given name. 382014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * 383b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Can be called on multiple threads. 384014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * 385014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * The default implementation treats the voice name as a language tag, creating a Locale from 386b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * the voice name, and passes it to {@link #onIsLanguageAvailable(String, String, String)}. 387b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 388014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * @param voiceName Name of the voice. 389b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}. 390b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 391b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int onIsValidVoiceName(String voiceName) { 392b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Locale locale = Locale.forLanguageTag(voiceName); 393b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (locale == null) { 394b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 395b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 396b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int expectedStatus = getExpectedLanguageAvailableStatus(locale); 397b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch try { 398b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int localeStatus = onIsLanguageAvailable(locale.getISO3Language(), 399b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch locale.getISO3Country(), locale.getVariant()); 400014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (localeStatus != expectedStatus) { 401014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return TextToSpeech.ERROR; 402014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 403014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return TextToSpeech.SUCCESS; 404014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } catch (MissingResourceException e) { 405014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return TextToSpeech.ERROR; 406014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 407b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 408b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 409b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private int getDefaultSpeechRate() { 410b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE); 411b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 412014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 413b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private String[] getSettingsLocale() { 414b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch final Locale locale = mEngineHelper.getLocalePrefForEngine(mPackageName); 415b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TtsEngines.toOldLocaleStringFormat(locale); 416b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 417b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 418b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private int getSecureSettingInt(String name, int defaultValue) { 419014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return Settings.Secure.getInt(getContentResolver(), name, defaultValue); 420b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 421b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 422b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 423b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Synthesizer thread. This thread is used to run {@link SynthHandler}. 424b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 425b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private class SynthThread extends HandlerThread implements MessageQueue.IdleHandler { 426b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 427b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private boolean mFirstIdle = true; 428014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 429b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public SynthThread() { 430014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_DEFAULT); 431b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 432b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 433b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 434b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void onLooperPrepared() { 435b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch getLooper().getQueue().addIdleHandler(this); 436014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 437b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 438b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 439b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean queueIdle() { 440b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mFirstIdle) { 441014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch mFirstIdle = false; 442b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 443b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch broadcastTtsQueueProcessingCompleted(); 444014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 445b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return true; 446b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 447b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 448b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private void broadcastTtsQueueProcessingCompleted() { 449b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED); 450b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (DBG) Log.d(TAG, "Broadcasting: " + i); 451b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch sendBroadcast(i); 452b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 453b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 454b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 455b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private class SynthHandler extends Handler { 456b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private SpeechItem mCurrentSpeechItem = null; 457b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 458b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private ArrayList<Object> mFlushedObjects = new ArrayList<Object>(); 459b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private boolean mFlushAll; 460014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 461b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public SynthHandler(Looper looper) { 462014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch super(looper); 463b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 464b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 465b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private void startFlushingSpeechItems(Object callerIdentity) { 466b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (mFlushedObjects) { 467014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (callerIdentity == null) { 468b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mFlushAll = true; 469014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } else { 470b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mFlushedObjects.add(callerIdentity); 471b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 472b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 473b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 474b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private void endFlushingSpeechItems(Object callerIdentity) { 475b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (mFlushedObjects) { 476b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (callerIdentity == null) { 477b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mFlushAll = false; 478b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 479014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch mFlushedObjects.remove(callerIdentity); 480b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 481b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 482b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 483b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private boolean isFlushed(SpeechItem speechItem) { 484b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (mFlushedObjects) { 485b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mFlushAll || mFlushedObjects.contains(speechItem.getCallerIdentity()); 486b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 487b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 488b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 489b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private synchronized SpeechItem getCurrentSpeechItem() { 490b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mCurrentSpeechItem; 491b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 492b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 493b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) { 494b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem old = mCurrentSpeechItem; 495b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCurrentSpeechItem = speechItem; 496b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return old; 497b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 498b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 499b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private synchronized SpeechItem maybeRemoveCurrentSpeechItem(Object callerIdentity) { 500b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mCurrentSpeechItem != null && 501b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch (mCurrentSpeechItem.getCallerIdentity() == callerIdentity)) { 502b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem current = mCurrentSpeechItem; 503b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCurrentSpeechItem = null; 504b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return current; 505b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 506b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 507b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return null; 508b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 509b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 510b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean isSpeaking() { 511b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getCurrentSpeechItem() != null; 512b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 513b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 514b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void quit() { 515b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Don't process any more speech items 516b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch getLooper().quit(); 517b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Stop the current speech item 518014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch SpeechItem current = setCurrentSpeechItem(null); 519b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (current != null) { 520b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch current.stop(); 521b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 522b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // The AudioPlaybackHandler will be destroyed by the caller. 523b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 524b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 525b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 526b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Adds a speech item to the queue. 527b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 528b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Called on a service binder thread. 529b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 530b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) { 531b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch UtteranceProgressDispatcher utterenceProgress = null; 532b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (speechItem instanceof UtteranceProgressDispatcher) { 533b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch utterenceProgress = (UtteranceProgressDispatcher) speechItem; 534b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 535b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 536b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!speechItem.isValid()) { 537014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (utterenceProgress != null) { 538b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch utterenceProgress.dispatchOnError( 539b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch TextToSpeech.ERROR_INVALID_REQUEST); 540b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 541b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 542b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 543b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 544b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (queueMode == TextToSpeech.QUEUE_FLUSH) { 545b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch stopForApp(speechItem.getCallerIdentity()); 546b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else if (queueMode == TextToSpeech.QUEUE_DESTROY) { 547b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch stopAll(); 548b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 549b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Runnable runnable = new Runnable() { 550b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 551b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void run() { 552b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (isFlushed(speechItem)) { 553b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch speechItem.stop(); 554b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 5553b9bc31999c9787eb726ecdbfd5796bfdec32a18Ben Murdoch setCurrentSpeechItem(speechItem); 556b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch speechItem.play(); 557b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch setCurrentSpeechItem(null); 558b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 559014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 560b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch }; 561b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Message msg = Message.obtain(this, runnable); 562b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 563b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // The obj is used to remove all callbacks from the given app in 564b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // stopForApp(String). 565b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // 566b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Note that this string is interned, so the == comparison works. 567b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch msg.obj = speechItem.getCallerIdentity(); 568b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 569b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (sendMessage(msg)) { 570b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.SUCCESS; 571b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 572b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.w(TAG, "SynthThread has quit"); 573014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (utterenceProgress != null) { 574b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch utterenceProgress.dispatchOnError(TextToSpeech.ERROR_SERVICE); 575b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 576b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 577014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 578b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 579b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 580b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 581b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Stops all speech output and removes any utterances still in the queue for 582b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * the calling app. 583b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 584b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Called on a service binder thread. 585b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 586b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int stopForApp(final Object callerIdentity) { 587b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (callerIdentity == null) { 588b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 589014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 590b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 591b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Flush pending messages from callerIdentity 592b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch startFlushingSpeechItems(callerIdentity); 593b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 594b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // This stops writing data to the file / or publishing 595b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // items to the audio playback handler. 596014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch // 597b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Note that the current speech item must be removed only if it 598b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // belongs to the callingApp, else the item will be "orphaned" and 599b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // not stopped correctly if a stop request comes along for the item 600b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // from the app it belongs to. 601b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem current = maybeRemoveCurrentSpeechItem(callerIdentity); 602014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (current != null) { 603b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch current.stop(); 604b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 605b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 606b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Remove any enqueued audio too. 607b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioPlaybackHandler.stopForApp(callerIdentity); 608b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 609b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Stop flushing pending messages 610b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Runnable runnable = new Runnable() { 611b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 612b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void run() { 613b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch endFlushingSpeechItems(callerIdentity); 614b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 615b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch }; 616b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch sendMessage(Message.obtain(this, runnable)); 617b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.SUCCESS; 618b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 619b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 620014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public int stopAll() { 621b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Order to flush pending messages 622b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch startFlushingSpeechItems(null); 623b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 624b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Stop the current speech item unconditionally . 625b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem current = setCurrentSpeechItem(null); 626b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (current != null) { 627b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch current.stop(); 628b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 629b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Remove all pending playback as well. 630b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioPlaybackHandler.stop(); 631b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 632b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Message to stop flushing pending messages 633b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Runnable runnable = new Runnable() { 634b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 635b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void run() { 636b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch endFlushingSpeechItems(null); 637b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 638b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch }; 639b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch sendMessage(Message.obtain(this, runnable)); 640b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 641b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 642b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.SUCCESS; 643b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 644b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 645014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 646b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch interface UtteranceProgressDispatcher { 647b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnStop(); 648b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnSuccess(); 649b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnStart(); 650b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnError(int errorCode); 651b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 652b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 653b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** Set of parameters affecting audio output. */ 654b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch static class AudioOutputParams { 655b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 656b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Audio session identifier. May be used to associate audio playback with one of the 657b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link android.media.audiofx.AudioEffect} objects. If not specified by client, 658b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * it should be equal to {@link AudioSystem#AUDIO_SESSION_ALLOCATE}. 659b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 660b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public final int mSessionId; 661b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 662014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch /** 663b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Volume, in the range [0.0f, 1.0f]. The default value is 664b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f). 665b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 666b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public final float mVolume; 667b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 668b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 669b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Left/right position of the audio, in the range [-1.0f, 1.0f]. 670b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f). 671b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 672b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public final float mPan; 673b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 674b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 675b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 676b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Audio attributes, set by {@link TextToSpeech#setAudioAttributes} 677b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * or created from the value of {@link TextToSpeech.Engine#KEY_PARAM_STREAM}. 678b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 679b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public final AudioAttributes mAudioAttributes; 680014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 681b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** Create AudioOutputParams with default values */ 682b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioOutputParams() { 683b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mSessionId = AudioSystem.AUDIO_SESSION_ALLOCATE; 684b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mVolume = Engine.DEFAULT_VOLUME; 685b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mPan = Engine.DEFAULT_PAN; 686b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioAttributes = null; 687b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 688b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 689b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioOutputParams(int sessionId, float volume, float pan, 690b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioAttributes audioAttributes) { 691b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mSessionId = sessionId; 692b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mVolume = volume; 693b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mPan = pan; 694b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioAttributes = audioAttributes; 695b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 696b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 697b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** Create AudioOutputParams from A {@link SynthesisRequest#getParams()} bundle */ 698b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch static AudioOutputParams createFromV1ParamsBundle(Bundle paramsBundle, 699b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch boolean isSpeech) { 700b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (paramsBundle == null) { 701b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return new AudioOutputParams(); 702b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 703b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 704b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioAttributes audioAttributes = 705014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch (AudioAttributes) paramsBundle.getParcelable( 706b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Engine.KEY_PARAM_AUDIO_ATTRIBUTES); 707b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (audioAttributes == null) { 708b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int streamType = paramsBundle.getInt( 709b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM); 710b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch audioAttributes = (new AudioAttributes.Builder()) 711b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch .setLegacyStreamType(streamType) 712b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch .setContentType((isSpeech ? 713b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioAttributes.CONTENT_TYPE_SPEECH : 714b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioAttributes.CONTENT_TYPE_SONIFICATION)) 715b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch .build(); 716b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 717b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 718b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return new AudioOutputParams( 719b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch paramsBundle.getInt( 720b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Engine.KEY_PARAM_SESSION_ID, 721b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioSystem.AUDIO_SESSION_ALLOCATE), 722014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch paramsBundle.getFloat( 723b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Engine.KEY_PARAM_VOLUME, 724b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Engine.DEFAULT_VOLUME), 725b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch paramsBundle.getFloat( 726b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Engine.KEY_PARAM_PAN, 727b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Engine.DEFAULT_PAN), 728b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch audioAttributes); 729b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 730b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 731b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 732b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 733b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 734b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * An item in the synth thread queue. 735b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 736b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private abstract class SpeechItem { 737b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final Object mCallerIdentity; 738b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final int mCallerUid; 739b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final int mCallerPid; 740b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private boolean mStarted = false; 741014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch private boolean mStopped = false; 742b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 743b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public SpeechItem(Object caller, int callerUid, int callerPid) { 744b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallerIdentity = caller; 745b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallerUid = callerUid; 746b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallerPid = callerPid; 747b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 748b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 749b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public Object getCallerIdentity() { 750b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mCallerIdentity; 751b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 752b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 753b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int getCallerUid() { 754b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mCallerUid; 755b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 756b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 757b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int getCallerPid() { 758b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mCallerPid; 759b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 760b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 761b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 762b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Checker whether the item is valid. If this method returns false, the item should not 763b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * be played. 764b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 765b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public abstract boolean isValid(); 766b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 767b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 768b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Plays the speech item. Blocks until playback is finished. 769014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Must not be called more than once. 770b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 771b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Only called on the synthesis thread. 772b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 773b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void play() { 774b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (this) { 775b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mStarted) { 776b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch throw new IllegalStateException("play() called twice"); 777b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 778b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mStarted = true; 779b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 780b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch playImpl(); 781b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 782b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 783b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected abstract void playImpl(); 784b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 785b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 786014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch * Stops the speech item. 787b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Must not be called more than once. 788b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * 789b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Can be called on multiple threads, but not on the synthesis thread. 790b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 791b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void stop() { 792b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (this) { 793b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mStopped) { 794b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch throw new IllegalStateException("stop() called twice"); 795b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 796b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mStopped = true; 797b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 798b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch stopImpl(); 799b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 800b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 801b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected abstract void stopImpl(); 802b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 803014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch protected synchronized boolean isStopped() { 804b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mStopped; 805b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 806b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 807b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected synchronized boolean isStarted() { 808b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mStarted; 809b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 810b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 811b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 812b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 813b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * An item in the synth thread queue that process utterance (and call back to client about 814b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * progress). 815b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 816b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private abstract class UtteranceSpeechItem extends SpeechItem 817b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch implements UtteranceProgressDispatcher { 818b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 819b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public UtteranceSpeechItem(Object caller, int callerUid, int callerPid) { 820b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super(caller, callerUid, callerPid); 821b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 822014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 823b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 824b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnSuccess() { 825b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch final String utteranceId = getUtteranceId(); 826014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (utteranceId != null) { 827b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallbacks.dispatchOnSuccess(getCallerIdentity(), utteranceId); 828b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 829b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 830b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 831b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 832b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnStop() { 833b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch final String utteranceId = getUtteranceId(); 834b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (utteranceId != null) { 835b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallbacks.dispatchOnStop(getCallerIdentity(), utteranceId, isStarted()); 836b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 837b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 838b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 839014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch @Override 840b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnStart() { 841b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch final String utteranceId = getUtteranceId(); 842b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (utteranceId != null) { 843b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallbacks.dispatchOnStart(getCallerIdentity(), utteranceId); 844b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 845b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 846b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 847b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 848b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnError(int errorCode) { 849b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch final String utteranceId = getUtteranceId(); 850b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (utteranceId != null) { 851b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallbacks.dispatchOnError(getCallerIdentity(), utteranceId, errorCode); 852014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 853b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 854b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 855b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch abstract public String getUtteranceId(); 856b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 857b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String getStringParam(Bundle params, String key, String defaultValue) { 858b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return params == null ? defaultValue : params.getString(key, defaultValue); 859b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 860b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 861b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int getIntParam(Bundle params, String key, int defaultValue) { 862014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return params == null ? defaultValue : params.getInt(key, defaultValue); 863b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 864b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 865b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch float getFloatParam(Bundle params, String key, float defaultValue) { 866b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return params == null ? defaultValue : params.getFloat(key, defaultValue); 867b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 868b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 869b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 870b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 871b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * UtteranceSpeechItem for V1 API speech items. V1 API speech items keep 872b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * synthesis parameters in a single Bundle passed as parameter. This class 873b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * allow subclasses to access them conveniently. 874b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 875b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private abstract class SpeechItemV1 extends UtteranceSpeechItem { 876b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected final Bundle mParams; 877014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch protected final String mUtteranceId; 878b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 879b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItemV1(Object callerIdentity, int callerUid, int callerPid, 880b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Bundle params, String utteranceId) { 881b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super(callerIdentity, callerUid, callerPid); 882b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mParams = params; 883b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mUtteranceId = utteranceId; 884b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 885b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 886b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch boolean hasLanguage() { 887b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return !TextUtils.isEmpty(getStringParam(mParams, Engine.KEY_PARAM_LANGUAGE, null)); 888b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 889014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 890b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int getSpeechRate() { 891b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getIntParam(mParams, Engine.KEY_PARAM_RATE, getDefaultSpeechRate()); 892b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 893b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 894b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int getPitch() { 895b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getIntParam(mParams, Engine.KEY_PARAM_PITCH, Engine.DEFAULT_PITCH); 896b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 897b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 898b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 899b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String getUtteranceId() { 90013e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch return mUtteranceId; 90113e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch } 90213e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch 90313e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch AudioOutputParams getAudioParams() { 90413e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch return AudioOutputParams.createFromV1ParamsBundle(mParams, true); 905b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 90613e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch } 90713e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch 90813e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch class SynthesisSpeechItemV1 extends SpeechItemV1 { 90913e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch // Never null. 910b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final CharSequence mText; 91113e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch private final SynthesisRequest mSynthesisRequest; 91213e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch private final String[] mDefaultLocale; 91313e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch // Non null after synthesis has started, and all accesses 91413e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch // guarded by 'this'. 91513e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch private AbstractSynthesisCallback mSynthesisCallback; 91613e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch private final EventLoggerV1 mEventLogger; 91713e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch private final int mCallerUid; 91813e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch 91913e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch public SynthesisSpeechItemV1(Object callerIdentity, int callerUid, int callerPid, 92013e2dadd00298019ed862f2b2fc5068bba730bcfBen Murdoch Bundle params, String utteranceId, CharSequence text) { 921b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super(callerIdentity, callerUid, callerPid, params, utteranceId); 922b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mText = text; 923b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallerUid = callerUid; 924b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mSynthesisRequest = new SynthesisRequest(mText, mParams); 925b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mDefaultLocale = getSettingsLocale(); 926b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch setRequestParams(mSynthesisRequest); 927b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mEventLogger = new EventLoggerV1(mSynthesisRequest, callerUid, callerPid, 928014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch mPackageName); 929b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 930b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 931b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public CharSequence getText() { 932b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mText; 933b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 934b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 935b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 936b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean isValid() { 937b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mText == null) { 938b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.e(TAG, "null synthesis text"); 939b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return false; 940b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 941b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) { 942b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.w(TAG, "Text too long: " + mText.length() + " chars"); 943b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return false; 944b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 945b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return true; 946014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 947b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 948b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 949b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void playImpl() { 950b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AbstractSynthesisCallback synthesisCallback; 951b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mEventLogger.onRequestProcessingStart(); 952b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (this) { 953b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // stop() might have been called before we enter this 954b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // synchronized block. 955b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (isStopped()) { 956014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return; 957b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 958b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mSynthesisCallback = createSynthesisCallback(); 959b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synthesisCallback = mSynthesisCallback; 960b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 961b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 962b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch TextToSpeechService.this.onSynthesizeText(mSynthesisRequest, synthesisCallback); 963b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 964b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // Fix for case where client called .start() & .error(), but did not called .done() 965b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (synthesisCallback.hasStarted() && !synthesisCallback.hasFinished()) { 966b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synthesisCallback.done(); 967b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 968b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 969b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 970014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch protected AbstractSynthesisCallback createSynthesisCallback() { 971b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return new PlaybackSynthesisCallback(getAudioParams(), 972b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioPlaybackHandler, this, getCallerIdentity(), mEventLogger, false); 973b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 974b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 975b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private void setRequestParams(SynthesisRequest request) { 976b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String voiceName = getVoiceName(); 977b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch request.setLanguage(getLanguage(), getCountry(), getVariant()); 978b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!TextUtils.isEmpty(voiceName)) { 979b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch request.setVoiceName(getVoiceName()); 980b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 981b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch request.setSpeechRate(getSpeechRate()); 982b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch request.setCallerUid(mCallerUid); 983014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch request.setPitch(getPitch()); 984b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 985b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 986b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 987b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void stopImpl() { 988b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AbstractSynthesisCallback synthesisCallback; 989b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (this) { 990b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synthesisCallback = mSynthesisCallback; 991b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 992b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (synthesisCallback != null) { 993b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // If the synthesis callback is null, it implies that we haven't 994b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // entered the synchronized(this) block in playImpl which in 995b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // turn implies that synthesis would not have started. 996014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch synthesisCallback.stop(); 997b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch TextToSpeechService.this.onStop(); 998b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 999b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch dispatchOnStop(); 1000014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1001b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1002b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1003b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private String getCountry() { 1004b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!hasLanguage()) return mDefaultLocale[1]; 1005b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getStringParam(mParams, Engine.KEY_PARAM_COUNTRY, ""); 1006b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1007b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1008b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private String getVariant() { 1009b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!hasLanguage()) return mDefaultLocale[2]; 1010b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getStringParam(mParams, Engine.KEY_PARAM_VARIANT, ""); 1011014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1012b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1013b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String getLanguage() { 1014b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getStringParam(mParams, Engine.KEY_PARAM_LANGUAGE, mDefaultLocale[0]); 1015014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1016b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1017b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String getVoiceName() { 1018b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getStringParam(mParams, Engine.KEY_PARAM_VOICE_NAME, ""); 1019b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1020b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1021b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1022b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private class SynthesisToFileOutputStreamSpeechItemV1 extends SynthesisSpeechItemV1 { 1023b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final FileOutputStream mFileOutputStream; 1024b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1025b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public SynthesisToFileOutputStreamSpeechItemV1(Object callerIdentity, int callerUid, 1026b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int callerPid, Bundle params, String utteranceId, CharSequence text, 1027b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch FileOutputStream fileOutputStream) { 1028b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super(callerIdentity, callerUid, callerPid, params, utteranceId, text); 1029014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch mFileOutputStream = fileOutputStream; 1030b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1031b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1032b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1033014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch protected AbstractSynthesisCallback createSynthesisCallback() { 1034b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return new FileSynthesisCallback(mFileOutputStream.getChannel(), 1035b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch this, getCallerIdentity(), false); 1036b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1037b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1038b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1039b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void playImpl() { 1040b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch dispatchOnStart(); 1041014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch super.playImpl(); 1042b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch try { 1043b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mFileOutputStream.close(); 1044b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } catch(IOException e) { 1045b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.w(TAG, "Failed to close output file", e); 1046b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1047b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1048b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1049014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1050b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private class AudioSpeechItemV1 extends SpeechItemV1 { 1051b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final AudioPlaybackQueueItem mItem; 1052b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1053b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public AudioSpeechItemV1(Object callerIdentity, int callerUid, int callerPid, 1054b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Bundle params, String utteranceId, Uri uri) { 1055014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch super(callerIdentity, callerUid, callerPid, params, utteranceId); 1056b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(), 1057b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch TextToSpeechService.this, uri, getAudioParams()); 1058b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1059b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1060b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1061b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean isValid() { 1062b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return true; 1063b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1064b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1065b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1066b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void playImpl() { 1067b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioPlaybackHandler.enqueue(mItem); 1068b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1069014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1070b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1071b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void stopImpl() { 1072014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch // Do nothing. 1073b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1074b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1075b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1076b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String getUtteranceId() { 1077b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getStringParam(mParams, Engine.KEY_PARAM_UTTERANCE_ID, null); 1078b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1079b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1080b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1081b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch AudioOutputParams getAudioParams() { 1082b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return AudioOutputParams.createFromV1ParamsBundle(mParams, false); 1083b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1084b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1085b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1086b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private class SilenceSpeechItem extends UtteranceSpeechItem { 1087b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final long mDuration; 1088014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch private final String mUtteranceId; 1089b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1090b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public SilenceSpeechItem(Object callerIdentity, int callerUid, int callerPid, 1091b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String utteranceId, long duration) { 1092014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch super(callerIdentity, callerUid, callerPid); 1093b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mUtteranceId = utteranceId; 1094b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mDuration = duration; 1095b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1096b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1097b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1098b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean isValid() { 1099b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return true; 1100b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1101b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1102b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1103b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void playImpl() { 1104b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mAudioPlaybackHandler.enqueue(new SilencePlaybackQueueItem( 1105b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch this, getCallerIdentity(), mDuration)); 1106b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1107b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1108014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch @Override 1109b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void stopImpl() { 1110014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1111014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1112014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1113014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch @Override 1114014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public String getUtteranceId() { 1115b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mUtteranceId; 1116b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1117014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1118014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1119014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch /** 1120b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Call {@link TextToSpeechService#onLoadLanguage} on synth thread. 1121014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch */ 1122014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch private class LoadLanguageItem extends SpeechItem { 1123014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch private final String mLanguage; 1124b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final String mCountry; 1125b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final String mVariant; 1126b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1127014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public LoadLanguageItem(Object callerIdentity, int callerUid, int callerPid, 1128b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String language, String country, String variant) { 1129b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super(callerIdentity, callerUid, callerPid); 1130b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mLanguage = language; 1131b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCountry = country; 1132b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mVariant = variant; 1133b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1134b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1135b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1136b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean isValid() { 1137b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return true; 1138b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1139b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1140b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1141b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void playImpl() { 1142014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch TextToSpeechService.this.onLoadLanguage(mLanguage, mCountry, mVariant); 1143b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1144b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1145b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1146b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void stopImpl() { 1147b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // No-op 1148b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1149b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1150b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1151b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 1152b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Call {@link TextToSpeechService#onLoadLanguage} on synth thread. 1153b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 1154b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private class LoadVoiceItem extends SpeechItem { 1155b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final String mVoiceName; 1156b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1157b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public LoadVoiceItem(Object callerIdentity, int callerUid, int callerPid, 1158b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String voiceName) { 1159014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch super(callerIdentity, callerUid, callerPid); 1160b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mVoiceName = voiceName; 1161b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1162b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1163b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1164b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean isValid() { 1165b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return true; 1166b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1167b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1168b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1169b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void playImpl() { 1170b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch TextToSpeechService.this.onLoadVoice(mVoiceName); 1171b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1172b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1173b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1174b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch protected void stopImpl() { 1175b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // No-op 1176b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1177b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1178b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1179b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1180b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1181014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public IBinder onBind(Intent intent) { 1182b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) { 1183b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mBinder; 1184b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1185b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return null; 1186b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1187b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1188b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /** 1189b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * Binder returned from {@code #onBind(Intent)}. The methods in this class can be 1190b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * called called from several different threads. 1191b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 1192b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // NOTE: All calls that are passed in a calling app are interned so that 1193b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // they can be used as message objects (which are tested for equality using ==). 1194b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() { 1195b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1196014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public int speak(IBinder caller, CharSequence text, int queueMode, Bundle params, 1197b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String utteranceId) { 1198b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(caller, text, params)) { 1199b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1200b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1201b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1202b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem item = new SynthesisSpeechItemV1(caller, 1203b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text); 1204b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mSynthHandler.enqueueSpeechItem(queueMode, item); 1205014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1206b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1207b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1208b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int synthesizeToFileDescriptor(IBinder caller, CharSequence text, ParcelFileDescriptor 1209b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch fileDescriptor, Bundle params, String utteranceId) { 1210b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(caller, text, fileDescriptor, params)) { 1211b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1212b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1213b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1214014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch // In test env, ParcelFileDescriptor instance may be EXACTLY the same 1215b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // one that is used by client. And it will be closed by a client, thus 1216b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // preventing us from writing anything to it. 1217b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch final ParcelFileDescriptor sameFileDescriptor = ParcelFileDescriptor.adoptFd( 1218b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch fileDescriptor.detachFd()); 1219014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1220b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem item = new SynthesisToFileOutputStreamSpeechItemV1(caller, 1221b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, text, 1222b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor)); 1223014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item); 1224b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1225b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1226b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1227b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params, 1228b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String utteranceId) { 1229b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(caller, audioUri, params)) { 1230b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1231b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1232b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1233b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem item = new AudioSpeechItemV1(caller, 1234014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch Binder.getCallingUid(), Binder.getCallingPid(), params, utteranceId, audioUri); 1235b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mSynthHandler.enqueueSpeechItem(queueMode, item); 1236b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1237b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1238b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1239b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int playSilence(IBinder caller, long duration, int queueMode, String utteranceId) { 1240b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(caller)) { 1241b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1242b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1243b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1244b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem item = new SilenceSpeechItem(caller, 1245014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch Binder.getCallingUid(), Binder.getCallingPid(), utteranceId, duration); 1246b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mSynthHandler.enqueueSpeechItem(queueMode, item); 1247b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1248b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1249b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1250b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public boolean isSpeaking() { 1251b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking(); 1252b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1253b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1254b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1255b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int stop(IBinder caller) { 1256b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(caller)) { 1257b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1258014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1259b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1260b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return mSynthHandler.stopForApp(caller); 1261b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1262014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1263b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1264b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String[] getLanguage() { 1265b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return onGetLanguage(); 1266b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1267b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1268b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1269b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String[] getClientDefaultLanguage() { 1270b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return getSettingsLocale(); 1271b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1272b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1273b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /* 1274b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * If defaults are enforced, then no language is "available" except 1275b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * perhaps the default language selected by the user. 1276b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 1277014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch @Override 1278b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int isLanguageAvailable(String lang, String country, String variant) { 1279b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(lang)) { 1280b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1281b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1282b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1283b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return onIsLanguageAvailable(lang, country, variant); 1284b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1285b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1286b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1287b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String[] getFeaturesForLanguage(String lang, String country, String variant) { 1288b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Set<String> features = onGetFeaturesForLanguage(lang, country, variant); 1289b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch String[] featuresArray = null; 1290b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (features != null) { 1291b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch featuresArray = new String[features.size()]; 1292014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch features.toArray(featuresArray); 1293b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 1294b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch featuresArray = new String[0]; 1295b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1296b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return featuresArray; 1297b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1298b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1299b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch /* 1300b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * There is no point loading a non default language if defaults 1301b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch * are enforced. 1302b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch */ 1303b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1304b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int loadLanguage(IBinder caller, String lang, String country, String variant) { 1305b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(lang)) { 1306b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1307b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1308b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int retVal = onIsLanguageAvailable(lang, country, variant); 1309b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1310b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (retVal == TextToSpeech.LANG_AVAILABLE || 1311b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE || 1312b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) { 1313014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1314b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem item = new LoadLanguageItem(caller, Binder.getCallingUid(), 1315b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Binder.getCallingPid(), lang, country, variant); 1316b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1317b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) != 1318b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch TextToSpeech.SUCCESS) { 1319b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1320b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1321b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1322b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return retVal; 1323b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1324b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1325b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1326b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public List<Voice> getVoices() { 1327b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return onGetVoices(); 1328b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1329b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1330b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1331b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public int loadVoice(IBinder caller, String voiceName) { 1332b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(voiceName)) { 1333014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return TextToSpeech.ERROR; 1334b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1335b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int retVal = onIsValidVoiceName(voiceName); 1336b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1337b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (retVal == TextToSpeech.SUCCESS) { 1338b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch SpeechItem item = new LoadVoiceItem(caller, Binder.getCallingUid(), 1339b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Binder.getCallingPid(), voiceName); 1340b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) != 1341b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch TextToSpeech.SUCCESS) { 1342b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return TextToSpeech.ERROR; 1343b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1344b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1345b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return retVal; 1346b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1347b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1348b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public String getDefaultVoiceNameFor(String lang, String country, String variant) { 1349b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(lang)) { 1350014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch return null; 1351b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1352b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int retVal = onIsLanguageAvailable(lang, country, variant); 1353b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1354b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (retVal == TextToSpeech.LANG_AVAILABLE || 1355b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE || 1356b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) { 1357b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return onGetDefaultVoiceNameFor(lang, country, variant); 1358b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 1359b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return null; 1360b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1361b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1362b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1363b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1364b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void setCallback(IBinder caller, ITextToSpeechCallback cb) { 1365014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch // Note that passing in a null callback is a valid use case. 1366b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (!checkNonNull(caller)) { 1367b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return; 1368b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1369b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1370b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallbacks.setCallback(caller, cb); 1371b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1372b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1373b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private String intern(String in) { 1374b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch // The input parameter will be non null. 1375b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return in.intern(); 1376b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1377b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1378014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch private boolean checkNonNull(Object... args) { 1379014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch for (Object o : args) { 1380014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (o == null) return false; 1381b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1382b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return true; 1383b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1384b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch }; 1385b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1386b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> { 1387014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch private final HashMap<IBinder, ITextToSpeechCallback> mCallerToCallback 1388b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch = new HashMap<IBinder, ITextToSpeechCallback>(); 1389b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1390b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void setCallback(IBinder caller, ITextToSpeechCallback cb) { 1391b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (mCallerToCallback) { 1392b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch ITextToSpeechCallback old; 1393b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (cb != null) { 1394b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch register(cb, caller); 1395b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch old = mCallerToCallback.put(caller, cb); 1396b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } else { 1397b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch old = mCallerToCallback.remove(caller); 1398b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1399b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (old != null && old != cb) { 1400b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch unregister(old); 1401b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1402014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1403b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1404014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch 1405014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public void dispatchOnStop(Object callerIdentity, String utteranceId, boolean started) { 1406014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 1407b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (cb == null) return; 1408014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch try { 1409014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch cb.onStop(utteranceId, started); 1410b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } catch (RemoteException e) { 1411b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.e(TAG, "Callback onStop failed: " + e); 1412b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1413b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1414b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1415014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch public void dispatchOnSuccess(Object callerIdentity, String utteranceId) { 1416b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 1417b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (cb == null) return; 1418b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch try { 1419b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch cb.onSuccess(utteranceId); 1420b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } catch (RemoteException e) { 1421b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.e(TAG, "Callback onDone failed: " + e); 1422b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1423b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1424b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1425b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnStart(Object callerIdentity, String utteranceId) { 1426b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 1427b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch if (cb == null) return; 1428b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch try { 1429b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch cb.onStart(utteranceId); 1430b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } catch (RemoteException e) { 1431b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.e(TAG, "Callback onStart failed: " + e); 1432b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1433b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1434b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1435b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1436b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void dispatchOnError(Object callerIdentity, String utteranceId, 1437b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch int errorCode) { 1438b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 1439014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch if (cb == null) return; 1440b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch try { 1441b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch cb.onError(utteranceId, errorCode); 1442b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } catch (RemoteException e) { 1443b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch Log.e(TAG, "Callback onError failed: " + e); 1444b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1445b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1446b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1447b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1448b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) { 1449b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch IBinder caller = (IBinder) cookie; 1450b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (mCallerToCallback) { 1451b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallerToCallback.remove(caller); 1452014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch } 1453b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch //mSynthHandler.stopForApp(caller); 1454b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1455b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1456b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch @Override 1457b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch public void kill() { 1458b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch synchronized (mCallerToCallback) { 1459b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch mCallerToCallback.clear(); 1460b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch super.kill(); 1461b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1462b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1463b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1464b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch private ITextToSpeechCallback getCallbackFor(Object caller) { 1465b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch ITextToSpeechCallback cb; 1466b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch IBinder asBinder = (IBinder) caller; 1467014dc512cdd3e367bee49a713fdc5ed92584a3e5Ben Murdoch synchronized (mCallerToCallback) { 1468b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch cb = mCallerToCallback.get(asBinder); 1469b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1470b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch 1471b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch return cb; 1472b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1473b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch } 1474b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch} 1475b8a8cc1952d61a2f3a2568848933943a543b5d3eBen Murdoch