diff options
author | Przemyslaw Szczepaniak <pszczepaniak@google.com> | 2014-06-13 12:04:00 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2014-06-11 15:57:38 +0000 |
commit | e31ab2d8fcb7413e4775a7cfdd340636a9eea7e7 (patch) | |
tree | 29cb279ff1b39d7ed83e46a2d27f95fbd21a15db /core/java/android/speech | |
parent | 53a1eaa7711af753a276290f9c9b812704dfd023 (diff) | |
parent | 1b5637ee32c5d4e5d857fa86a1b1c1db23d027b7 (diff) | |
download | frameworks_base-e31ab2d8fcb7413e4775a7cfdd340636a9eea7e7.zip frameworks_base-e31ab2d8fcb7413e4775a7cfdd340636a9eea7e7.tar.gz frameworks_base-e31ab2d8fcb7413e4775a7cfdd340636a9eea7e7.tar.bz2 |
Merge "Expose "default tts locale" to the TTS V2 API."
Diffstat (limited to 'core/java/android/speech')
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 3b5490b..bc65280 100644 --- a/core/java/android/speech/tts/RequestConfigHelper.java +++ b/core/java/android/speech/tts/RequestConfigHelper.java @@ -46,7 +46,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) { @@ -70,7 +71,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) { @@ -164,10 +166,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. @@ -179,7 +181,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 a42aa16..938458c 100644 --- a/core/java/android/speech/tts/SynthesisRequestV2.java +++ b/core/java/android/speech/tts/SynthesisRequestV2.java @@ -167,9 +167,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 0c0be83..f726743 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()); |