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; 47d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 48d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath/** 49d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Support class for querying the list of available engines 50d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * on the device and deciding which one to use etc. 51d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 52d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Comments in this class the use the shorthand "system engines" for engines that 53d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * are a part of the system image. 54d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 55d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * @hide 56d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 57d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamathpublic class TtsEngines { 584d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath private static final String TAG = "TtsEngines"; 59e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private static final boolean DBG = false; 60e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 61e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private static final String LOCALE_DELIMITER = "-"; 624d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 63d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private final Context mContext; 64d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 65d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public TtsEngines(Context ctx) { 66d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath mContext = ctx; 67d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 68d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 69d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 700e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath * @return the default TTS engine. If the user has set a default, and the engine 710e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath * is available on the device, the default is returned. Otherwise, 720e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath * the highest ranked engine is returned as per {@link EngineInfoComparator}. 73d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 74d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public String getDefaultEngine() { 75e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String engine = getString(mContext.getContentResolver(), 76d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Settings.Secure.TTS_DEFAULT_SYNTH); 770e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath return isEngineInstalled(engine) ? engine : getHighestRankedEngineName(); 78d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 79d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 80d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 81d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * @return the package name of the highest ranked system engine, {@code null} 82d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * if no TTS engines were present in the system image. 83d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 84d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public String getHighestRankedEngineName() { 85d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath final List<EngineInfo> engines = getEngines(); 86d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 87d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (engines.size() > 0 && engines.get(0).system) { 88d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return engines.get(0).name; 89d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 90d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 91d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return null; 92d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 93d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 94d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 95d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Returns the engine info for a given engine name. Note that engines are 96d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * identified by their package name. 97d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 98d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public EngineInfo getEngineInfo(String packageName) { 99d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath PackageManager pm = mContext.getPackageManager(); 100d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 101d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath intent.setPackage(packageName); 102d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 103d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath PackageManager.MATCH_DEFAULT_ONLY); 104d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Note that the current API allows only one engine per 105d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // package name. Since the "engine name" is the same as 106d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // the package name. 107d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (resolveInfos != null && resolveInfos.size() == 1) { 108d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return getEngineInfo(resolveInfos.get(0), pm); 109d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 110d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 111d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return null; 112d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 113d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 114d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 115d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Gets a list of all installed TTS engines. 116d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * 117d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * @return A list of engine info objects. The list can be empty, but never {@code null}. 118d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 119d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public List<EngineInfo> getEngines() { 120d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath PackageManager pm = mContext.getPackageManager(); 121d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 122d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath List<ResolveInfo> resolveInfos = 123d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath pm.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY); 124d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (resolveInfos == null) return Collections.emptyList(); 125d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 126d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath List<EngineInfo> engines = new ArrayList<EngineInfo>(resolveInfos.size()); 127d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 128d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath for (ResolveInfo resolveInfo : resolveInfos) { 129d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath EngineInfo engine = getEngineInfo(resolveInfo, pm); 130d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (engine != null) { 131d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engines.add(engine); 132d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 133d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 134d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath Collections.sort(engines, EngineInfoComparator.INSTANCE); 135d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 136d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return engines; 137d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 138d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 139d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private boolean isSystemEngine(ServiceInfo info) { 140d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath final ApplicationInfo appInfo = info.applicationInfo; 141d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 142d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 143d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 1440e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath /** 145c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath * @return true if a given engine is installed on the system. 1460e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath */ 147c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath public boolean isEngineInstalled(String engine) { 1480e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath if (engine == null) { 1490e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath return false; 1500e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath } 1510e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath 152c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0Narayan Kamath return getEngineInfo(engine) != null; 1530e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath } 1540e20fe5bab7dc3aff488d133961acfe0239f5240Narayan Kamath 1554d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath /** 1564d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * @return an intent that can launch the settings activity for a given tts engine. 1574d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath */ 1584d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath public Intent getSettingsIntent(String engine) { 1594d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath PackageManager pm = mContext.getPackageManager(); 1604d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 1614d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath intent.setPackage(engine); 1624d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 1634d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath PackageManager.MATCH_DEFAULT_ONLY | PackageManager.GET_META_DATA); 1644d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath // Note that the current API allows only one engine per 1654d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath // package name. Since the "engine name" is the same as 1664d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath // the package name. 1674d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (resolveInfos != null && resolveInfos.size() == 1) { 1684d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath ServiceInfo service = resolveInfos.get(0).serviceInfo; 1694d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (service != null) { 1704d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final String settings = settingsActivityFromServiceInfo(service, pm); 1714d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (settings != null) { 1724d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Intent i = new Intent(); 1734d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath i.setClassName(engine, settings); 1744d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return i; 1754d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1764d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1774d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1784d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 1794d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 1804d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1814d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 1824d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath /** 1834d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * The name of the XML tag that text to speech engines must use to 1844d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * declare their meta data. 1854d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath * 186e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link com.android.internal.R.styleable#TextToSpeechEngine} 1874d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath */ 1884d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath private static final String XML_TAG_NAME = "tts-engine"; 1894d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 1904d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath private String settingsActivityFromServiceInfo(ServiceInfo si, PackageManager pm) { 1914d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath XmlResourceParser parser = null; 1924d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath try { 1934d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath parser = si.loadXmlMetaData(pm, TextToSpeech.Engine.SERVICE_META_DATA); 1944d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (parser == null) { 1954d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "No meta-data found for :" + si); 1964d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 1974d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 1984d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 1994d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final Resources res = pm.getResourcesForApplication(si.applicationInfo); 2004d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2014d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath int type; 2024d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT) { 2034d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (type == XmlResourceParser.START_TAG) { 2044d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (!XML_TAG_NAME.equals(parser.getName())) { 2054d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Package " + si + " uses unknown tag :" 2064d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath + parser.getName()); 2074d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2084d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2094d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2104d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final AttributeSet attrs = Xml.asAttributeSet(parser); 2114d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final TypedArray array = res.obtainAttributes(attrs, 2124d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath com.android.internal.R.styleable.TextToSpeechEngine); 2134d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath final String settings = array.getString( 2144d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath com.android.internal.R.styleable.TextToSpeechEngine_settingsActivity); 2154d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath array.recycle(); 2164d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2174d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return settings; 2184d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2194d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2204d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 2214d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2224d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } catch (NameNotFoundException e) { 2234d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Could not load resources for : " + si); 2244d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2254d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } catch (XmlPullParserException e) { 2264d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Error parsing metadata for " + si + ":" + e); 2274d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2284d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } catch (IOException e) { 2294d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath Log.w(TAG, "Error parsing metadata for " + si + ":" + e); 2304d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath return null; 2314d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } finally { 2324d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath if (parser != null) { 2334d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath parser.close(); 2344d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2354d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2364d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath } 2374d03462b374dfc080f0c7c78d458c102a26be5c6Narayan Kamath 238d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private EngineInfo getEngineInfo(ResolveInfo resolve, PackageManager pm) { 239d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath ServiceInfo service = resolve.serviceInfo; 240d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (service != null) { 241d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath EngineInfo engine = new EngineInfo(); 242d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Using just the package name isn't great, since it disallows having 243d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // multiple engines in the same package, but that's what the existing API does. 244d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.name = service.packageName; 245d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath CharSequence label = service.loadLabel(pm); 246d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString(); 247d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.icon = service.getIconResource(); 248d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.priority = resolve.priority; 249d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath engine.system = isSystemEngine(service); 250d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return engine; 251d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 252d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 253d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return null; 254d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 255d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 256d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private static class EngineInfoComparator implements Comparator<EngineInfo> { 257d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath private EngineInfoComparator() { } 258d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 259d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath static EngineInfoComparator INSTANCE = new EngineInfoComparator(); 260d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 261d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath /** 262d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * Engines that are a part of the system image are always lesser 263d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * than those that are not. Within system engines / non system engines 264d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath * the engines are sorted in order of their declared priority. 265d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath */ 266d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath @Override 267d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath public int compare(EngineInfo lhs, EngineInfo rhs) { 268d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath if (lhs.system && !rhs.system) { 269d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return -1; 270d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } else if (rhs.system && !lhs.system) { 271d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return 1; 272d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } else { 273d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Either both system engines, or both non system 274d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // engines. 275d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // 276d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // Note, this isn't a typo. Higher priority numbers imply 277d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath // higher priority, but are "lower" in the sort order. 278d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath return rhs.priority - lhs.priority; 279d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 280d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 281d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath } 282d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath 283e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 284e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Returns the locale string for a given TTS engine. Attempts to read the 285e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * value from {@link Settings.Secure#TTS_DEFAULT_LOCALE}, failing which the 286e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * old style value from {@link Settings.Secure#TTS_DEFAULT_LANG} is read. If 287e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * both these values are empty, the default phone locale is returned. 288e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * 289e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * @param engineName the engine to return the locale for. 290e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * @return the locale string preference for this engine. Will be non null 291e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * and non empty. 292e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 293e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath public String getLocalePrefForEngine(String engineName) { 294e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String locale = parseEnginePrefFromList( 295e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath getString(mContext.getContentResolver(), Settings.Secure.TTS_DEFAULT_LOCALE), 296e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath engineName); 297e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 298e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(locale)) { 299e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // The new style setting is unset, attempt to return the old style setting. 300e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath locale = getV1Locale(); 301e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 302e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 303e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) Log.d(TAG, "getLocalePrefForEngine(" + engineName + ")= " + locale); 304e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 305e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return locale; 306e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 307e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 308e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 309e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Parses a locale preference value delimited by {@link #LOCALE_DELIMITER}. 310e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Varies from {@link String#split} in that it will always return an array 311e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * of length 3 with non null values. 312e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 313e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath public static String[] parseLocalePref(String pref) { 314e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] returnVal = new String[] { "", "", ""}; 315e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!TextUtils.isEmpty(pref)) { 316e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] split = pref.split(LOCALE_DELIMITER); 317e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath System.arraycopy(split, 0, returnVal, 0, split.length); 318e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 319e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 320e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) Log.d(TAG, "parseLocalePref(" + returnVal[0] + "," + returnVal[1] + 321e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath "," + returnVal[2] +")"); 322e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 323e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return returnVal; 324e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 325e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 326e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 327e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * @return the old style locale string constructed from 328e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link Settings.Secure#TTS_DEFAULT_LANG}, 329e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link Settings.Secure#TTS_DEFAULT_COUNTRY} and 330e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * {@link Settings.Secure#TTS_DEFAULT_VARIANT}. If no such locale is set, 331e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * then return the default phone locale. 332e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 333e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private String getV1Locale() { 334e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final ContentResolver cr = mContext.getContentResolver(); 335e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 336e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String lang = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_LANG); 337e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String country = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_COUNTRY); 338e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String variant = Settings.Secure.getString(cr, Settings.Secure.TTS_DEFAULT_VARIANT); 339e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 340e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(lang)) { 341e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return getDefaultLocale(); 342e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 343e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 344e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String v1Locale = lang; 345e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!TextUtils.isEmpty(country)) { 346e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath v1Locale += LOCALE_DELIMITER + country; 34739268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath } else { 34839268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath return v1Locale; 349e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 35039268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath 351e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!TextUtils.isEmpty(variant)) { 352e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath v1Locale += LOCALE_DELIMITER + variant; 353e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 354e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 355e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return v1Locale; 356e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 357e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 358e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private String getDefaultLocale() { 359e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final Locale locale = Locale.getDefault(); 360e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 36139268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath // Note that the default locale might have an empty variant 36239268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath // or language, and we take care that the construction is 36339268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath // the same as {@link #getV1Locale} i.e no trailing delimiters 36439268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath // or spaces. 36539268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath String defaultLocale = locale.getISO3Language(); 36639268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath if (TextUtils.isEmpty(defaultLocale)) { 36739268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath Log.w(TAG, "Default locale is empty."); 36839268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath return ""; 36939268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath } 37039268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath 37139268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath if (!TextUtils.isEmpty(locale.getISO3Country())) { 37239268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath defaultLocale += LOCALE_DELIMITER + locale.getISO3Country(); 37339268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath } else { 37439268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath // Do not allow locales of the form lang--variant with 37539268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath // an empty country. 37639268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath return defaultLocale; 37739268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath } 37839268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath if (!TextUtils.isEmpty(locale.getVariant())) { 37939268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath defaultLocale += LOCALE_DELIMITER + locale.getVariant(); 38039268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath } 38139268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath 38239268ffcb74f4c177e5e7427b66480c77743f928Narayan Kamath return defaultLocale; 383e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 384e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 385e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 386e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Parses a comma separated list of engine locale preferences. The list is of the 387e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * form {@code "engine_name_1:locale_1,engine_name_2:locale2"} and so on and 388e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * so forth. Returns null if the list is empty, malformed or if there is no engine 389e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * specific preference in the list. 390e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 391e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private static String parseEnginePrefFromList(String prefValue, String engineName) { 392e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(prefValue)) { 393e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return null; 394e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 395e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 396e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] prefValues = prefValue.split(","); 397e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 398e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath for (String value : prefValues) { 399e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final int delimiter = value.indexOf(':'); 400e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (delimiter > 0) { 401e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (engineName.equals(value.substring(0, delimiter))) { 402e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return value.substring(delimiter + 1); 403e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 404e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 405e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 406e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 407e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return null; 408e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 409e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 410e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath public synchronized void updateLocalePrefForEngine(String name, String newLocale) { 411e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String prefList = Settings.Secure.getString(mContext.getContentResolver(), 412e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Settings.Secure.TTS_DEFAULT_LOCALE); 413e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) { 414e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Log.d(TAG, "updateLocalePrefForEngine(" + name + ", " + newLocale + 415e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath "), originally: " + prefList); 416e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 417e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 418e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final String newPrefList = updateValueInCommaSeparatedList(prefList, 419e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath name, newLocale); 420e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 421e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (DBG) Log.d(TAG, "updateLocalePrefForEngine(), writing: " + newPrefList.toString()); 422e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 423e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Settings.Secure.putString(mContext.getContentResolver(), 424e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath Settings.Secure.TTS_DEFAULT_LOCALE, newPrefList.toString()); 425e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 426e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 427e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath /** 428e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * Updates the value for a given key in a comma separated list of key value pairs, 429e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * each of which are delimited by a colon. If no value exists for the given key, 430e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath * the kay value pair are appended to the end of the list. 431e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath */ 432e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath private String updateValueInCommaSeparatedList(String list, String key, 433e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String newValue) { 434e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath StringBuilder newPrefList = new StringBuilder(); 435e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (TextUtils.isEmpty(list)) { 436e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // If empty, create a new list with a single entry. 437e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(key).append(':').append(newValue); 438e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 439e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath String[] prefValues = list.split(","); 440e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Whether this is the first iteration in the loop. 441e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath boolean first = true; 442e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Whether we found the given key. 443e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath boolean found = false; 444e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath for (String value : prefValues) { 445e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath final int delimiter = value.indexOf(':'); 446e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (delimiter > 0) { 447e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (key.equals(value.substring(0, delimiter))) { 448e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (first) { 449e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath first = false; 450e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 451e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(','); 452e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 453e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath found = true; 454e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(key).append(':').append(newValue); 455e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 456e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (first) { 457e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath first = false; 458e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } else { 459e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(','); 460e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 461e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Copy across the entire key + value as is. 462e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(value); 463e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 464e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 465e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 466e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 467e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath if (!found) { 468e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // Not found, but the rest of the keys would have been copied 469e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath // over already, so just append it to the end. 470e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(','); 471e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath newPrefList.append(key).append(':').append(newValue); 472e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 473e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 474e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath 475e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath return newPrefList.toString(); 476e5b8c4dfc70288f661e0da4f082dd51cc1399f86Narayan Kamath } 477d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffceNarayan Kamath} 478