blob: 11998a3537db2f38358770f90dcbd91083f1e45d [file] [log] [blame]
Jean-Michel Trivied29a652009-06-05 18:37:29 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
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
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,
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.
15 */
16
17package com.android.settings;
18
19import static android.provider.Settings.Secure.TTS_USE_DEFAULTS;
20import static android.provider.Settings.Secure.TTS_DEFAULT_RATE;
Jean-Michel Trivi80368622009-06-09 19:29:27 -070021import static android.provider.Settings.Secure.TTS_DEFAULT_LANG;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070022import static android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY;
23import static android.provider.Settings.Secure.TTS_DEFAULT_VARIANT;
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -070024import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070025
26import android.content.ContentResolver;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070027import android.content.Intent;
28import android.content.pm.ActivityInfo;
29import android.content.pm.PackageManager;
30import android.content.pm.ResolveInfo;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070031import android.os.Bundle;
32import android.preference.ListPreference;
33import android.preference.Preference;
34import android.preference.PreferenceActivity;
35import android.preference.CheckBoxPreference;
36import android.provider.Settings;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070037import android.provider.Settings.SettingNotFoundException;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070038import android.speech.tts.TextToSpeech;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070039import android.util.Log;
40
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070041import java.util.List;
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -070042import java.util.Locale;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070043import java.util.StringTokenizer;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070044
Jean-Michel Trivied29a652009-06-05 18:37:29 -070045public class TextToSpeechSettings extends PreferenceActivity implements
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070046 Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
47 TextToSpeech.OnInitListener {
Jean-Michel Trivied29a652009-06-05 18:37:29 -070048
49 private static final String TAG = "TextToSpeechSettings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070050
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070051 private static final String KEY_TTS_PLAY_EXAMPLE = "tts_play_example";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070052 private static final String KEY_TTS_INSTALL_DATA = "tts_install_data";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070053 private static final String KEY_TTS_USE_DEFAULT = "toggle_use_default_tts_settings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070054 private static final String KEY_TTS_DEFAULT_RATE = "tts_default_rate";
Jean-Michel Trivi80368622009-06-09 19:29:27 -070055 private static final String KEY_TTS_DEFAULT_LANG = "tts_default_lang";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070056 private static final String KEY_TTS_DEFAULT_COUNTRY = "tts_default_country";
57 private static final String KEY_TTS_DEFAULT_VARIANT = "tts_default_variant";
58
59 private static final String LOCALE_DELIMITER = "-";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070060
61 // TODO move this to android.speech.tts.TextToSpeech.Engine
62 private static final String FALLBACK_TTS_DEFAULT_SYNTH = "com.svox.pico";
63
64 private Preference mPlayExample = null;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070065 private Preference mInstallData = null;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070066 private CheckBoxPreference mUseDefaultPref = null;
67 private ListPreference mDefaultRatePref = null;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070068 private ListPreference mDefaultLocPref = null;
69 private String mDefaultLanguage = null;
70 private String mDefaultCountry = null;
71 private String mDefaultLocVariant = null;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070072 private String mDefaultEng = "";
73
74 private boolean mEnableDemo = false;
75
76 private TextToSpeech mTts = null;
77
78 /**
79 * Request code (arbitrary value) for voice data check through
80 * startActivityForResult.
81 */
82 private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -070083 /**
84 * Request code (arbitrary value) for voice data installation through
85 * startActivityForResult.
86 */
87 private static final int VOICE_DATA_INSTALLATION = 1980;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070088
Jean-Michel Trivied29a652009-06-05 18:37:29 -070089 @Override
90 protected void onCreate(Bundle savedInstanceState) {
91 super.onCreate(savedInstanceState);
92
93 addPreferencesFromResource(R.xml.tts_settings);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070094
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -070095 mEnableDemo = false;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070096 initClickers();
Jean-Michel Trivied29a652009-06-05 18:37:29 -070097 initDefaultSettings();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070098 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -070099
100
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700101 @Override
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700102 protected void onStart() {
103 super.onStart();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700104 // whenever we return to this screen, we don't know the state of the
105 // system, so we have to recheck that we can play the demo, or it must be disabled.
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700106 // TODO make the TTS service listen to "changes in the system", i.e. sd card un/mount
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700107 initClickers();
108 updateWidgetState();
109 checkVoiceData();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700110 }
111
112
113 @Override
114 protected void onDestroy() {
115 super.onDestroy();
116 if (mTts != null) {
117 mTts.shutdown();
118 }
119 }
120
121
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700122 private void initClickers() {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700123 mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700124 mPlayExample.setOnPreferenceClickListener(this);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700125
126 mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
127 mInstallData.setOnPreferenceClickListener(this);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700128 }
129
130
131 private void initDefaultSettings() {
132 ContentResolver resolver = getContentResolver();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700133 int intVal = 0;
134
135 // Find the default TTS values in the settings, initialize and store the
136 // settings if they are not found.
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700137
138 // "Use Defaults"
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700139 mUseDefaultPref = (CheckBoxPreference) findPreference(KEY_TTS_USE_DEFAULT);
140 try {
141 intVal = Settings.Secure.getInt(resolver, TTS_USE_DEFAULTS);
142 } catch (SettingNotFoundException e) {
143 // "use default" setting not found, initialize it
144 intVal = TextToSpeech.Engine.FALLBACK_TTS_USE_DEFAULTS;
145 Settings.Secure.putInt(resolver, TTS_USE_DEFAULTS, intVal);
146 }
147 mUseDefaultPref.setChecked(intVal == 1);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700148 mUseDefaultPref.setOnPreferenceChangeListener(this);
149
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700150 // Default engine
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700151 String engine = Settings.Secure.getString(resolver, TTS_DEFAULT_SYNTH);
152 if (engine == null) {
153 // TODO move FALLBACK_TTS_DEFAULT_SYNTH to TextToSpeech
154 engine = FALLBACK_TTS_DEFAULT_SYNTH;
155 Settings.Secure.putString(resolver, TTS_DEFAULT_SYNTH, engine);
156 }
157 mDefaultEng = engine;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700158
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700159 // Default rate
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700160 mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
161 try {
162 intVal = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
163 } catch (SettingNotFoundException e) {
164 // default rate setting not found, initialize it
165 intVal = TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_RATE;
166 Settings.Secure.putInt(resolver, TTS_DEFAULT_RATE, intVal);
167 }
168 mDefaultRatePref.setValue(String.valueOf(intVal));
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700169 mDefaultRatePref.setOnPreferenceChangeListener(this);
170
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700171 // Default language / country / variant : these three values map to a single ListPref
172 // representing the matching Locale
173 String language = null;
174 String country = null;
175 String variant = null;
176 mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700177 language = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700178 if (language == null) {
179 // the default language property isn't set, use that of the current locale
180 Locale currentLocale = Locale.getDefault();
181 language = currentLocale.getISO3Language();
182 country = currentLocale.getISO3Country();
183 variant = currentLocale.getVariant();
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700184 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, language);
185 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, country);
186 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, variant);
Jean-Michel Trivi80368622009-06-09 19:29:27 -0700187 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700188 mDefaultLanguage = language;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700189 if (country == null) {
190 // country wasn't initialized yet because a default language was found
191 country = Settings.Secure.getString(resolver, KEY_TTS_DEFAULT_COUNTRY);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700192 if (country == null) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700193 // default country setting not found, initialize it, as well as the variant;
194 country = TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_COUNTRY;
195 variant = TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_VARIANT;
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700196 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, country);
197 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, variant);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700198 }
199 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700200 mDefaultCountry = country;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700201 if (variant == null) {
202 // variant wasn't initialized yet because a default country was found
203 variant = Settings.Secure.getString(resolver, KEY_TTS_DEFAULT_VARIANT);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700204 if (variant == null) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700205 // default variant setting not found, initialize it
206 variant = TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_VARIANT;
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700207 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, variant);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700208 }
209 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700210 mDefaultLocVariant = variant;
211
212 setDefaultLocalePref(language, country, variant);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700213 mDefaultLocPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700214 }
215
216
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700217 /**
218 * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
219 * to check the required TTS files are properly installed.
220 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700221 private void checkVoiceData() {
222 PackageManager pm = getPackageManager();
223 Intent intent = new Intent();
224 intent.setAction("android.intent.action.CHECK_TTS_DATA");
225 List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
226 // query only the package that matches that of the default engine
227 for (int i = 0; i < resolveInfos.size(); i++) {
228 ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
229 if (mDefaultEng.equals(currentActivityInfo.packageName)) {
230 intent.setClassName(mDefaultEng, currentActivityInfo.name);
231 this.startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
232 }
233 }
234 }
235
236
237 /**
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700238 * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
239 * so the required TTS files are properly installed.
240 */
241 private void installVoiceData() {
242 PackageManager pm = getPackageManager();
243 Intent intent = new Intent();
244 intent.setAction("android.intent.action.INSTALL_TTS_DATA");
245 List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
246 // query only the package that matches that of the default engine
247 for (int i = 0; i < resolveInfos.size(); i++) {
248 ActivityInfo currentActivityInfo = resolveInfos.get(i).activityInfo;
249 if (mDefaultEng.equals(currentActivityInfo.packageName)) {
250 intent.setClassName(mDefaultEng, currentActivityInfo.name);
251 this.startActivityForResult(intent, VOICE_DATA_INSTALLATION);
252 }
253 }
254 }
255
256
257 /**
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700258 * Called when the TTS engine is initialized.
259 */
260 public void onInit(int status) {
261 if (status == TextToSpeech.TTS_SUCCESS) {
262 Log.v(TAG, "TTS engine for settings screen initialized.");
263 mEnableDemo = true;
264 } else {
265 Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
266 mEnableDemo = false;
267 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700268 updateWidgetState();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700269 }
270
271
272 /**
273 * Called when voice data integrity check returns
274 */
275 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
276 if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
277 if (resultCode == TextToSpeech.Engine.CHECK_VOICE_DATA_PASS) {
278 Log.v(TAG, "Voice data check passed");
279 if (mTts == null) {
280 mTts = new TextToSpeech(this, this);
281 }
282 } else {
283 Log.v(TAG, "Voice data check failed");
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700284 mEnableDemo = false;
285 updateWidgetState();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700286 }
287 }
288 }
289
290
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700291 public boolean onPreferenceChange(Preference preference, Object objValue) {
292 if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
293 // "Use Defaults"
294 int value = (Boolean)objValue ? 1 : 0;
Jean-Michel Trivi80368622009-06-09 19:29:27 -0700295 Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS,
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700296 value);
297 Log.i(TAG, "TTS use default settings is "+objValue.toString());
298 } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
299 // Default rate
300 int value = Integer.parseInt((String) objValue);
301 try {
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700302 Settings.Secure.putInt(getContentResolver(),
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700303 TTS_DEFAULT_RATE, value);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700304 if (mTts != null) {
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700305 mTts.setSpeechRate((float)(value/100.0f));
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700306 }
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700307 Log.i(TAG, "TTS default rate is "+value);
308 } catch (NumberFormatException e) {
309 Log.e(TAG, "could not persist default TTS rate setting", e);
310 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700311 } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700312 // Default locale
313 ContentResolver resolver = getContentResolver();
314 parseLocaleInfo((String) objValue);
315 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
316 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
317 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
318 Log.v(TAG, "TTS default lang/country/variant set to "
319 + mDefaultLanguage + "/" + mDefaultCountry + "/" + mDefaultLocVariant);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700320 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700321
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700322 return true;
323 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700324
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700325
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700326 /**
327 * Called when mPlayExample or mInstallData is clicked
328 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700329 public boolean onPreferenceClick(Preference preference) {
330 if (preference == mPlayExample) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700331 // Play example
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700332 if (mTts != null) {
333 mTts.speak(getResources().getString(R.string.tts_demo),
334 TextToSpeech.TTS_QUEUE_FLUSH, null);
335 }
336 return true;
337 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700338 if (preference == mInstallData) {
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700339 installVoiceData();
340 // quit this activity so it needs to be restarted after installation of the voice data
341 finish();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700342 return true;
343 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700344 return false;
345 }
346
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700347
348 private void updateWidgetState() {
349 mPlayExample.setEnabled(mEnableDemo);
350 mUseDefaultPref.setEnabled(mEnableDemo);
351 mDefaultRatePref.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700352 mDefaultLocPref.setEnabled(mEnableDemo);
353
354 mInstallData.setEnabled(!mEnableDemo);
355 }
356
357
358 private void parseLocaleInfo(String locale) {
359 StringTokenizer tokenizer = new StringTokenizer(locale, LOCALE_DELIMITER);
360 mDefaultLanguage = "";
361 mDefaultCountry = "";
362 mDefaultLocVariant = "";
363 if (tokenizer.hasMoreTokens()) {
364 mDefaultLanguage = tokenizer.nextToken().trim();
365 }
366 if (tokenizer.hasMoreTokens()) {
367 mDefaultCountry = tokenizer.nextToken().trim();
368 }
369 if (tokenizer.hasMoreTokens()) {
370 mDefaultLocVariant = tokenizer.nextToken().trim();
371 }
372 }
373
374
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700375 private void setDefaultLocalePref(String language, String country, String variant) {
376 // build a string from the default lang/country/variant trio,
377 String localeString = new String(language);
378 if (country.compareTo("") != 0) {
379 localeString += LOCALE_DELIMITER + country;
380 } else {
381 localeString += LOCALE_DELIMITER + " ";
382 }
383 if (variant.compareTo("") != 0) {
384 localeString += LOCALE_DELIMITER + variant;
385 }
386
387 if (mDefaultLocPref.findIndexOfValue(localeString) > -1) {
388 mDefaultLocPref.setValue(localeString);
389 } else {
390 mDefaultLocPref.setValueIndex(0);
391 }
392
393 Log.v(TAG, "In initDefaultSettings: localeString=" + localeString);
394 }
395
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700396}