From d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffce Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Fri, 10 Jun 2011 16:19:52 +0100 Subject: [PATCH] Refactor some of TextToSpeech required to let the Settings code to the same logic and not have to roll it's own. Change-Id: I2766b8562c940182a2954555fb1854889c3ac91a --- .../java/android/speech/tts/TextToSpeech.java | 146 ++----------- core/java/android/speech/tts/TtsEngines.java | 200 ++++++++++++++++++ 2 files changed, 212 insertions(+), 134 deletions(-) create mode 100644 core/java/android/speech/tts/TtsEngines.java diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 2d466bfcfb722..23fd96fbdd70c 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -22,10 +22,6 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; import android.media.AudioManager; import android.net.Uri; import android.os.Bundle; @@ -35,9 +31,6 @@ import android.provider.Settings; import android.text.TextUtils; import android.util.Log; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -440,6 +433,7 @@ public class TextToSpeech { private final Map mEarcons; private final Map mUtterances; private final Bundle mParams = new Bundle(); + private final TtsEngines mEnginesHelper; private String mCurrentEngine = null; /** @@ -475,6 +469,7 @@ public class TextToSpeech { mEarcons = new HashMap(); mUtterances = new HashMap(); + mEnginesHelper = new TtsEngines(mContext); initTts(); } @@ -504,7 +499,7 @@ public class TextToSpeech { String defaultEngine = getDefaultEngine(); String engine = defaultEngine; if (!areDefaultsEnforced() && !TextUtils.isEmpty(mRequestedEngine) - && isEngineEnabled(engine)) { + && mEnginesHelper.isEngineEnabled(engine)) { engine = mRequestedEngine; } @@ -520,7 +515,7 @@ public class TextToSpeech { } } - final String highestRanked = getHighestRankedEngineName(); + final String highestRanked = mEnginesHelper.getHighestRankedEngineName(); // Fall back to the hardcoded default if different from the two above if (!defaultEngine.equals(highestRanked) && !engine.equals(highestRanked)) { @@ -1076,9 +1071,7 @@ public class TextToSpeech { * as their default. */ public String getDefaultEngine() { - String engine = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.TTS_DEFAULT_SYNTH); - return engine != null ? engine : getHighestRankedEngineName(); + return mEnginesHelper.getDefaultEngine(); } /** @@ -1090,108 +1083,15 @@ public class TextToSpeech { Settings.Secure.TTS_USE_DEFAULTS, Engine.USE_DEFAULTS) == 1; } - private boolean isEngineEnabled(String engine) { - // System engines are enabled by default always. - EngineInfo info = getEngineInfo(engine); - if (info != null && info.system) { - return true; - } - - for (String enabled : getUserEnabledEngines()) { - if (engine.equals(enabled)) { - return true; - } - } - return false; - } - - // Note that in addition to this list, all engines that are a part - // of the system are enabled by default. - private String[] getUserEnabledEngines() { - String str = Settings.Secure.getString(mContext.getContentResolver(), - Settings.Secure.TTS_ENABLED_PLUGINS); - if (TextUtils.isEmpty(str)) { - return new String[0]; - } - return str.split(" "); - } - /** * Gets a list of all installed TTS engines. * * @return A list of engine info objects. The list can be empty, but never {@code null}. */ public List getEngines() { - PackageManager pm = mContext.getPackageManager(); - Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); - List resolveInfos = - pm.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY); - if (resolveInfos == null) return Collections.emptyList(); - - List engines = new ArrayList(resolveInfos.size()); - - for (ResolveInfo resolveInfo : resolveInfos) { - EngineInfo engine = getEngineInfo(resolveInfo, pm); - if (engine != null) { - engines.add(engine); - } - } - Collections.sort(engines, EngineInfoComparator.INSTANCE); - - return engines; + return mEnginesHelper.getEngines(); } - /* - * Returns the highest ranked engine in the system. - */ - private String getHighestRankedEngineName() { - final List engines = getEngines(); - - if (engines.size() > 0 && engines.get(0).system) { - return engines.get(0).name; - } - - return null; - } - - private EngineInfo getEngineInfo(String packageName) { - PackageManager pm = mContext.getPackageManager(); - Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); - intent.setPackage(packageName); - List resolveInfos = pm.queryIntentServices(intent, - PackageManager.MATCH_DEFAULT_ONLY); - // Note that the current API allows only one engine per - // package name. Since the "engine name" is the same as - // the package name. - if (resolveInfos != null && resolveInfos.size() == 1) { - return getEngineInfo(resolveInfos.get(0), pm); - } - - return null; - } - - private EngineInfo getEngineInfo(ResolveInfo resolve, PackageManager pm) { - ServiceInfo service = resolve.serviceInfo; - if (service != null) { - EngineInfo engine = new EngineInfo(); - // Using just the package name isn't great, since it disallows having - // multiple engines in the same package, but that's what the existing API does. - engine.name = service.packageName; - CharSequence label = service.loadLabel(pm); - engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString(); - engine.icon = service.getIconResource(); - engine.priority = resolve.priority; - engine.system = isSystemApp(service); - return engine; - } - - return null; - } - - private boolean isSystemApp(ServiceInfo info) { - final ApplicationInfo appInfo = info.applicationInfo; - return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; - } private class Connection implements ServiceConnection { private ITextToSpeechService mService; @@ -1268,13 +1168,17 @@ public class TextToSpeech { /** * Whether this engine is a part of the system * image. + * + * @hide */ - boolean system; + public boolean system; /** * The priority the engine declares for the the intent filter * {@code android.intent.action.TTS_SERVICE} + * + * @hide */ - int priority; + public int priority; @Override public String toString() { @@ -1283,30 +1187,4 @@ public class TextToSpeech { } - private static class EngineInfoComparator implements Comparator { - private EngineInfoComparator() { } - - static EngineInfoComparator INSTANCE = new EngineInfoComparator(); - - /** - * Engines that are a part of the system image are always lesser - * than those that are not. Within system engines / non system engines - * the engines are sorted in order of their declared priority. - */ - @Override - public int compare(EngineInfo lhs, EngineInfo rhs) { - if (lhs.system && !rhs.system) { - return -1; - } else if (rhs.system && !lhs.system) { - return 1; - } else { - // Either both system engines, or both non system - // engines. - // - // Note, this isn't a typo. Higher priority numbers imply - // higher priority, but are "lower" in the sort order. - return rhs.priority - lhs.priority; - } - } - } } diff --git a/core/java/android/speech/tts/TtsEngines.java b/core/java/android/speech/tts/TtsEngines.java new file mode 100644 index 0000000000000..e8d74eb328934 --- /dev/null +++ b/core/java/android/speech/tts/TtsEngines.java @@ -0,0 +1,200 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package android.speech.tts; + +import android.content.Context; +import android.content.Intent; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.ServiceInfo; +import android.provider.Settings; +import android.speech.tts.TextToSpeech.Engine; +import android.speech.tts.TextToSpeech.EngineInfo; +import android.text.TextUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Support class for querying the list of available engines + * on the device and deciding which one to use etc. + * + * Comments in this class the use the shorthand "system engines" for engines that + * are a part of the system image. + * + * @hide + */ +public class TtsEngines { + private final Context mContext; + + public TtsEngines(Context ctx) { + mContext = ctx; + } + + /** + * @return the default TTS engine. If the user has set a default, that + * value is returned else the highest ranked engine is returned, + * as per {@link EngineInfoComparator}. + */ + public String getDefaultEngine() { + String engine = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.TTS_DEFAULT_SYNTH); + return engine != null ? engine : getHighestRankedEngineName(); + } + + /** + * @return the package name of the highest ranked system engine, {@code null} + * if no TTS engines were present in the system image. + */ + public String getHighestRankedEngineName() { + final List engines = getEngines(); + + if (engines.size() > 0 && engines.get(0).system) { + return engines.get(0).name; + } + + return null; + } + + /** + * Returns the engine info for a given engine name. Note that engines are + * identified by their package name. + */ + public EngineInfo getEngineInfo(String packageName) { + PackageManager pm = mContext.getPackageManager(); + Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); + intent.setPackage(packageName); + List resolveInfos = pm.queryIntentServices(intent, + PackageManager.MATCH_DEFAULT_ONLY); + // Note that the current API allows only one engine per + // package name. Since the "engine name" is the same as + // the package name. + if (resolveInfos != null && resolveInfos.size() == 1) { + return getEngineInfo(resolveInfos.get(0), pm); + } + + return null; + } + + /** + * Gets a list of all installed TTS engines. + * + * @return A list of engine info objects. The list can be empty, but never {@code null}. + */ + public List getEngines() { + PackageManager pm = mContext.getPackageManager(); + Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); + List resolveInfos = + pm.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY); + if (resolveInfos == null) return Collections.emptyList(); + + List engines = new ArrayList(resolveInfos.size()); + + for (ResolveInfo resolveInfo : resolveInfos) { + EngineInfo engine = getEngineInfo(resolveInfo, pm); + if (engine != null) { + engines.add(engine); + } + } + Collections.sort(engines, EngineInfoComparator.INSTANCE); + + return engines; + } + + /** + * Checks whether a given engine is enabled or not. Note that all system + * engines are enabled by default. + */ + public boolean isEngineEnabled(String engine) { + // System engines are enabled by default always. + EngineInfo info = getEngineInfo(engine); + if (info != null && info.system) { + return true; + } + + for (String enabled : getUserEnabledEngines()) { + if (engine.equals(enabled)) { + return true; + } + } + return false; + } + + private boolean isSystemEngine(ServiceInfo info) { + final ApplicationInfo appInfo = info.applicationInfo; + return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; + } + + private EngineInfo getEngineInfo(ResolveInfo resolve, PackageManager pm) { + ServiceInfo service = resolve.serviceInfo; + if (service != null) { + EngineInfo engine = new EngineInfo(); + // Using just the package name isn't great, since it disallows having + // multiple engines in the same package, but that's what the existing API does. + engine.name = service.packageName; + CharSequence label = service.loadLabel(pm); + engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString(); + engine.icon = service.getIconResource(); + engine.priority = resolve.priority; + engine.system = isSystemEngine(service); + return engine; + } + + return null; + } + + // Note that in addition to this list, all engines that are a part + // of the system are enabled by default. + private String[] getUserEnabledEngines() { + String str = Settings.Secure.getString(mContext.getContentResolver(), + Settings.Secure.TTS_ENABLED_PLUGINS); + if (TextUtils.isEmpty(str)) { + return new String[0]; + } + return str.split(" "); + } + + private static class EngineInfoComparator implements Comparator { + private EngineInfoComparator() { } + + static EngineInfoComparator INSTANCE = new EngineInfoComparator(); + + /** + * Engines that are a part of the system image are always lesser + * than those that are not. Within system engines / non system engines + * the engines are sorted in order of their declared priority. + */ + @Override + public int compare(EngineInfo lhs, EngineInfo rhs) { + if (lhs.system && !rhs.system) { + return -1; + } else if (rhs.system && !lhs.system) { + return 1; + } else { + // Either both system engines, or both non system + // engines. + // + // Note, this isn't a typo. Higher priority numbers imply + // higher priority, but are "lower" in the sort order. + return rhs.priority - lhs.priority; + } + } + } + +}