150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/* 250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Copyright (C) 2011 The Android Open Source Project 350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); you may not 550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * use this file except in compliance with the License. You may obtain a copy of 650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the License at 750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0 950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 1050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Unless required by applicable law or agreed to in writing, software 1150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 1250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 1350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * License for the specific language governing permissions and limitations under 1450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the License. 1550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 1650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertpackage android.speech.tts; 1750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 1850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.app.Service; 1950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.content.Intent; 2050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.net.Uri; 21492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamathimport android.os.Binder; 2250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.Bundle; 2350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.Handler; 2450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.HandlerThread; 2550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.IBinder; 2650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.Looper; 2750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.Message; 2850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.MessageQueue; 295acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniakimport android.os.ParcelFileDescriptor; 3050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.RemoteCallbackList; 3150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.RemoteException; 3250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.provider.Settings; 3350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.speech.tts.TextToSpeech.Engine; 3450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.text.TextUtils; 3550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.util.Log; 3650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 375acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniakimport java.io.FileDescriptor; 385acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniakimport java.io.FileOutputStream; 39fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniakimport java.io.IOException; 4050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.util.HashMap; 4150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.util.Locale; 42748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamathimport java.util.Set; 4350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 4450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 4550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/** 46e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * Abstract base class for TTS engine implementations. The following methods 47e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * need to be implemented. 48e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 49e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <ul> 50e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onIsLanguageAvailable}</li> 51e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onLoadLanguage}</li> 52e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onGetLanguage}</li> 53e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onSynthesizeText}</li> 54e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onStop}</li> 55e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * </ul> 56e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 57e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * The first three deal primarily with language management, and are used to 58e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * query the engine for it's support for a given language and indicate to it 59e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * that requests in a given language are imminent. 60e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 61e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * {@link #onSynthesizeText} is central to the engine implementation. The 62e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * implementation should synthesize text as per the request parameters and 63e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * return synthesized data via the supplied callback. This class and its helpers 64e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * will then consume that data, which might mean queueing it for playback or writing 65e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * it to a file or similar. All calls to this method will be on a single 66e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * thread, which will be different from the main thread of the service. Synthesis 67e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * must be synchronous which means the engine must NOT hold on the callback or call 68e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * any methods on it after the method returns 69e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 70e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * {@link #onStop} tells the engine that it should stop all ongoing synthesis, if 71e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * any. Any pending data from the current synthesis will be discarded. 72e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * 7350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 7450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertpublic abstract class TextToSpeechService extends Service { 7550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 7650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private static final boolean DBG = false; 7750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private static final String TAG = "TextToSpeechService"; 7850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 792d940bcbd1c472f8b11ce1495354f340604b4f2cPrzemyslaw Szczepaniak 8050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private static final String SYNTH_THREAD_NAME = "SynthThread"; 8150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 8250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private SynthHandler mSynthHandler; 838d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // A thread and it's associated handler for playing back any audio 848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // associated with this TTS engine. Will handle all requests except synthesis 858d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // to file requests, which occur on the synthesis thread. 868d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath private AudioPlaybackHandler mAudioPlaybackHandler; 87e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private TtsEngines mEngineHelper; 8850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 8950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private CallbackMap mCallbacks; 906dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private String mPackageName; 917a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath 9250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 9350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void onCreate() { 9450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (DBG) Log.d(TAG, "onCreate()"); 9550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super.onCreate(); 9650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 9750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert SynthThread synthThread = new SynthThread(); 9850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synthThread.start(); 9950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mSynthHandler = new SynthHandler(synthThread.getLooper()); 10050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 1014924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mAudioPlaybackHandler = new AudioPlaybackHandler(); 1024924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mAudioPlaybackHandler.start(); 103c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath 104e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath mEngineHelper = new TtsEngines(this); 105e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 10650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mCallbacks = new CallbackMap(); 10750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 1086dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mPackageName = getApplicationInfo().packageName; 1096dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 110e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] defaultLocale = getSettingsLocale(); 11150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Load default language 112e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath onLoadLanguage(defaultLocale[0], defaultLocale[1], defaultLocale[2]); 11350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 11450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 11550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 11650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void onDestroy() { 11750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (DBG) Log.d(TAG, "onDestroy()"); 11850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 11950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Tell the synthesizer to stop 12050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mSynthHandler.quit(); 1218d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Tell the audio playback thread to stop. 1228d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath mAudioPlaybackHandler.quit(); 12350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Unregister all callbacks. 12450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mCallbacks.kill(); 12550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 12650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super.onDestroy(); 12750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 12850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 12950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 13050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Checks whether the engine supports a given language. 13150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 13250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads. 13350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 13413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak * Its return values HAVE to be consistent with onLoadLanguage. 13513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak * 13650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param lang ISO-3 language code. 13750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param country ISO-3 country code. May be empty or null. 13850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param variant Language variant. May be empty or null. 13950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return Code indicating the support status for the locale. 14050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * One of {@link TextToSpeech#LANG_AVAILABLE}, 14150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE}, 14250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE}, 14350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_MISSING_DATA} 14450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_NOT_SUPPORTED}. 14550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 14650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract int onIsLanguageAvailable(String lang, String country, String variant); 14750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 14850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 14950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Returns the language, country and variant currently being used by the TTS engine. 15050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 15150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads. 15250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 15350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return A 3-element array, containing language (ISO 3-letter code), 15450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * country (ISO 3-letter code) and variant used by the engine. 15550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * The country and variant may be {@code ""}. If country is empty, then variant must 15650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * be empty too. 15750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @see Locale#getISO3Language() 15850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @see Locale#getISO3Country() 15950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @see Locale#getVariant() 16050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 16150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract String[] onGetLanguage(); 16250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 16350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 16450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Notifies the engine that it should load a speech synthesis language. There is no guarantee 16550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * that this method is always called before the language is used for synthesis. It is merely 16650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * a hint to the engine that it will probably get some synthesis requests for this language 16750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * at some point in the future. 16850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 16950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads. 17013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak * In <= Android 4.2 (<= API 17) can be called on main and service binder threads. 17113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak * In > Android 4.2 (> API 17) can be called on main and synthesis threads. 17250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 17350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param lang ISO-3 language code. 17450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param country ISO-3 country code. May be empty or null. 17550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param variant Language variant. May be empty or null. 17650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return Code indicating the support status for the locale. 17750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * One of {@link TextToSpeech#LANG_AVAILABLE}, 17850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE}, 17950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE}, 18050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_MISSING_DATA} 18150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_NOT_SUPPORTED}. 18250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 18350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract int onLoadLanguage(String lang, String country, String variant); 18450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 18550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 18650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Notifies the service that it should stop any in-progress speech synthesis. 18750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * This method can be called even if no speech synthesis is currently in progress. 18850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 18950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads, but not on the synthesis thread. 19050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 19150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract void onStop(); 19250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 19350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 19450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Tells the service to synthesize speech from the given text. This method should 19550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * block until the synthesis is finished. 19650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 19750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Called on the synthesis thread. 19850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 199e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * @param request The synthesis request. 200e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * @param callback The callback the the engine must use to make data available for 201e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * playback or for writing to a file. 20250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 203e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath protected abstract void onSynthesizeText(SynthesisRequest request, 204e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath SynthesisCallback callback); 20550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 206748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath /** 207748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * Queries the service for a set of features supported for a given language. 208748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * 209748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @param lang ISO-3 language code. 210748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @param country ISO-3 country code. May be empty or null. 211748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @param variant Language variant. May be empty or null. 212748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @return A list of features supported for the given language. 213748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath */ 214748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath protected Set<String> onGetFeaturesForLanguage(String lang, String country, String variant) { 215748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath return null; 216748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath } 217748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath 21850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getDefaultSpeechRate() { 21950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE); 22050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 22150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 222e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private String[] getSettingsLocale() { 223e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String locale = mEngineHelper.getLocalePrefForEngine(mPackageName); 224e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return TtsEngines.parseLocalePref(locale); 22550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 22650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 22750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getSecureSettingInt(String name, int defaultValue) { 22850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return Settings.Secure.getInt(getContentResolver(), name, defaultValue); 22950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 23050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 23150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 23250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Synthesizer thread. This thread is used to run {@link SynthHandler}. 23350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 23450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class SynthThread extends HandlerThread implements MessageQueue.IdleHandler { 23550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 23650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean mFirstIdle = true; 23750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 23850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public SynthThread() { 23984deb60cf1055385b9922811e5942076dd72e315Narayan Kamath super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_DEFAULT); 24050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 24150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 24250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 24350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void onLooperPrepared() { 24450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert getLooper().getQueue().addIdleHandler(this); 24550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 24650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 24750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 24850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean queueIdle() { 24950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (mFirstIdle) { 25050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mFirstIdle = false; 25150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } else { 25250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert broadcastTtsQueueProcessingCompleted(); 25350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 25450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 25550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 25650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 25750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private void broadcastTtsQueueProcessingCompleted() { 25850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED); 25950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (DBG) Log.d(TAG, "Broadcasting: " + i); 26050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert sendBroadcast(i); 26150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 26250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 26350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 26450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class SynthHandler extends Handler { 26550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private SpeechItem mCurrentSpeechItem = null; 26650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 26750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public SynthHandler(Looper looper) { 26850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super(looper); 26950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 27050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 27150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private synchronized SpeechItem getCurrentSpeechItem() { 27250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mCurrentSpeechItem; 27350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 27450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 27550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) { 27650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert SpeechItem old = mCurrentSpeechItem; 27750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mCurrentSpeechItem = speechItem; 27850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return old; 27950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 28050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 281492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private synchronized SpeechItem maybeRemoveCurrentSpeechItem(Object callerIdentity) { 282a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (mCurrentSpeechItem != null && 28313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak (mCurrentSpeechItem.getCallerIdentity() == callerIdentity)) { 284a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath SpeechItem current = mCurrentSpeechItem; 285a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath mCurrentSpeechItem = null; 286a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return current; 287a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 288a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 289a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return null; 290a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 291a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 29250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isSpeaking() { 29350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getCurrentSpeechItem() != null; 29450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 29550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 29650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void quit() { 29750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Don't process any more speech items 29850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert getLooper().quit(); 29950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Stop the current speech item 30050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert SpeechItem current = setCurrentSpeechItem(null); 30150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (current != null) { 30250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert current.stop(); 30350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 304be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // The AudioPlaybackHandler will be destroyed by the caller. 30550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 30650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 30750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 30850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Adds a speech item to the queue. 30950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 31050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Called on a service binder thread. 31150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 31250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) { 31313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak UtteranceProgressDispatcher utterenceProgress = null; 31413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak if (speechItem instanceof UtteranceProgressDispatcher) { 31513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak utterenceProgress = (UtteranceProgressDispatcher) speechItem; 31613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 31713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 31850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (!speechItem.isValid()) { 31913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak if (utterenceProgress != null) { 32013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak utterenceProgress.dispatchOnError(); 32113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 32250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.ERROR; 32350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 3244924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 32550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (queueMode == TextToSpeech.QUEUE_FLUSH) { 326492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath stopForApp(speechItem.getCallerIdentity()); 327abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } else if (queueMode == TextToSpeech.QUEUE_DESTROY) { 328a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath stopAll(); 32950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 33050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Runnable runnable = new Runnable() { 33150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 33250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void run() { 33350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert setCurrentSpeechItem(speechItem); 3348d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath speechItem.play(); 33550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert setCurrentSpeechItem(null); 33650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 33750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert }; 33850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Message msg = Message.obtain(this, runnable); 33913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 340a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // The obj is used to remove all callbacks from the given app in 341a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // stopForApp(String). 342abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // 343abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // Note that this string is interned, so the == comparison works. 344492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath msg.obj = speechItem.getCallerIdentity(); 34550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (sendMessage(msg)) { 34650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.SUCCESS; 34750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } else { 34850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.w(TAG, "SynthThread has quit"); 34913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak if (utterenceProgress != null) { 35013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak utterenceProgress.dispatchOnError(); 35113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 35250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.ERROR; 35350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 35450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 35550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 35650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 35750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Stops all speech output and removes any utterances still in the queue for 35850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the calling app. 35950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 36050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Called on a service binder thread. 36150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 362492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int stopForApp(Object callerIdentity) { 363492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (callerIdentity == null) { 36450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.ERROR; 36550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 3664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 367492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath removeCallbacksAndMessages(callerIdentity); 368be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // This stops writing data to the file / or publishing 369be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // items to the audio playback handler. 370a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // 371a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // Note that the current speech item must be removed only if it 372a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // belongs to the callingApp, else the item will be "orphaned" and 373a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // not stopped correctly if a stop request comes along for the item 374a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // from the app it belongs to. 375492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem current = maybeRemoveCurrentSpeechItem(callerIdentity); 376a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (current != null) { 37750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert current.stop(); 37850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 3798d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3804924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // Remove any enqueued audio too. 38167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mAudioPlaybackHandler.stopForApp(callerIdentity); 3824924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 38350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.SUCCESS; 38450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 385a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 386a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath public int stopAll() { 38713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak // Stop the current speech item unconditionally . 388a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath SpeechItem current = setCurrentSpeechItem(null); 389a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (current != null) { 390a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath current.stop(); 391a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 392a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // Remove all other items from the queue. 393a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath removeCallbacksAndMessages(null); 394a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // Remove all pending playback as well. 39567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mAudioPlaybackHandler.stop(); 396a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 397a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return TextToSpeech.SUCCESS; 398a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 39950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 40050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 401754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath interface UtteranceProgressDispatcher { 402754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnDone(); 403754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnStart(); 404754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnError(); 4058d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 4068d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 40750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 40850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * An item in the synth thread queue. 40950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 41013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private abstract class SpeechItem { 411492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final Object mCallerIdentity; 412b956f37e375bb2588208d4b5e8a40fae6fae5f86Narayan Kamath protected final Bundle mParams; 413492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final int mCallerUid; 414492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final int mCallerPid; 41550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean mStarted = false; 41650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean mStopped = false; 41750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 418492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public SpeechItem(Object caller, int callerUid, int callerPid, Bundle params) { 419492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerIdentity = caller; 42050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mParams = params; 421492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerUid = callerUid; 422492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerPid = callerPid; 42350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 42450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 425492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public Object getCallerIdentity() { 426492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath return mCallerIdentity; 42750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 42850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 42913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 43013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak public int getCallerUid() { 43113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return mCallerUid; 43213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 43313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 43413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak public int getCallerPid() { 43513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return mCallerPid; 43613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 43713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 43850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 43950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Checker whether the item is valid. If this method returns false, the item should not 44050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * be played. 44150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 44250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public abstract boolean isValid(); 44350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 44450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 44550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Plays the speech item. Blocks until playback is finished. 44650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Must not be called more than once. 44750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 44850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Only called on the synthesis thread. 44950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 45050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. 45150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 45250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int play() { 45350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 45450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (mStarted) { 45550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert throw new IllegalStateException("play() called twice"); 45650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 45750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mStarted = true; 45850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 45950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return playImpl(); 46050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 46150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 46213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak protected abstract int playImpl(); 46313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 46450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 46550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Stops the speech item. 46650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Must not be called more than once. 46750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 46850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads, but not on the synthesis thread. 46950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 47050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void stop() { 47150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 47250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (mStopped) { 47350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert throw new IllegalStateException("stop() called twice"); 47450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 47550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mStopped = true; 47650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 47750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert stopImpl(); 47850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 47950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 48013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak protected abstract void stopImpl(); 48113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 48213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak protected synchronized boolean isStopped() { 48313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return mStopped; 48413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 48513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 48613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 48713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak /** 48813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak * An item in the synth thread queue that process utterance. 48913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak */ 49013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private abstract class UtteranceSpeechItem extends SpeechItem 49113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak implements UtteranceProgressDispatcher { 49213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 49313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak public UtteranceSpeechItem(Object caller, int callerUid, int callerPid, Bundle params) { 49413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak super(caller, callerUid, callerPid, params); 49513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 49613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 497754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath @Override 498754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnDone() { 499754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath final String utteranceId = getUtteranceId(); 50068e2af55d65d2e61fbf8096eccaa2e4ca02b6c5aNarayan Kamath if (utteranceId != null) { 501492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.dispatchOnDone(getCallerIdentity(), utteranceId); 502754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 503754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 504754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 505754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath @Override 506754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnStart() { 507754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath final String utteranceId = getUtteranceId(); 50868e2af55d65d2e61fbf8096eccaa2e4ca02b6c5aNarayan Kamath if (utteranceId != null) { 509492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.dispatchOnStart(getCallerIdentity(), utteranceId); 510754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 511754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 512754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 513754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath @Override 514754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnError() { 5158d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath final String utteranceId = getUtteranceId(); 51668e2af55d65d2e61fbf8096eccaa2e4ca02b6c5aNarayan Kamath if (utteranceId != null) { 517492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.dispatchOnError(getCallerIdentity(), utteranceId); 5188d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 5198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 5208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 52150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int getStreamType() { 52250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM); 52350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 52450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 52550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public float getVolume() { 52650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getFloatParam(Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME); 52750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 52850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 52950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public float getPan() { 53050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getFloatParam(Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN); 53150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 53250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 53350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String getUtteranceId() { 53450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getStringParam(Engine.KEY_PARAM_UTTERANCE_ID, null); 53550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 53650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 53750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected String getStringParam(String key, String defaultValue) { 53850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mParams == null ? defaultValue : mParams.getString(key, defaultValue); 53950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 54050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 54150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int getIntParam(String key, int defaultValue) { 54250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mParams == null ? defaultValue : mParams.getInt(key, defaultValue); 54350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 54450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 54550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected float getFloatParam(String key, float defaultValue) { 54650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue); 54750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 54813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 54950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 55050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 55113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak class SynthesisSpeechItem extends UtteranceSpeechItem { 55240f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath // Never null. 55350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private final String mText; 554e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath private final SynthesisRequest mSynthesisRequest; 555e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private final String[] mDefaultLocale; 556e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath // Non null after synthesis has started, and all accesses 557e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath // guarded by 'this'. 558e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath private AbstractSynthesisCallback mSynthesisCallback; 5596dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private final EventLogger mEventLogger; 560653278341d76d0b23a008087ff94250ae0beb54bPrzemyslaw Szczepaniak private final int mCallerUid; 56150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 562492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public SynthesisSpeechItem(Object callerIdentity, int callerUid, int callerPid, 563492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Bundle params, String text) { 564492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params); 56550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mText = text; 566653278341d76d0b23a008087ff94250ae0beb54bPrzemyslaw Szczepaniak mCallerUid = callerUid; 567e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath mSynthesisRequest = new SynthesisRequest(mText, mParams); 568e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath mDefaultLocale = getSettingsLocale(); 569e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath setRequestParams(mSynthesisRequest); 570492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mEventLogger = new EventLogger(mSynthesisRequest, callerUid, callerPid, 571492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mPackageName); 57250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 57350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 57450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String getText() { 57550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mText; 57650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 57750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 57850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 57950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isValid() { 58040f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath if (mText == null) { 5819c3d7a888d0c5c09f0153e81018ff68aa6e91712Narayan Kamath Log.e(TAG, "null synthesis text"); 58250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 58350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 5842d940bcbd1c472f8b11ce1495354f340604b4f2cPrzemyslaw Szczepaniak if (mText.length() >= TextToSpeech.getMaxSpeechInputLength()) { 58550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.w(TAG, "Text too long: " + mText.length() + " chars"); 58650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 58750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 58850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 58950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 59050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 59150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 59250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int playImpl() { 593e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath AbstractSynthesisCallback synthesisCallback; 5946dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mEventLogger.onRequestProcessingStart(); 59550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 596a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // stop() might have been called before we enter this 597a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // synchronized block. 598a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (isStopped()) { 599a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return TextToSpeech.ERROR; 600a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 601e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath mSynthesisCallback = createSynthesisCallback(); 602e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath synthesisCallback = mSynthesisCallback; 60350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 604e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath TextToSpeechService.this.onSynthesizeText(mSynthesisRequest, synthesisCallback); 605e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath return synthesisCallback.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR; 60650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 60750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 608e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath protected AbstractSynthesisCallback createSynthesisCallback() { 609e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(), 610492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mAudioPlaybackHandler, this, getCallerIdentity(), mEventLogger); 61150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 61250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 61350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private void setRequestParams(SynthesisRequest request) { 614c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath request.setLanguage(getLanguage(), getCountry(), getVariant()); 615c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath request.setSpeechRate(getSpeechRate()); 616653278341d76d0b23a008087ff94250ae0beb54bPrzemyslaw Szczepaniak request.setCallerUid(mCallerUid); 61750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert request.setPitch(getPitch()); 61850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 61950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 62050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 62150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void stopImpl() { 622e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath AbstractSynthesisCallback synthesisCallback; 62350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 624e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath synthesisCallback = mSynthesisCallback; 62550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 626a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (synthesisCallback != null) { 627a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // If the synthesis callback is null, it implies that we haven't 628a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // entered the synchronized(this) block in playImpl which in 629a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // turn implies that synthesis would not have started. 630a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath synthesisCallback.stop(); 631a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath TextToSpeechService.this.onStop(); 632a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 63350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 63450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 63550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String getLanguage() { 636e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return getStringParam(Engine.KEY_PARAM_LANGUAGE, mDefaultLocale[0]); 63750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 63850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 63950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean hasLanguage() { 64050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return !TextUtils.isEmpty(getStringParam(Engine.KEY_PARAM_LANGUAGE, null)); 64150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 64250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 64350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private String getCountry() { 644e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!hasLanguage()) return mDefaultLocale[1]; 64550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getStringParam(Engine.KEY_PARAM_COUNTRY, ""); 64650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 64750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 64850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private String getVariant() { 649e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!hasLanguage()) return mDefaultLocale[2]; 65050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getStringParam(Engine.KEY_PARAM_VARIANT, ""); 65150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 65250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 65350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getSpeechRate() { 65450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getIntParam(Engine.KEY_PARAM_RATE, getDefaultSpeechRate()); 65550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 65650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 65750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getPitch() { 65850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getIntParam(Engine.KEY_PARAM_PITCH, Engine.DEFAULT_PITCH); 65950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 66050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 66150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 662fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak private class SynthesisToFileOutputStreamSpeechItem extends SynthesisSpeechItem { 663fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak private final FileOutputStream mFileOutputStream; 66450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 6657341786b138cb52eac053108b524ea3296d40f6dPrzemyslaw Szczepaniak public SynthesisToFileOutputStreamSpeechItem(Object callerIdentity, int callerUid, 666fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak int callerPid, Bundle params, String text, FileOutputStream fileOutputStream) { 667492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params, text); 668fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak mFileOutputStream = fileOutputStream; 66950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 67050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 67150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 672e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath protected AbstractSynthesisCallback createSynthesisCallback() { 673fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak return new FileSynthesisCallback(mFileOutputStream.getChannel()); 67450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 67550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 6768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath @Override 6778d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath protected int playImpl() { 678754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath dispatchOnStart(); 6798d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath int status = super.playImpl(); 6808d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (status == TextToSpeech.SUCCESS) { 681754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath dispatchOnDone(); 682754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } else { 683754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath dispatchOnError(); 6848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 685fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak try { 686fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak mFileOutputStream.close(); 687fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak } catch(IOException e) { 688fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak Log.w(TAG, "Failed to close output file", e); 689fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak } 6908d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return status; 6918d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 69250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 69350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 69413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private class AudioSpeechItem extends UtteranceSpeechItem { 695af802c6831551323126537cf8edabea97d2fc762Narayan Kamath private final AudioPlaybackQueueItem mItem; 696492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public AudioSpeechItem(Object callerIdentity, int callerUid, int callerPid, 697492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Bundle params, Uri uri) { 698492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params); 699af802c6831551323126537cf8edabea97d2fc762Narayan Kamath mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(), 700af802c6831551323126537cf8edabea97d2fc762Narayan Kamath TextToSpeechService.this, uri, getStreamType()); 70150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 70250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 70350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 70450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isValid() { 70550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 70650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 70750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 70850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 70950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int playImpl() { 710af802c6831551323126537cf8edabea97d2fc762Narayan Kamath mAudioPlaybackHandler.enqueue(mItem); 7118d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return TextToSpeech.SUCCESS; 71250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 71350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 71450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 71550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void stopImpl() { 716be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // Do nothing. 71750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 71850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 71950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 72013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private class SilenceSpeechItem extends UtteranceSpeechItem { 72150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private final long mDuration; 72250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 723492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public SilenceSpeechItem(Object callerIdentity, int callerUid, int callerPid, 724492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Bundle params, long duration) { 725492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params); 72650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mDuration = duration; 72750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 72850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 72950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 73050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isValid() { 73150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 73250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 73350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 73450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 73550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int playImpl() { 73667ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mAudioPlaybackHandler.enqueue(new SilencePlaybackQueueItem( 73767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath this, getCallerIdentity(), mDuration)); 7388d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return TextToSpeech.SUCCESS; 73950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 74050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 74150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 74250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void stopImpl() { 74367ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // Do nothing, handled by AudioPlaybackHandler#stopForApp 74450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 74550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 74650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 74713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private class LoadLanguageItem extends SpeechItem { 74813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private final String mLanguage; 74913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private final String mCountry; 75013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak private final String mVariant; 75113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 75213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak public LoadLanguageItem(Object callerIdentity, int callerUid, int callerPid, 75313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak Bundle params, String language, String country, String variant) { 75413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak super(callerIdentity, callerUid, callerPid, params); 75513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak mLanguage = language; 75613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak mCountry = country; 75713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak mVariant = variant; 75813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 75913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 76013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak @Override 76113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak public boolean isValid() { 76213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return true; 76313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 76413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 76513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak @Override 76613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak protected int playImpl() { 76713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak int result = TextToSpeechService.this.onLoadLanguage(mLanguage, mCountry, mVariant); 76813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak if (result == TextToSpeech.LANG_AVAILABLE || 76913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak result == TextToSpeech.LANG_COUNTRY_AVAILABLE || 77013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak result == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) { 77113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return TextToSpeech.SUCCESS; 77213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 77313896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return TextToSpeech.ERROR; 77413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 77513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 77613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak @Override 77713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak protected void stopImpl() { 77813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak // No-op 77913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 78013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 78113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 78250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 78350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public IBinder onBind(Intent intent) { 78450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) { 78550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mBinder; 78650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 78750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return null; 78850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 78950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 79050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 79150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Binder returned from {@code #onBind(Intent)}. The methods in this class can be 79250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * called called from several different threads. 79350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 794abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // NOTE: All calls that are passed in a calling app are interned so that 795abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // they can be used as message objects (which are tested for equality using ==). 79650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() { 797492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 798492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int speak(IBinder caller, String text, int queueMode, Bundle params) { 799492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller, text, params)) { 800abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 801abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 802abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 803492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem item = new SynthesisSpeechItem(caller, 804492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Binder.getCallingUid(), Binder.getCallingPid(), params, text); 80550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(queueMode, item); 80650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 80750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 808492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 8095acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak public int synthesizeToFileDescriptor(IBinder caller, String text, ParcelFileDescriptor 8105acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak fileDescriptor, Bundle params) { 8115acb33af357b56fffb055997718b1e4aa97f53fcPrzemyslaw Szczepaniak if (!checkNonNull(caller, text, fileDescriptor, params)) { 812abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 813abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 814abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 81524943bf19d3d000f9f5840513526b48523b62c71Przemyslaw Szczepaniak // In test env, ParcelFileDescriptor instance may be EXACTLY the same 81624943bf19d3d000f9f5840513526b48523b62c71Przemyslaw Szczepaniak // one that is used by client. And it will be closed by a client, thus 81724943bf19d3d000f9f5840513526b48523b62c71Przemyslaw Szczepaniak // preventing us from writing anything to it. 81824943bf19d3d000f9f5840513526b48523b62c71Przemyslaw Szczepaniak final ParcelFileDescriptor sameFileDescriptor = ParcelFileDescriptor.adoptFd( 81924943bf19d3d000f9f5840513526b48523b62c71Przemyslaw Szczepaniak fileDescriptor.detachFd()); 82024943bf19d3d000f9f5840513526b48523b62c71Przemyslaw Szczepaniak 821fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak SpeechItem item = new SynthesisToFileOutputStreamSpeechItem(caller, 822fcf671be890f9fb49067c5f86b182bf8b8e7b9c0Przemyslaw Szczepaniak Binder.getCallingUid(), Binder.getCallingPid(), params, text, 82324943bf19d3d000f9f5840513526b48523b62c71Przemyslaw Szczepaniak new ParcelFileDescriptor.AutoCloseOutputStream(sameFileDescriptor)); 82450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item); 82550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 82650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 827492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 828492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params) { 829492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller, audioUri, params)) { 830abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 831abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 832abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 833492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem item = new AudioSpeechItem(caller, 834492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Binder.getCallingUid(), Binder.getCallingPid(), params, audioUri); 83550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(queueMode, item); 83650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 83750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 838492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 839492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int playSilence(IBinder caller, long duration, int queueMode, Bundle params) { 840492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller, params)) { 841abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 842abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 843abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 844492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem item = new SilenceSpeechItem(caller, 845492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Binder.getCallingUid(), Binder.getCallingPid(), params, duration); 84650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(queueMode, item); 84750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 84850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 849492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 85050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isSpeaking() { 851c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking(); 85250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 85350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 854492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 855492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int stop(IBinder caller) { 856492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller)) { 857abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 858abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 859abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 860492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath return mSynthHandler.stopForApp(caller); 86150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 86250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 863492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 86450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String[] getLanguage() { 86550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return onGetLanguage(); 86650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 86750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 868b46533732c40c6aa4d0d7357176835a33d863234Przemyslaw Szczepaniak @Override 869b46533732c40c6aa4d0d7357176835a33d863234Przemyslaw Szczepaniak public String[] getClientDefaultLanguage() { 870b46533732c40c6aa4d0d7357176835a33d863234Przemyslaw Szczepaniak return getSettingsLocale(); 871b46533732c40c6aa4d0d7357176835a33d863234Przemyslaw Szczepaniak } 872b46533732c40c6aa4d0d7357176835a33d863234Przemyslaw Szczepaniak 8737a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath /* 8747a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * If defaults are enforced, then no language is "available" except 8757a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * perhaps the default language selected by the user. 8767a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath */ 877492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 87850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int isLanguageAvailable(String lang, String country, String variant) { 879abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath if (!checkNonNull(lang)) { 880abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 881abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 882abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 88350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return onIsLanguageAvailable(lang, country, variant); 88450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 88550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 886492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 887748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath public String[] getFeaturesForLanguage(String lang, String country, String variant) { 888748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath Set<String> features = onGetFeaturesForLanguage(lang, country, variant); 8896c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath String[] featuresArray = null; 8906c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath if (features != null) { 8916c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath featuresArray = new String[features.size()]; 8926c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath features.toArray(featuresArray); 8936c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath } else { 8946c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath featuresArray = new String[0]; 8956c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath } 896748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath return featuresArray; 897748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath } 898748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath 8997a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath /* 9007a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * There is no point loading a non default language if defaults 9017a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * are enforced. 9027a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath */ 903492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 90413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak public int loadLanguage(IBinder caller, String lang, String country, String variant) { 905abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath if (!checkNonNull(lang)) { 906abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 907abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 90813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak int retVal = onIsLanguageAvailable(lang, country, variant); 90913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 91013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak if (retVal == TextToSpeech.LANG_AVAILABLE || 91113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak retVal == TextToSpeech.LANG_COUNTRY_AVAILABLE || 91213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak retVal == TextToSpeech.LANG_COUNTRY_VAR_AVAILABLE) { 913abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 91413896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak SpeechItem item = new LoadLanguageItem(caller, Binder.getCallingUid(), 91513896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak Binder.getCallingPid(), null, lang, country, variant); 91613896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak 91713896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak if (mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item) != 91813896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak TextToSpeech.SUCCESS) { 91913896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return TextToSpeech.ERROR; 92013896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 92113896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak } 92213896b74194b07c821d5d89713e4e747b9b77d73Przemyslaw Szczepaniak return retVal; 92350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 92450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 925492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 926492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void setCallback(IBinder caller, ITextToSpeechCallback cb) { 927abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // Note that passing in a null callback is a valid use case. 928492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller)) { 929abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return; 930abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 931abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 932492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.setCallback(caller, cb); 93350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 9347a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath 935abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath private String intern(String in) { 936abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // The input parameter will be non null. 937abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return in.intern(); 938abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 939abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 940abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath private boolean checkNonNull(Object... args) { 941abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath for (Object o : args) { 942abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath if (o == null) return false; 943abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 944abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return true; 945abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 94650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert }; 94750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 94850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> { 949492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final HashMap<IBinder, ITextToSpeechCallback> mCallerToCallback 950492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath = new HashMap<IBinder, ITextToSpeechCallback>(); 95150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 952492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void setCallback(IBinder caller, ITextToSpeechCallback cb) { 953492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 95450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert ITextToSpeechCallback old; 95550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (cb != null) { 956492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath register(cb, caller); 957492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath old = mCallerToCallback.put(caller, cb); 95850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } else { 959492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath old = mCallerToCallback.remove(caller); 96050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 96150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (old != null && old != cb) { 96250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert unregister(old); 96350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 96450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 96550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 96650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 967492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void dispatchOnDone(Object callerIdentity, String utteranceId) { 968492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 969754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath if (cb == null) return; 970754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath try { 971754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath cb.onDone(utteranceId); 972754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } catch (RemoteException e) { 973754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath Log.e(TAG, "Callback onDone failed: " + e); 97450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 975754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 976754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 977492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void dispatchOnStart(Object callerIdentity, String utteranceId) { 978492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 979754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath if (cb == null) return; 980754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath try { 981754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath cb.onStart(utteranceId); 982754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } catch (RemoteException e) { 983754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath Log.e(TAG, "Callback onStart failed: " + e); 984754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 985754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 986754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 987754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 988492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void dispatchOnError(Object callerIdentity, String utteranceId) { 989492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 99050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (cb == null) return; 99150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert try { 992754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath cb.onError(utteranceId); 99350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } catch (RemoteException e) { 994754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath Log.e(TAG, "Callback onError failed: " + e); 99550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 99650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 99750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 99850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 99950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) { 1000492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath IBinder caller = (IBinder) cookie; 1001492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 1002492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerToCallback.remove(caller); 100350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 1004492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mSynthHandler.stopForApp(caller); 100550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 100650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 100750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 100850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void kill() { 1009492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 1010492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerToCallback.clear(); 101150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super.kill(); 101250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 101350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 101450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 1015492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private ITextToSpeechCallback getCallbackFor(Object caller) { 1016754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath ITextToSpeechCallback cb; 1017492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath IBinder asBinder = (IBinder) caller; 1018492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 1019492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath cb = mCallerToCallback.get(asBinder); 1020754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 1021754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 1022754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath return cb; 1023754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 1024754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 102550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 102650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 102750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert} 1028