OSDN Git Service

Fixing bug 2418114. Added a check to make sure the TTS sample string for
[android-x86/packages-apps-Settings.git] / src / com / android / settings / TextToSpeechSettings.java
1 /*
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
17 package com.android.settings;
18
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;
25
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;
40
41 import java.util.List;
42 import java.util.Locale;
43 import java.util.StringTokenizer;
44
45 public class TextToSpeechSettings extends PreferenceActivity implements
46         Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
47         TextToSpeech.OnInitListener {
48
49     private static final String TAG = "TextToSpeechSettings";
50
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 = "";
63
64     private static final String LOCALE_DELIMITER = "-";
65
66     private static final String FALLBACK_TTS_DEFAULT_SYNTH =
67             TextToSpeech.Engine.DEFAULT_SYNTH;
68
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;
80
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;
85
86     private boolean mEnableDemo = false;
87
88     private TextToSpeech mTts = null;
89
90     /**
91      * Request code (arbitrary value) for voice data check through
92      * startActivityForResult.
93      */
94     private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
95     private static final int GET_SAMPLE_TEXT = 1983;
96
97     @Override
98     protected void onCreate(Bundle savedInstanceState) {
99         super.onCreate(savedInstanceState);
100
101         addPreferencesFromResource(R.xml.tts_settings);
102
103         mDemoStrings = getResources().getStringArray(R.array.tts_demo_strings);
104
105         setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
106
107         mEnableDemo = false;
108         initClickers();
109         initDefaultSettings();
110     }
111
112
113     @Override
114     protected void onStart() {
115         super.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
119         initClickers();
120         updateWidgetState();
121         checkVoiceData();
122     }
123
124
125     @Override
126     protected void onDestroy() {
127         super.onDestroy();
128         if (mTts != null) {
129             mTts.shutdown();
130         }
131     }
132
133
134     private void initClickers() {
135         mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
136         mPlayExample.setOnPreferenceClickListener(this);
137
138         mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
139         mInstallData.setOnPreferenceClickListener(this);
140     }
141
142
143     private void initDefaultSettings() {
144         ContentResolver resolver = getContentResolver();
145
146         // Find the default TTS values in the settings, initialize and store the
147         // settings if they are not found.
148
149         // "Use Defaults"
150         int useDefault = 0;
151         mUseDefaultPref = (CheckBoxPreference) findPreference(KEY_TTS_USE_DEFAULT);
152         try {
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);
158         }
159         mUseDefaultPref.setChecked(useDefault == 1);
160         mUseDefaultPref.setOnPreferenceChangeListener(this);
161
162         // Default synthesis engine
163         mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
164         loadEngines();
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);
171         }
172         mDefaultEng = engine;
173
174         // Default rate
175         mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
176         try {
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);
182         }
183         mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
184         mDefaultRatePref.setOnPreferenceChangeListener(this);
185
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);
189         initDefaultLang();
190         mDefaultLocPref.setOnPreferenceChangeListener(this);
191     }
192
193
194     /**
195      * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
196      * to check the required TTS files are properly installed.
197      */
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);
209             }
210         }
211     }
212
213
214     /**
215      * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
216      * so the required TTS files are properly installed.
217      */
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);
230             }
231         }
232     }
233
234     /**
235      * Ask the current default engine to return a string of sample text to be
236      * spoken to the user.
237      */
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);
254             }
255         }
256     }
257
258
259     /**
260      * Called when the TTS engine is initialized.
261      */
262     public void onInit(int status) {
263         if (status == TextToSpeech.SUCCESS) {
264             Log.v(TAG, "TTS engine for settings screen initialized.");
265             mEnableDemo = true;
266             mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry));
267             mTts.setSpeechRate((float)(mDefaultRate/100.0f));
268         } else {
269             Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
270             mEnableDemo = false;
271         }
272         updateWidgetState();
273     }
274
275
276     /**
277      * Called when voice data integrity check returns
278      */
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");
283                 if (mTts == null) {
284                     mTts = new TextToSpeech(this, this);
285                 }
286             } else {
287                 Log.v(TAG, "Voice data check failed");
288                 mEnableDemo = false;
289                 updateWidgetState();
290             }
291         } else if (requestCode == GET_SAMPLE_TEXT) {
292             if (resultCode == TextToSpeech.LANG_AVAILABLE) {
293                 if (mTts != null) {
294                     String sample = data.getExtras().getString("sampleText");
295                     mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
296                 }
297             } else {
298                 // TODO: Display an error here to the user.
299                 Log.e(TAG, "Did not have a sample string for the requested language");
300             }
301         }
302     }
303
304
305     public boolean onPreferenceChange(Preference preference, Object objValue) {
306         if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
307             // "Use Defaults"
308             int value = (Boolean)objValue ? 1 : 0;
309             Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS,
310                     value);
311             Log.i(TAG, "TTS use default settings is "+objValue.toString());
312         } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
313             // Default rate
314             mDefaultRate = Integer.parseInt((String) objValue);
315             try {
316                 Settings.Secure.putInt(getContentResolver(),
317                         TTS_DEFAULT_RATE, mDefaultRate);
318                 if (mTts != null) {
319                     mTts.setSpeechRate((float)(mDefaultRate/100.0f));
320                 }
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);
324             }
325         } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
326             // Default locale
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);
334             if (mTts != null) {
335                 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry));
336             }
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);
344             if (mTts != null) {
345                 mTts.setEngineByPackageName(mDefaultEng);
346             }
347             Log.v("Settings", "The default synth is: " + objValue.toString());
348         }
349
350         return true;
351     }
352
353
354     /**
355      * Called when mPlayExample or mInstallData is clicked
356      */
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
361             getSampleText();
362             return true;
363         }
364         if (preference == mInstallData) {
365             installVoiceData();
366             // quit this activity so it needs to be restarted after installation of the voice data
367             finish();
368             return true;
369         }
370         return false;
371     }
372
373
374     private void updateWidgetState() {
375         mPlayExample.setEnabled(mEnableDemo);
376         mUseDefaultPref.setEnabled(mEnableDemo);
377         mDefaultRatePref.setEnabled(mEnableDemo);
378         mDefaultLocPref.setEnabled(mEnableDemo);
379
380         mInstallData.setEnabled(!mEnableDemo);
381     }
382
383
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();
391         }
392         if (tokenizer.hasMoreTokens()) {
393             mDefaultCountry = tokenizer.nextToken().trim();
394         }
395         if (tokenizer.hasMoreTokens()) {
396             mDefaultLocVariant = tokenizer.nextToken().trim();
397         }
398     }
399
400
401     /**
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.
404      */
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();
412             } else {
413                 // otherwise use a default supported Locale as the default language
414                 useSupportedLocAsDefault();
415             }
416         }
417
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);
424
425         // update the demo string
426         mDemoStringIndex = mDefaultLocPref.findIndexOfValue(mDefaultLanguage + LOCALE_DELIMITER
427                 + mDefaultCountry);
428         if (mDemoStringIndex > -1){
429             mDefaultLocPref.setValueIndex(mDemoStringIndex);
430         }
431     }
432
433     /**
434      * (helper function for initDefaultLang() )
435      * Returns whether there is a default language in the TTS settings.
436      */
437     private boolean hasLangPref() {
438         String language = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_LANG);
439         return (language != null);
440     }
441
442     /**
443      * (helper function for initDefaultLang() )
444      * Returns whether the current Locale is supported by this Settings screen
445      */
446     private boolean isCurrentLocSupported() {
447         String currentLocID = Locale.getDefault().getISO3Language() + LOCALE_DELIMITER
448                 + Locale.getDefault().getISO3Country();
449         return (mDefaultLocPref.findIndexOfValue(currentLocID) > -1);
450     }
451
452     /**
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.
456      */
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());
463     }
464
465     /**
466      * (helper function for initDefaultLang() )
467      * Sets the default language in TTS settings to be one known to be supported
468      */
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);
474     }
475
476
477   private void loadEngines() {
478     ListPreference enginesPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
479
480     Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
481
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;
490     }
491     enginesPref.setEntries(entries);
492     enginesPref.setEntryValues(values);
493   }
494
495 }