Index: chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java |
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java b/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..462020bd8808bcb7e0a384d62382c9674ce3ab80 |
--- /dev/null |
+++ b/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java |
@@ -0,0 +1,225 @@ |
+// Copyright (c) 2013 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+package org.chromium.chrome.browser; |
+ |
+import android.content.Context; |
+import android.speech.tts.TextToSpeech; |
+import android.speech.tts.UtteranceProgressListener; |
+import java.lang.Double; |
+import java.lang.Integer; |
+import java.util.ArrayList; |
+import java.util.HashMap; |
+import java.util.Locale; |
+import org.chromium.base.CalledByNative; |
+import org.chromium.base.ThreadUtils; |
+ |
+/** |
+ * This class is the Java counterpart to the C++ TtsPlatformImplAndroid class. |
+ * It implements the Android-native text-to-speech code to support the web |
+ * speech synthesis API. |
+ */ |
+public class TtsPlatformImpl { |
bulach
2013/05/16 11:10:28
nit: if this class is only used as a bridge to c++
dmazzoni
2013/05/16 16:36:40
I'm not sure I understand - I thought a top-level
bulach
2013/05/17 10:09:04
sorry, my bad. :) I meant the class would be "pack
|
+ private class TtsVoice { |
bulach
2013/05/16 11:10:28
nit: static
dmazzoni
2013/05/16 16:36:40
Done.
|
+ public String name; |
bulach
2013/05/16 11:10:28
nit: mName, mLanguage
also, they don't need to be
dmazzoni
2013/05/16 16:36:40
Done.
|
+ public String language; |
+ }; |
+ |
+ private int mNativeTtsPlatformImplAndroid; |
+ private TextToSpeech mTextToSpeech; |
+ private boolean mInitialized; |
+ private ArrayList<TtsVoice> mVoices; |
+ private String mCurrentLanguage; |
+ |
+ private TtsPlatformImpl(int nativeTtsPlatformImplAndroid, Context context) { |
+ mInitialized = false; |
+ mNativeTtsPlatformImplAndroid = nativeTtsPlatformImplAndroid; |
+ mTextToSpeech = new TextToSpeech(context, new TextToSpeech.OnInitListener() { |
+ public void onInit(int status) { |
+ if (status == TextToSpeech.SUCCESS) { |
+ initialize(); |
bulach
2013/05/16 11:10:28
not clear to me what is the threading model here.
dmazzoni
2013/05/16 16:36:40
I added a class comment to clarify: all C++ calls
|
+ } |
+ } |
+ }); |
+ mTextToSpeech.setOnUtteranceProgressListener(new UtteranceProgressListener() { |
+ public void onDone(final String utteranceId) { |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (mNativeTtsPlatformImplAndroid != 0) { |
+ nativeOnEndEvent(mNativeTtsPlatformImplAndroid, |
+ Integer.parseInt(utteranceId)); |
+ } |
+ } |
+ }); |
+ } |
+ |
+ public void onError(final String utteranceId) { |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (mNativeTtsPlatformImplAndroid != 0) { |
+ nativeOnErrorEvent(mNativeTtsPlatformImplAndroid, |
+ Integer.parseInt(utteranceId)); |
+ } |
+ } |
+ }); |
+ } |
+ |
+ public void onStart(final String utteranceId) { |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (mNativeTtsPlatformImplAndroid != 0) { |
+ nativeOnStartEvent(mNativeTtsPlatformImplAndroid, |
+ Integer.parseInt(utteranceId)); |
+ } |
+ } |
+ }); |
+ } |
+ }); |
+ }; |
+ |
+ /** |
+ * Create a TtsPlatformImpl object, which is owned by TtsPlatformImplAndroid |
+ * on the C++ side. |
+ * |
+ * @param nativeTtsPlatformImplAndroid The C++ object that owns us. |
+ * @param context The app context. |
+ */ |
+ @CalledByNative |
+ public static TtsPlatformImpl create(int nativeTtsPlatformImplAndroid, |
+ Context context) { |
+ return new TtsPlatformImpl(nativeTtsPlatformImplAndroid, context); |
+ } |
+ |
+ /** |
+ * Called when our C++ counterpoint is deleted. Clear the handle to our |
+ * native C++ object, ensuring it's never called. |
+ */ |
+ @CalledByNative |
+ public void destroy() { |
+ mNativeTtsPlatformImplAndroid = 0; |
+ } |
+ |
+ /** |
+ * @return true if our TextToSpeech object is initialized and we've |
+ * finished scanning the list of voices. |
+ */ |
+ @CalledByNative |
+ public boolean isInitialized() { |
+ return mInitialized; |
+ } |
+ |
+ /** |
+ * @return the number of voices. |
+ */ |
+ @CalledByNative |
+ public int getVoiceCount() { |
+ assert mInitialized == true; |
+ return mVoices.size(); |
+ } |
+ |
+ /** |
+ * @return the name of the voice at a given index. |
+ */ |
+ @CalledByNative |
+ public String getVoiceName(int voiceIndex) { |
+ assert mInitialized == true; |
+ return mVoices.get(voiceIndex).name; |
+ } |
+ |
+ /** |
+ * @return the language of the voice at a given index. |
+ */ |
+ @CalledByNative |
+ public String getVoiceLanguage(int voiceIndex) { |
+ assert mInitialized == true; |
+ return mVoices.get(voiceIndex).language; |
+ } |
+ |
+ /** |
+ * Attempt to start speaking an utterance. If it returns true, will call back on |
+ * start and end. |
+ * |
+ * @param utteranceId A unique id for this utterance so that callbacks can be tied |
+ * to a particular utterance. |
+ * @param text The text to speak. |
+ * @param lang The language code for the text (e.g., "en-US"). |
+ * @param rate The speech rate, in the units expected by Android TextToSpeech. |
+ * @param pitch The speech pitch, in the units expected by Android TextToSpeech. |
+ * @param volume The speech volume, in the units expected by Android TextToSpeech. |
+ * @return true on success. |
+ */ |
+ @CalledByNative |
+ public boolean speak(int utteranceId, String text, String lang, |
+ float rate, float pitch, float volume) { |
+ assert mInitialized == true; |
+ if (lang != mCurrentLanguage) { |
+ mTextToSpeech.setLanguage(new Locale(lang)); |
+ mCurrentLanguage = lang; |
+ } |
+ |
+ mTextToSpeech.setSpeechRate(rate); |
+ mTextToSpeech.setPitch(pitch); |
+ HashMap<String, String> params = new HashMap<String, String>(); |
+ if (volume != 1.0) { |
+ params.put(TextToSpeech.Engine.KEY_PARAM_VOLUME, Double.toString(volume)); |
+ } |
+ params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, Integer.toString(utteranceId)); |
+ int result = mTextToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, params); |
+ return (result == TextToSpeech.SUCCESS); |
+ } |
+ |
+ /** |
+ * Stop the current utterance. |
+ */ |
+ @CalledByNative |
+ public void stop() { |
+ assert mInitialized == true; |
+ mTextToSpeech.stop(); |
+ } |
+ |
+ private void initialize() { |
+ // Note: Android supports multiple speech engines, but querying the |
+ // metadata about all of them is expensive. So we deliberately only |
+ // support the default speech engine, and expose the different |
+ // supported languages for the default engine as different voices. |
+ String defaultEngineName = mTextToSpeech.getDefaultEngine(); |
+ String engineLabel = defaultEngineName; |
+ for (TextToSpeech.EngineInfo info : mTextToSpeech.getEngines()) { |
+ if (info.name.equals(defaultEngineName)) engineLabel = info.label; |
+ } |
+ Locale[] locales = Locale.getAvailableLocales(); |
+ mVoices = new ArrayList<TtsVoice>(); |
+ for (int i = 0; i < locales.length; ++i) { |
+ if (!locales[i].getVariant().isEmpty()) continue; |
+ if (mTextToSpeech.isLanguageAvailable(locales[i]) > 0) { |
+ TtsVoice voice = new TtsVoice(); |
+ voice.name = locales[i].getDisplayLanguage(); |
+ if (!locales[i].getCountry().isEmpty()) { |
+ voice.name += " " + locales[i].getDisplayCountry(); |
+ } |
+ voice.language = locales[i].toString(); |
+ mVoices.add(voice); |
+ } |
+ } |
+ |
+ mInitialized = true; |
+ |
+ ThreadUtils.runOnUiThread(new Runnable() { |
+ @Override |
+ public void run() { |
+ if (mNativeTtsPlatformImplAndroid != 0) { |
+ nativeVoicesChanged(mNativeTtsPlatformImplAndroid); |
+ } |
+ } |
+ }); |
+ } |
+ |
+ private native void nativeVoicesChanged(int nativeTtsPlatformImplAndroid); |
+ private native void nativeOnEndEvent(int nativeTtsPlatformImplAndroid, int utteranceId); |
+ private native void nativeOnStartEvent(int nativeTtsPlatformImplAndroid, int utteranceId); |
+ private native void nativeOnErrorEvent(int nativeTtsPlatformImplAndroid, int utteranceId); |
+} |