2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.settings;
19 import static android.provider.Settings.Secure.TTS_USE_DEFAULTS;
20 import static android.provider.Settings.Secure.TTS_DEFAULT_RATE;
21 import static android.provider.Settings.Secure.TTS_DEFAULT_LANG;
22 import static android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY;
23 import static android.provider.Settings.Secure.TTS_DEFAULT_VARIANT;
24 import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
26 import android.content.ContentResolver;
27 import android.content.Intent;
28 import android.content.pm.ActivityInfo;
29 import android.content.pm.PackageManager;
30 import android.content.pm.ResolveInfo;
31 import android.os.Bundle;
32 import android.preference.ListPreference;
33 import android.preference.Preference;
34 import android.preference.PreferenceActivity;
35 import android.preference.CheckBoxPreference;
36 import android.provider.Settings;
37 import android.provider.Settings.SettingNotFoundException;
38 import android.speech.tts.TextToSpeech;
39 import android.util.Log;
41 import java.util.List;
42 import java.util.Locale;
43 import java.util.StringTokenizer;
45 public class TextToSpeechSettings extends PreferenceActivity implements
46 Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
47 TextToSpeech.OnInitListener {
49 private static final String TAG = "TextToSpeechSettings";
51 private static final String KEY_TTS_PLAY_EXAMPLE = "tts_play_example";
52 private static final String KEY_TTS_INSTALL_DATA = "tts_install_data";
53 private static final String KEY_TTS_USE_DEFAULT = "toggle_use_default_tts_settings";
54 private static final String KEY_TTS_DEFAULT_RATE = "tts_default_rate";
55 private static final String KEY_TTS_DEFAULT_LANG = "tts_default_lang";
56 private static final String KEY_TTS_DEFAULT_COUNTRY = "tts_default_country";
57 private static final String KEY_TTS_DEFAULT_VARIANT = "tts_default_variant";
58 private static final String KEY_TTS_DEFAULT_SYNTH = "tts_default_synth";
59 // TODO move default Locale values to TextToSpeech.Engine
60 private static final String DEFAULT_LANG_VAL = "eng";
61 private static final String DEFAULT_COUNTRY_VAL = "USA";
62 private static final String DEFAULT_VARIANT_VAL = "";
64 private static final String LOCALE_DELIMITER = "-";
66 private static final String FALLBACK_TTS_DEFAULT_SYNTH =
67 TextToSpeech.Engine.DEFAULT_SYNTH;
69 private Preference mPlayExample = null;
70 private Preference mInstallData = null;
71 private CheckBoxPreference mUseDefaultPref = null;
72 private ListPreference mDefaultRatePref = null;
73 private ListPreference mDefaultLocPref = null;
74 private ListPreference mDefaultSynthPref = null;
75 private String mDefaultLanguage = null;
76 private String mDefaultCountry = null;
77 private String mDefaultLocVariant = null;
78 private String mDefaultEng = "";
79 private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
81 // Array of strings used to demonstrate TTS in the different languages.
82 private String[] mDemoStrings;
83 // Index of the current string to use for the demo.
84 private int mDemoStringIndex = 0;
86 private boolean mEnableDemo = false;
88 private TextToSpeech mTts = null;
91 * Request code (arbitrary value) for voice data check through
92 * startActivityForResult.
94 private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
95 private static final int GET_SAMPLE_TEXT = 1983;
98 protected void onCreate(Bundle savedInstanceState) {
99 super.onCreate(savedInstanceState);
101 addPreferencesFromResource(R.xml.tts_settings);
103 mDemoStrings = getResources().getStringArray(R.array.tts_demo_strings);
105 setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
109 initDefaultSettings();
114 protected void onStart() {
116 // whenever we return to this screen, we don't know the state of the
117 // system, so we have to recheck that we can play the demo, or it must be disabled.
118 // TODO make the TTS service listen to "changes in the system", i.e. sd card un/mount
126 protected void onDestroy() {
134 private void initClickers() {
135 mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
136 mPlayExample.setOnPreferenceClickListener(this);
138 mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
139 mInstallData.setOnPreferenceClickListener(this);
143 private void initDefaultSettings() {
144 ContentResolver resolver = getContentResolver();
146 // Find the default TTS values in the settings, initialize and store the
147 // settings if they are not found.
151 mUseDefaultPref = (CheckBoxPreference) findPreference(KEY_TTS_USE_DEFAULT);
153 useDefault = Settings.Secure.getInt(resolver, TTS_USE_DEFAULTS);
154 } catch (SettingNotFoundException e) {
155 // "use default" setting not found, initialize it
156 useDefault = TextToSpeech.Engine.USE_DEFAULTS;
157 Settings.Secure.putInt(resolver, TTS_USE_DEFAULTS, useDefault);
159 mUseDefaultPref.setChecked(useDefault == 1);
160 mUseDefaultPref.setOnPreferenceChangeListener(this);
162 // Default synthesis engine
163 mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
165 mDefaultSynthPref.setOnPreferenceChangeListener(this);
166 String engine = Settings.Secure.getString(resolver, TTS_DEFAULT_SYNTH);
167 if (engine == null) {
168 // TODO move FALLBACK_TTS_DEFAULT_SYNTH to TextToSpeech
169 engine = FALLBACK_TTS_DEFAULT_SYNTH;
170 Settings.Secure.putString(resolver, TTS_DEFAULT_SYNTH, engine);
172 mDefaultEng = engine;
175 mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
177 mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
178 } catch (SettingNotFoundException e) {
179 // default rate setting not found, initialize it
180 mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
181 Settings.Secure.putInt(resolver, TTS_DEFAULT_RATE, mDefaultRate);
183 mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
184 mDefaultRatePref.setOnPreferenceChangeListener(this);
186 // Default language / country / variant : these three values map to a single ListPref
187 // representing the matching Locale
188 mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
190 mDefaultLocPref.setOnPreferenceChangeListener(this);
195 * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
196 * to check the required TTS files are properly installed.
198 private void checkVoiceData() {
199 PackageManager pm = getPackageManager();
200 Intent intent = new Intent();
201 intent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
202 List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
203 // query only the package that matches that of the default engine
204 for (int i = 0; i < resolveInfos.size(); i++) {
205 ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
206 if (mDefaultEng.equals(currentActivityInfo.packageName)) {
207 intent.setClassName(mDefaultEng, currentActivityInfo.name);
208 this.startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
215 * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
216 * so the required TTS files are properly installed.
218 private void installVoiceData() {
219 PackageManager pm = getPackageManager();
220 Intent intent = new Intent();
221 intent.setAction(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
222 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
223 List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
224 // query only the package that matches that of the default engine
225 for (int i = 0; i < resolveInfos.size(); i++) {
226 ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
227 if (mDefaultEng.equals(currentActivityInfo.packageName)) {
228 intent.setClassName(mDefaultEng, currentActivityInfo.name);
229 this.startActivity(intent);
235 * Ask the current default engine to return a string of sample text to be
236 * spoken to the user.
238 private void getSampleText() {
239 PackageManager pm = getPackageManager();
240 Intent intent = new Intent();
241 // TODO (clchen): Replace Intent string with the actual
242 // Intent defined in the list of platform Intents.
243 intent.setAction("android.speech.tts.engine.GET_SAMPLE_TEXT");
244 intent.putExtra("language", mDefaultLanguage);
245 intent.putExtra("country", mDefaultCountry);
246 intent.putExtra("variant", mDefaultLocVariant);
247 List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
248 // query only the package that matches that of the default engine
249 for (int i = 0; i < resolveInfos.size(); i++) {
250 ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
251 if (mDefaultEng.equals(currentActivityInfo.packageName)) {
252 intent.setClassName(mDefaultEng, currentActivityInfo.name);
253 this.startActivityForResult(intent, GET_SAMPLE_TEXT);
260 * Called when the TTS engine is initialized.
262 public void onInit(int status) {
263 if (status == TextToSpeech.SUCCESS) {
264 Log.v(TAG, "TTS engine for settings screen initialized.");
266 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry));
267 mTts.setSpeechRate((float)(mDefaultRate/100.0f));
269 Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
277 * Called when voice data integrity check returns
279 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
280 if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
281 if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
282 Log.v(TAG, "Voice data check passed");
284 mTts = new TextToSpeech(this, this);
287 Log.v(TAG, "Voice data check failed");
291 } else if (requestCode == GET_SAMPLE_TEXT) {
292 if (resultCode == TextToSpeech.LANG_AVAILABLE) {
294 String sample = data.getExtras().getString("sampleText");
295 mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
298 // TODO: Display an error here to the user.
299 Log.e(TAG, "Did not have a sample string for the requested language");
305 public boolean onPreferenceChange(Preference preference, Object objValue) {
306 if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
308 int value = (Boolean)objValue ? 1 : 0;
309 Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS,
311 Log.i(TAG, "TTS use default settings is "+objValue.toString());
312 } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
314 mDefaultRate = Integer.parseInt((String) objValue);
316 Settings.Secure.putInt(getContentResolver(),
317 TTS_DEFAULT_RATE, mDefaultRate);
319 mTts.setSpeechRate((float)(mDefaultRate/100.0f));
321 Log.i(TAG, "TTS default rate is " + mDefaultRate);
322 } catch (NumberFormatException e) {
323 Log.e(TAG, "could not persist default TTS rate setting", e);
325 } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
327 ContentResolver resolver = getContentResolver();
328 parseLocaleInfo((String) objValue);
329 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
330 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
331 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
332 Log.v(TAG, "TTS default lang/country/variant set to "
333 + mDefaultLanguage + "/" + mDefaultCountry + "/" + mDefaultLocVariant);
335 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry));
337 int newIndex = mDefaultLocPref.findIndexOfValue((String)objValue);
338 Log.v("Settings", " selected is " + newIndex);
339 mDemoStringIndex = newIndex > -1 ? newIndex : 0;
340 } else if (KEY_TTS_DEFAULT_SYNTH.equals(preference.getKey())) {
341 // TODO: Do a data check here
342 mDefaultEng = objValue.toString();
343 Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, mDefaultEng);
345 mTts.setEngineByPackageName(mDefaultEng);
347 Log.v("Settings", "The default synth is: " + objValue.toString());
355 * Called when mPlayExample or mInstallData is clicked
357 public boolean onPreferenceClick(Preference preference) {
358 if (preference == mPlayExample) {
359 // Get the sample text from the TTS engine; onActivityResult will do
360 // the actual speaking
364 if (preference == mInstallData) {
366 // quit this activity so it needs to be restarted after installation of the voice data
374 private void updateWidgetState() {
375 mPlayExample.setEnabled(mEnableDemo);
376 mUseDefaultPref.setEnabled(mEnableDemo);
377 mDefaultRatePref.setEnabled(mEnableDemo);
378 mDefaultLocPref.setEnabled(mEnableDemo);
380 mInstallData.setEnabled(!mEnableDemo);
384 private void parseLocaleInfo(String locale) {
385 StringTokenizer tokenizer = new StringTokenizer(locale, LOCALE_DELIMITER);
386 mDefaultLanguage = "";
387 mDefaultCountry = "";
388 mDefaultLocVariant = "";
389 if (tokenizer.hasMoreTokens()) {
390 mDefaultLanguage = tokenizer.nextToken().trim();
392 if (tokenizer.hasMoreTokens()) {
393 mDefaultCountry = tokenizer.nextToken().trim();
395 if (tokenizer.hasMoreTokens()) {
396 mDefaultLocVariant = tokenizer.nextToken().trim();
402 * Initialize the default language in the UI and in the preferences.
403 * After this method has been invoked, the default language is a supported Locale.
405 private void initDefaultLang() {
406 // if there isn't already a default language preference
407 if (!hasLangPref()) {
408 // if the current Locale is supported
409 if (isCurrentLocSupported()) {
410 // then use the current Locale as the default language
411 useCurrentLocAsDefault();
413 // otherwise use a default supported Locale as the default language
414 useSupportedLocAsDefault();
418 // Update the language preference list with the default language and the matching
419 // demo string (at this stage there is a default language pref)
420 ContentResolver resolver = getContentResolver();
421 mDefaultLanguage = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
422 mDefaultCountry = Settings.Secure.getString(resolver, KEY_TTS_DEFAULT_COUNTRY);
423 mDefaultLocVariant = Settings.Secure.getString(resolver, KEY_TTS_DEFAULT_VARIANT);
425 // update the demo string
426 mDemoStringIndex = mDefaultLocPref.findIndexOfValue(mDefaultLanguage + LOCALE_DELIMITER
428 if (mDemoStringIndex > -1){
429 mDefaultLocPref.setValueIndex(mDemoStringIndex);
434 * (helper function for initDefaultLang() )
435 * Returns whether there is a default language in the TTS settings.
437 private boolean hasLangPref() {
438 String language = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_LANG);
439 return (language != null);
443 * (helper function for initDefaultLang() )
444 * Returns whether the current Locale is supported by this Settings screen
446 private boolean isCurrentLocSupported() {
447 String currentLocID = Locale.getDefault().getISO3Language() + LOCALE_DELIMITER
448 + Locale.getDefault().getISO3Country();
449 return (mDefaultLocPref.findIndexOfValue(currentLocID) > -1);
453 * (helper function for initDefaultLang() )
454 * Sets the default language in TTS settings to be the current Locale.
455 * This should only be used after checking that the current Locale is supported.
457 private void useCurrentLocAsDefault() {
458 Locale currentLocale = Locale.getDefault();
459 ContentResolver resolver = getContentResolver();
460 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, currentLocale.getISO3Language());
461 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, currentLocale.getISO3Country());
462 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, currentLocale.getVariant());
466 * (helper function for initDefaultLang() )
467 * Sets the default language in TTS settings to be one known to be supported
469 private void useSupportedLocAsDefault() {
470 ContentResolver resolver = getContentResolver();
471 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, DEFAULT_LANG_VAL);
472 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, DEFAULT_COUNTRY_VAL);
473 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, DEFAULT_VARIANT_VAL);
477 private void loadEngines() {
478 ListPreference enginesPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
480 Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
482 ResolveInfo[] enginesArray = new ResolveInfo[0];
483 PackageManager pm = getPackageManager();
484 enginesArray = pm.queryIntentActivities(intent, 0).toArray(enginesArray);
485 CharSequence entries[] = new CharSequence[enginesArray.length];
486 CharSequence values[] = new CharSequence[enginesArray.length];
487 for (int i = 0; i < enginesArray.length; i++) {
488 entries[i] = enginesArray[i].loadLabel(pm);
489 values[i] = enginesArray[i].activityInfo.packageName;
491 enginesPref.setEntries(entries);
492 enginesPref.setEntryValues(values);