From 35c7698a1b17c3e4ca0eae753e68bf069a463d70 Mon Sep 17 00:00:00 2001 From: Przemyslaw Szczepaniak Date: Fri, 5 Sep 2014 15:36:49 +0100 Subject: API review requests for the TTS package. - New TextToSpeechService methods are no longer protected. - s/getRequiresNetworkConnection/isNetworkConnectionRequired - New TextToSpeec#play.. methods use a Bundle instead of a HashMap - New synthesizeToFile(), addSpeech(), addEarcon() methods take a File instead of a String with filepath. - TextToSpeechService#s/isValidVoiceName/onIsValidVoiceName Bug:17389935,17253934 Change-Id: Iec76f59015c34104683c050fe1ff1ceccd604134 --- core/java/android/speech/tts/TextToSpeech.java | 149 +++++++++++++++++---- .../android/speech/tts/TextToSpeechService.java | 12 +- core/java/android/speech/tts/Voice.java | 2 +- 3 files changed, 130 insertions(+), 33 deletions(-) (limited to 'core/java/android/speech') diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 7245975..a4b6e92 100644 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -578,7 +578,7 @@ public class TextToSpeech { * * @deprecated Starting from API level 20, to select network synthesis, call * ({@link TextToSpeech#getVoices()}, find a suitable network voice - * ({@link Voice#getRequiresNetworkConnection()}) and pass it + * ({@link Voice#isNetworkConnectionRequired()}) and pass it * to {@link TextToSpeech#setVoice(Voice)}). */ @Deprecated @@ -596,7 +596,7 @@ public class TextToSpeech { * @deprecated Starting from API level 20, to select embedded synthesis, call * ({@link TextToSpeech#getVoices()}, find a suitable embedded voice - * ({@link Voice#getRequiresNetworkConnection()}) and pass it + * ({@link Voice#isNetworkConnectionRequired()}) and pass it * to {@link TextToSpeech#setVoice(Voice)}). */ @Deprecated @@ -957,20 +957,18 @@ public class TextToSpeech { * * @param text * The string of text. Example: "south_south_east" - * @param filename - * The full path to the sound file (for example: - * "/sdcard/mysounds/hello.wav") + * @param file + * File object pointing to the sound file. * * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. */ - public int addSpeech(CharSequence text, String filename) { + public int addSpeech(CharSequence text, File file) { synchronized (mStartLock) { - mUtterances.put(text, Uri.parse(filename)); + mUtterances.put(text, Uri.fromFile(file)); return SUCCESS; } } - /** * Adds a mapping between a string of text and a sound resource in a * package. Use this to add custom earcons. @@ -1017,7 +1015,11 @@ public class TextToSpeech { * "/sdcard/mysounds/tick.wav") * * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. + * + * @deprecated As of API level 20, replaced by + * {@link #addEarcon(String, File)}. */ + @Deprecated public int addEarcon(String earcon, String filename) { synchronized(mStartLock) { mEarcons.put(earcon, Uri.parse(filename)); @@ -1025,6 +1027,27 @@ public class TextToSpeech { } } + /** + * Adds a mapping between a string of text and a sound file. + * Use this to add custom earcons. + * + * @see #playEarcon(String, int, HashMap) + * + * @param earcon + * The name of the earcon. + * Example: "[tick]" + * @param file + * File object pointing to the sound file. + * + * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. + */ + public int addEarcon(String earcon, File file) { + synchronized(mStartLock) { + mEarcons.put(earcon, Uri.fromFile(file)); + return SUCCESS; + } + } + private Uri makeResourceUri(String packageName, int resourceId) { return new Uri.Builder() .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE) @@ -1061,7 +1084,7 @@ public class TextToSpeech { */ public int speak(final CharSequence text, final int queueMode, - final HashMap params, + final Bundle params, final String utteranceId) { return runAction(new Action() { @Override @@ -1103,11 +1126,11 @@ public class TextToSpeech { * * @return {@link #ERROR} or {@link #SUCCESS} of queuing the speak operation. * @deprecated As of API level 20, replaced by - * {@link #speak(CharSequence, int, HashMap, String)}. + * {@link #speak(CharSequence, int, Bundle, String)}. */ @Deprecated public int speak(final String text, final int queueMode, final HashMap params) { - return speak(text, queueMode, params, + return speak(text, queueMode, convertParamsHashMaptoBundle(params), params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); } @@ -1135,7 +1158,7 @@ public class TextToSpeech { * @return {@link #ERROR} or {@link #SUCCESS} of queuing the playEarcon operation. */ public int playEarcon(final String earcon, final int queueMode, - final HashMap params, final String utteranceId) { + final Bundle params, final String utteranceId) { return runAction(new Action() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { @@ -1173,12 +1196,12 @@ public class TextToSpeech { * * @return {@link #ERROR} or {@link #SUCCESS} of queuing the playEarcon operation. * @deprecated As of API level 20, replaced by - * {@link #playEarcon(String, int, HashMap, String)}. + * {@link #playEarcon(String, int, Bundle, String)}. */ @Deprecated public int playEarcon(final String earcon, final int queueMode, final HashMap params) { - return playEarcon(earcon, queueMode, params, + return playEarcon(earcon, queueMode, convertParamsHashMaptoBundle(params), params == null ? null : params.get(Engine.KEY_PARAM_UTTERANCE_ID)); } @@ -1757,22 +1780,20 @@ public class TextToSpeech { * must be prefixed by the name of the engine they are intended for. For example * the keys "com.svox.pico_foo" and "com.svox.pico:bar" will be passed to the * engine named "com.svox.pico" if it is being used. - * @param filename Absolute file filename to write the generated audio data to.It should be - * something like "/sdcard/myappsounds/mysound.wav". + * @param file File to write the generated audio data to. * @param utteranceId An unique identifier for this request. * @return {@link #ERROR} or {@link #SUCCESS} of queuing the synthesizeToFile operation. */ - public int synthesizeToFile(final CharSequence text, final HashMap params, - final String filename, final String utteranceId) { + public int synthesizeToFile(final CharSequence text, final Bundle params, + final File file, final String utteranceId) { return runAction(new Action() { @Override public Integer run(ITextToSpeechService service) throws RemoteException { ParcelFileDescriptor fileDescriptor; int returnValue; try { - File file = new File(filename); if(file.exists() && !file.canWrite()) { - Log.e(TAG, "Can't write to " + filename); + Log.e(TAG, "Can't write to " + file); return ERROR; } fileDescriptor = ParcelFileDescriptor.open(file, @@ -1784,10 +1805,10 @@ public class TextToSpeech { fileDescriptor.close(); return returnValue; } catch (FileNotFoundException e) { - Log.e(TAG, "Opening file " + filename + " failed", e); + Log.e(TAG, "Opening file " + file + " failed", e); return ERROR; } catch (IOException e) { - Log.e(TAG, "Closing file " + filename + " failed", e); + Log.e(TAG, "Closing file " + file + " failed", e); return ERROR; } } @@ -1817,16 +1838,18 @@ public class TextToSpeech { * * @return {@link #ERROR} or {@link #SUCCESS} of queuing the synthesizeToFile operation. * @deprecated As of API level 20, replaced by - * {@link #synthesizeToFile(CharSequence, HashMap, String, String)}. + * {@link #synthesizeToFile(CharSequence, Bundle, File, String)}. */ + @Deprecated public int synthesizeToFile(final String text, final HashMap params, final String filename) { - return synthesizeToFile(text, params, filename, params.get(Engine.KEY_PARAM_UTTERANCE_ID)); + return synthesizeToFile(text, convertParamsHashMaptoBundle(params), + new File(filename), params.get(Engine.KEY_PARAM_UTTERANCE_ID)); } - private Bundle getParams(HashMap params) { + private Bundle convertParamsHashMaptoBundle(HashMap params) { if (params != null && !params.isEmpty()) { - Bundle bundle = new Bundle(mParams); + Bundle bundle = new Bundle(); copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM); copyIntParam(bundle, params, Engine.KEY_PARAM_SESSION_ID); copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID); @@ -1852,11 +1875,85 @@ public class TextToSpeech { } return bundle; + } + return null; + } + + private Bundle getParams(Bundle params) { + if (params != null && !params.isEmpty()) { + Bundle bundle = new Bundle(mParams); + bundle.putAll(params); + + verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_STREAM); + verifyIntegerBundleParam(bundle, Engine.KEY_PARAM_SESSION_ID); + verifyStringBundleParam(bundle, Engine.KEY_PARAM_UTTERANCE_ID); + verifyFloatBundleParam(bundle, Engine.KEY_PARAM_VOLUME); + verifyFloatBundleParam(bundle, Engine.KEY_PARAM_PAN); + + // Copy feature strings defined by the framework. + verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_SYNTHESIS); + verifyBooleanBundleParam(bundle, Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS); + verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_TIMEOUT_MS); + verifyIntegerBundleParam(bundle, Engine.KEY_FEATURE_NETWORK_RETRIES_COUNT); + + return bundle; } else { return mParams; } } + private static boolean verifyIntegerBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof Integer || + bundle.get(key) instanceof Long)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be an Integer or a Long"); + return false; + } + } + return true; + } + + private static boolean verifyStringBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof String)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be a String"); + return false; + } + } + return true; + } + + private static boolean verifyBooleanBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof Boolean || + bundle.get(key) instanceof String)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be a Boolean or String"); + return false; + } + } + return true; + } + + + private static boolean verifyFloatBundleParam(Bundle bundle, String key) { + if (bundle.containsKey(key)) { + if (!(bundle.get(key) instanceof Float || + bundle.get(key) instanceof Double)) { + bundle.remove(key); + Log.w(TAG, "Synthesis request paramter " + key + " containst value " + + " with invalid type. Should be a Float or a Double"); + return false; + } + } + return true; + } + private void copyStringParam(Bundle bundle, HashMap params, String key) { String value = params.get(key); if (value != null) { diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 4fea109..d00a433 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -84,7 +84,7 @@ import java.util.Set; * the following methods: *
    *
  • {@link #onGetVoices()}
  • - *
  • {@link #isValidVoiceName(String)}
  • + *
  • {@link #onIsValidVoiceName(String)}
  • *
  • {@link #onLoadVoice(String)}
  • *
  • {@link #onGetDefaultVoiceNameFor(String, String, String)}
  • *
@@ -278,7 +278,7 @@ public abstract class TextToSpeechService extends Service { * * @return A list of voices supported. */ - protected List onGetVoices() { + public List onGetVoices() { // Enumerate all locales and check if they are available ArrayList voices = new ArrayList(); for (Locale locale : Locale.getAvailableLocales()) { @@ -335,7 +335,7 @@ public abstract class TextToSpeechService extends Service { } Locale properLocale = TtsEngines.normalizeTTSLocale(iso3Locale); String voiceName = properLocale.toLanguageTag(); - if (isValidVoiceName(voiceName) == TextToSpeech.SUCCESS) { + if (onIsValidVoiceName(voiceName) == TextToSpeech.SUCCESS) { return voiceName; } else { return null; @@ -357,7 +357,7 @@ public abstract class TextToSpeechService extends Service { * @param voiceName Name of the voice. * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}. */ - protected int onLoadVoice(String voiceName) { + public int onLoadVoice(String voiceName) { Locale locale = Locale.forLanguageTag(voiceName); if (locale == null) { return TextToSpeech.ERROR; @@ -388,7 +388,7 @@ public abstract class TextToSpeechService extends Service { * @param voiceName Name of the voice. * @return {@link TextToSpeech#ERROR} or {@link TextToSpeech#SUCCESS}. */ - protected int isValidVoiceName(String voiceName) { + public int onIsValidVoiceName(String voiceName) { Locale locale = Locale.forLanguageTag(voiceName); if (locale == null) { return TextToSpeech.ERROR; @@ -1275,7 +1275,7 @@ public abstract class TextToSpeechService extends Service { if (!checkNonNull(voiceName)) { return TextToSpeech.ERROR; } - int retVal = isValidVoiceName(voiceName); + int retVal = onIsValidVoiceName(voiceName); if (retVal == TextToSpeech.SUCCESS) { SpeechItem item = new LoadVoiceItem(caller, Binder.getCallingUid(), diff --git a/core/java/android/speech/tts/Voice.java b/core/java/android/speech/tts/Voice.java index a97141c..a1fa51d 100644 --- a/core/java/android/speech/tts/Voice.java +++ b/core/java/android/speech/tts/Voice.java @@ -162,7 +162,7 @@ public class Voice implements Parcelable { /** * @return Does the Voice require a network connection to work. */ - public boolean getRequiresNetworkConnection() { + public boolean isNetworkConnectionRequired() { return mRequiresNetworkConnection; } -- cgit v1.1