1d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath/* 2d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Copyright (C) 2011 The Android Open Source Project 3d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 4d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * use this file except in compliance with the License. You may obtain a copy of 6d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * the License at 7d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 8d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * http://www.apache.org/licenses/LICENSE-2.0 9d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 10d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Unless required by applicable law or agreed to in writing, software 11d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * License for the specific language governing permissions and limitations under 14d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * the License. 15d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 16d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathpackage android.speech.tts; 17d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 184d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport org.xmlpull.v1.XmlPullParserException; 194d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 20e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamathimport android.content.ContentResolver; 21d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.content.Context; 22d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.content.Intent; 23d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.content.pm.ApplicationInfo; 24d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.content.pm.PackageManager; 254d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport android.content.pm.PackageManager.NameNotFoundException; 26d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.content.pm.ResolveInfo; 27d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.content.pm.ServiceInfo; 284d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport android.content.res.Resources; 294d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport android.content.res.TypedArray; 304d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport android.content.res.XmlResourceParser; 31e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamathimport static android.provider.Settings.Secure.getString; 32e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 33d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.provider.Settings; 34d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.speech.tts.TextToSpeech.Engine; 35d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.speech.tts.TextToSpeech.EngineInfo; 36d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport android.text.TextUtils; 374d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport android.util.AttributeSet; 384d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport android.util.Log; 394d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport android.util.Xml; 40d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 414d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamathimport java.io.IOException; 42d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport java.util.ArrayList; 43d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport java.util.Collections; 44d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport java.util.Comparator; 45d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathimport java.util.List; 46e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamathimport java.util.Locale; 47ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniakimport java.util.MissingResourceException; 48d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 49d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath/** 50d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Support class for querying the list of available engines 51d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * on the device and deciding which one to use etc. 52d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 53d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Comments in this class the use the shorthand "system engines" for engines that 54d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * are a part of the system image. 55d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 56d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * @hide 57d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 58d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathpublic class TtsEngines { 594d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath private static final String TAG = "TtsEngines"; 60e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private static final boolean DBG = false; 61e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 62e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private static final String LOCALE_DELIMITER = "-"; 634d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 64d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private final Context mContext; 65d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 66d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public TtsEngines(Context ctx) { 67d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath mContext = ctx; 68d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 69d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 70d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 710e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath * @return the default TTS engine. If the user has set a default, and the engine 720e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath * is available on the device, the default is returned. Otherwise, 730e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath * the highest ranked engine is returned as per {@link EngineInfoComparator}. 74d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 75d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public String getDefaultEngine() { 76e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String engine = getString(mContext.getContentResolver(), 77d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Settings.Secure.TTS_DEFAULT_SYNTH); 780e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath return isEngineInstalled(engine) ? engine : getHighestRankedEngineName(); 79d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 80d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 81d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 82d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * @return the package name of the highest ranked system engine, {@code null} 83d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * if no TTS engines were present in the system image. 84d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 85d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public String getHighestRankedEngineName() { 86d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath final List<EngineInfo> engines = getEngines(); 87d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 88d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (engines.size() > 0 && engines.get(0).system) { 89d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return engines.get(0).name; 90d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 91d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 92d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return null; 93d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 94d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 95d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 96d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Returns the engine info for a given engine name. Note that engines are 97d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * identified by their package name. 98d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 99d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public EngineInfo getEngineInfo(String packageName) { 100d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath PackageManager pm = mContext.getPackageManager(); 101d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 102d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath intent.setPackage(packageName); 103d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 104d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath PackageManager.MATCH_DEFAULT_ONLY); 105d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Note that the current API allows only one engine per 106d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // package name. Since the "engine name" is the same as 107d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // the package name. 108d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (resolveInfos != null && resolveInfos.size() == 1) { 109d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return getEngineInfo(resolveInfos.get(0), pm); 110d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 111d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 112d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return null; 113d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 114d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 115d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 116d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Gets a list of all installed TTS engines. 117d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 118d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * @return A list of engine info objects. The list can be empty, but never {@code null}. 119d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 120d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public List<EngineInfo> getEngines() { 121d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath PackageManager pm = mContext.getPackageManager(); 122d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 123d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath List<ResolveInfo> resolveInfos = 124d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath pm.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY); 125d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (resolveInfos == null) return Collections.emptyList(); 126d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 127d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath List<EngineInfo> engines = new ArrayList<EngineInfo>(resolveInfos.size()); 128d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 129d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath for (ResolveInfo resolveInfo : resolveInfos) { 130d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath EngineInfo engine = getEngineInfo(resolveInfo, pm); 131d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (engine != null) { 132d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engines.add(engine); 133d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 134d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 135d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Collections.sort(engines, EngineInfoComparator.INSTANCE); 136d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 137d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return engines; 138d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 139d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 140d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private boolean isSystemEngine(ServiceInfo info) { 141d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath final ApplicationInfo appInfo = info.applicationInfo; 142d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 143d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 144d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 1450e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath /** 146c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath * @return true if a given engine is installed on the system. 1470e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath */ 148c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath public boolean isEngineInstalled(String engine) { 1490e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath if (engine == null) { 1500e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath return false; 1510e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath } 1520e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath 153c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath return getEngineInfo(engine) != null; 1540e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath } 1550e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath 1564d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath /** 1574d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * @return an intent that can launch the settings activity for a given tts engine. 1584d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath */ 1594d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath public Intent getSettingsIntent(String engine) { 1604d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath PackageManager pm = mContext.getPackageManager(); 1614d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 1624d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath intent.setPackage(engine); 1634d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 1644d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA); 1654d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath // Note that the current API allows only one engine per 1664d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath // package name. Since the "engine name" is the same as 1674d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath // the package name. 1684d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (resolveInfos != null && resolveInfos.size() == 1) { 1694d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath ServiceInfo service = resolveInfos.get(0).serviceInfo; 1704d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (service != null) { 1714d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final String settings = settingsActivityFromServiceInfo(service, pm); 1724d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (settings != null) { 1734d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Intent i = new Intent(); 1744d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath i.setClassName(engine, settings); 1754d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return i; 1764d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1774d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1784d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1794d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 1804d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 1814d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1824d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 1834d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath /** 1844d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * The name of the XML tag that text to speech engines must use to 1854d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * declare their meta data. 1864d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * 187e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link com.android.internal.R.styleable#TextToSpeechEngine} 1884d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath */ 1894d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath private static final String XML_TAG_NAME = "tts-engine"; 1904d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 1914d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath private String settingsActivityFromServiceInfo(ServiceInfo si, PackageManager pm) { 1924d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath XmlResourceParser parser = null; 1934d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath try { 1944d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath parser = si.loadXmlMetaData(pm, TextToSpeech.Engine.SERVICE_META_DATA); 1954d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (parser == null) { 1964d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "No meta-data found for :" + si); 1974d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 1984d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1994d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2004d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final Resources res = pm.getResourcesForApplication(si.applicationInfo); 2014d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2024d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath int type; 2034d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT) { 2044d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (type == XmlResourceParser.START_TAG) { 2054d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (!XML_TAG_NAME.equals(parser.getName())) { 2064d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Package " + si + " uses unknown tag :" 2074d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath + parser.getName()); 2084d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2094d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2104d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2114d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final AttributeSet attrs = Xml.asAttributeSet(parser); 2124d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final TypedArray array = res.obtainAttributes(attrs, 2134d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath com.android.internal.R.styleable.TextToSpeechEngine); 2144d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final String settings = array.getString( 2154d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath com.android.internal.R.styleable.TextToSpeechEngine_settingsActivity); 2164d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath array.recycle(); 2174d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2184d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return settings; 2194d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2204d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2214d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2224d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2234d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } catch (NameNotFoundException e) { 2244d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Could not load resources for : " + si); 2254d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2264d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } catch (XmlPullParserException e) { 2274d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Error parsing metadata for " + si + ":" + e); 2284d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2294d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } catch (IOException e) { 2304d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Error parsing metadata for " + si + ":" + e); 2314d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2324d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } finally { 2334d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (parser != null) { 2344d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath parser.close(); 2354d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2364d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2374d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2384d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 239d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private EngineInfo getEngineInfo(ResolveInfo resolve, PackageManager pm) { 240d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath ServiceInfo service = resolve.serviceInfo; 241d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (service != null) { 242d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath EngineInfo engine = new EngineInfo(); 243d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Using just the package name isn't great, since it disallows having 244d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // multiple engines in the same package, but that's what the existing API does. 245d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.name = service.packageName; 246d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath CharSequence label = service.loadLabel(pm); 247d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString(); 248d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.icon = service.getIconResource(); 249d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.priority = resolve.priority; 250d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.system = isSystemEngine(service); 251d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return engine; 252d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 253d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 254d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return null; 255d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 256d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 257d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private static class EngineInfoComparator implements Comparator<EngineInfo> { 258d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private EngineInfoComparator() { } 259d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 260d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath static EngineInfoComparator INSTANCE = new EngineInfoComparator(); 261d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 262d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 263d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Engines that are a part of the system image are always lesser 264d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * than those that are not. Within system engines / non system engines 265d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * the engines are sorted in order of their declared priority. 266d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 267d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath @Override 268d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public int compare(EngineInfo lhs, EngineInfo rhs) { 269d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (lhs.system && !rhs.system) { 270d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return -1; 271d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } else if (rhs.system && !lhs.system) { 272d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return 1; 273d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } else { 274d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Either both system engines, or both non system 275d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // engines. 276d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // 277d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Note, this isn't a typo. Higher priority numbers imply 278d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // higher priority, but are "lower" in the sort order. 279d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return rhs.priority - lhs.priority; 280d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 281d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 282d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 283d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 284e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 285e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Returns the locale string for a given TTS engine. Attempts to read the 286e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the 287e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If 288e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * both these values are empty, the default phone locale is returned. 289e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * 290e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * @param engineName the engine to return the locale for. 291e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * @return the locale string preference for this engine. Will be non null 292e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * and non empty. 293e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 294e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath public String getLocalePrefForEngine(String engineName) { 295e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String locale = parseEnginePrefFromList( 296e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE), 297e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath engineName); 298e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 299e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(locale)) { 300e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // The new style setting is unset, attempt to return the old style setting. 301e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath locale = getV1Locale(); 302e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 303e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 304e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) Log.d(TAG, "getLocalePrefForEngine(" + engineName + ")= " + locale); 305e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 306e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return locale; 307e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 308e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 309e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 310e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}. 311e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Varies from {@link String#split} in that it will always return an array 312e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * of length 3 with non null values. 313e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 314e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath public static String[] parseLocalePref(String pref) { 315e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] returnVal = new String[] { "", "", ""}; 316e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!TextUtils.isEmpty(pref)) { 317e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] split = pref.split(LOCALE_DELIMITER); 318e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath System.arraycopy(split, 0, returnVal, 0, split.length); 319e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 320e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 321e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) Log.d(TAG, "parseLocalePref(" + returnVal[0] + "," + returnVal[1] + 322e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath "," + returnVal[2] +")"); 323e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 324e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return returnVal; 325e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 326e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 327e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 328e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * @return the old style locale string constructed from 329e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link Settings.Secure#TTS_DEFAULT_LANG}, 330e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link Settings.Secure#TTS_DEFAULT_COUNTRY} and 331e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link Settings.Secure#TTS_DEFAULT_VARIANT}. If no such locale is set, 332e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * then return the default phone locale. 333e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 334e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private String getV1Locale() { 335e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final ContentResolver cr = mContext.getContentResolver(); 336e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 337e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String lang = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_LANG); 338e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String country = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_COUNTRY); 339e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String variant = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_VARIANT); 340e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 341e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(lang)) { 342e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return getDefaultLocale(); 343e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 344e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 345e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String v1Locale = lang; 346e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!TextUtils.isEmpty(country)) { 347e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath v1Locale += LOCALE_DELIMITER + country; 34839268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath } else { 34939268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath return v1Locale; 350e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 35139268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath 352e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!TextUtils.isEmpty(variant)) { 353e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath v1Locale += LOCALE_DELIMITER + variant; 354e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 355e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 356e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return v1Locale; 357e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 358e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 35940d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak /** 36040d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * Return the default device locale in form of 3 letter codes delimited by 36140d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * {@link #LOCALE_DELIMITER}: 36240d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * <ul> 36340d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * <li> "ISO 639-2/T language code" if locale have no country entry</li> 36440d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country code " 36540d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * if locale have no variant entry</li> 36640d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * <li> "ISO 639-2/T language code{@link #LOCALE_DELIMITER}ISO 3166 country code 36740d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * {@link #LOCALE_DELIMITER} variant" if locale have variant entry</li> 36840d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak * </ul> 36940d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak */ 37040d51e70037c8d32f9bbafd54775e526d86caf23Przemyslaw Szczepaniak public String getDefaultLocale() { 371e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final Locale locale = Locale.getDefault(); 372e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 373ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak try { 374ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // Note that the default locale might have an empty variant 375ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // or language, and we take care that the construction is 376ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // the same as {@link #getV1Locale} i.e no trailing delimiters 377ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // or spaces. 378ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak String defaultLocale = locale.getISO3Language(); 379ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak if (TextUtils.isEmpty(defaultLocale)) { 380ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak Log.w(TAG, "Default locale is empty."); 381ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak return ""; 382ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak } 383ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak 384ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak if (!TextUtils.isEmpty(locale.getISO3Country())) { 385ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak defaultLocale += LOCALE_DELIMITER + locale.getISO3Country(); 386ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak } else { 387ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // Do not allow locales of the form lang--variant with 388ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // an empty country. 389ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak return defaultLocale; 390ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak } 391ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak if (!TextUtils.isEmpty(locale.getVariant())) { 392ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak defaultLocale += LOCALE_DELIMITER + locale.getVariant(); 393ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak } 39439268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath 39539268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath return defaultLocale; 396ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak } catch (MissingResourceException e) { 397ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // Default locale does not have a ISO 3166 and/or ISO 639-2/T codes. Return the 398ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak // default "eng-usa" (that would be the result of Locale.getDefault() == Locale.US). 399ca4f1fc57c86fb7e6094ba4117a027fffd79d227Przemyslaw Szczepaniak return "eng-usa"; 40039268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath } 401e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 402e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 403e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 404e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Parses a comma separated list of engine locale preferences. The list is of the 405e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * form {@code "engine_name_1:locale_1,engine_name_2:locale2"} and so on and 406e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * so forth. Returns null if the list is empty, malformed or if there is no engine 407e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * specific preference in the list. 408e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 409e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private static String parseEnginePrefFromList(String prefValue, String engineName) { 410e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(prefValue)) { 411e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return null; 412e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 413e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 414e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] prefValues = prefValue.split(","); 415e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 416e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath for (String value : prefValues) { 417e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final int delimiter = value.indexOf(':'); 418e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (delimiter > 0) { 419e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (engineName.equals(value.substring(0, delimiter))) { 420e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return value.substring(delimiter + 1); 421e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 422e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 423e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 424e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 425e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return null; 426e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 427e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 428e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath public synchronized void updateLocalePrefForEngine(String name, String newLocale) { 429e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String prefList = Settings.Secure.getString(mContext.getContentResolver(), 430e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Settings.Secure.TTS_DEFAULT_LOCALE); 431e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) { 432e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Log.d(TAG, "updateLocalePrefForEngine(" + name + ", " + newLocale + 433e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath "), originally: " + prefList); 434e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 435e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 436e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String newPrefList = updateValueInCommaSeparatedList(prefList, 437e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath name, newLocale); 438e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 439e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) Log.d(TAG, "updateLocalePrefForEngine(), writing: " + newPrefList.toString()); 440e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 441e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Settings.Secure.putString(mContext.getContentResolver(), 442e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Settings.Secure.TTS_DEFAULT_LOCALE, newPrefList.toString()); 443e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 444e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 445e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 446e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Updates the value for a given key in a comma separated list of key value pairs, 447e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * each of which are delimited by a colon. If no value exists for the given key, 448e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * the kay value pair are appended to the end of the list. 449e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 450e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private String updateValueInCommaSeparatedList(String list, String key, 451e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String newValue) { 452e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath StringBuilder newPrefList = new StringBuilder(); 453e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(list)) { 454e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // If empty, create a new list with a single entry. 455e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(key).append(':').append(newValue); 456e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 457e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] prefValues = list.split(","); 458e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Whether this is the first iteration in the loop. 459e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath boolean first = true; 460e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Whether we found the given key. 461e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath boolean found = false; 462e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath for (String value : prefValues) { 463e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final int delimiter = value.indexOf(':'); 464e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (delimiter > 0) { 465e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (key.equals(value.substring(0, delimiter))) { 466e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (first) { 467e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath first = false; 468e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 469e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(','); 470e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 471e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath found = true; 472e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(key).append(':').append(newValue); 473e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 474e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (first) { 475e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath first = false; 476e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 477e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(','); 478e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 479e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Copy across the entire key + value as is. 480e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(value); 481e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 482e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 483e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 484e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 485e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!found) { 486e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Not found, but the rest of the keys would have been copied 487e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // over already, so just append it to the end. 488e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(','); 489e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(key).append(':').append(newValue); 490e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 491e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 492e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 493e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return newPrefList.toString(); 494e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 495d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath} 496