blob: d76f08f4ea02314083199daa775ea02a5fe6bff1 [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;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070024import static android.provider.Settings.Secure.TTS_USE_DEFAULTS;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070025
Charles Chen0a0eb5f2010-03-16 20:09:17 -070026import android.app.AlertDialog;
Bjorn Bringertc7762972011-03-11 16:52:51 +000027import android.content.ActivityNotFoundException;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070028import android.content.ContentResolver;
Charles Chen0a0eb5f2010-03-16 20:09:17 -070029import android.content.DialogInterface;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070030import android.content.Intent;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070031import android.os.Bundle;
32import android.preference.ListPreference;
33import android.preference.Preference;
Narayan Kamath934d21d2011-05-05 13:41:11 +010034import android.preference.Preference.OnPreferenceClickListener;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070035import android.provider.Settings;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070036import android.provider.Settings.SettingNotFoundException;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070037import android.speech.tts.TextToSpeech;
Narayan Kamath3dbba082011-06-10 16:50:48 +010038import android.speech.tts.TextToSpeech.EngineInfo;
39import android.speech.tts.TtsEngines;
Bjorn Bringertc7762972011-03-11 16:52:51 +000040import android.text.TextUtils;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070041import android.util.Log;
42
Charles Chenc8298712010-02-10 13:58:23 -080043import java.util.ArrayList;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070044import java.util.List;
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -070045import java.util.Locale;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070046import java.util.StringTokenizer;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070047
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070048public class TextToSpeechSettings extends SettingsPreferenceFragment implements
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070049 Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
50 TextToSpeech.OnInitListener {
Jean-Michel Trivied29a652009-06-05 18:37:29 -070051
52 private static final String TAG = "TextToSpeechSettings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070053
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070054 private static final String KEY_TTS_PLAY_EXAMPLE = "tts_play_example";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070055 private static final String KEY_TTS_INSTALL_DATA = "tts_install_data";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070056 private static final String KEY_TTS_USE_DEFAULT = "toggle_use_default_tts_settings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070057 private static final String KEY_TTS_DEFAULT_RATE = "tts_default_rate";
Jean-Michel Trivi80368622009-06-09 19:29:27 -070058 private static final String KEY_TTS_DEFAULT_LANG = "tts_default_lang";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070059 private static final String KEY_TTS_DEFAULT_COUNTRY = "tts_default_country";
60 private static final String KEY_TTS_DEFAULT_VARIANT = "tts_default_variant";
Charles Chen5dbc74a2009-12-07 12:08:13 -080061 private static final String KEY_TTS_DEFAULT_SYNTH = "tts_default_synth";
Narayan Kamathd5642f72011-06-14 18:17:40 +010062 private static final String KEY_TTS_ENGINE_SETTINGS = "tts_engine_settings";
Charles Chen0a0eb5f2010-03-16 20:09:17 -070063
64 private static final String KEY_PLUGIN_ENABLED_PREFIX = "ENABLED_";
65 private static final String KEY_PLUGIN_SETTINGS_PREFIX = "SETTINGS_";
66
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070067 // TODO move default Locale values to TextToSpeech.Engine
68 private static final String DEFAULT_LANG_VAL = "eng";
69 private static final String DEFAULT_COUNTRY_VAL = "USA";
70 private static final String DEFAULT_VARIANT_VAL = "";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070071
72 private static final String LOCALE_DELIMITER = "-";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070073
Narayan Kamathd5642f72011-06-14 18:17:40 +010074 private Preference mPlayExample = null;
75
76 private ListPreference mDefaultRatePref = null;
77 private ListPreference mDefaultLocPref = null;
78 private ListPreference mDefaultSynthPref = null;
79
80 private Preference mInstallData = null;
81 private Preference mEngineSettings = null;
Bjorn Bringertc7762972011-03-11 16:52:51 +000082
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070083 private String mDefaultLanguage = null;
84 private String mDefaultCountry = null;
85 private String mDefaultLocVariant = null;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070086 private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
87
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070088 // Index of the current string to use for the demo.
89 private int mDemoStringIndex = 0;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070090
91 private boolean mEnableDemo = false;
Charles Chenc8298712010-02-10 13:58:23 -080092 private boolean mVoicesMissing = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070093
94 private TextToSpeech mTts = null;
Narayan Kamath3dbba082011-06-10 16:50:48 +010095 private TtsEngines mEnginesHelper = null;
Charles Chencf31e652010-04-07 14:26:31 -070096 private boolean mTtsStarted = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070097
98 /**
99 * Request code (arbitrary value) for voice data check through
100 * startActivityForResult.
101 */
102 private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
Charles Chen4df6c792010-01-22 11:17:40 -0800103 private static final int GET_SAMPLE_TEXT = 1983;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700104
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700105 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700106 public void onCreate(Bundle savedInstanceState) {
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700107 super.onCreate(savedInstanceState);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700108 addPreferencesFromResource(R.xml.tts_settings);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700109
Bjorn Bringertc7762972011-03-11 16:52:51 +0000110 getActivity().setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
Jean-Michel Trivi6dde8962009-08-11 09:06:58 -0700111
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700112 mEnableDemo = false;
Charles Chencf31e652010-04-07 14:26:31 -0700113 mTtsStarted = false;
Charles Chenbe6e8272010-03-22 16:06:05 -0700114
Charles Chen8c8185b2010-04-08 16:51:35 -0700115 Locale currentLocale = Locale.getDefault();
116 mDefaultLanguage = currentLocale.getISO3Language();
117 mDefaultCountry = currentLocale.getISO3Country();
118 mDefaultLocVariant = currentLocale.getVariant();
119
Bjorn Bringertc7762972011-03-11 16:52:51 +0000120 mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
121 mPlayExample.setOnPreferenceClickListener(this);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700122
Bjorn Bringertc7762972011-03-11 16:52:51 +0000123 mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
124 mInstallData.setOnPreferenceClickListener(this);
125
Bjorn Bringertc7762972011-03-11 16:52:51 +0000126 mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
127 mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
128 mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
129
Narayan Kamath222a6192011-06-20 11:49:42 +0100130 mEngineSettings = findPreference(KEY_TTS_ENGINE_SETTINGS);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100131 mEngineSettings.setEnabled(false);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000132
133 mTts = new TextToSpeech(getActivity().getApplicationContext(), this);
Narayan Kamath3dbba082011-06-10 16:50:48 +0100134 mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
Bjorn Bringertc7762972011-03-11 16:52:51 +0000135
136 initDefaultSettings();
Narayan Kamathd5642f72011-06-14 18:17:40 +0100137 initEngineSpecificSettings();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000138 }
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
Narayan Kamathd5642f72011-06-14 18:17:40 +0100174 private void initEngineSpecificSettings() {
175 final String engineName = mEnginesHelper.getDefaultEngine();
176 final EngineInfo engine = mEnginesHelper.getEngineInfo(engineName);
Narayan Kamath3dbba082011-06-10 16:50:48 +0100177
Narayan Kamathd5642f72011-06-14 18:17:40 +0100178 mEngineSettings.setTitle(getResources().getString(R.string.tts_engine_settings_title,
179 engine.label));
180
Narayan Kamath222a6192011-06-20 11:49:42 +0100181 final Intent settingsIntent = mEnginesHelper.getSettingsIntent(engineName);
182 if (settingsIntent != null) {
Narayan Kamathd5642f72011-06-14 18:17:40 +0100183 mEngineSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() {
184 public boolean onPreferenceClick(Preference preference) {
Narayan Kamath222a6192011-06-20 11:49:42 +0100185 startActivity(settingsIntent);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100186 return true;
187 }
188 });
189 mEngineSettings.setEnabled(true);
190 } else {
191 mEngineSettings.setEnabled(false);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700192 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100193
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700194 }
195
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700196 private void initDefaultSettings() {
197 ContentResolver resolver = getContentResolver();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700198
199 // Find the default TTS values in the settings, initialize and store the
200 // settings if they are not found.
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700201
Charles Chen5dbc74a2009-12-07 12:08:13 -0800202 // Default synthesis engine
Charles Chen5dbc74a2009-12-07 12:08:13 -0800203 loadEngines();
204 mDefaultSynthPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700205
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700206 // Default rate
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700207 try {
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700208 mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700209 } catch (SettingNotFoundException e) {
210 // default rate setting not found, initialize it
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700211 mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
212 Settings.Secure.putInt(resolver, TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700213 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700214 mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700215 mDefaultRatePref.setOnPreferenceChangeListener(this);
216
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700217 // Default language / country / variant : these three values map to a single ListPref
218 // representing the matching Locale
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700219 initDefaultLang();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700220 mDefaultLocPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700221 }
222
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700223 /**
224 * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
225 * to check the required TTS files are properly installed.
226 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700227 private void checkVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000228 String defaultEngine = mTts.getDefaultEngine();
229 if (TextUtils.isEmpty(defaultEngine)) return;
230 Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
231 intent.setPackage(defaultEngine);
232 try {
233 Log.v(TAG, "Checking voice data: " + intent.toUri(0));
234 startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
235 } catch (ActivityNotFoundException ex) {
236 Log.e(TAG, "Failed to check TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700237 }
238 }
239
240
241 /**
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700242 * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
243 * so the required TTS files are properly installed.
244 */
245 private void installVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000246 String defaultEngine = mTts.getDefaultEngine();
247 if (TextUtils.isEmpty(defaultEngine)) return;
248 Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
Jean-Michel Trivi58ea43a2009-09-09 15:13:38 -0700249 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000250 intent.setPackage(defaultEngine);
251 try {
252 Log.v(TAG, "Installing voice data: " + intent.toUri(0));
253 startActivity(intent);
254 } catch (ActivityNotFoundException ex) {
255 Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700256 }
257 }
258
Charles Chen4df6c792010-01-22 11:17:40 -0800259 /**
260 * Ask the current default engine to return a string of sample text to be
261 * spoken to the user.
262 */
263 private void getSampleText() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000264 String defaultEngine = mTts.getDefaultEngine();
265 if (TextUtils.isEmpty(defaultEngine)) return;
266 Intent intent = new Intent(TextToSpeech.Engine.ACTION_GET_SAMPLE_TEXT);
Charles Chen4df6c792010-01-22 11:17:40 -0800267 intent.putExtra("language", mDefaultLanguage);
268 intent.putExtra("country", mDefaultCountry);
269 intent.putExtra("variant", mDefaultLocVariant);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000270 intent.setPackage(defaultEngine);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100271
Bjorn Bringertc7762972011-03-11 16:52:51 +0000272 try {
273 Log.v(TAG, "Getting sample text: " + intent.toUri(0));
274 startActivityForResult(intent, GET_SAMPLE_TEXT);
275 } catch (ActivityNotFoundException ex) {
276 Log.e(TAG, "Failed to get sample text, no acitivty found for " + intent + ")");
Charles Chen4df6c792010-01-22 11:17:40 -0800277 }
278 }
279
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700280 /**
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700281 * Called when the TTS engine is initialized.
282 */
283 public void onInit(int status) {
Jean-Michel Trivi387dc0c2009-07-28 15:13:35 -0700284 if (status == TextToSpeech.SUCCESS) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700285 mEnableDemo = true;
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800286 if (mDefaultLanguage == null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800287 mDefaultLanguage = Locale.getDefault().getISO3Language();
288 }
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800289 if (mDefaultCountry == null) {
290 mDefaultCountry = Locale.getDefault().getISO3Country();
291 }
292 if (mDefaultLocVariant == null) {
293 mDefaultLocVariant = new String();
294 }
Charles Chencf3998b2010-02-11 18:13:42 -0800295 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Charles Chencf31e652010-04-07 14:26:31 -0700296 updateWidgetState();
297 checkVoiceData();
298 mTtsStarted = true;
299 Log.v(TAG, "TTS engine for settings screen initialized.");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700300 } else {
301 Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
302 mEnableDemo = false;
303 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700304 updateWidgetState();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700305 }
306
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700307 /**
308 * Called when voice data integrity check returns
309 */
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700310 @Override
311 public void onActivityResult(int requestCode, int resultCode, Intent data) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700312 if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000313 onVoiceDataIntegrityCheckDone(data);
Charles Chen4df6c792010-01-22 11:17:40 -0800314 } else if (requestCode == GET_SAMPLE_TEXT) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000315 onSampleTextReceived(resultCode, data);
316 }
317 }
318
319 private void onVoiceDataIntegrityCheckDone(Intent data) {
320 if (data == null){
321 Log.e(TAG, "TTS data check failed data = null");
322 // The CHECK_TTS_DATA activity for the plugin did not run properly;
323 // disable the preview and install controls and return.
324 mEnableDemo = false;
325 mVoicesMissing = false;
326 updateWidgetState();
327 return;
328 }
329 Log.v(TAG, "TTS data check completed, data = " + data.toUri(0));
330 ArrayList<String> available =
331 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
332 ArrayList<String> unavailable =
333 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
334 if (available == null || unavailable == null){
335 Log.e(TAG, "TTS data check failed (available == == null)");
336 // The CHECK_TTS_DATA activity for the plugin did not run properly;
337 // disable the preview and install controls and return.
338 mEnableDemo = false;
339 mVoicesMissing = false;
340 updateWidgetState();
341 return;
342 }
343 if (available.size() > 0){
344 if (mTts == null) {
345 mTts = new TextToSpeech(getActivity(), this);
Charles Chen4df6c792010-01-22 11:17:40 -0800346 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000347
348 updateDefaultLocPref(available);
349
350 mEnableDemo = true;
351 // Make sure that the default language can be used.
352 int languageResult = mTts.setLanguage(
353 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
354 if (languageResult < TextToSpeech.LANG_AVAILABLE){
355 Locale currentLocale = Locale.getDefault();
356 mDefaultLanguage = currentLocale.getISO3Language();
357 mDefaultCountry = currentLocale.getISO3Country();
358 mDefaultLocVariant = currentLocale.getVariant();
359 languageResult = mTts.setLanguage(
360 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
361 // If the default Locale isn't supported, just choose the first available
362 // language so that there is at least something.
363 if (languageResult < TextToSpeech.LANG_AVAILABLE){
364 parseLocaleInfo(mDefaultLocPref.getEntryValues()[0].toString());
365 mTts.setLanguage(
366 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
367 }
368 ContentResolver resolver = getContentResolver();
369 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
370 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
371 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
372 }
373 } else {
374 mEnableDemo = false;
375 }
376
377 if (unavailable.size() > 0){
378 mVoicesMissing = true;
379 } else {
380 mVoicesMissing = false;
381 }
382
383 updateWidgetState();
384 }
385
386 private void updateDefaultLocPref(ArrayList<String> availableLangs) {
387 CharSequence[] entries = new CharSequence[availableLangs.size()];
388 CharSequence[] entryValues = new CharSequence[availableLangs.size()];
389 int selectedLanguageIndex = -1;
390 String selectedLanguagePref = mDefaultLanguage;
391 if (mDefaultCountry.length() > 0) {
392 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
393 mDefaultCountry;
394 }
395 if (mDefaultLocVariant.length() > 0) {
396 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
397 mDefaultLocVariant;
398 }
399 for (int i = 0; i < availableLangs.size(); i++) {
400 String[] langCountryVariant = availableLangs.get(i).split("-");
401 Locale loc = null;
402 if (langCountryVariant.length == 1){
403 loc = new Locale(langCountryVariant[0]);
404 } else if (langCountryVariant.length == 2){
405 loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
406 } else if (langCountryVariant.length == 3){
407 loc = new Locale(langCountryVariant[0], langCountryVariant[1],
408 langCountryVariant[2]);
409 }
410 if (loc != null){
411 entries[i] = loc.getDisplayName();
412 entryValues[i] = availableLangs.get(i);
413 if (entryValues[i].equals(selectedLanguagePref)) {
414 selectedLanguageIndex = i;
415 }
416 }
417 }
418 mDefaultLocPref.setEntries(entries);
419 mDefaultLocPref.setEntryValues(entryValues);
420 if (selectedLanguageIndex > -1) {
421 mDefaultLocPref.setValueIndex(selectedLanguageIndex);
422 }
423 }
424
425 private void onSampleTextReceived(int resultCode, Intent data) {
426 if (resultCode == TextToSpeech.LANG_AVAILABLE) {
427 String sample = getActivity().getString(R.string.tts_demo);
428 if (data != null && data.getStringExtra("sampleText") != null) {
429 sample = data.getStringExtra("sampleText");
430 }
431 Log.v(TAG, "Got sample text: " + sample);
432 if (mTts != null) {
433 mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
434 }
435 } else {
436 // TODO: Display an error here to the user.
437 Log.e(TAG, "Did not have a sample string for the requested language");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700438 }
439 }
440
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700441 public boolean onPreferenceChange(Preference preference, Object objValue) {
442 if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
443 // "Use Defaults"
Bjorn Bringertc7762972011-03-11 16:52:51 +0000444 int value = ((Boolean) objValue) ? 1 : 0;
445 Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS, value);
446 Log.i(TAG, "TTS 'use default' settings changed, now " + value);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700447 } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
448 // Default rate
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700449 mDefaultRate = Integer.parseInt((String) objValue);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700450 try {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000451 Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700452 if (mTts != null) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000453 mTts.setSpeechRate(mDefaultRate / 100.0f);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700454 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000455 Log.v(TAG, "TTS default rate changed, now " + mDefaultRate);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700456 } catch (NumberFormatException e) {
457 Log.e(TAG, "could not persist default TTS rate setting", e);
458 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700459 } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700460 // Default locale
461 ContentResolver resolver = getContentResolver();
462 parseLocaleInfo((String) objValue);
463 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
464 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
465 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
466 Log.v(TAG, "TTS default lang/country/variant set to "
467 + mDefaultLanguage + "/" + mDefaultCountry + "/" + mDefaultLocVariant);
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700468 if (mTts != null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800469 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700470 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700471 int newIndex = mDefaultLocPref.findIndexOfValue((String)objValue);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000472 Log.v(TAG, " selected is " + newIndex);
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700473 mDemoStringIndex = newIndex > -1 ? newIndex : 0;
Charles Chen5dbc74a2009-12-07 12:08:13 -0800474 } else if (KEY_TTS_DEFAULT_SYNTH.equals(preference.getKey())) {
Narayan Kamathd5642f72011-06-14 18:17:40 +0100475 final String name = objValue.toString();
476 final EngineInfo info = mEnginesHelper.getEngineInfo(name);
477
478 if (info.system) {
479 // For system engines, do away with the alert dialog.
480 updateDefaultEngine(name);
481 initEngineSpecificSettings();
482 } else {
483 // For all other engines, display a warning message before
484 // turning them on.
485 displayDataAlert(preference, name);
Charles Chen5dbc74a2009-12-07 12:08:13 -0800486 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100487
488 // We'll deal with updating the UI ourselves.
489 return false;
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700490 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700491
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700492 return true;
493 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700494
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700495
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700496 /**
497 * Called when mPlayExample or mInstallData is clicked
498 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700499 public boolean onPreferenceClick(Preference preference) {
500 if (preference == mPlayExample) {
Charles Chen4df6c792010-01-22 11:17:40 -0800501 // Get the sample text from the TTS engine; onActivityResult will do
502 // the actual speaking
503 getSampleText();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700504 return true;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000505 } else if (preference == mInstallData) {
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700506 installVoiceData();
507 // quit this activity so it needs to be restarted after installation of the voice data
508 finish();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700509 return true;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700510 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100511
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700512 return false;
513 }
514
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700515 private void updateWidgetState() {
516 mPlayExample.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700517 mDefaultRatePref.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700518 mDefaultLocPref.setEnabled(mEnableDemo);
519
Charles Chenc8298712010-02-10 13:58:23 -0800520 mInstallData.setEnabled(mVoicesMissing);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700521 }
522
523
524 private void parseLocaleInfo(String locale) {
525 StringTokenizer tokenizer = new StringTokenizer(locale, LOCALE_DELIMITER);
526 mDefaultLanguage = "";
527 mDefaultCountry = "";
528 mDefaultLocVariant = "";
Narayan Kamathd5642f72011-06-14 18:17:40 +0100529
530 if (locale != null) {
531 String[] components = locale.split(LOCALE_DELIMITER);
532 if (components.length > 0) {
533 mDefaultLanguage = components[0];
534 }
535 if (components.length > 1) {
536 mDefaultCountry = components[1];
537 }
538 if (components.length > 2) {
539 mDefaultLocVariant = components[2];
540 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700541 }
542 }
543
544
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700545 /**
546 * Initialize the default language in the UI and in the preferences.
547 * After this method has been invoked, the default language is a supported Locale.
548 */
549 private void initDefaultLang() {
550 // if there isn't already a default language preference
551 if (!hasLangPref()) {
552 // if the current Locale is supported
553 if (isCurrentLocSupported()) {
554 // then use the current Locale as the default language
555 useCurrentLocAsDefault();
556 } else {
557 // otherwise use a default supported Locale as the default language
558 useSupportedLocAsDefault();
559 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700560 }
561
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700562 // Update the language preference list with the default language and the matching
563 // demo string (at this stage there is a default language pref)
564 ContentResolver resolver = getContentResolver();
565 mDefaultLanguage = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
Charles Chen681d0b82010-04-12 12:06:30 -0700566 mDefaultCountry = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
567 mDefaultLocVariant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700568
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700569 // update the demo string
570 mDemoStringIndex = mDefaultLocPref.findIndexOfValue(mDefaultLanguage + LOCALE_DELIMITER
571 + mDefaultCountry);
Charles Chen8a37e612010-02-04 15:52:30 -0800572 if (mDemoStringIndex > -1){
573 mDefaultLocPref.setValueIndex(mDemoStringIndex);
574 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700575 }
576
577 /**
578 * (helper function for initDefaultLang() )
579 * Returns whether there is a default language in the TTS settings.
580 */
581 private boolean hasLangPref() {
Jean-Baptiste Queru6e61b212010-04-14 10:40:48 -0700582 ContentResolver resolver = getContentResolver();
583 String language = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
584 if ((language == null) || (language.length() < 1)) {
585 return false;
586 }
587 String country = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
588 if (country == null) {
589 return false;
590 }
591 String variant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
592 if (variant == null) {
593 return false;
594 }
595 return true;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700596 }
597
598 /**
599 * (helper function for initDefaultLang() )
600 * Returns whether the current Locale is supported by this Settings screen
601 */
602 private boolean isCurrentLocSupported() {
603 String currentLocID = Locale.getDefault().getISO3Language() + LOCALE_DELIMITER
604 + Locale.getDefault().getISO3Country();
605 return (mDefaultLocPref.findIndexOfValue(currentLocID) > -1);
606 }
607
608 /**
609 * (helper function for initDefaultLang() )
610 * Sets the default language in TTS settings to be the current Locale.
611 * This should only be used after checking that the current Locale is supported.
612 */
613 private void useCurrentLocAsDefault() {
614 Locale currentLocale = Locale.getDefault();
615 ContentResolver resolver = getContentResolver();
616 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, currentLocale.getISO3Language());
617 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, currentLocale.getISO3Country());
618 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, currentLocale.getVariant());
619 }
620
621 /**
622 * (helper function for initDefaultLang() )
623 * Sets the default language in TTS settings to be one known to be supported
624 */
625 private void useSupportedLocAsDefault() {
626 ContentResolver resolver = getContentResolver();
627 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, DEFAULT_LANG_VAL);
628 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, DEFAULT_COUNTRY_VAL);
629 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, DEFAULT_VARIANT_VAL);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700630 }
631
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700632 private void loadEngines() {
Narayan Kamath3dbba082011-06-10 16:50:48 +0100633 List<EngineInfo> engines = mEnginesHelper.getEngines();
Narayan Kamathd5642f72011-06-14 18:17:40 +0100634 CharSequence entries[] = new CharSequence[engines.size()];
635 CharSequence values[] = new CharSequence[engines.size()];
Narayan Kamath62f153d2011-06-14 16:37:11 +0100636
Narayan Kamathd5642f72011-06-14 18:17:40 +0100637 final int count = engines.size();
638 for (int i = 0; i < count; ++i) {
639 final EngineInfo engine = engines.get(i);
640 entries[i] = engine.label;
641 values[i] = engine.name;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700642 }
Narayan Kamath62f153d2011-06-14 16:37:11 +0100643
Narayan Kamathd5642f72011-06-14 18:17:40 +0100644 mDefaultSynthPref.setEntries(entries);
645 mDefaultSynthPref.setEntryValues(values);
Charles Chenbe6e8272010-03-22 16:06:05 -0700646
647 // Set the selected engine based on the saved preference
648 String selectedEngine = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_SYNTH);
Charles Chen8c8185b2010-04-08 16:51:35 -0700649 int selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(selectedEngine);
Charles Chenbe6e8272010-03-22 16:06:05 -0700650 if (selectedEngineIndex == -1){
Narayan Kamath3dbba082011-06-10 16:50:48 +0100651 selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(
652 mEnginesHelper.getHighestRankedEngineName());
Charles Chenbe6e8272010-03-22 16:06:05 -0700653 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000654 if (selectedEngineIndex >= 0) {
655 mDefaultSynthPref.setValueIndex(selectedEngineIndex);
656 }
657 }
658
Narayan Kamathd5642f72011-06-14 18:17:40 +0100659 private void displayDataAlert(Preference pref, final String key) {
660 Log.v(TAG, "Displaying data alert for :" + key);
661 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
662 builder.setTitle(android.R.string.dialog_alert_title);
663 builder.setIcon(android.R.drawable.ic_dialog_alert);
664 builder.setMessage(getActivity().getString(
665 R.string.tts_engine_security_warning, pref.getTitle()));
666 builder.setCancelable(true);
667 builder.setPositiveButton(android.R.string.ok,
668 new DialogInterface.OnClickListener() {
669 public void onClick(DialogInterface dialog, int which) {
670 updateDefaultEngine(key);
671 loadEngines();
672 initEngineSpecificSettings();
673 }
674 });
675 builder.setNegativeButton(android.R.string.cancel, null);
676
677 AlertDialog dialog = builder.create();
678 dialog.show();
679 }
680
681 private void updateDefaultEngine(String engine) {
682 Log.v(TAG, "Updating default synth to : " + engine);
683 if (mTts != null) {
684 try {
685 mTts.shutdown();
686 mTts = null;
687 } catch (Exception e) {
688 Log.e(TAG, "Error shutting down TTS engine" + e);
Narayan Kamath62f153d2011-06-14 16:37:11 +0100689 }
690 }
691
Narayan Kamathd5642f72011-06-14 18:17:40 +0100692 mTts = new TextToSpeech(getActivity().getApplicationContext(), this, engine);
693 mEnableDemo = false;
694 mVoicesMissing = false;
Narayan Kamath62f153d2011-06-14 16:37:11 +0100695
Narayan Kamath8cb09822011-06-28 09:44:46 +0100696 // Persist this value to settings and update the UI before we check
697 // voice data because if the TTS class connected without any exception, "engine"
698 // will be the default engine irrespective of whether the voice check
699 // passes or not.
Narayan Kamathd5642f72011-06-14 18:17:40 +0100700 Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100701 mDefaultSynthPref.setValue(engine);
Narayan Kamath8cb09822011-06-28 09:44:46 +0100702 updateWidgetState();
703
704 checkVoiceData();
Narayan Kamathd5642f72011-06-14 18:17:40 +0100705
706 Log.v(TAG, "The default synth is now: " + engine);
Narayan Kamath62f153d2011-06-14 16:37:11 +0100707 }
708
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700709}