summaryrefslogtreecommitdiffstats
path: root/core/java/android/speech
diff options
context:
space:
mode:
authorPrzemyslaw Szczepaniak <pszczepaniak@google.com>2014-05-30 11:00:17 +0100
committerPrzemyslaw Szczepaniak <pszczepaniak@google.com>2014-06-11 13:49:48 +0100
commit1b5637ee32c5d4e5d857fa86a1b1c1db23d027b7 (patch)
tree4c857c995524f7763fb4ef3b3cd280d3a9af7d48 /core/java/android/speech
parentdad8f819a710510be28e9fdeee84f9b5b2ddacbf (diff)
downloadframeworks_base-1b5637ee32c5d4e5d857fa86a1b1c1db23d027b7.zip
frameworks_base-1b5637ee32c5d4e5d857fa86a1b1c1db23d027b7.tar.gz
frameworks_base-1b5637ee32c5d4e5d857fa86a1b1c1db23d027b7.tar.bz2
Expose "default tts locale" to the TTS V2 API.
This change allows TTS V2 API client applications to honor the "default TTS locale" set by the user in the TTS settings. Note that the TTS V1 API uses 3 character country/language codes for locale encoding. It's the only system component that does that. TTS V2 uses the standard, valid Locale objects. The default TTS locale setting was stored as a 3 character locale string with "-" as the separator. This change switches the TTS locale setting format to the output of Locale.toString() call (on a valid Locale object). Methods used for reading this value can interpret both forms and try to return a valid Locale object as an output. Change-Id: Ice2e6c25a43eb9dd6e17d371ee582c2dae3329c9
Diffstat (limited to 'core/java/android/speech')
-rw-r--r--core/java/android/speech/tts/RequestConfigHelper.java12
-rw-r--r--core/java/android/speech/tts/SynthesisRequestV2.java4
-rw-r--r--core/java/android/speech/tts/TextToSpeechClient.java25
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java4
-rw-r--r--core/java/android/speech/tts/TtsEngines.java228
5 files changed, 168 insertions, 105 deletions
diff --git a/core/java/android/speech/tts/RequestConfigHelper.java b/core/java/android/speech/tts/RequestConfigHelper.java
index b25c985..521221b 100644
--- a/core/java/android/speech/tts/RequestConfigHelper.java
+++ b/core/java/android/speech/tts/RequestConfigHelper.java
@@ -31,7 +31,8 @@ public final class RequestConfigHelper {
/**
* Score positively voices that exactly match the given locale
- * @param locale Reference locale. If null, the default locale will be used.
+ * @param locale Reference locale. If null, the system default locale for the
+ * current user will be used ({@link Locale#getDefault()}).
*/
public ExactLocaleMatcher(Locale locale) {
if (locale == null) {
@@ -55,7 +56,8 @@ public final class RequestConfigHelper {
/**
* Score positively voices with similar locale.
- * @param locale Reference locale. If null, default will be used.
+ * @param locale Reference locale. If null, the system default locale for the
+ * current user will be used ({@link Locale#getDefault()}).
*/
public LanguageMatcher(Locale locale) {
if (locale == null) {
@@ -149,10 +151,10 @@ public final class RequestConfigHelper {
}
/**
- * Get highest quality voice for the default locale.
+ * Get highest quality voice for the TTS default locale.
*
* Call {@link #highestQuality(EngineStatus, boolean, VoiceScorer)} with
- * {@link LanguageMatcher} set to device default locale.
+ * {@link LanguageMatcher} set to the {@link EngineStatus#getDefaultLocale()}.
*
* @param engineStatus
* Voices status received from a {@link TextToSpeechClient#getEngineStatus()} call.
@@ -164,7 +166,7 @@ public final class RequestConfigHelper {
public static RequestConfig highestQuality(EngineStatus engineStatus,
boolean hasToBeEmbedded) {
return highestQuality(engineStatus, hasToBeEmbedded,
- new LanguageMatcher(Locale.getDefault()));
+ new LanguageMatcher(engineStatus.getDefaultLocale()));
}
}
diff --git a/core/java/android/speech/tts/SynthesisRequestV2.java b/core/java/android/speech/tts/SynthesisRequestV2.java
index 130e3f9..4ecdbc0 100644
--- a/core/java/android/speech/tts/SynthesisRequestV2.java
+++ b/core/java/android/speech/tts/SynthesisRequestV2.java
@@ -152,9 +152,7 @@ public final class SynthesisRequestV2 implements Parcelable {
}
};
- /**
- * @hide
- */
+ /** @hide */
@Override
public int describeContents() {
return 0;
diff --git a/core/java/android/speech/tts/TextToSpeechClient.java b/core/java/android/speech/tts/TextToSpeechClient.java
index e17b498..5a21229 100644
--- a/core/java/android/speech/tts/TextToSpeechClient.java
+++ b/core/java/android/speech/tts/TextToSpeechClient.java
@@ -42,6 +42,7 @@ import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -357,9 +358,13 @@ public class TextToSpeechClient {
/** Name of the TTS engine package */
private final String mPackageName;
- private EngineStatus(String packageName, List<VoiceInfo> voices) {
+ /** Engine default locale */
+ private final Locale mDefaultLocale;
+
+ private EngineStatus(String packageName, List<VoiceInfo> voices, Locale defaultLocale) {
this.mVoices = Collections.unmodifiableList(voices);
this.mPackageName = packageName;
+ this.mDefaultLocale = defaultLocale;
}
/**
@@ -375,6 +380,16 @@ public class TextToSpeechClient {
public String getEnginePackage() {
return mPackageName;
}
+
+ /**
+ * Get the default locale to use for TTS with this TTS engine.
+ * Unless the user changed the TTS settings for this engine, the value returned should be
+ * the same as the system default locale for the current user
+ * ({@link Locale#getDefault()}).
+ */
+ public Locale getDefaultLocale() {
+ return mDefaultLocale;
+ }
}
/** Unique synthesis request identifier. */
@@ -638,7 +653,9 @@ public class TextToSpeechClient {
return null;
}
- return new EngineStatus(mServiceConnection.getEngineName(), voices);
+ return new EngineStatus(mServiceConnection.getEngineName(), voices,
+ mEnginesHelper.getLocalePrefForEngine(
+ mServiceConnection.getEngineName()));
}
private class Connection implements ServiceConnection {
@@ -696,7 +713,9 @@ public class TextToSpeechClient {
public void onVoicesInfoChange(List<VoiceInfo> voicesInfo) {
synchronized (mLock) {
mEngineStatus = new EngineStatus(mServiceConnection.getEngineName(),
- voicesInfo);
+ voicesInfo,
+ mEnginesHelper.getLocalePrefForEngine(
+ mServiceConnection.getEngineName()));
mMainHandler.obtainMessage(InternalHandler.WHAT_ENGINE_STATUS_CHANGED,
mEngineStatus).sendToTarget();
}
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index 14a4024..20f3ad7 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -460,8 +460,8 @@ public abstract class TextToSpeechService extends Service {
}
private String[] getSettingsLocale() {
- final String locale = mEngineHelper.getLocalePrefForEngine(mPackageName);
- return TtsEngines.parseLocalePref(locale);
+ final Locale locale = mEngineHelper.getLocalePrefForEngine(mPackageName);
+ return TtsEngines.toOldLocaleStringFormat(locale);
}
private int getSecureSettingInt(String name, int defaultValue) {
diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java
index 9b929a3..b4c2824 100644
--- a/core/java/android/speech/tts/TtsEngines.java
+++ b/core/java/android/speech/tts/TtsEngines.java
@@ -28,6 +28,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+
import static android.provider.Settings.Secure.getString;
import android.provider.Settings;
@@ -42,8 +43,10 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.MissingResourceException;
/**
@@ -53,16 +56,52 @@ import java.util.MissingResourceException;
* Comments in this class the use the shorthand "system engines" for engines that
* are a part of the system image.
*
+ * This class is thread-safe/
+ *
* @hide
*/
public class TtsEngines {
private static final String TAG = "TtsEngines";
private static final boolean DBG = false;
- private static final String LOCALE_DELIMITER = "-";
+ /** Locale delimiter used by the old-style 3 char locale string format (like "eng-usa") */
+ private static final String LOCALE_DELIMITER_OLD = "-";
+
+ /** Locale delimiter used by the new-style locale string format (Locale.toString() results,
+ * like "en_US") */
+ private static final String LOCALE_DELIMITER_NEW = "_";
private final Context mContext;
+ /** Mapping of various language strings to the normalized Locale form */
+ private static final Map<String, String> sNormalizeLanguage;
+
+ /** Mapping of various country strings to the normalized Locale form */
+ private static final Map<String, String> sNormalizeCountry;
+
+ // Populate the sNormalize* maps
+ static {
+ HashMap<String, String> normalizeLanguage = new HashMap<String, String>();
+ for (String language : Locale.getISOLanguages()) {
+ try {
+ normalizeLanguage.put(new Locale(language).getISO3Language(), language);
+ } catch (MissingResourceException e) {
+ continue;
+ }
+ }
+ sNormalizeLanguage = Collections.unmodifiableMap(normalizeLanguage);
+
+ HashMap<String, String> normalizeCountry = new HashMap<String, String>();
+ for (String country : Locale.getISOCountries()) {
+ try {
+ normalizeCountry.put(new Locale("", country).getISO3Country(), country);
+ } catch (MissingResourceException e) {
+ continue;
+ }
+ }
+ sNormalizeCountry = Collections.unmodifiableMap(normalizeCountry);
+ }
+
public TtsEngines(Context ctx) {
mContext = ctx;
}
@@ -282,139 +321,139 @@ public class TtsEngines {
}
/**
- * Returns the locale string for a given TTS engine. Attempts to read the
+ * Returns the default locale for a given TTS engine. Attempts to read the
* value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the
- * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If
- * both these values are empty, the default phone locale is returned.
+ * default phone locale is returned.
*
* @param engineName the engine to return the locale for.
- * @return the locale string preference for this engine. Will be non null
- * and non empty.
+ * @return the locale preference for this engine. Will be non null.
*/
- public String getLocalePrefForEngine(String engineName) {
- String locale = parseEnginePrefFromList(
- getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE),
+ public Locale getLocalePrefForEngine(String engineName) {
+ return getLocalePrefForEngine(engineName,
+ getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE));
+ }
+
+ /**
+ * Returns the default locale for a given TTS engine from given settings string. */
+ public Locale getLocalePrefForEngine(String engineName, String prefValue) {
+ String localeString = parseEnginePrefFromList(
+ prefValue,
engineName);
- if (TextUtils.isEmpty(locale)) {
+ if (TextUtils.isEmpty(localeString)) {
// The new style setting is unset, attempt to return the old style setting.
- locale = getV1Locale();
+ return Locale.getDefault();
+ }
+
+ Locale result = parseLocaleString(localeString);
+ if (result == null) {
+ Log.w(TAG, "Failed to parse locale " + localeString + ", returning en_US instead");
+ result = Locale.US;
}
- if (DBG) Log.d(TAG, "getLocalePrefForEngine(" + engineName + ")= " + locale);
+ if (DBG) Log.d(TAG, "getLocalePrefForEngine(" + engineName + ")= " + result);
- return locale;
+ return result;
}
+
/**
* True if a given TTS engine uses the default phone locale as a default locale. Attempts to
- * read the value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the
- * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If
- * both these values are empty, this methods returns true.
+ * read the value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}. If
+ * its value is empty, this methods returns true.
*
* @param engineName the engine to return the locale for.
*/
public boolean isLocaleSetToDefaultForEngine(String engineName) {
- return (TextUtils.isEmpty(parseEnginePrefFromList(
+ return TextUtils.isEmpty(parseEnginePrefFromList(
getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE),
- engineName)) &&
- TextUtils.isEmpty(
- Settings.Secure.getString(mContext.getContentResolver(),
- Settings.Secure.TTS_DEFAULT_LANG)));
+ engineName));
}
-
/**
- * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}.
- * Varies from {@link String#split} in that it will always return an array
- * of length 3 with non null values.
+ * Parses a locale encoded as a string, and tries its best to return a valid {@link Locale}
+ * object, even if the input string is encoded using the old-style 3 character format e.g.
+ * "deu-deu". At the end, we test if the resulting locale can return ISO3 language and
+ * country codes ({@link Locale#getISO3Language()} and {@link Locale#getISO3Country()}),
+ * if it fails to do so, we return null.
*/
- public static String[] parseLocalePref(String pref) {
- String[] returnVal = new String[] { "", "", ""};
- if (!TextUtils.isEmpty(pref)) {
- String[] split = pref.split(LOCALE_DELIMITER);
- System.arraycopy(split, 0, returnVal, 0, split.length);
- }
-
- if (DBG) Log.d(TAG, "parseLocalePref(" + returnVal[0] + "," + returnVal[1] +
- "," + returnVal[2] +")");
-
- return returnVal;
- }
-
- /**
- * @return the old style locale string constructed from
- * {@link Settings.Secure#TTS_DEFAULT_LANG},
- * {@link Settings.Secure#TTS_DEFAULT_COUNTRY} and
- * {@link Settings.Secure#TTS_DEFAULT_VARIANT}. If no such locale is set,
- * then return the default phone locale.
- */
- private String getV1Locale() {
- final ContentResolver cr = mContext.getContentResolver();
-
- final String lang = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_LANG);
- final String country = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_COUNTRY);
- final String variant = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_VARIANT);
+ public Locale parseLocaleString(String localeString) {
+ String language = "", country = "", variant = "";
+ if (!TextUtils.isEmpty(localeString)) {
+ String[] split = localeString.split(
+ "[" + LOCALE_DELIMITER_OLD + LOCALE_DELIMITER_NEW + "]");
+ language = split[0].toLowerCase();
+ if (split.length == 0) {
+ Log.w(TAG, "Failed to convert " + localeString + " to a valid Locale object. Only" +
+ " separators");
+ return null;
+ }
+ if (split.length > 3) {
+ Log.w(TAG, "Failed to convert " + localeString + " to a valid Locale object. Too" +
+ " many separators");
+ return null;
+ }
+ if (split.length >= 2) {
+ country = split[1].toUpperCase();
+ }
+ if (split.length >= 3) {
+ variant = split[2];
+ }
- if (TextUtils.isEmpty(lang)) {
- return getDefaultLocale();
}
- String v1Locale = lang;
- if (!TextUtils.isEmpty(country)) {
- v1Locale += LOCALE_DELIMITER + country;
- } else {
- return v1Locale;
+ String normalizedLanguage = sNormalizeLanguage.get(language);
+ if (normalizedLanguage != null) {
+ language = normalizedLanguage;
}
- if (!TextUtils.isEmpty(variant)) {
- v1Locale += LOCALE_DELIMITER + variant;
+ String normalizedCountry= sNormalizeCountry.get(country);
+ if (normalizedCountry != null) {
+ country = normalizedCountry;
}
- return v1Locale;
+ if (DBG) Log.d(TAG, "parseLocalePref(" + language + "," + country +
+ "," + variant +")");
+
+ Locale result = new Locale(language, country, variant);
+ try {
+ result.getISO3Language();
+ result.getISO3Country();
+ return result;
+ } catch(MissingResourceException e) {
+ Log.w(TAG, "Failed to convert " + localeString + " to a valid Locale object.");
+ return null;
+ }
}
/**
- * Return the default device locale in form of 3 letter codes delimited by
- * {@link #LOCALE_DELIMITER}:
+ * Return the old-style string form of the locale. It consists of 3 letter codes:
* <ul>
- * <li> "ISO 639-2/T language code" if locale have no country entry</li>
- * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country code "
- * if locale have no variant entry</li>
- * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country code
- * {@link #LOCALE_DELIMITER} variant" if locale have variant entry</li>
+ * <li>"ISO 639-2/T language code" if the locale has no country entry</li>
+ * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country code"
+ * if the locale has no variant entry</li>
+ * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country
+ * code{@link #LOCALE_DELIMITER}variant" if the locale has a variant entry</li>
* </ul>
+ * If we fail to generate those codes using {@link Locale#getISO3Country()} and
+ * {@link Locale#getISO3Language()}, then we return new String[]{"eng","USA",""};
*/
- public String getDefaultLocale() {
- final Locale locale = Locale.getDefault();
-
+ static public String[] toOldLocaleStringFormat(Locale locale) {
+ String[] ret = new String[]{"","",""};
try {
// Note that the default locale might have an empty variant
// or language, and we take care that the construction is
// the same as {@link #getV1Locale} i.e no trailing delimiters
// or spaces.
- String defaultLocale = locale.getISO3Language();
- if (TextUtils.isEmpty(defaultLocale)) {
- Log.w(TAG, "Default locale is empty.");
- return "";
- }
+ ret[0] = locale.getISO3Language();
+ ret[1] = locale.getISO3Country();
+ ret[2] = locale.getVariant();
- if (!TextUtils.isEmpty(locale.getISO3Country())) {
- defaultLocale += LOCALE_DELIMITER + locale.getISO3Country();
- } else {
- // Do not allow locales of the form lang--variant with
- // an empty country.
- return defaultLocale;
- }
- if (!TextUtils.isEmpty(locale.getVariant())) {
- defaultLocale += LOCALE_DELIMITER + locale.getVariant();
- }
-
- return defaultLocale;
+ return ret;
} catch (MissingResourceException e) {
// Default locale does not have a ISO 3166 and/or ISO 639-2/T codes. Return the
// default "eng-usa" (that would be the result of Locale.getDefault() == Locale.US).
- return "eng-usa";
+ return new String[]{"eng","USA",""};
}
}
@@ -443,16 +482,21 @@ public class TtsEngines {
return null;
}
- public synchronized void updateLocalePrefForEngine(String name, String newLocale) {
+ /**
+ * Serialize the locale to a string and store it as a default locale for the given engine. If
+ * the passed locale is null, an empty string will be serialized; that empty string, when
+ * read back, will evaluate to {@line Locale#getDefault()}.
+ */
+ public synchronized void updateLocalePrefForEngine(String engineName, Locale newLocale) {
final String prefList = Settings.Secure.getString(mContext.getContentResolver(),
Settings.Secure.TTS_DEFAULT_LOCALE);
if (DBG) {
- Log.d(TAG, "updateLocalePrefForEngine(" + name + ", " + newLocale +
+ Log.d(TAG, "updateLocalePrefForEngine(" + engineName + ", " + newLocale +
"), originally: " + prefList);
}
final String newPrefList = updateValueInCommaSeparatedList(prefList,
- name, newLocale);
+ engineName, (newLocale != null) ? newLocale.toString() : "");
if (DBG) Log.d(TAG, "updateLocalePrefForEngine(), writing: " + newPrefList.toString());