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; 2950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.RemoteCallbackList; 3050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.os.RemoteException; 3150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.provider.Settings; 3250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.speech.tts.TextToSpeech.Engine; 3350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.text.TextUtils; 3450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport android.util.Log; 3550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 3650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.io.File; 3750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.io.IOException; 3850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.util.HashMap; 3950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertimport java.util.Locale; 40748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamathimport java.util.Set; 4150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 4250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 4350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert/** 44e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * Abstract base class for TTS engine implementations. The following methods 45e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * need to be implemented. 46e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 47e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <ul> 48e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onIsLanguageAvailable}</li> 49e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onLoadLanguage}</li> 50e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onGetLanguage}</li> 51e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onSynthesizeText}</li> 52e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * <li>{@link #onStop}</li> 53e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * </ul> 54e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 55e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * The first three deal primarily with language management, and are used to 56e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * query the engine for it's support for a given language and indicate to it 57e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * that requests in a given language are imminent. 58e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 59e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * {@link #onSynthesizeText} is central to the engine implementation. The 60e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * implementation should synthesize text as per the request parameters and 61e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * return synthesized data via the supplied callback. This class and its helpers 62e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * will then consume that data, which might mean queueing it for playback or writing 63e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * it to a file or similar. All calls to this method will be on a single 64e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * thread, which will be different from the main thread of the service. Synthesis 65e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * must be synchronous which means the engine must NOT hold on the callback or call 66e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * any methods on it after the method returns 67e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * 68e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * {@link #onStop} tells the engine that it should stop all ongoing synthesis, if 69e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * any. Any pending data from the current synthesis will be discarded. 70e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * 7150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 7250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringertpublic abstract class TextToSpeechService extends Service { 7350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 7450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private static final boolean DBG = false; 7550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private static final String TAG = "TextToSpeechService"; 7650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 7750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000; 7850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private static final String SYNTH_THREAD_NAME = "SynthThread"; 7950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 8050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private SynthHandler mSynthHandler; 818d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // A thread and it's associated handler for playing back any audio 828d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // associated with this TTS engine. Will handle all requests except synthesis 838d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // to file requests, which occur on the synthesis thread. 848d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath private AudioPlaybackHandler mAudioPlaybackHandler; 85e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private TtsEngines mEngineHelper; 8650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 8750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private CallbackMap mCallbacks; 886dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private String mPackageName; 897a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath 9050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 9150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void onCreate() { 9250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (DBG) Log.d(TAG, "onCreate()"); 9350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super.onCreate(); 9450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 9550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert SynthThread synthThread = new SynthThread(); 9650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synthThread.start(); 9750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mSynthHandler = new SynthHandler(synthThread.getLooper()); 9850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 994924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mAudioPlaybackHandler = new AudioPlaybackHandler(); 1004924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath mAudioPlaybackHandler.start(); 101c90f1c815dc06d5bb563474a340f7bb158fab2e2Narayan Kamath 102e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath mEngineHelper = new TtsEngines(this); 103e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 10450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mCallbacks = new CallbackMap(); 10550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 1066dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mPackageName = getApplicationInfo().packageName; 1076dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath 108e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] defaultLocale = getSettingsLocale(); 10950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Load default language 110e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath onLoadLanguage(defaultLocale[0], defaultLocale[1], defaultLocale[2]); 11150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 11250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 11350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 11450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void onDestroy() { 11550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (DBG) Log.d(TAG, "onDestroy()"); 11650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 11750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Tell the synthesizer to stop 11850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mSynthHandler.quit(); 1198d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath // Tell the audio playback thread to stop. 1208d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath mAudioPlaybackHandler.quit(); 12150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Unregister all callbacks. 12250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mCallbacks.kill(); 12350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 12450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super.onDestroy(); 12550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 12650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 12750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 12850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Checks whether the engine supports a given language. 12950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 13050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads. 13150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 13250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param lang ISO-3 language code. 13350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param country ISO-3 country code. May be empty or null. 13450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param variant Language variant. May be empty or null. 13550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return Code indicating the support status for the locale. 13650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * One of {@link TextToSpeech#LANG_AVAILABLE}, 13750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE}, 13850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE}, 13950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_MISSING_DATA} 14050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_NOT_SUPPORTED}. 14150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 14250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract int onIsLanguageAvailable(String lang, String country, String variant); 14350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 14450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 14550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Returns the language, country and variant currently being used by the TTS engine. 14650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 14750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads. 14850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 14950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return A 3-element array, containing language (ISO 3-letter code), 15050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * country (ISO 3-letter code) and variant used by the engine. 15150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * The country and variant may be {@code ""}. If country is empty, then variant must 15250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * be empty too. 15350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @see Locale#getISO3Language() 15450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @see Locale#getISO3Country() 15550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @see Locale#getVariant() 15650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 15750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract String[] onGetLanguage(); 15850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 15950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 16050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Notifies the engine that it should load a speech synthesis language. There is no guarantee 16150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * that this method is always called before the language is used for synthesis. It is merely 16250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * a hint to the engine that it will probably get some synthesis requests for this language 16350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * at some point in the future. 16450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 16550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads. 16650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 16750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param lang ISO-3 language code. 16850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param country ISO-3 country code. May be empty or null. 16950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @param variant Language variant. May be empty or null. 17050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return Code indicating the support status for the locale. 17150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * One of {@link TextToSpeech#LANG_AVAILABLE}, 17250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_AVAILABLE}, 17350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE}, 17450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_MISSING_DATA} 17550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * {@link TextToSpeech#LANG_NOT_SUPPORTED}. 17650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 17750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract int onLoadLanguage(String lang, String country, String variant); 17850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 17950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 18050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Notifies the service that it should stop any in-progress speech synthesis. 18150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * This method can be called even if no speech synthesis is currently in progress. 18250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 18350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads, but not on the synthesis thread. 18450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 18550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract void onStop(); 18650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 18750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 18850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Tells the service to synthesize speech from the given text. This method should 18950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * block until the synthesis is finished. 19050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 19150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Called on the synthesis thread. 19250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 193e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * @param request The synthesis request. 194e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * @param callback The callback the the engine must use to make data available for 195e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath * playback or for writing to a file. 19650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 197e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath protected abstract void onSynthesizeText(SynthesisRequest request, 198e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath SynthesisCallback callback); 19950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 200748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath /** 201748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * Queries the service for a set of features supported for a given language. 202748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * 203748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @param lang ISO-3 language code. 204748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @param country ISO-3 country code. May be empty or null. 205748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @param variant Language variant. May be empty or null. 206748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath * @return A list of features supported for the given language. 207748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath */ 208748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath protected Set<String> onGetFeaturesForLanguage(String lang, String country, String variant) { 209748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath return null; 210748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath } 211748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath 21250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getDefaultSpeechRate() { 21350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE); 21450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 21550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 216e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private String[] getSettingsLocale() { 217e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String locale = mEngineHelper.getLocalePrefForEngine(mPackageName); 218e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return TtsEngines.parseLocalePref(locale); 21950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 22050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 22150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getSecureSettingInt(String name, int defaultValue) { 22250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return Settings.Secure.getInt(getContentResolver(), name, defaultValue); 22350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 22450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 22550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 22650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Synthesizer thread. This thread is used to run {@link SynthHandler}. 22750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 22850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class SynthThread extends HandlerThread implements MessageQueue.IdleHandler { 22950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 23050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean mFirstIdle = true; 23150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 23250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public SynthThread() { 23384deb60cf1055385b9922811e5942076dd72e315Narayan Kamath super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_DEFAULT); 23450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 23550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 23650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 23750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void onLooperPrepared() { 23850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert getLooper().getQueue().addIdleHandler(this); 23950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 24050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 24150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 24250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean queueIdle() { 24350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (mFirstIdle) { 24450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mFirstIdle = false; 24550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } else { 24650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert broadcastTtsQueueProcessingCompleted(); 24750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 24850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 24950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 25050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 25150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private void broadcastTtsQueueProcessingCompleted() { 25250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED); 25350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (DBG) Log.d(TAG, "Broadcasting: " + i); 25450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert sendBroadcast(i); 25550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 25650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 25750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 25850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class SynthHandler extends Handler { 25950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 26050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private SpeechItem mCurrentSpeechItem = null; 26150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 26250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public SynthHandler(Looper looper) { 26350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super(looper); 26450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 26550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 26650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private synchronized SpeechItem getCurrentSpeechItem() { 26750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mCurrentSpeechItem; 26850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 26950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 27050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) { 27150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert SpeechItem old = mCurrentSpeechItem; 27250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mCurrentSpeechItem = speechItem; 27350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return old; 27450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 27550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 276492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private synchronized SpeechItem maybeRemoveCurrentSpeechItem(Object callerIdentity) { 277a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (mCurrentSpeechItem != null && 278492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCurrentSpeechItem.getCallerIdentity() == callerIdentity) { 279a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath SpeechItem current = mCurrentSpeechItem; 280a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath mCurrentSpeechItem = null; 281a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return current; 282a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 283a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 284a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return null; 285a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 286a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 28750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isSpeaking() { 28850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getCurrentSpeechItem() != null; 28950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 29050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 29150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void quit() { 29250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Don't process any more speech items 29350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert getLooper().quit(); 29450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert // Stop the current speech item 29550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert SpeechItem current = setCurrentSpeechItem(null); 29650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (current != null) { 29750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert current.stop(); 29850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 299be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath 300be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // The AudioPlaybackHandler will be destroyed by the caller. 30150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 30250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 30350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 30450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Adds a speech item to the queue. 30550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 30650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Called on a service binder thread. 30750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 30850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) { 30950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (!speechItem.isValid()) { 310754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath speechItem.dispatchOnError(); 31150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.ERROR; 31250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 3134924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 31450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (queueMode == TextToSpeech.QUEUE_FLUSH) { 315492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath stopForApp(speechItem.getCallerIdentity()); 316abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } else if (queueMode == TextToSpeech.QUEUE_DESTROY) { 317a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath stopAll(); 31850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 31950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Runnable runnable = new Runnable() { 32050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 32150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void run() { 32250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert setCurrentSpeechItem(speechItem); 3238d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath speechItem.play(); 32450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert setCurrentSpeechItem(null); 32550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 32650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert }; 32750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Message msg = Message.obtain(this, runnable); 328a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // The obj is used to remove all callbacks from the given app in 329a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // stopForApp(String). 330abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // 331abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // Note that this string is interned, so the == comparison works. 332492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath msg.obj = speechItem.getCallerIdentity(); 33350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (sendMessage(msg)) { 33450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.SUCCESS; 33550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } else { 33650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.w(TAG, "SynthThread has quit"); 337754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath speechItem.dispatchOnError(); 33850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.ERROR; 33950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 34050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 34150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 34250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 34350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Stops all speech output and removes any utterances still in the queue for 34450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * the calling app. 34550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 34650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Called on a service binder thread. 34750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 348492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int stopForApp(Object callerIdentity) { 349492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (callerIdentity == null) { 35050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.ERROR; 35150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 3524924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 353492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath removeCallbacksAndMessages(callerIdentity); 354be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // This stops writing data to the file / or publishing 355be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // items to the audio playback handler. 356a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // 357a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // Note that the current speech item must be removed only if it 358a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // belongs to the callingApp, else the item will be "orphaned" and 359a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // not stopped correctly if a stop request comes along for the item 360a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // from the app it belongs to. 361492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem current = maybeRemoveCurrentSpeechItem(callerIdentity); 362a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (current != null) { 36350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert current.stop(); 36450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 3658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 3664924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath // Remove any enqueued audio too. 36767ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mAudioPlaybackHandler.stopForApp(callerIdentity); 3684924fe38675f0bf69bb0c16fc059ffa1606807ceNarayan Kamath 36950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return TextToSpeech.SUCCESS; 37050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 371a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 372a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath public int stopAll() { 373a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // Stop the current speech item unconditionally. 374a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath SpeechItem current = setCurrentSpeechItem(null); 375a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (current != null) { 376a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath current.stop(); 377a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 378a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // Remove all other items from the queue. 379a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath removeCallbacksAndMessages(null); 380a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // Remove all pending playback as well. 38167ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mAudioPlaybackHandler.stop(); 382a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 383a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return TextToSpeech.SUCCESS; 384a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 38550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 38650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 387754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath interface UtteranceProgressDispatcher { 388754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnDone(); 389754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnStart(); 390754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnError(); 3918d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 3928d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 39350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 39450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * An item in the synth thread queue. 39550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 396754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath private abstract class SpeechItem implements UtteranceProgressDispatcher { 397492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final Object mCallerIdentity; 398b956f37e375bb2588208d4b5e8a40fae6fae5f86Narayan Kamath protected final Bundle mParams; 399492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final int mCallerUid; 400492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final int mCallerPid; 40150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean mStarted = false; 40250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean mStopped = false; 40350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 404492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public SpeechItem(Object caller, int callerUid, int callerPid, Bundle params) { 405492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerIdentity = caller; 40650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mParams = params; 407492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerUid = callerUid; 408492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerPid = callerPid; 40950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 41050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 411492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public Object getCallerIdentity() { 412492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath return mCallerIdentity; 41350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 41450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 41550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 41650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Checker whether the item is valid. If this method returns false, the item should not 41750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * be played. 41850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 41950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public abstract boolean isValid(); 42050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 42150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 42250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Plays the speech item. Blocks until playback is finished. 42350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Must not be called more than once. 42450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 42550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Only called on the synthesis thread. 42650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 42750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}. 42850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 42950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int play() { 43050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 43150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (mStarted) { 43250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert throw new IllegalStateException("play() called twice"); 43350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 43450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mStarted = true; 43550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 43650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return playImpl(); 43750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 43850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 43950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 44050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Stops the speech item. 44150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Must not be called more than once. 44250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * 44350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Can be called on multiple threads, but not on the synthesis thread. 44450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 44550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void stop() { 44650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 44750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (mStopped) { 44850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert throw new IllegalStateException("stop() called twice"); 44950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 45050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mStopped = true; 45150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 45250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert stopImpl(); 45350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 45450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 455754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath @Override 456754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnDone() { 457754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath final String utteranceId = getUtteranceId(); 45868e2af55d65d2e61fbf8096eccaa2e4ca02b6c5aNarayan Kamath if (utteranceId != null) { 459492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.dispatchOnDone(getCallerIdentity(), utteranceId); 460754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 461754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 462754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 463754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath @Override 464754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnStart() { 465754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath final String utteranceId = getUtteranceId(); 46668e2af55d65d2e61fbf8096eccaa2e4ca02b6c5aNarayan Kamath if (utteranceId != null) { 467492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.dispatchOnStart(getCallerIdentity(), utteranceId); 468754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 469754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 470754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 471754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath @Override 472754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath public void dispatchOnError() { 4738d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath final String utteranceId = getUtteranceId(); 47468e2af55d65d2e61fbf8096eccaa2e4ca02b6c5aNarayan Kamath if (utteranceId != null) { 475492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.dispatchOnError(getCallerIdentity(), utteranceId); 4768d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 4778d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 4788d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 479492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int getCallerUid() { 480492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath return mCallerUid; 481492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath } 482492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath 483492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int getCallerPid() { 484492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath return mCallerPid; 485492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath } 486492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath 487a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath protected synchronized boolean isStopped() { 488a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return mStopped; 489a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 490a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath 49150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract int playImpl(); 49250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 49350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected abstract void stopImpl(); 49450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 49550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int getStreamType() { 49650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM); 49750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 49850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 49950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public float getVolume() { 50050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getFloatParam(Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME); 50150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 50250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 50350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public float getPan() { 50450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getFloatParam(Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN); 50550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 50650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 50750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String getUtteranceId() { 50850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getStringParam(Engine.KEY_PARAM_UTTERANCE_ID, null); 50950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 51050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 51150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected String getStringParam(String key, String defaultValue) { 51250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mParams == null ? defaultValue : mParams.getString(key, defaultValue); 51350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 51450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 51550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int getIntParam(String key, int defaultValue) { 51650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mParams == null ? defaultValue : mParams.getInt(key, defaultValue); 51750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 51850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 51950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected float getFloatParam(String key, float defaultValue) { 52050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue); 52150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 52250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 52350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 5248d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath class SynthesisSpeechItem extends SpeechItem { 52540f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath // Never null. 52650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private final String mText; 527e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath private final SynthesisRequest mSynthesisRequest; 528e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private final String[] mDefaultLocale; 529e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath // Non null after synthesis has started, and all accesses 530e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath // guarded by 'this'. 531e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath private AbstractSynthesisCallback mSynthesisCallback; 5326dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath private final EventLogger mEventLogger; 53350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 534492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public SynthesisSpeechItem(Object callerIdentity, int callerUid, int callerPid, 535492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Bundle params, String text) { 536492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params); 53750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mText = text; 538e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath mSynthesisRequest = new SynthesisRequest(mText, mParams); 539e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath mDefaultLocale = getSettingsLocale(); 540e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath setRequestParams(mSynthesisRequest); 541492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mEventLogger = new EventLogger(mSynthesisRequest, callerUid, callerPid, 542492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mPackageName); 54350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 54450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 54550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String getText() { 54650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mText; 54750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 54850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 54950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 55050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isValid() { 55140f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath if (mText == null) { 55240f71f0be3cefabde9dc066d7707a1e5ebaec820Narayan Kamath Log.wtf(TAG, "Got null text"); 55350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 55450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 555a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) { 55650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.w(TAG, "Text too long: " + mText.length() + " chars"); 55750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 55850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 55950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 56050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 56150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 56250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 56350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int playImpl() { 564e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath AbstractSynthesisCallback synthesisCallback; 5656dabb63307a0b63f9386d61e8444aed29db2081eNarayan Kamath mEventLogger.onRequestProcessingStart(); 56650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 567a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // stop() might have been called before we enter this 568a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // synchronized block. 569a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (isStopped()) { 570a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath return TextToSpeech.ERROR; 571a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 572e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath mSynthesisCallback = createSynthesisCallback(); 573e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath synthesisCallback = mSynthesisCallback; 57450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 575e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath TextToSpeechService.this.onSynthesizeText(mSynthesisRequest, synthesisCallback); 576e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath return synthesisCallback.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR; 57750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 57850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 579e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath protected AbstractSynthesisCallback createSynthesisCallback() { 580e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath return new PlaybackSynthesisCallback(getStreamType(), getVolume(), getPan(), 581492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mAudioPlaybackHandler, this, getCallerIdentity(), mEventLogger); 58250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 58350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 58450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private void setRequestParams(SynthesisRequest request) { 585c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath request.setLanguage(getLanguage(), getCountry(), getVariant()); 586c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath request.setSpeechRate(getSpeechRate()); 587c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath 58850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert request.setPitch(getPitch()); 58950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 59050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 59150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 59250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void stopImpl() { 593e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath AbstractSynthesisCallback synthesisCallback; 59450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert synchronized (this) { 595e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath synthesisCallback = mSynthesisCallback; 59650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 597a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath if (synthesisCallback != null) { 598a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // If the synthesis callback is null, it implies that we haven't 599a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // entered the synchronized(this) block in playImpl which in 600a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath // turn implies that synthesis would not have started. 601a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath synthesisCallback.stop(); 602a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath TextToSpeechService.this.onStop(); 603a65c62acabd204a0a1eb5504160238b288bee52bNarayan Kamath } 60450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 60550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 60650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String getLanguage() { 607e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return getStringParam(Engine.KEY_PARAM_LANGUAGE, mDefaultLocale[0]); 60850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 60950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 61050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean hasLanguage() { 61150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return !TextUtils.isEmpty(getStringParam(Engine.KEY_PARAM_LANGUAGE, null)); 61250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 61350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 61450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private String getCountry() { 615e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!hasLanguage()) return mDefaultLocale[1]; 61650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getStringParam(Engine.KEY_PARAM_COUNTRY, ""); 61750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 61850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 61950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private String getVariant() { 620e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!hasLanguage()) return mDefaultLocale[2]; 62150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getStringParam(Engine.KEY_PARAM_VARIANT, ""); 62250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 62350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 62450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getSpeechRate() { 62550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getIntParam(Engine.KEY_PARAM_RATE, getDefaultSpeechRate()); 62650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 62750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 62850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private int getPitch() { 62950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return getIntParam(Engine.KEY_PARAM_PITCH, Engine.DEFAULT_PITCH); 63050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 63150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 63250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 63350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class SynthesisToFileSpeechItem extends SynthesisSpeechItem { 63450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private final File mFile; 63550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 636492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public SynthesisToFileSpeechItem(Object callerIdentity, int callerUid, int callerPid, 637492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Bundle params, String text, 63850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert File file) { 639492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params, text); 64050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mFile = file; 64150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 64250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 64350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 64450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isValid() { 64550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (!super.isValid()) { 64650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 64750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 64850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return checkFile(mFile); 64950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 65050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 65150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 652e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath protected AbstractSynthesisCallback createSynthesisCallback() { 653e22b69a7de0349b99d3107349d1d3aa72d62c841Narayan Kamath return new FileSynthesisCallback(mFile); 65450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 65550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 6568d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath @Override 6578d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath protected int playImpl() { 658754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath dispatchOnStart(); 6598d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath int status = super.playImpl(); 6608d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath if (status == TextToSpeech.SUCCESS) { 661754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath dispatchOnDone(); 662754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } else { 663754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath dispatchOnError(); 6648d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 6658d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return status; 6668d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath } 6678d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath 66850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 66950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Checks that the given file can be used for synthesis output. 67050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 67150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private boolean checkFile(File file) { 67250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert try { 67350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (file.exists()) { 67450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.v(TAG, "File " + file + " exists, deleting."); 67550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (!file.delete()) { 67650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.e(TAG, "Failed to delete " + file); 67750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 67850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 67950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 68050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (!file.createNewFile()) { 68150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.e(TAG, "Can't create file " + file); 68250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 68350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 68450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (!file.delete()) { 68550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.e(TAG, "Failed to delete " + file); 68650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 68750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 68850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 68950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } catch (IOException e) { 69050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Log.e(TAG, "Can't use " + file + " due to exception " + e); 69150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return false; 69250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 69350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 69450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 69550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 69650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class AudioSpeechItem extends SpeechItem { 697af802c6831551323126537cf8edabea97d2fc762Narayan Kamath private final AudioPlaybackQueueItem mItem; 698492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public AudioSpeechItem(Object callerIdentity, int callerUid, int callerPid, 699492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Bundle params, Uri uri) { 700492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params); 701af802c6831551323126537cf8edabea97d2fc762Narayan Kamath mItem = new AudioPlaybackQueueItem(this, getCallerIdentity(), 702af802c6831551323126537cf8edabea97d2fc762Narayan Kamath TextToSpeechService.this, uri, getStreamType()); 70350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 70450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 70550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 70650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isValid() { 70750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 70850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 70950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 71050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 71150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int playImpl() { 712af802c6831551323126537cf8edabea97d2fc762Narayan Kamath mAudioPlaybackHandler.enqueue(mItem); 7138d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return TextToSpeech.SUCCESS; 71450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 71550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 71650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 71750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void stopImpl() { 718be4ad4ac66d6b4b878ed052975f7fb09af92c6d6Narayan Kamath // Do nothing. 71950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 72050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 72150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 72250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class SilenceSpeechItem extends SpeechItem { 72350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private final long mDuration; 72450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 725492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public SilenceSpeechItem(Object callerIdentity, int callerUid, int callerPid, 726492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Bundle params, long duration) { 727492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath super(callerIdentity, callerUid, callerPid, params); 72850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert mDuration = duration; 72950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 73050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 73150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 73250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isValid() { 73350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return true; 73450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 73550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 73650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 73750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected int playImpl() { 73867ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath mAudioPlaybackHandler.enqueue(new SilencePlaybackQueueItem( 73967ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath this, getCallerIdentity(), mDuration)); 7408d1fc2403b8277e68d7816b2bbf05464a4c7a58aNarayan Kamath return TextToSpeech.SUCCESS; 74150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 74250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 74350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 74450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert protected void stopImpl() { 74567ae6bc83cf2b30b0c61b9ebba5fed7a0038549cNarayan Kamath // Do nothing, handled by AudioPlaybackHandler#stopForApp 74650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 74750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 74850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 74950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 75050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public IBinder onBind(Intent intent) { 75150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) { 75250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mBinder; 75350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 75450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return null; 75550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 75650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 75750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert /** 75850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * Binder returned from {@code #onBind(Intent)}. The methods in this class can be 75950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert * called called from several different threads. 76050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert */ 761abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // NOTE: All calls that are passed in a calling app are interned so that 762abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // they can be used as message objects (which are tested for equality using ==). 76350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() { 764492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 765492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int speak(IBinder caller, String text, int queueMode, Bundle params) { 766492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller, text, params)) { 767abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 768abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 769abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 770492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem item = new SynthesisSpeechItem(caller, 771492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Binder.getCallingUid(), Binder.getCallingPid(), params, text); 77250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(queueMode, item); 77350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 77450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 775492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 776492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int synthesizeToFile(IBinder caller, String text, String filename, 77750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert Bundle params) { 778492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller, text, filename, params)) { 779abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 780abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 781abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 78250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert File file = new File(filename); 783492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem item = new SynthesisToFileSpeechItem(caller, Binder.getCallingUid(), 784492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Binder.getCallingPid(), params, text, file); 78550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item); 78650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 78750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 788492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 789492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int playAudio(IBinder caller, Uri audioUri, int queueMode, Bundle params) { 790492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller, audioUri, params)) { 791abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 792abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 793abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 794492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem item = new AudioSpeechItem(caller, 795492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Binder.getCallingUid(), Binder.getCallingPid(), params, audioUri); 79650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(queueMode, item); 79750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 79850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 799492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 800492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int playSilence(IBinder caller, long duration, int queueMode, Bundle params) { 801492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller, params)) { 802abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 803abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 804abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 805492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath SpeechItem item = new SilenceSpeechItem(caller, 806492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath Binder.getCallingUid(), Binder.getCallingPid(), params, duration); 80750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return mSynthHandler.enqueueSpeechItem(queueMode, item); 80850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 80950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 810492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 81150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public boolean isSpeaking() { 812c34f76fe89b5a31d01d63067c2f24b9a6a76df18Narayan Kamath return mSynthHandler.isSpeaking() || mAudioPlaybackHandler.isSpeaking(); 81350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 81450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 815492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 816492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public int stop(IBinder caller) { 817492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller)) { 818abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 819abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 820abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 821492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath return mSynthHandler.stopForApp(caller); 82250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 82350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 824492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 82550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public String[] getLanguage() { 82650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return onGetLanguage(); 82750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 82850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 8297a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath /* 8307a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * If defaults are enforced, then no language is "available" except 8317a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * perhaps the default language selected by the user. 8327a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath */ 833492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 83450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int isLanguageAvailable(String lang, String country, String variant) { 835abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath if (!checkNonNull(lang)) { 836abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 837abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 838abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 83950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return onIsLanguageAvailable(lang, country, variant); 84050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 84150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 842492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 843748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath public String[] getFeaturesForLanguage(String lang, String country, String variant) { 844748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath Set<String> features = onGetFeaturesForLanguage(lang, country, variant); 8456c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath String[] featuresArray = null; 8466c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath if (features != null) { 8476c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath featuresArray = new String[features.size()]; 8486c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath features.toArray(featuresArray); 8496c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath } else { 8506c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath featuresArray = new String[0]; 8516c07a2028505e28abd601870f05d4752e92a4b7bNarayan Kamath } 852748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath return featuresArray; 853748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath } 854748af66ca27d3afe2e16ccc80b147d447635292aNarayan Kamath 8557a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath /* 8567a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * There is no point loading a non default language if defaults 8577a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath * are enforced. 8587a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath */ 859492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 86050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public int loadLanguage(String lang, String country, String variant) { 861abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath if (!checkNonNull(lang)) { 862abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return TextToSpeech.ERROR; 863abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 864abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 86550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert return onLoadLanguage(lang, country, variant); 86650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 86750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 868492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath @Override 869492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void setCallback(IBinder caller, ITextToSpeechCallback cb) { 870abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // Note that passing in a null callback is a valid use case. 871492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath if (!checkNonNull(caller)) { 872abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return; 873abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 874abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 875492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallbacks.setCallback(caller, cb); 87650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 8777a3af86dc03a45280ceca6154040577f614c571eNarayan Kamath 878abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath private String intern(String in) { 879abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath // The input parameter will be non null. 880abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return in.intern(); 881abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 882abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath 883abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath private boolean checkNonNull(Object... args) { 884abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath for (Object o : args) { 885abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath if (o == null) return false; 886abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 887abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath return true; 888abc63fbddab2477a2954bc804aba2826e1f11084Narayan Kamath } 88950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert }; 89050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 89150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> { 892492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private final HashMap<IBinder, ITextToSpeechCallback> mCallerToCallback 893492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath = new HashMap<IBinder, ITextToSpeechCallback>(); 89450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 895492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void setCallback(IBinder caller, ITextToSpeechCallback cb) { 896492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 89750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert ITextToSpeechCallback old; 89850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (cb != null) { 899492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath register(cb, caller); 900492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath old = mCallerToCallback.put(caller, cb); 90150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } else { 902492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath old = mCallerToCallback.remove(caller); 90350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 90450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (old != null && old != cb) { 90550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert unregister(old); 90650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 90750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 90850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 90950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 910492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void dispatchOnDone(Object callerIdentity, String utteranceId) { 911492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 912754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath if (cb == null) return; 913754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath try { 914754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath cb.onDone(utteranceId); 915754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } catch (RemoteException e) { 916754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath Log.e(TAG, "Callback onDone failed: " + e); 91750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 918754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 919754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 920492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void dispatchOnStart(Object callerIdentity, String utteranceId) { 921492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 922754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath if (cb == null) return; 923754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath try { 924754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath cb.onStart(utteranceId); 925754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } catch (RemoteException e) { 926754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath Log.e(TAG, "Callback onStart failed: " + e); 927754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 928754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 929754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 930754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 931492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath public void dispatchOnError(Object callerIdentity, String utteranceId) { 932492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath ITextToSpeechCallback cb = getCallbackFor(callerIdentity); 93350e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert if (cb == null) return; 93450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert try { 935754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath cb.onError(utteranceId); 93650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } catch (RemoteException e) { 937754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath Log.e(TAG, "Callback onError failed: " + e); 93850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 93950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 94050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 94150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 94250e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) { 943492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath IBinder caller = (IBinder) cookie; 944492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 945492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerToCallback.remove(caller); 94650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 947492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mSynthHandler.stopForApp(caller); 94850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 94950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 95050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert @Override 95150e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert public void kill() { 952492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 953492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath mCallerToCallback.clear(); 95450e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert super.kill(); 95550e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 95650e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 95750e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 958492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath private ITextToSpeechCallback getCallbackFor(Object caller) { 959754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath ITextToSpeechCallback cb; 960492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath IBinder asBinder = (IBinder) caller; 961492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath synchronized (mCallerToCallback) { 962492b7f0d51f53164aa6eb974cd7ab6a7889af677Narayan Kamath cb = mCallerToCallback.get(asBinder); 963754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 964754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 965754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath return cb; 966754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath } 967754c72ed9e8e83e5a913aa7552fc2e1b1b5277e0Narayan Kamath 96850e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert } 96950e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert 97050e657bb2d005568f5dd8bc1d904d07b0d94018fBjorn Bringert} 971