blob: f7945d85d1fba670af499d5f4abb2ad01799a094 [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;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070029import android.content.Context;
Charles Chen0a0eb5f2010-03-16 20:09:17 -070030import android.content.DialogInterface;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070031import android.content.Intent;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070032import android.content.pm.PackageManager;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070033import android.os.Bundle;
34import android.preference.ListPreference;
35import android.preference.Preference;
Narayan Kamath934d21d2011-05-05 13:41:11 +010036import android.preference.Preference.OnPreferenceClickListener;
Charles Chen0a0eb5f2010-03-16 20:09:17 -070037import android.preference.PreferenceGroup;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070038import android.provider.Settings;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070039import android.provider.Settings.SettingNotFoundException;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070040import android.speech.tts.TextToSpeech;
Narayan Kamath3dbba082011-06-10 16:50:48 +010041import android.speech.tts.TextToSpeech.EngineInfo;
42import android.speech.tts.TtsEngines;
Bjorn Bringertc7762972011-03-11 16:52:51 +000043import android.text.TextUtils;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070044import android.util.Log;
45
Charles Chenc8298712010-02-10 13:58:23 -080046import java.util.ArrayList;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070047import java.util.List;
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -070048import java.util.Locale;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070049import java.util.StringTokenizer;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070050
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070051public class TextToSpeechSettings extends SettingsPreferenceFragment implements
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070052 Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
53 TextToSpeech.OnInitListener {
Jean-Michel Trivied29a652009-06-05 18:37:29 -070054
55 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";
Narayan Kamathd5642f72011-06-14 18:17:40 +010065 private static final String KEY_TTS_ENGINE_SETTINGS = "tts_engine_settings";
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
Narayan Kamathd5642f72011-06-14 18:17:40 +010077 private Preference mPlayExample = null;
78
79 private ListPreference mDefaultRatePref = null;
80 private ListPreference mDefaultLocPref = null;
81 private ListPreference mDefaultSynthPref = null;
82
83 private Preference mInstallData = null;
84 private Preference mEngineSettings = null;
Bjorn Bringertc7762972011-03-11 16:52:51 +000085
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070086 private String mDefaultLanguage = null;
87 private String mDefaultCountry = null;
88 private String mDefaultLocVariant = null;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070089 private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
90
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070091 // Index of the current string to use for the demo.
92 private int mDemoStringIndex = 0;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070093
94 private boolean mEnableDemo = false;
Charles Chenc8298712010-02-10 13:58:23 -080095 private boolean mVoicesMissing = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070096
97 private TextToSpeech mTts = null;
Narayan Kamath3dbba082011-06-10 16:50:48 +010098 private TtsEngines mEnginesHelper = null;
Charles Chencf31e652010-04-07 14:26:31 -070099 private boolean mTtsStarted = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700100
101 /**
102 * Request code (arbitrary value) for voice data check through
103 * startActivityForResult.
104 */
105 private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
Charles Chen4df6c792010-01-22 11:17:40 -0800106 private static final int GET_SAMPLE_TEXT = 1983;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700107
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700108 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700109 public void onCreate(Bundle savedInstanceState) {
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700110 super.onCreate(savedInstanceState);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700111 addPreferencesFromResource(R.xml.tts_settings);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700112
Bjorn Bringertc7762972011-03-11 16:52:51 +0000113 getActivity().setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
Jean-Michel Trivi6dde8962009-08-11 09:06:58 -0700114
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700115 mEnableDemo = false;
Charles Chencf31e652010-04-07 14:26:31 -0700116 mTtsStarted = false;
Charles Chenbe6e8272010-03-22 16:06:05 -0700117
Charles Chen8c8185b2010-04-08 16:51:35 -0700118 Locale currentLocale = Locale.getDefault();
119 mDefaultLanguage = currentLocale.getISO3Language();
120 mDefaultCountry = currentLocale.getISO3Country();
121 mDefaultLocVariant = currentLocale.getVariant();
122
Bjorn Bringertc7762972011-03-11 16:52:51 +0000123 mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
124 mPlayExample.setOnPreferenceClickListener(this);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700125
Bjorn Bringertc7762972011-03-11 16:52:51 +0000126 mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
127 mInstallData.setOnPreferenceClickListener(this);
128
Bjorn Bringertc7762972011-03-11 16:52:51 +0000129 mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
130 mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
131 mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
132
Narayan Kamath222a6192011-06-20 11:49:42 +0100133 mEngineSettings = findPreference(KEY_TTS_ENGINE_SETTINGS);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100134 mEngineSettings.setEnabled(false);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000135
136 mTts = new TextToSpeech(getActivity().getApplicationContext(), this);
Narayan Kamath3dbba082011-06-10 16:50:48 +0100137 mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
Bjorn Bringertc7762972011-03-11 16:52:51 +0000138
139 initDefaultSettings();
Narayan Kamathd5642f72011-06-14 18:17:40 +0100140 initEngineSpecificSettings();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000141 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700142
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700143 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700144 public void onStart() {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700145 super.onStart();
Charles Chencf31e652010-04-07 14:26:31 -0700146 if (mTtsStarted){
147 // whenever we return to this screen, we don't know the state of the
148 // system, so we have to recheck that we can play the demo, or it must be disabled.
149 // TODO make the TTS service listen to "changes in the system", i.e. sd card un/mount
Charles Chencf31e652010-04-07 14:26:31 -0700150 updateWidgetState();
151 checkVoiceData();
152 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700153 }
154
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700155 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700156 public void onDestroy() {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700157 super.onDestroy();
158 if (mTts != null) {
159 mTts.shutdown();
160 }
161 }
162
Charles Chen8c8185b2010-04-08 16:51:35 -0700163 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700164 public void onPause() {
Charles Chen8c8185b2010-04-08 16:51:35 -0700165 super.onPause();
166 if ((mDefaultRatePref != null) && (mDefaultRatePref.getDialog() != null)) {
167 mDefaultRatePref.getDialog().dismiss();
168 }
169 if ((mDefaultLocPref != null) && (mDefaultLocPref.getDialog() != null)) {
170 mDefaultLocPref.getDialog().dismiss();
171 }
172 if ((mDefaultSynthPref != null) && (mDefaultSynthPref.getDialog() != null)) {
173 mDefaultSynthPref.getDialog().dismiss();
174 }
175 }
176
Narayan Kamathd5642f72011-06-14 18:17:40 +0100177 private void initEngineSpecificSettings() {
178 final String engineName = mEnginesHelper.getDefaultEngine();
179 final EngineInfo engine = mEnginesHelper.getEngineInfo(engineName);
Narayan Kamath3dbba082011-06-10 16:50:48 +0100180
Narayan Kamathd5642f72011-06-14 18:17:40 +0100181 mEngineSettings.setTitle(getResources().getString(R.string.tts_engine_settings_title,
182 engine.label));
183
Narayan Kamath222a6192011-06-20 11:49:42 +0100184 final Intent settingsIntent = mEnginesHelper.getSettingsIntent(engineName);
185 if (settingsIntent != null) {
Narayan Kamathd5642f72011-06-14 18:17:40 +0100186 mEngineSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() {
187 public boolean onPreferenceClick(Preference preference) {
Narayan Kamath222a6192011-06-20 11:49:42 +0100188 startActivity(settingsIntent);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100189 return true;
190 }
191 });
192 mEngineSettings.setEnabled(true);
193 } else {
194 mEngineSettings.setEnabled(false);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700195 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100196
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700197 }
198
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700199 private void initDefaultSettings() {
200 ContentResolver resolver = getContentResolver();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700201
202 // Find the default TTS values in the settings, initialize and store the
203 // settings if they are not found.
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700204
Charles Chen5dbc74a2009-12-07 12:08:13 -0800205 // Default synthesis engine
Charles Chen5dbc74a2009-12-07 12:08:13 -0800206 loadEngines();
207 mDefaultSynthPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700208
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700209 // Default rate
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700210 try {
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700211 mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700212 } catch (SettingNotFoundException e) {
213 // default rate setting not found, initialize it
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700214 mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
215 Settings.Secure.putInt(resolver, TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700216 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700217 mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700218 mDefaultRatePref.setOnPreferenceChangeListener(this);
219
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700220 // Default language / country / variant : these three values map to a single ListPref
221 // representing the matching Locale
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700222 initDefaultLang();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700223 mDefaultLocPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700224 }
225
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700226 /**
227 * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
228 * to check the required TTS files are properly installed.
229 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700230 private void checkVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000231 String defaultEngine = mTts.getDefaultEngine();
232 if (TextUtils.isEmpty(defaultEngine)) return;
233 Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
234 intent.setPackage(defaultEngine);
235 try {
236 Log.v(TAG, "Checking voice data: " + intent.toUri(0));
237 startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
238 } catch (ActivityNotFoundException ex) {
239 Log.e(TAG, "Failed to check TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700240 }
241 }
242
243
244 /**
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700245 * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
246 * so the required TTS files are properly installed.
247 */
248 private void installVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000249 String defaultEngine = mTts.getDefaultEngine();
250 if (TextUtils.isEmpty(defaultEngine)) return;
251 Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
Jean-Michel Trivi58ea43a2009-09-09 15:13:38 -0700252 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000253 intent.setPackage(defaultEngine);
254 try {
255 Log.v(TAG, "Installing voice data: " + intent.toUri(0));
256 startActivity(intent);
257 } catch (ActivityNotFoundException ex) {
258 Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700259 }
260 }
261
Charles Chen4df6c792010-01-22 11:17:40 -0800262 /**
263 * Ask the current default engine to return a string of sample text to be
264 * spoken to the user.
265 */
266 private void getSampleText() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000267 String defaultEngine = mTts.getDefaultEngine();
268 if (TextUtils.isEmpty(defaultEngine)) return;
269 Intent intent = new Intent(TextToSpeech.Engine.ACTION_GET_SAMPLE_TEXT);
Charles Chen4df6c792010-01-22 11:17:40 -0800270 intent.putExtra("language", mDefaultLanguage);
271 intent.putExtra("country", mDefaultCountry);
272 intent.putExtra("variant", mDefaultLocVariant);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000273 intent.setPackage(defaultEngine);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100274
Bjorn Bringertc7762972011-03-11 16:52:51 +0000275 try {
276 Log.v(TAG, "Getting sample text: " + intent.toUri(0));
277 startActivityForResult(intent, GET_SAMPLE_TEXT);
278 } catch (ActivityNotFoundException ex) {
279 Log.e(TAG, "Failed to get sample text, no acitivty found for " + intent + ")");
Charles Chen4df6c792010-01-22 11:17:40 -0800280 }
281 }
282
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700283 /**
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700284 * Called when the TTS engine is initialized.
285 */
286 public void onInit(int status) {
Jean-Michel Trivi387dc0c2009-07-28 15:13:35 -0700287 if (status == TextToSpeech.SUCCESS) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700288 mEnableDemo = true;
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800289 if (mDefaultLanguage == null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800290 mDefaultLanguage = Locale.getDefault().getISO3Language();
291 }
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800292 if (mDefaultCountry == null) {
293 mDefaultCountry = Locale.getDefault().getISO3Country();
294 }
295 if (mDefaultLocVariant == null) {
296 mDefaultLocVariant = new String();
297 }
Charles Chencf3998b2010-02-11 18:13:42 -0800298 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Charles Chencf31e652010-04-07 14:26:31 -0700299 updateWidgetState();
300 checkVoiceData();
301 mTtsStarted = true;
302 Log.v(TAG, "TTS engine for settings screen initialized.");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700303 } else {
304 Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
305 mEnableDemo = false;
306 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700307 updateWidgetState();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700308 }
309
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700310 /**
311 * Called when voice data integrity check returns
312 */
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700313 @Override
314 public void onActivityResult(int requestCode, int resultCode, Intent data) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700315 if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000316 onVoiceDataIntegrityCheckDone(data);
Charles Chen4df6c792010-01-22 11:17:40 -0800317 } else if (requestCode == GET_SAMPLE_TEXT) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000318 onSampleTextReceived(resultCode, data);
319 }
320 }
321
322 private void onVoiceDataIntegrityCheckDone(Intent data) {
323 if (data == null){
324 Log.e(TAG, "TTS data check failed data = null");
325 // The CHECK_TTS_DATA activity for the plugin did not run properly;
326 // disable the preview and install controls and return.
327 mEnableDemo = false;
328 mVoicesMissing = false;
329 updateWidgetState();
330 return;
331 }
332 Log.v(TAG, "TTS data check completed, data = " + data.toUri(0));
333 ArrayList<String> available =
334 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
335 ArrayList<String> unavailable =
336 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
337 if (available == null || unavailable == null){
338 Log.e(TAG, "TTS data check failed (available == == null)");
339 // The CHECK_TTS_DATA activity for the plugin did not run properly;
340 // disable the preview and install controls and return.
341 mEnableDemo = false;
342 mVoicesMissing = false;
343 updateWidgetState();
344 return;
345 }
346 if (available.size() > 0){
347 if (mTts == null) {
348 mTts = new TextToSpeech(getActivity(), this);
Charles Chen4df6c792010-01-22 11:17:40 -0800349 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000350
351 updateDefaultLocPref(available);
352
353 mEnableDemo = true;
354 // Make sure that the default language can be used.
355 int languageResult = mTts.setLanguage(
356 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
357 if (languageResult < TextToSpeech.LANG_AVAILABLE){
358 Locale currentLocale = Locale.getDefault();
359 mDefaultLanguage = currentLocale.getISO3Language();
360 mDefaultCountry = currentLocale.getISO3Country();
361 mDefaultLocVariant = currentLocale.getVariant();
362 languageResult = mTts.setLanguage(
363 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
364 // If the default Locale isn't supported, just choose the first available
365 // language so that there is at least something.
366 if (languageResult < TextToSpeech.LANG_AVAILABLE){
367 parseLocaleInfo(mDefaultLocPref.getEntryValues()[0].toString());
368 mTts.setLanguage(
369 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
370 }
371 ContentResolver resolver = getContentResolver();
372 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
373 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
374 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
375 }
376 } else {
377 mEnableDemo = false;
378 }
379
380 if (unavailable.size() > 0){
381 mVoicesMissing = true;
382 } else {
383 mVoicesMissing = false;
384 }
385
386 updateWidgetState();
387 }
388
389 private void updateDefaultLocPref(ArrayList<String> availableLangs) {
390 CharSequence[] entries = new CharSequence[availableLangs.size()];
391 CharSequence[] entryValues = new CharSequence[availableLangs.size()];
392 int selectedLanguageIndex = -1;
393 String selectedLanguagePref = mDefaultLanguage;
394 if (mDefaultCountry.length() > 0) {
395 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
396 mDefaultCountry;
397 }
398 if (mDefaultLocVariant.length() > 0) {
399 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
400 mDefaultLocVariant;
401 }
402 for (int i = 0; i < availableLangs.size(); i++) {
403 String[] langCountryVariant = availableLangs.get(i).split("-");
404 Locale loc = null;
405 if (langCountryVariant.length == 1){
406 loc = new Locale(langCountryVariant[0]);
407 } else if (langCountryVariant.length == 2){
408 loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
409 } else if (langCountryVariant.length == 3){
410 loc = new Locale(langCountryVariant[0], langCountryVariant[1],
411 langCountryVariant[2]);
412 }
413 if (loc != null){
414 entries[i] = loc.getDisplayName();
415 entryValues[i] = availableLangs.get(i);
416 if (entryValues[i].equals(selectedLanguagePref)) {
417 selectedLanguageIndex = i;
418 }
419 }
420 }
421 mDefaultLocPref.setEntries(entries);
422 mDefaultLocPref.setEntryValues(entryValues);
423 if (selectedLanguageIndex > -1) {
424 mDefaultLocPref.setValueIndex(selectedLanguageIndex);
425 }
426 }
427
428 private void onSampleTextReceived(int resultCode, Intent data) {
429 if (resultCode == TextToSpeech.LANG_AVAILABLE) {
430 String sample = getActivity().getString(R.string.tts_demo);
431 if (data != null && data.getStringExtra("sampleText") != null) {
432 sample = data.getStringExtra("sampleText");
433 }
434 Log.v(TAG, "Got sample text: " + sample);
435 if (mTts != null) {
436 mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
437 }
438 } else {
439 // TODO: Display an error here to the user.
440 Log.e(TAG, "Did not have a sample string for the requested language");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700441 }
442 }
443
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700444 public boolean onPreferenceChange(Preference preference, Object objValue) {
445 if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
446 // "Use Defaults"
Bjorn Bringertc7762972011-03-11 16:52:51 +0000447 int value = ((Boolean) objValue) ? 1 : 0;
448 Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS, value);
449 Log.i(TAG, "TTS 'use default' settings changed, now " + value);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700450 } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
451 // Default rate
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700452 mDefaultRate = Integer.parseInt((String) objValue);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700453 try {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000454 Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700455 if (mTts != null) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000456 mTts.setSpeechRate(mDefaultRate / 100.0f);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700457 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000458 Log.v(TAG, "TTS default rate changed, now " + mDefaultRate);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700459 } catch (NumberFormatException e) {
460 Log.e(TAG, "could not persist default TTS rate setting", e);
461 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700462 } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700463 // Default locale
464 ContentResolver resolver = getContentResolver();
465 parseLocaleInfo((String) objValue);
466 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
467 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
468 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
469 Log.v(TAG, "TTS default lang/country/variant set to "
470 + mDefaultLanguage + "/" + mDefaultCountry + "/" + mDefaultLocVariant);
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700471 if (mTts != null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800472 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700473 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700474 int newIndex = mDefaultLocPref.findIndexOfValue((String)objValue);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000475 Log.v(TAG, " selected is " + newIndex);
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700476 mDemoStringIndex = newIndex > -1 ? newIndex : 0;
Charles Chen5dbc74a2009-12-07 12:08:13 -0800477 } else if (KEY_TTS_DEFAULT_SYNTH.equals(preference.getKey())) {
Narayan Kamathd5642f72011-06-14 18:17:40 +0100478 final String name = objValue.toString();
479 final EngineInfo info = mEnginesHelper.getEngineInfo(name);
480
481 if (info.system) {
482 // For system engines, do away with the alert dialog.
483 updateDefaultEngine(name);
484 initEngineSpecificSettings();
485 } else {
486 // For all other engines, display a warning message before
487 // turning them on.
488 displayDataAlert(preference, name);
Charles Chen5dbc74a2009-12-07 12:08:13 -0800489 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100490
491 // We'll deal with updating the UI ourselves.
492 return false;
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700493 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700494
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700495 return true;
496 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700497
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700498
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700499 /**
500 * Called when mPlayExample or mInstallData is clicked
501 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700502 public boolean onPreferenceClick(Preference preference) {
503 if (preference == mPlayExample) {
Charles Chen4df6c792010-01-22 11:17:40 -0800504 // Get the sample text from the TTS engine; onActivityResult will do
505 // the actual speaking
506 getSampleText();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700507 return true;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000508 } else if (preference == mInstallData) {
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700509 installVoiceData();
510 // quit this activity so it needs to be restarted after installation of the voice data
511 finish();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700512 return true;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700513 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100514
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700515 return false;
516 }
517
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700518 private void updateWidgetState() {
519 mPlayExample.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700520 mDefaultRatePref.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700521 mDefaultLocPref.setEnabled(mEnableDemo);
522
Charles Chenc8298712010-02-10 13:58:23 -0800523 mInstallData.setEnabled(mVoicesMissing);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700524 }
525
526
527 private void parseLocaleInfo(String locale) {
528 StringTokenizer tokenizer = new StringTokenizer(locale, LOCALE_DELIMITER);
529 mDefaultLanguage = "";
530 mDefaultCountry = "";
531 mDefaultLocVariant = "";
Narayan Kamathd5642f72011-06-14 18:17:40 +0100532
533 if (locale != null) {
534 String[] components = locale.split(LOCALE_DELIMITER);
535 if (components.length > 0) {
536 mDefaultLanguage = components[0];
537 }
538 if (components.length > 1) {
539 mDefaultCountry = components[1];
540 }
541 if (components.length > 2) {
542 mDefaultLocVariant = components[2];
543 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700544 }
545 }
546
547
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700548 /**
549 * Initialize the default language in the UI and in the preferences.
550 * After this method has been invoked, the default language is a supported Locale.
551 */
552 private void initDefaultLang() {
553 // if there isn't already a default language preference
554 if (!hasLangPref()) {
555 // if the current Locale is supported
556 if (isCurrentLocSupported()) {
557 // then use the current Locale as the default language
558 useCurrentLocAsDefault();
559 } else {
560 // otherwise use a default supported Locale as the default language
561 useSupportedLocAsDefault();
562 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700563 }
564
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700565 // Update the language preference list with the default language and the matching
566 // demo string (at this stage there is a default language pref)
567 ContentResolver resolver = getContentResolver();
568 mDefaultLanguage = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
Charles Chen681d0b82010-04-12 12:06:30 -0700569 mDefaultCountry = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
570 mDefaultLocVariant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700571
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700572 // update the demo string
573 mDemoStringIndex = mDefaultLocPref.findIndexOfValue(mDefaultLanguage + LOCALE_DELIMITER
574 + mDefaultCountry);
Charles Chen8a37e612010-02-04 15:52:30 -0800575 if (mDemoStringIndex > -1){
576 mDefaultLocPref.setValueIndex(mDemoStringIndex);
577 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700578 }
579
580 /**
581 * (helper function for initDefaultLang() )
582 * Returns whether there is a default language in the TTS settings.
583 */
584 private boolean hasLangPref() {
Jean-Baptiste Queru6e61b212010-04-14 10:40:48 -0700585 ContentResolver resolver = getContentResolver();
586 String language = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
587 if ((language == null) || (language.length() < 1)) {
588 return false;
589 }
590 String country = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
591 if (country == null) {
592 return false;
593 }
594 String variant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
595 if (variant == null) {
596 return false;
597 }
598 return true;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700599 }
600
601 /**
602 * (helper function for initDefaultLang() )
603 * Returns whether the current Locale is supported by this Settings screen
604 */
605 private boolean isCurrentLocSupported() {
606 String currentLocID = Locale.getDefault().getISO3Language() + LOCALE_DELIMITER
607 + Locale.getDefault().getISO3Country();
608 return (mDefaultLocPref.findIndexOfValue(currentLocID) > -1);
609 }
610
611 /**
612 * (helper function for initDefaultLang() )
613 * Sets the default language in TTS settings to be the current Locale.
614 * This should only be used after checking that the current Locale is supported.
615 */
616 private void useCurrentLocAsDefault() {
617 Locale currentLocale = Locale.getDefault();
618 ContentResolver resolver = getContentResolver();
619 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, currentLocale.getISO3Language());
620 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, currentLocale.getISO3Country());
621 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, currentLocale.getVariant());
622 }
623
624 /**
625 * (helper function for initDefaultLang() )
626 * Sets the default language in TTS settings to be one known to be supported
627 */
628 private void useSupportedLocAsDefault() {
629 ContentResolver resolver = getContentResolver();
630 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, DEFAULT_LANG_VAL);
631 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, DEFAULT_COUNTRY_VAL);
632 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, DEFAULT_VARIANT_VAL);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700633 }
634
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700635 private void loadEngines() {
Narayan Kamath3dbba082011-06-10 16:50:48 +0100636 List<EngineInfo> engines = mEnginesHelper.getEngines();
Narayan Kamathd5642f72011-06-14 18:17:40 +0100637 CharSequence entries[] = new CharSequence[engines.size()];
638 CharSequence values[] = new CharSequence[engines.size()];
Narayan Kamath62f153d2011-06-14 16:37:11 +0100639
Narayan Kamathd5642f72011-06-14 18:17:40 +0100640 final int count = engines.size();
641 for (int i = 0; i < count; ++i) {
642 final EngineInfo engine = engines.get(i);
643 entries[i] = engine.label;
644 values[i] = engine.name;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700645 }
Narayan Kamath62f153d2011-06-14 16:37:11 +0100646
Narayan Kamathd5642f72011-06-14 18:17:40 +0100647 mDefaultSynthPref.setEntries(entries);
648 mDefaultSynthPref.setEntryValues(values);
Charles Chenbe6e8272010-03-22 16:06:05 -0700649
650 // Set the selected engine based on the saved preference
651 String selectedEngine = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_SYNTH);
Charles Chen8c8185b2010-04-08 16:51:35 -0700652 int selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(selectedEngine);
Charles Chenbe6e8272010-03-22 16:06:05 -0700653 if (selectedEngineIndex == -1){
Narayan Kamath3dbba082011-06-10 16:50:48 +0100654 selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(
655 mEnginesHelper.getHighestRankedEngineName());
Charles Chenbe6e8272010-03-22 16:06:05 -0700656 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000657 if (selectedEngineIndex >= 0) {
658 mDefaultSynthPref.setValueIndex(selectedEngineIndex);
659 }
660 }
661
Narayan Kamathd5642f72011-06-14 18:17:40 +0100662 private void displayDataAlert(Preference pref, final String key) {
663 Log.v(TAG, "Displaying data alert for :" + key);
664 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
665 builder.setTitle(android.R.string.dialog_alert_title);
666 builder.setIcon(android.R.drawable.ic_dialog_alert);
667 builder.setMessage(getActivity().getString(
668 R.string.tts_engine_security_warning, pref.getTitle()));
669 builder.setCancelable(true);
670 builder.setPositiveButton(android.R.string.ok,
671 new DialogInterface.OnClickListener() {
672 public void onClick(DialogInterface dialog, int which) {
673 updateDefaultEngine(key);
674 loadEngines();
675 initEngineSpecificSettings();
676 }
677 });
678 builder.setNegativeButton(android.R.string.cancel, null);
679
680 AlertDialog dialog = builder.create();
681 dialog.show();
682 }
683
684 private void updateDefaultEngine(String engine) {
685 Log.v(TAG, "Updating default synth to : " + engine);
686 if (mTts != null) {
687 try {
688 mTts.shutdown();
689 mTts = null;
690 } catch (Exception e) {
691 Log.e(TAG, "Error shutting down TTS engine" + e);
Narayan Kamath62f153d2011-06-14 16:37:11 +0100692 }
693 }
694
Narayan Kamathd5642f72011-06-14 18:17:40 +0100695 mTts = new TextToSpeech(getActivity().getApplicationContext(), this, engine);
696 mEnableDemo = false;
697 mVoicesMissing = false;
698 updateWidgetState();
699 checkVoiceData();
Narayan Kamath62f153d2011-06-14 16:37:11 +0100700
Narayan Kamathd5642f72011-06-14 18:17:40 +0100701 // Finally, persist this value to settings.
702 Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine);
703 // .. and update the UI.
704 mDefaultSynthPref.setValue(engine);
705
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}