TtsEngines.java revision 0e20fe5bab7dc3aff488d133961acfe0239f5240
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    /**
121     * Checks whether a given engine is enabled or not. Note that all system
122     * engines are enabled by default.
123     */
124    public boolean isEngineEnabled(String engine) {
125        // System engines are enabled by default always.
126        EngineInfo info = getEngineInfo(engine);
127        if (info == null) {
128            // The engine is not installed, and therefore cannot
129            // be enabled.
130            return false;
131        }
132
133        if (info.system) {
134            // All system engines are enabled by default.
135            return true;
136        }
137
138        for (String enabled : getUserEnabledEngines()) {
139            if (engine.equals(enabled)) {
140                return true;
141            }
142        }
143        return false;
144    }
145
146    private boolean isSystemEngine(ServiceInfo info) {
147        final ApplicationInfo appInfo = info.applicationInfo;
148        return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
149    }
150
151    /**
152     * @return true if a given engine is installed on the system. Useful to deal
153     *         with cases where an engine has been uninstalled by the user or removed
154     *         for any other reason.
155     */
156    private boolean isEngineInstalled(String engine) {
157        if (engine == null) {
158            return false;
159        }
160
161        for (EngineInfo info : getEngines()) {
162            if (engine.equals(info.name)) {
163                return true;
164            }
165        }
166
167        return false;
168    }
169
170    private EngineInfo getEngineInfo(ResolveInfo resolve, PackageManager pm) {
171        ServiceInfo service = resolve.serviceInfo;
172        if (service != null) {
173            EngineInfo engine = new EngineInfo();
174            // Using just the package name isn't great, since it disallows having
175            // multiple engines in the same package, but that's what the existing API does.
176            engine.name = service.packageName;
177            CharSequence label = service.loadLabel(pm);
178            engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString();
179            engine.icon = service.getIconResource();
180            engine.priority = resolve.priority;
181            engine.system = isSystemEngine(service);
182            return engine;
183        }
184
185        return null;
186    }
187
188    // Note that in addition to this list, all engines that are a part
189    // of the system are enabled by default.
190    private String[] getUserEnabledEngines() {
191        String str = Settings.Secure.getString(mContext.getContentResolver(),
192                Settings.Secure.TTS_ENABLED_PLUGINS);
193        if (TextUtils.isEmpty(str)) {
194            return new String[0];
195        }
196        return str.split(" ");
197    }
198
199    private static class EngineInfoComparator implements Comparator<EngineInfo> {
200        private EngineInfoComparator() { }
201
202        static EngineInfoComparator INSTANCE = new EngineInfoComparator();
203
204        /**
205         * Engines that are a part of the system image are always lesser
206         * than those that are not. Within system engines / non system engines
207         * the engines are sorted in order of their declared priority.
208         */
209        @Override
210        public int compare(EngineInfo lhs, EngineInfo rhs) {
211            if (lhs.system && !rhs.system) {
212                return -1;
213            } else if (rhs.system && !lhs.system) {
214                return 1;
215            } else {
216                // Either both system engines, or both non system
217                // engines.
218                //
219                // Note, this isn't a typo. Higher priority numbers imply
220                // higher priority, but are "lower" in the sort order.
221                return rhs.priority - lhs.priority;
222            }
223        }
224    }
225
226}
227