TtsEngines.java revision d3ee2fa18464fb7e4d7f6d27610fbf60b6d1ffce
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, that
52     *         value is returned else the highest ranked engine is returned,
53     *         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 engine != null ? 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 && info.system) {
128            return true;
129        }
130
131        for (String enabled : getUserEnabledEngines()) {
132            if (engine.equals(enabled)) {
133                return true;
134            }
135        }
136        return false;
137    }
138
139    private boolean isSystemEngine(ServiceInfo info) {
140        final ApplicationInfo appInfo = info.applicationInfo;
141        return appInfo != null && (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
142    }
143
144    private EngineInfo getEngineInfo(ResolveInfo resolve, PackageManager pm) {
145        ServiceInfo service = resolve.serviceInfo;
146        if (service != null) {
147            EngineInfo engine = new EngineInfo();
148            // Using just the package name isn't great, since it disallows having
149            // multiple engines in the same package, but that's what the existing API does.
150            engine.name = service.packageName;
151            CharSequence label = service.loadLabel(pm);
152            engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString();
153            engine.icon = service.getIconResource();
154            engine.priority = resolve.priority;
155            engine.system = isSystemEngine(service);
156            return engine;
157        }
158
159        return null;
160    }
161
162    // Note that in addition to this list, all engines that are a part
163    // of the system are enabled by default.
164    private String[] getUserEnabledEngines() {
165        String str = Settings.Secure.getString(mContext.getContentResolver(),
166                Settings.Secure.TTS_ENABLED_PLUGINS);
167        if (TextUtils.isEmpty(str)) {
168            return new String[0];
169        }
170        return str.split(" ");
171    }
172
173    private static class EngineInfoComparator implements Comparator<EngineInfo> {
174        private EngineInfoComparator() { }
175
176        static EngineInfoComparator INSTANCE = new EngineInfoComparator();
177
178        /**
179         * Engines that are a part of the system image are always lesser
180         * than those that are not. Within system engines / non system engines
181         * the engines are sorted in order of their declared priority.
182         */
183        @Override
184        public int compare(EngineInfo lhs, EngineInfo rhs) {
185            if (lhs.system && !rhs.system) {
186                return -1;
187            } else if (rhs.system && !lhs.system) {
188                return 1;
189            } else {
190                // Either both system engines, or both non system
191                // engines.
192                //
193                // Note, this isn't a typo. Higher priority numbers imply
194                // higher priority, but are "lower" in the sort order.
195                return rhs.priority - lhs.priority;
196            }
197        }
198    }
199
200}
201