TtsEngines.java revision c3edf2a01a2cf2123a3de17ec1da11a3b6c459f0
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16package android.speech.tts; 17 18import android.content.Context; 19import android.content.Intent; 20import android.content.pm.ApplicationInfo; 21import android.content.pm.PackageManager; 22import android.content.pm.ResolveInfo; 23import android.content.pm.ServiceInfo; 24import android.provider.Settings; 25import android.speech.tts.TextToSpeech.Engine; 26import android.speech.tts.TextToSpeech.EngineInfo; 27import android.text.TextUtils; 28 29import java.util.ArrayList; 30import java.util.Collections; 31import java.util.Comparator; 32import java.util.List; 33 34/** 35 * Support class for querying the list of available engines 36 * on the device and deciding which one to use etc. 37 * 38 * Comments in this class the use the shorthand "system engines" for engines that 39 * are a part of the system image. 40 * 41 * @hide 42 */ 43public class TtsEngines { 44 private final Context mContext; 45 46 public TtsEngines(Context ctx) { 47 mContext = ctx; 48 } 49 50 /** 51 * @return the default TTS engine. If the user has set a default, and the engine 52 * is available on the device, the default is returned. Otherwise, 53 * the highest ranked engine is returned as per {@link EngineInfoComparator}. 54 */ 55 public String getDefaultEngine() { 56 String engine = Settings.Secure.getString(mContext.getContentResolver(), 57 Settings.Secure.TTS_DEFAULT_SYNTH); 58 return isEngineInstalled(engine) ? engine : getHighestRankedEngineName(); 59 } 60 61 /** 62 * @return the package name of the highest ranked system engine, {@code null} 63 * if no TTS engines were present in the system image. 64 */ 65 public String getHighestRankedEngineName() { 66 final List<EngineInfo> engines = getEngines(); 67 68 if (engines.size() > 0 && engines.get(0).system) { 69 return engines.get(0).name; 70 } 71 72 return null; 73 } 74 75 /** 76 * Returns the engine info for a given engine name. Note that engines are 77 * identified by their package name. 78 */ 79 public EngineInfo getEngineInfo(String packageName) { 80 PackageManager pm = mContext.getPackageManager(); 81 Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 82 intent.setPackage(packageName); 83 List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 84 PackageManager.MATCH_DEFAULT_ONLY); 85 // Note that the current API allows only one engine per 86 // package name. Since the "engine name" is the same as 87 // the package name. 88 if (resolveInfos != null && resolveInfos.size() == 1) { 89 return getEngineInfo(resolveInfos.get(0), pm); 90 } 91 92 return null; 93 } 94 95 /** 96 * Gets a list of all installed TTS engines. 97 * 98 * @return A list of engine info objects. The list can be empty, but never {@code null}. 99 */ 100 public List<EngineInfo> getEngines() { 101 PackageManager pm = mContext.getPackageManager(); 102 Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE); 103 List<ResolveInfo> resolveInfos = 104 pm.queryIntentServices(intent, PackageManager.MATCH_DEFAULT_ONLY); 105 if (resolveInfos == null) return Collections.emptyList(); 106 107 List<EngineInfo> engines = new ArrayList<EngineInfo>(resolveInfos.size()); 108 109 for (ResolveInfo resolveInfo : resolveInfos) { 110 EngineInfo engine = getEngineInfo(resolveInfo, pm); 111 if (engine != null) { 112 engines.add(engine); 113 } 114 } 115 Collections.sort(engines, EngineInfoComparator.INSTANCE); 116 117 return engines; 118 } 119 120 // TODO: Used only by the settings app. Remove once 121 // the settings UI change has been finalized. 122 public boolean isEngineEnabled(String engine) { 123 return isEngineInstalled(engine); 124 } 125 126 private boolean isSystemEngine(ServiceInfo info) { 127 final ApplicationInfo appInfo = info.applicationInfo; 128 return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 129 } 130 131 /** 132 * @return true if a given engine is installed on the system. 133 */ 134 public boolean isEngineInstalled(String engine) { 135 if (engine == null) { 136 return false; 137 } 138 139 return getEngineInfo(engine) != null; 140 } 141 142 private EngineInfo getEngineInfo(ResolveInfo resolve, PackageManager pm) { 143 ServiceInfo service = resolve.serviceInfo; 144 if (service != null) { 145 EngineInfo engine = new EngineInfo(); 146 // Using just the package name isn't great, since it disallows having 147 // multiple engines in the same package, but that's what the existing API does. 148 engine.name = service.packageName; 149 CharSequence label = service.loadLabel(pm); 150 engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString(); 151 engine.icon = service.getIconResource(); 152 engine.priority = resolve.priority; 153 engine.system = isSystemEngine(service); 154 return engine; 155 } 156 157 return null; 158 } 159 160 private static class EngineInfoComparator implements Comparator<EngineInfo> { 161 private EngineInfoComparator() { } 162 163 static EngineInfoComparator INSTANCE = new EngineInfoComparator(); 164 165 /** 166 * Engines that are a part of the system image are always lesser 167 * than those that are not. Within system engines / non system engines 168 * the engines are sorted in order of their declared priority. 169 */ 170 @Override 171 public int compare(EngineInfo lhs, EngineInfo rhs) { 172 if (lhs.system && !rhs.system) { 173 return -1; 174 } else if (rhs.system && !lhs.system) { 175 return 1; 176 } else { 177 // Either both system engines, or both non system 178 // engines. 179 // 180 // Note, this isn't a typo. Higher priority numbers imply 181 // higher priority, but are "lower" in the sort order. 182 return rhs.priority - lhs.priority; 183 } 184 } 185 } 186 187} 188