Chromium Code Reviews| 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..eff6af4335256a48613ef26bd1d59645281c47e4 |
| --- /dev/null |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/TtsPlatformImpl.java |
| @@ -0,0 +1,233 @@ |
| +// 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. |
| + * |
| + * Threading model note: all calls from C++ must happen on the UI thread. |
| + * Callbacks from Android may happen on a different thread, so we always |
| + * use ThreadUtils.runOnUiThread when calling back to C++. |
| + */ |
| +public class TtsPlatformImpl { |
| + private static class TtsVoice { |
| + private TtsVoice(String name, String language) { |
| + mName = name; |
| + mLanguage = language; |
| + } |
| + private final String mName; |
| + private final String mLanguage; |
| + }; |
| + |
| + private int mNativeTtsPlatformImplAndroid; |
| + private TextToSpeech mTextToSpeech; |
|
bulach
2013/05/17 10:09:04
nit: this could be "final"
dmazzoni
2013/05/17 16:41:58
Done.
|
| + 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) { |
| + ThreadUtils.runOnUiThread(new Runnable() { |
| + @Override |
| + public void run() { |
| + initialize(); |
| + } |
| + }); |
| + } |
| + } |
| + }); |
| + 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 |
| + private static TtsPlatformImpl create(int nativeTtsPlatformImplAndroid, |
| + Context context) { |
|
bulach
2013/05/17 10:09:04
nit: align with parens
dmazzoni
2013/05/17 16:41:58
Done.
|
| + 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 |
| + private void destroy() { |
| + mNativeTtsPlatformImplAndroid = 0; |
| + } |
| + |
| + /** |
| + * @return true if our TextToSpeech object is initialized and we've |
| + * finished scanning the list of voices. |
| + */ |
| + @CalledByNative |
| + private boolean isInitialized() { |
| + return mInitialized; |
| + } |
| + |
| + /** |
| + * @return the number of voices. |
| + */ |
| + @CalledByNative |
| + private int getVoiceCount() { |
| + assert mInitialized == true; |
| + return mVoices.size(); |
| + } |
| + |
| + /** |
| + * @return the name of the voice at a given index. |
| + */ |
| + @CalledByNative |
| + private String getVoiceName(int voiceIndex) { |
| + assert mInitialized == true; |
| + return mVoices.get(voiceIndex).mName; |
| + } |
| + |
| + /** |
| + * @return the language of the voice at a given index. |
| + */ |
| + @CalledByNative |
| + private String getVoiceLanguage(int voiceIndex) { |
| + assert mInitialized == true; |
| + return mVoices.get(voiceIndex).mLanguage; |
| + } |
| + |
| + /** |
| + * 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 |
| + private boolean speak(int utteranceId, String text, String lang, |
| + float rate, float pitch, float volume) { |
|
bulach
2013/05/17 10:09:04
nit: align with parens
dmazzoni
2013/05/17 16:41:58
Done.
|
| + 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 |
| + private void stop() { |
| + assert mInitialized == true; |
| + mTextToSpeech.stop(); |
| + } |
| + |
| + /** |
| + * Note: we enforce that this method is called on the UI thread, so |
| + * we can call nativeVoicesChanged directly. |
| + */ |
| + 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) { |
| + String name = locales[i].getDisplayLanguage(); |
| + if (!locales[i].getCountry().isEmpty()) { |
| + name += " " + locales[i].getDisplayCountry(); |
| + } |
| + TtsVoice voice = new TtsVoice(name, locales[i].toString()); |
| + mVoices.add(voice); |
| + } |
| + } |
| + |
| + mInitialized = true; |
| + 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); |
| +} |