blob: e5d5afc9f09bc9652279a50935b09c24884364c1 [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
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070019import static android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070020import static android.provider.Settings.Secure.TTS_DEFAULT_LANG;
21import static android.provider.Settings.Secure.TTS_DEFAULT_RATE;
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -070022import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070023import static android.provider.Settings.Secure.TTS_DEFAULT_VARIANT;
Charles Chenf47cce02010-03-17 17:33:23 -070024import static android.provider.Settings.Secure.TTS_ENABLED_PLUGINS;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070025import static android.provider.Settings.Secure.TTS_USE_DEFAULTS;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070026
Charles Chen0a0eb5f2010-03-16 20:09:17 -070027import android.app.AlertDialog;
Bjorn Bringertc7762972011-03-11 16:52:51 +000028import android.content.ActivityNotFoundException;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070029import android.content.ContentResolver;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070030import android.content.Context;
Charles Chen0a0eb5f2010-03-16 20:09:17 -070031import android.content.DialogInterface;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070032import android.content.Intent;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070033import android.content.pm.PackageManager;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070034import android.os.Bundle;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070035import android.preference.CheckBoxPreference;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070036import android.preference.ListPreference;
37import android.preference.Preference;
Charles Chen0a0eb5f2010-03-16 20:09:17 -070038import android.preference.PreferenceGroup;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070039import android.provider.Settings;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070040import android.provider.Settings.SettingNotFoundException;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070041import android.speech.tts.TextToSpeech;
Bjorn Bringertc7762972011-03-11 16:52:51 +000042import android.text.TextUtils;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070043import android.util.Log;
44
Charles Chenc8298712010-02-10 13:58:23 -080045import java.util.ArrayList;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070046import java.util.List;
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -070047import java.util.Locale;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070048import java.util.StringTokenizer;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070049
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070050public class TextToSpeechSettings extends SettingsPreferenceFragment implements
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070051 Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
52 TextToSpeech.OnInitListener {
Jean-Michel Trivied29a652009-06-05 18:37:29 -070053
Bjorn Bringertc7762972011-03-11 16:52:51 +000054 private static final boolean DBG = true;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070055 private static final String TAG = "TextToSpeechSettings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070056
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070057 private static final String KEY_TTS_PLAY_EXAMPLE = "tts_play_example";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070058 private static final String KEY_TTS_INSTALL_DATA = "tts_install_data";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070059 private static final String KEY_TTS_USE_DEFAULT = "toggle_use_default_tts_settings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070060 private static final String KEY_TTS_DEFAULT_RATE = "tts_default_rate";
Jean-Michel Trivi80368622009-06-09 19:29:27 -070061 private static final String KEY_TTS_DEFAULT_LANG = "tts_default_lang";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070062 private static final String KEY_TTS_DEFAULT_COUNTRY = "tts_default_country";
63 private static final String KEY_TTS_DEFAULT_VARIANT = "tts_default_variant";
Charles Chen5dbc74a2009-12-07 12:08:13 -080064 private static final String KEY_TTS_DEFAULT_SYNTH = "tts_default_synth";
Bjorn Bringertc7762972011-03-11 16:52:51 +000065 private static final String KEY_TTS_ENGINES = "tts_engines_section";
Charles Chen0a0eb5f2010-03-16 20:09:17 -070066
67 private static final String KEY_PLUGIN_ENABLED_PREFIX = "ENABLED_";
68 private static final String KEY_PLUGIN_SETTINGS_PREFIX = "SETTINGS_";
69
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070070 // TODO move default Locale values to TextToSpeech.Engine
71 private static final String DEFAULT_LANG_VAL = "eng";
72 private static final String DEFAULT_COUNTRY_VAL = "USA";
73 private static final String DEFAULT_VARIANT_VAL = "";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070074
75 private static final String LOCALE_DELIMITER = "-";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070076
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070077 private Preference mPlayExample = null;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070078 private Preference mInstallData = null;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070079 private CheckBoxPreference mUseDefaultPref = null;
80 private ListPreference mDefaultRatePref = null;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070081 private ListPreference mDefaultLocPref = null;
Charles Chen5dbc74a2009-12-07 12:08:13 -080082 private ListPreference mDefaultSynthPref = null;
Bjorn Bringertc7762972011-03-11 16:52:51 +000083 private PreferenceGroup mEnginesGroup;
84
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070085 private String mDefaultLanguage = null;
86 private String mDefaultCountry = null;
87 private String mDefaultLocVariant = null;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070088 private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
89
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070090 // Index of the current string to use for the demo.
91 private int mDemoStringIndex = 0;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070092
93 private boolean mEnableDemo = false;
Charles Chenc8298712010-02-10 13:58:23 -080094 private boolean mVoicesMissing = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070095
96 private TextToSpeech mTts = null;
Charles Chencf31e652010-04-07 14:26:31 -070097 private boolean mTtsStarted = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070098
99 /**
100 * Request code (arbitrary value) for voice data check through
101 * startActivityForResult.
102 */
103 private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
Charles Chen4df6c792010-01-22 11:17:40 -0800104 private static final int GET_SAMPLE_TEXT = 1983;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700105
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700106 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700107 public void onCreate(Bundle savedInstanceState) {
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700108 super.onCreate(savedInstanceState);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700109 addPreferencesFromResource(R.xml.tts_settings);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700110
Bjorn Bringertc7762972011-03-11 16:52:51 +0000111 getActivity().setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
Jean-Michel Trivi6dde8962009-08-11 09:06:58 -0700112
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700113 mEnableDemo = false;
Charles Chencf31e652010-04-07 14:26:31 -0700114 mTtsStarted = false;
Charles Chenbe6e8272010-03-22 16:06:05 -0700115
Charles Chen8c8185b2010-04-08 16:51:35 -0700116 Locale currentLocale = Locale.getDefault();
117 mDefaultLanguage = currentLocale.getISO3Language();
118 mDefaultCountry = currentLocale.getISO3Country();
119 mDefaultLocVariant = currentLocale.getVariant();
120
Bjorn Bringertc7762972011-03-11 16:52:51 +0000121 mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
122 mPlayExample.setOnPreferenceClickListener(this);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700123
Bjorn Bringertc7762972011-03-11 16:52:51 +0000124 mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
125 mInstallData.setOnPreferenceClickListener(this);
126
127 mUseDefaultPref = (CheckBoxPreference) findPreference(KEY_TTS_USE_DEFAULT);
128 mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
129 mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
130 mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
131
132 mEnginesGroup = (PreferenceGroup) findPreference(KEY_TTS_ENGINES);
133
134 mTts = new TextToSpeech(getActivity().getApplicationContext(), this);
135
136 initDefaultSettings();
137 addEngineSpecificSettings();
138 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700139
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700140 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700141 public void onStart() {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700142 super.onStart();
Charles Chencf31e652010-04-07 14:26:31 -0700143 if (mTtsStarted){
144 // whenever we return to this screen, we don't know the state of the
145 // system, so we have to recheck that we can play the demo, or it must be disabled.
146 // TODO make the TTS service listen to "changes in the system", i.e. sd card un/mount
Charles Chencf31e652010-04-07 14:26:31 -0700147 updateWidgetState();
148 checkVoiceData();
149 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700150 }
151
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700152 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700153 public void onDestroy() {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700154 super.onDestroy();
155 if (mTts != null) {
156 mTts.shutdown();
157 }
158 }
159
Charles Chen8c8185b2010-04-08 16:51:35 -0700160 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700161 public void onPause() {
Charles Chen8c8185b2010-04-08 16:51:35 -0700162 super.onPause();
163 if ((mDefaultRatePref != null) && (mDefaultRatePref.getDialog() != null)) {
164 mDefaultRatePref.getDialog().dismiss();
165 }
166 if ((mDefaultLocPref != null) && (mDefaultLocPref.getDialog() != null)) {
167 mDefaultLocPref.getDialog().dismiss();
168 }
169 if ((mDefaultSynthPref != null) && (mDefaultSynthPref.getDialog() != null)) {
170 mDefaultSynthPref.getDialog().dismiss();
171 }
172 }
173
Bjorn Bringertc7762972011-03-11 16:52:51 +0000174 private void addEngineSpecificSettings() {
175 Context context = getActivity();
176 List<TextToSpeech.EngineInfo> engines = mTts.getEngines();
177 for (TextToSpeech.EngineInfo engine : engines) {
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700178 String prefKey = "";
Bjorn Bringertc7762972011-03-11 16:52:51 +0000179 final String engineName = engine.name;
180 if (!engineName.equals(TextToSpeech.Engine.DEFAULT_ENGINE)) {
181 CheckBoxPreference enablePref = new CheckBoxPreference(context);
182 prefKey = KEY_PLUGIN_ENABLED_PREFIX + engineName;
183 enablePref.setKey(prefKey);
184 enablePref.setTitle(engine.label);
185 mEnginesGroup.addPreference(enablePref);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700186 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000187 if (engineHasSettings(engineName)) {
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700188 Preference pref = new Preference(context);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000189 prefKey = KEY_PLUGIN_SETTINGS_PREFIX + engineName;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700190 pref.setKey(prefKey);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000191 pref.setTitle(engine.label);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700192 CharSequence settingsLabel = getResources().getString(
Bjorn Bringertc7762972011-03-11 16:52:51 +0000193 R.string.tts_engine_name_settings, engine.label);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700194 pref.setSummary(settingsLabel);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000195 // TODO: add a new API for this
196// pref.setOnPreferenceClickListener(new OnPreferenceClickListener(){
197// public boolean onPreferenceClick(Preference preference){
198// Intent i = new Intent();
199// i.setClassName(pluginPackageName,
200// pluginPackageName + ".EngineSettings");
201// startActivity(i);
202// return true;
203// }
204// });
205 mEnginesGroup.addPreference(pref);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700206 }
207 }
208 }
209
Bjorn Bringertc7762972011-03-11 16:52:51 +0000210 private boolean engineHasSettings(String enginePackageName) {
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700211 PackageManager pm = getPackageManager();
212 Intent i = new Intent();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000213 i.setClassName(enginePackageName, enginePackageName + ".EngineSettings");
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700214 if (pm.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY) != null){
215 return true;
216 }
217 return false;
218 }
219
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700220 private void initDefaultSettings() {
221 ContentResolver resolver = getContentResolver();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700222
223 // Find the default TTS values in the settings, initialize and store the
224 // settings if they are not found.
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700225
226 // "Use Defaults"
Bjorn Bringertc7762972011-03-11 16:52:51 +0000227 int useDefault = Settings.Secure.getInt(resolver, TTS_USE_DEFAULTS,
228 TextToSpeech.Engine.USE_DEFAULTS);
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700229 mUseDefaultPref.setChecked(useDefault == 1);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700230 mUseDefaultPref.setOnPreferenceChangeListener(this);
231
Charles Chen5dbc74a2009-12-07 12:08:13 -0800232 // Default synthesis engine
Charles Chen5dbc74a2009-12-07 12:08:13 -0800233 loadEngines();
234 mDefaultSynthPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700235
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700236 // Default rate
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700237 try {
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700238 mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700239 } catch (SettingNotFoundException e) {
240 // default rate setting not found, initialize it
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700241 mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
242 Settings.Secure.putInt(resolver, TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700243 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700244 mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700245 mDefaultRatePref.setOnPreferenceChangeListener(this);
246
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700247 // Default language / country / variant : these three values map to a single ListPref
248 // representing the matching Locale
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700249 initDefaultLang();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700250 mDefaultLocPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700251 }
252
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700253 /**
254 * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
255 * to check the required TTS files are properly installed.
256 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700257 private void checkVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000258 String defaultEngine = mTts.getDefaultEngine();
259 if (TextUtils.isEmpty(defaultEngine)) return;
260 Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
261 intent.setPackage(defaultEngine);
262 try {
263 Log.v(TAG, "Checking voice data: " + intent.toUri(0));
264 startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
265 } catch (ActivityNotFoundException ex) {
266 Log.e(TAG, "Failed to check TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700267 }
268 }
269
270
271 /**
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700272 * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
273 * so the required TTS files are properly installed.
274 */
275 private void installVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000276 String defaultEngine = mTts.getDefaultEngine();
277 if (TextUtils.isEmpty(defaultEngine)) return;
278 Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
Jean-Michel Trivi58ea43a2009-09-09 15:13:38 -0700279 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000280 intent.setPackage(defaultEngine);
281 try {
282 Log.v(TAG, "Installing voice data: " + intent.toUri(0));
283 startActivity(intent);
284 } catch (ActivityNotFoundException ex) {
285 Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700286 }
287 }
288
Charles Chen4df6c792010-01-22 11:17:40 -0800289 /**
290 * Ask the current default engine to return a string of sample text to be
291 * spoken to the user.
292 */
293 private void getSampleText() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000294 String defaultEngine = mTts.getDefaultEngine();
295 if (TextUtils.isEmpty(defaultEngine)) return;
296 Intent intent = new Intent(TextToSpeech.Engine.ACTION_GET_SAMPLE_TEXT);
Charles Chen4df6c792010-01-22 11:17:40 -0800297 intent.putExtra("language", mDefaultLanguage);
298 intent.putExtra("country", mDefaultCountry);
299 intent.putExtra("variant", mDefaultLocVariant);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000300 intent.setPackage(defaultEngine);
301 try {
302 Log.v(TAG, "Getting sample text: " + intent.toUri(0));
303 startActivityForResult(intent, GET_SAMPLE_TEXT);
304 } catch (ActivityNotFoundException ex) {
305 Log.e(TAG, "Failed to get sample text, no acitivty found for " + intent + ")");
Charles Chen4df6c792010-01-22 11:17:40 -0800306 }
307 }
308
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700309 /**
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700310 * Called when the TTS engine is initialized.
311 */
312 public void onInit(int status) {
Jean-Michel Trivi387dc0c2009-07-28 15:13:35 -0700313 if (status == TextToSpeech.SUCCESS) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700314 mEnableDemo = true;
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800315 if (mDefaultLanguage == null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800316 mDefaultLanguage = Locale.getDefault().getISO3Language();
317 }
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800318 if (mDefaultCountry == null) {
319 mDefaultCountry = Locale.getDefault().getISO3Country();
320 }
321 if (mDefaultLocVariant == null) {
322 mDefaultLocVariant = new String();
323 }
Charles Chencf3998b2010-02-11 18:13:42 -0800324 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Charles Chencf31e652010-04-07 14:26:31 -0700325 updateWidgetState();
326 checkVoiceData();
327 mTtsStarted = true;
328 Log.v(TAG, "TTS engine for settings screen initialized.");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700329 } else {
330 Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
331 mEnableDemo = false;
332 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700333 updateWidgetState();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700334 }
335
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700336 /**
337 * Called when voice data integrity check returns
338 */
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700339 @Override
340 public void onActivityResult(int requestCode, int resultCode, Intent data) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700341 if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000342 onVoiceDataIntegrityCheckDone(data);
Charles Chen4df6c792010-01-22 11:17:40 -0800343 } else if (requestCode == GET_SAMPLE_TEXT) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000344 onSampleTextReceived(resultCode, data);
345 }
346 }
347
348 private void onVoiceDataIntegrityCheckDone(Intent data) {
349 if (data == null){
350 Log.e(TAG, "TTS data check failed data = null");
351 // The CHECK_TTS_DATA activity for the plugin did not run properly;
352 // disable the preview and install controls and return.
353 mEnableDemo = false;
354 mVoicesMissing = false;
355 updateWidgetState();
356 return;
357 }
358 Log.v(TAG, "TTS data check completed, data = " + data.toUri(0));
359 ArrayList<String> available =
360 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
361 ArrayList<String> unavailable =
362 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
363 if (available == null || unavailable == null){
364 Log.e(TAG, "TTS data check failed (available == == null)");
365 // The CHECK_TTS_DATA activity for the plugin did not run properly;
366 // disable the preview and install controls and return.
367 mEnableDemo = false;
368 mVoicesMissing = false;
369 updateWidgetState();
370 return;
371 }
372 if (available.size() > 0){
373 if (mTts == null) {
374 mTts = new TextToSpeech(getActivity(), this);
Charles Chen4df6c792010-01-22 11:17:40 -0800375 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000376
377 updateDefaultLocPref(available);
378
379 mEnableDemo = true;
380 // Make sure that the default language can be used.
381 int languageResult = mTts.setLanguage(
382 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
383 if (languageResult < TextToSpeech.LANG_AVAILABLE){
384 Locale currentLocale = Locale.getDefault();
385 mDefaultLanguage = currentLocale.getISO3Language();
386 mDefaultCountry = currentLocale.getISO3Country();
387 mDefaultLocVariant = currentLocale.getVariant();
388 languageResult = mTts.setLanguage(
389 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
390 // If the default Locale isn't supported, just choose the first available
391 // language so that there is at least something.
392 if (languageResult < TextToSpeech.LANG_AVAILABLE){
393 parseLocaleInfo(mDefaultLocPref.getEntryValues()[0].toString());
394 mTts.setLanguage(
395 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
396 }
397 ContentResolver resolver = getContentResolver();
398 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
399 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
400 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
401 }
402 } else {
403 mEnableDemo = false;
404 }
405
406 if (unavailable.size() > 0){
407 mVoicesMissing = true;
408 } else {
409 mVoicesMissing = false;
410 }
411
412 updateWidgetState();
413 }
414
415 private void updateDefaultLocPref(ArrayList<String> availableLangs) {
416 CharSequence[] entries = new CharSequence[availableLangs.size()];
417 CharSequence[] entryValues = new CharSequence[availableLangs.size()];
418 int selectedLanguageIndex = -1;
419 String selectedLanguagePref = mDefaultLanguage;
420 if (mDefaultCountry.length() > 0) {
421 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
422 mDefaultCountry;
423 }
424 if (mDefaultLocVariant.length() > 0) {
425 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
426 mDefaultLocVariant;
427 }
428 for (int i = 0; i < availableLangs.size(); i++) {
429 String[] langCountryVariant = availableLangs.get(i).split("-");
430 Locale loc = null;
431 if (langCountryVariant.length == 1){
432 loc = new Locale(langCountryVariant[0]);
433 } else if (langCountryVariant.length == 2){
434 loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
435 } else if (langCountryVariant.length == 3){
436 loc = new Locale(langCountryVariant[0], langCountryVariant[1],
437 langCountryVariant[2]);
438 }
439 if (loc != null){
440 entries[i] = loc.getDisplayName();
441 entryValues[i] = availableLangs.get(i);
442 if (entryValues[i].equals(selectedLanguagePref)) {
443 selectedLanguageIndex = i;
444 }
445 }
446 }
447 mDefaultLocPref.setEntries(entries);
448 mDefaultLocPref.setEntryValues(entryValues);
449 if (selectedLanguageIndex > -1) {
450 mDefaultLocPref.setValueIndex(selectedLanguageIndex);
451 }
452 }
453
454 private void onSampleTextReceived(int resultCode, Intent data) {
455 if (resultCode == TextToSpeech.LANG_AVAILABLE) {
456 String sample = getActivity().getString(R.string.tts_demo);
457 if (data != null && data.getStringExtra("sampleText") != null) {
458 sample = data.getStringExtra("sampleText");
459 }
460 Log.v(TAG, "Got sample text: " + sample);
461 if (mTts != null) {
462 mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
463 }
464 } else {
465 // TODO: Display an error here to the user.
466 Log.e(TAG, "Did not have a sample string for the requested language");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700467 }
468 }
469
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700470 public boolean onPreferenceChange(Preference preference, Object objValue) {
471 if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
472 // "Use Defaults"
Bjorn Bringertc7762972011-03-11 16:52:51 +0000473 int value = ((Boolean) objValue) ? 1 : 0;
474 Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS, value);
475 Log.i(TAG, "TTS 'use default' settings changed, now " + value);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700476 } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
477 // Default rate
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700478 mDefaultRate = Integer.parseInt((String) objValue);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700479 try {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000480 Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700481 if (mTts != null) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000482 mTts.setSpeechRate(mDefaultRate / 100.0f);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700483 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000484 Log.v(TAG, "TTS default rate changed, now " + mDefaultRate);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700485 } catch (NumberFormatException e) {
486 Log.e(TAG, "could not persist default TTS rate setting", e);
487 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700488 } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700489 // Default locale
490 ContentResolver resolver = getContentResolver();
491 parseLocaleInfo((String) objValue);
492 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
493 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
494 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
495 Log.v(TAG, "TTS default lang/country/variant set to "
496 + mDefaultLanguage + "/" + mDefaultCountry + "/" + mDefaultLocVariant);
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700497 if (mTts != null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800498 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700499 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700500 int newIndex = mDefaultLocPref.findIndexOfValue((String)objValue);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000501 Log.v(TAG, " selected is " + newIndex);
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700502 mDemoStringIndex = newIndex > -1 ? newIndex : 0;
Charles Chen5dbc74a2009-12-07 12:08:13 -0800503 } else if (KEY_TTS_DEFAULT_SYNTH.equals(preference.getKey())) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000504 String defaultEng = objValue.toString();
505 Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, defaultEng);
Charles Chen5dbc74a2009-12-07 12:08:13 -0800506 if (mTts != null) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000507 mTts.setEngineByPackageName(defaultEng);
Charles Chencf3998b2010-02-11 18:13:42 -0800508 mEnableDemo = false;
509 mVoicesMissing = false;
510 updateWidgetState();
511 checkVoiceData();
Charles Chen5dbc74a2009-12-07 12:08:13 -0800512 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000513 Log.v(TAG, "The default synth is: " + objValue.toString());
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700514 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700515
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700516 return true;
517 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700518
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700519
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700520 /**
521 * Called when mPlayExample or mInstallData is clicked
522 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700523 public boolean onPreferenceClick(Preference preference) {
524 if (preference == mPlayExample) {
Charles Chen4df6c792010-01-22 11:17:40 -0800525 // Get the sample text from the TTS engine; onActivityResult will do
526 // the actual speaking
527 getSampleText();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700528 return true;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000529 } else if (preference == mInstallData) {
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700530 installVoiceData();
531 // quit this activity so it needs to be restarted after installation of the voice data
532 finish();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700533 return true;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000534 } else if (preference.getKey().startsWith(KEY_PLUGIN_ENABLED_PREFIX)) {
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700535 final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000536 if (chkPref.isChecked()) {
537 chkPref.setChecked(false);
538 AlertDialog d = (new AlertDialog.Builder(getActivity()))
539 .setTitle(android.R.string.dialog_alert_title)
540 .setIcon(android.R.drawable.ic_dialog_alert)
541 .setMessage(
542 getActivity().getString(R.string.tts_engine_security_warning,
543 chkPref.getTitle()))
544 .setCancelable(true)
545 .setPositiveButton(android.R.string.ok,
546 new DialogInterface.OnClickListener() {
547 public void onClick(DialogInterface dialog, int which) {
548 chkPref.setChecked(true);
549 loadEngines();
550 }
551 })
552 .setNegativeButton(android.R.string.cancel,
553 new DialogInterface.OnClickListener() {
554 public void onClick(DialogInterface dialog, int which) {
555 }
556 })
557 .create();
558 d.show();
559 } else {
560 loadEngines();
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700561 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000562 return true;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700563 }
564 return false;
565 }
566
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700567 private void updateWidgetState() {
568 mPlayExample.setEnabled(mEnableDemo);
569 mUseDefaultPref.setEnabled(mEnableDemo);
570 mDefaultRatePref.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700571 mDefaultLocPref.setEnabled(mEnableDemo);
572
Charles Chenc8298712010-02-10 13:58:23 -0800573 mInstallData.setEnabled(mVoicesMissing);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700574 }
575
576
577 private void parseLocaleInfo(String locale) {
578 StringTokenizer tokenizer = new StringTokenizer(locale, LOCALE_DELIMITER);
579 mDefaultLanguage = "";
580 mDefaultCountry = "";
581 mDefaultLocVariant = "";
582 if (tokenizer.hasMoreTokens()) {
583 mDefaultLanguage = tokenizer.nextToken().trim();
584 }
585 if (tokenizer.hasMoreTokens()) {
586 mDefaultCountry = tokenizer.nextToken().trim();
587 }
588 if (tokenizer.hasMoreTokens()) {
589 mDefaultLocVariant = tokenizer.nextToken().trim();
590 }
591 }
592
593
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700594 /**
595 * Initialize the default language in the UI and in the preferences.
596 * After this method has been invoked, the default language is a supported Locale.
597 */
598 private void initDefaultLang() {
599 // if there isn't already a default language preference
600 if (!hasLangPref()) {
601 // if the current Locale is supported
602 if (isCurrentLocSupported()) {
603 // then use the current Locale as the default language
604 useCurrentLocAsDefault();
605 } else {
606 // otherwise use a default supported Locale as the default language
607 useSupportedLocAsDefault();
608 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700609 }
610
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700611 // Update the language preference list with the default language and the matching
612 // demo string (at this stage there is a default language pref)
613 ContentResolver resolver = getContentResolver();
614 mDefaultLanguage = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
Charles Chen681d0b82010-04-12 12:06:30 -0700615 mDefaultCountry = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
616 mDefaultLocVariant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700617
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700618 // update the demo string
619 mDemoStringIndex = mDefaultLocPref.findIndexOfValue(mDefaultLanguage + LOCALE_DELIMITER
620 + mDefaultCountry);
Charles Chen8a37e612010-02-04 15:52:30 -0800621 if (mDemoStringIndex > -1){
622 mDefaultLocPref.setValueIndex(mDemoStringIndex);
623 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700624 }
625
626 /**
627 * (helper function for initDefaultLang() )
628 * Returns whether there is a default language in the TTS settings.
629 */
630 private boolean hasLangPref() {
Jean-Baptiste Queru6e61b212010-04-14 10:40:48 -0700631 ContentResolver resolver = getContentResolver();
632 String language = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
633 if ((language == null) || (language.length() < 1)) {
634 return false;
635 }
636 String country = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
637 if (country == null) {
638 return false;
639 }
640 String variant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
641 if (variant == null) {
642 return false;
643 }
644 return true;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700645 }
646
647 /**
648 * (helper function for initDefaultLang() )
649 * Returns whether the current Locale is supported by this Settings screen
650 */
651 private boolean isCurrentLocSupported() {
652 String currentLocID = Locale.getDefault().getISO3Language() + LOCALE_DELIMITER
653 + Locale.getDefault().getISO3Country();
654 return (mDefaultLocPref.findIndexOfValue(currentLocID) > -1);
655 }
656
657 /**
658 * (helper function for initDefaultLang() )
659 * Sets the default language in TTS settings to be the current Locale.
660 * This should only be used after checking that the current Locale is supported.
661 */
662 private void useCurrentLocAsDefault() {
663 Locale currentLocale = Locale.getDefault();
664 ContentResolver resolver = getContentResolver();
665 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, currentLocale.getISO3Language());
666 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, currentLocale.getISO3Country());
667 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, currentLocale.getVariant());
668 }
669
670 /**
671 * (helper function for initDefaultLang() )
672 * Sets the default language in TTS settings to be one known to be supported
673 */
674 private void useSupportedLocAsDefault() {
675 ContentResolver resolver = getContentResolver();
676 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, DEFAULT_LANG_VAL);
677 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, DEFAULT_COUNTRY_VAL);
678 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, DEFAULT_VARIANT_VAL);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700679 }
680
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700681 private void loadEngines() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000682 List<TextToSpeech.EngineInfo> engines = mTts.getEngines();
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700683 ArrayList<CharSequence> entries = new ArrayList<CharSequence>();
684 ArrayList<CharSequence> values = new ArrayList<CharSequence>();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000685 StringBuilder enabledEngines = new StringBuilder();
686
687 for (TextToSpeech.EngineInfo engine : engines) {
688 Log.v(TAG, "Engine: " + engine);
689 if (isEngineEnabled(engine.name)) {
690 entries.add(engine.label);
691 values.add(engine.name);
692 if (enabledEngines.length() > 0) enabledEngines.append(' ');
693 enabledEngines.append(engine.name);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700694 }
695 }
Charles Chenf47cce02010-03-17 17:33:23 -0700696 ContentResolver resolver = getContentResolver();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000697 Settings.Secure.putString(resolver, TTS_ENABLED_PLUGINS, enabledEngines.toString());
Charles Chen5dbc74a2009-12-07 12:08:13 -0800698
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700699 CharSequence entriesArray[] = new CharSequence[entries.size()];
700 CharSequence valuesArray[] = new CharSequence[values.size()];
701
Charles Chen8c8185b2010-04-08 16:51:35 -0700702 mDefaultSynthPref.setEntries(entries.toArray(entriesArray));
703 mDefaultSynthPref.setEntryValues(values.toArray(valuesArray));
Charles Chenbe6e8272010-03-22 16:06:05 -0700704
705 // Set the selected engine based on the saved preference
706 String selectedEngine = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_SYNTH);
Charles Chen8c8185b2010-04-08 16:51:35 -0700707 int selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(selectedEngine);
Charles Chenbe6e8272010-03-22 16:06:05 -0700708 if (selectedEngineIndex == -1){
Bjorn Bringertc7762972011-03-11 16:52:51 +0000709 selectedEngineIndex =
710 mDefaultSynthPref.findIndexOfValue(TextToSpeech.Engine.DEFAULT_ENGINE);
Charles Chenbe6e8272010-03-22 16:06:05 -0700711 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000712 if (selectedEngineIndex >= 0) {
713 mDefaultSynthPref.setValueIndex(selectedEngineIndex);
714 }
715 }
716
717 private boolean isEngineEnabled(String engineName) {
718 if (engineName.equals(TextToSpeech.Engine.DEFAULT_ENGINE)) return true;
719 String enginePref = KEY_PLUGIN_ENABLED_PREFIX + engineName;
720 return getPreferenceManager().getSharedPreferences().getBoolean(enginePref, false);
Charles Chen5dbc74a2009-12-07 12:08:13 -0800721 }
Charles Chen5dbc74a2009-12-07 12:08:13 -0800722
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700723}