blob: 1f81c9905f84d6ec69845b6f9c148fa38477158d [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 Kamathd5642f72011-06-14 18:17:40 +0100133 mEngineSettings = (Preference) findPreference(KEY_TTS_ENGINE_SETTINGS);
134 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
184 if (engineHasSettings(engineName)) {
185 mEngineSettings.setOnPreferenceClickListener(new OnPreferenceClickListener() {
186 public boolean onPreferenceClick(Preference preference) {
187 Intent i = new Intent();
188 i.setClassName(engineName, engineName + ".EngineSettings");
189 startActivity(i);
190 return true;
191 }
192 });
193 mEngineSettings.setEnabled(true);
194 } else {
195 mEngineSettings.setEnabled(false);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700196 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100197
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700198 }
199
Bjorn Bringertc7762972011-03-11 16:52:51 +0000200 private boolean engineHasSettings(String enginePackageName) {
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700201 PackageManager pm = getPackageManager();
202 Intent i = new Intent();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000203 i.setClassName(enginePackageName, enginePackageName + ".EngineSettings");
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700204 if (pm.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY) != null){
205 return true;
206 }
207 return false;
208 }
209
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700210 private void initDefaultSettings() {
211 ContentResolver resolver = getContentResolver();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700212
213 // Find the default TTS values in the settings, initialize and store the
214 // settings if they are not found.
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700215
Charles Chen5dbc74a2009-12-07 12:08:13 -0800216 // Default synthesis engine
Charles Chen5dbc74a2009-12-07 12:08:13 -0800217 loadEngines();
218 mDefaultSynthPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700219
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700220 // Default rate
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700221 try {
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700222 mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700223 } catch (SettingNotFoundException e) {
224 // default rate setting not found, initialize it
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700225 mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
226 Settings.Secure.putInt(resolver, TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700227 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700228 mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700229 mDefaultRatePref.setOnPreferenceChangeListener(this);
230
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700231 // Default language / country / variant : these three values map to a single ListPref
232 // representing the matching Locale
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700233 initDefaultLang();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700234 mDefaultLocPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700235 }
236
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700237 /**
238 * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
239 * to check the required TTS files are properly installed.
240 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700241 private void checkVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000242 String defaultEngine = mTts.getDefaultEngine();
243 if (TextUtils.isEmpty(defaultEngine)) return;
244 Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
245 intent.setPackage(defaultEngine);
246 try {
247 Log.v(TAG, "Checking voice data: " + intent.toUri(0));
248 startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
249 } catch (ActivityNotFoundException ex) {
250 Log.e(TAG, "Failed to check TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700251 }
252 }
253
254
255 /**
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700256 * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
257 * so the required TTS files are properly installed.
258 */
259 private void installVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000260 String defaultEngine = mTts.getDefaultEngine();
261 if (TextUtils.isEmpty(defaultEngine)) return;
262 Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
Jean-Michel Trivi58ea43a2009-09-09 15:13:38 -0700263 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000264 intent.setPackage(defaultEngine);
265 try {
266 Log.v(TAG, "Installing voice data: " + intent.toUri(0));
267 startActivity(intent);
268 } catch (ActivityNotFoundException ex) {
269 Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700270 }
271 }
272
Charles Chen4df6c792010-01-22 11:17:40 -0800273 /**
274 * Ask the current default engine to return a string of sample text to be
275 * spoken to the user.
276 */
277 private void getSampleText() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000278 String defaultEngine = mTts.getDefaultEngine();
279 if (TextUtils.isEmpty(defaultEngine)) return;
280 Intent intent = new Intent(TextToSpeech.Engine.ACTION_GET_SAMPLE_TEXT);
Charles Chen4df6c792010-01-22 11:17:40 -0800281 intent.putExtra("language", mDefaultLanguage);
282 intent.putExtra("country", mDefaultCountry);
283 intent.putExtra("variant", mDefaultLocVariant);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000284 intent.setPackage(defaultEngine);
Narayan Kamathd5642f72011-06-14 18:17:40 +0100285
Bjorn Bringertc7762972011-03-11 16:52:51 +0000286 try {
287 Log.v(TAG, "Getting sample text: " + intent.toUri(0));
288 startActivityForResult(intent, GET_SAMPLE_TEXT);
289 } catch (ActivityNotFoundException ex) {
290 Log.e(TAG, "Failed to get sample text, no acitivty found for " + intent + ")");
Charles Chen4df6c792010-01-22 11:17:40 -0800291 }
292 }
293
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700294 /**
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700295 * Called when the TTS engine is initialized.
296 */
297 public void onInit(int status) {
Jean-Michel Trivi387dc0c2009-07-28 15:13:35 -0700298 if (status == TextToSpeech.SUCCESS) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700299 mEnableDemo = true;
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800300 if (mDefaultLanguage == null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800301 mDefaultLanguage = Locale.getDefault().getISO3Language();
302 }
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800303 if (mDefaultCountry == null) {
304 mDefaultCountry = Locale.getDefault().getISO3Country();
305 }
306 if (mDefaultLocVariant == null) {
307 mDefaultLocVariant = new String();
308 }
Charles Chencf3998b2010-02-11 18:13:42 -0800309 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Charles Chencf31e652010-04-07 14:26:31 -0700310 updateWidgetState();
311 checkVoiceData();
312 mTtsStarted = true;
313 Log.v(TAG, "TTS engine for settings screen initialized.");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700314 } else {
315 Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
316 mEnableDemo = false;
317 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700318 updateWidgetState();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700319 }
320
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700321 /**
322 * Called when voice data integrity check returns
323 */
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700324 @Override
325 public void onActivityResult(int requestCode, int resultCode, Intent data) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700326 if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000327 onVoiceDataIntegrityCheckDone(data);
Charles Chen4df6c792010-01-22 11:17:40 -0800328 } else if (requestCode == GET_SAMPLE_TEXT) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000329 onSampleTextReceived(resultCode, data);
330 }
331 }
332
333 private void onVoiceDataIntegrityCheckDone(Intent data) {
334 if (data == null){
335 Log.e(TAG, "TTS data check failed data = 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 Log.v(TAG, "TTS data check completed, data = " + data.toUri(0));
344 ArrayList<String> available =
345 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
346 ArrayList<String> unavailable =
347 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
348 if (available == null || unavailable == null){
349 Log.e(TAG, "TTS data check failed (available == == null)");
350 // The CHECK_TTS_DATA activity for the plugin did not run properly;
351 // disable the preview and install controls and return.
352 mEnableDemo = false;
353 mVoicesMissing = false;
354 updateWidgetState();
355 return;
356 }
357 if (available.size() > 0){
358 if (mTts == null) {
359 mTts = new TextToSpeech(getActivity(), this);
Charles Chen4df6c792010-01-22 11:17:40 -0800360 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000361
362 updateDefaultLocPref(available);
363
364 mEnableDemo = true;
365 // Make sure that the default language can be used.
366 int languageResult = mTts.setLanguage(
367 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
368 if (languageResult < TextToSpeech.LANG_AVAILABLE){
369 Locale currentLocale = Locale.getDefault();
370 mDefaultLanguage = currentLocale.getISO3Language();
371 mDefaultCountry = currentLocale.getISO3Country();
372 mDefaultLocVariant = currentLocale.getVariant();
373 languageResult = mTts.setLanguage(
374 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
375 // If the default Locale isn't supported, just choose the first available
376 // language so that there is at least something.
377 if (languageResult < TextToSpeech.LANG_AVAILABLE){
378 parseLocaleInfo(mDefaultLocPref.getEntryValues()[0].toString());
379 mTts.setLanguage(
380 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
381 }
382 ContentResolver resolver = getContentResolver();
383 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
384 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
385 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
386 }
387 } else {
388 mEnableDemo = false;
389 }
390
391 if (unavailable.size() > 0){
392 mVoicesMissing = true;
393 } else {
394 mVoicesMissing = false;
395 }
396
397 updateWidgetState();
398 }
399
400 private void updateDefaultLocPref(ArrayList<String> availableLangs) {
401 CharSequence[] entries = new CharSequence[availableLangs.size()];
402 CharSequence[] entryValues = new CharSequence[availableLangs.size()];
403 int selectedLanguageIndex = -1;
404 String selectedLanguagePref = mDefaultLanguage;
405 if (mDefaultCountry.length() > 0) {
406 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
407 mDefaultCountry;
408 }
409 if (mDefaultLocVariant.length() > 0) {
410 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
411 mDefaultLocVariant;
412 }
413 for (int i = 0; i < availableLangs.size(); i++) {
414 String[] langCountryVariant = availableLangs.get(i).split("-");
415 Locale loc = null;
416 if (langCountryVariant.length == 1){
417 loc = new Locale(langCountryVariant[0]);
418 } else if (langCountryVariant.length == 2){
419 loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
420 } else if (langCountryVariant.length == 3){
421 loc = new Locale(langCountryVariant[0], langCountryVariant[1],
422 langCountryVariant[2]);
423 }
424 if (loc != null){
425 entries[i] = loc.getDisplayName();
426 entryValues[i] = availableLangs.get(i);
427 if (entryValues[i].equals(selectedLanguagePref)) {
428 selectedLanguageIndex = i;
429 }
430 }
431 }
432 mDefaultLocPref.setEntries(entries);
433 mDefaultLocPref.setEntryValues(entryValues);
434 if (selectedLanguageIndex > -1) {
435 mDefaultLocPref.setValueIndex(selectedLanguageIndex);
436 }
437 }
438
439 private void onSampleTextReceived(int resultCode, Intent data) {
440 if (resultCode == TextToSpeech.LANG_AVAILABLE) {
441 String sample = getActivity().getString(R.string.tts_demo);
442 if (data != null && data.getStringExtra("sampleText") != null) {
443 sample = data.getStringExtra("sampleText");
444 }
445 Log.v(TAG, "Got sample text: " + sample);
446 if (mTts != null) {
447 mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
448 }
449 } else {
450 // TODO: Display an error here to the user.
451 Log.e(TAG, "Did not have a sample string for the requested language");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700452 }
453 }
454
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700455 public boolean onPreferenceChange(Preference preference, Object objValue) {
456 if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
457 // "Use Defaults"
Bjorn Bringertc7762972011-03-11 16:52:51 +0000458 int value = ((Boolean) objValue) ? 1 : 0;
459 Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS, value);
460 Log.i(TAG, "TTS 'use default' settings changed, now " + value);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700461 } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
462 // Default rate
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700463 mDefaultRate = Integer.parseInt((String) objValue);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700464 try {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000465 Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700466 if (mTts != null) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000467 mTts.setSpeechRate(mDefaultRate / 100.0f);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700468 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000469 Log.v(TAG, "TTS default rate changed, now " + mDefaultRate);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700470 } catch (NumberFormatException e) {
471 Log.e(TAG, "could not persist default TTS rate setting", e);
472 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700473 } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700474 // Default locale
475 ContentResolver resolver = getContentResolver();
476 parseLocaleInfo((String) objValue);
477 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
478 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
479 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
480 Log.v(TAG, "TTS default lang/country/variant set to "
481 + mDefaultLanguage + "/" + mDefaultCountry + "/" + mDefaultLocVariant);
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700482 if (mTts != null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800483 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700484 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700485 int newIndex = mDefaultLocPref.findIndexOfValue((String)objValue);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000486 Log.v(TAG, " selected is " + newIndex);
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700487 mDemoStringIndex = newIndex > -1 ? newIndex : 0;
Charles Chen5dbc74a2009-12-07 12:08:13 -0800488 } else if (KEY_TTS_DEFAULT_SYNTH.equals(preference.getKey())) {
Narayan Kamathd5642f72011-06-14 18:17:40 +0100489 final String name = objValue.toString();
490 final EngineInfo info = mEnginesHelper.getEngineInfo(name);
491
492 if (info.system) {
493 // For system engines, do away with the alert dialog.
494 updateDefaultEngine(name);
495 initEngineSpecificSettings();
496 } else {
497 // For all other engines, display a warning message before
498 // turning them on.
499 displayDataAlert(preference, name);
Charles Chen5dbc74a2009-12-07 12:08:13 -0800500 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100501
502 // We'll deal with updating the UI ourselves.
503 return false;
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700504 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700505
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700506 return true;
507 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700508
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700509
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700510 /**
511 * Called when mPlayExample or mInstallData is clicked
512 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700513 public boolean onPreferenceClick(Preference preference) {
514 if (preference == mPlayExample) {
Charles Chen4df6c792010-01-22 11:17:40 -0800515 // Get the sample text from the TTS engine; onActivityResult will do
516 // the actual speaking
517 getSampleText();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700518 return true;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000519 } else if (preference == mInstallData) {
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700520 installVoiceData();
521 // quit this activity so it needs to be restarted after installation of the voice data
522 finish();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700523 return true;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700524 }
Narayan Kamathd5642f72011-06-14 18:17:40 +0100525
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700526 return false;
527 }
528
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700529 private void updateWidgetState() {
530 mPlayExample.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700531 mDefaultRatePref.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700532 mDefaultLocPref.setEnabled(mEnableDemo);
533
Charles Chenc8298712010-02-10 13:58:23 -0800534 mInstallData.setEnabled(mVoicesMissing);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700535 }
536
537
538 private void parseLocaleInfo(String locale) {
539 StringTokenizer tokenizer = new StringTokenizer(locale, LOCALE_DELIMITER);
540 mDefaultLanguage = "";
541 mDefaultCountry = "";
542 mDefaultLocVariant = "";
Narayan Kamathd5642f72011-06-14 18:17:40 +0100543
544 if (locale != null) {
545 String[] components = locale.split(LOCALE_DELIMITER);
546 if (components.length > 0) {
547 mDefaultLanguage = components[0];
548 }
549 if (components.length > 1) {
550 mDefaultCountry = components[1];
551 }
552 if (components.length > 2) {
553 mDefaultLocVariant = components[2];
554 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700555 }
556 }
557
558
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700559 /**
560 * Initialize the default language in the UI and in the preferences.
561 * After this method has been invoked, the default language is a supported Locale.
562 */
563 private void initDefaultLang() {
564 // if there isn't already a default language preference
565 if (!hasLangPref()) {
566 // if the current Locale is supported
567 if (isCurrentLocSupported()) {
568 // then use the current Locale as the default language
569 useCurrentLocAsDefault();
570 } else {
571 // otherwise use a default supported Locale as the default language
572 useSupportedLocAsDefault();
573 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700574 }
575
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700576 // Update the language preference list with the default language and the matching
577 // demo string (at this stage there is a default language pref)
578 ContentResolver resolver = getContentResolver();
579 mDefaultLanguage = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
Charles Chen681d0b82010-04-12 12:06:30 -0700580 mDefaultCountry = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
581 mDefaultLocVariant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700582
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700583 // update the demo string
584 mDemoStringIndex = mDefaultLocPref.findIndexOfValue(mDefaultLanguage + LOCALE_DELIMITER
585 + mDefaultCountry);
Charles Chen8a37e612010-02-04 15:52:30 -0800586 if (mDemoStringIndex > -1){
587 mDefaultLocPref.setValueIndex(mDemoStringIndex);
588 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700589 }
590
591 /**
592 * (helper function for initDefaultLang() )
593 * Returns whether there is a default language in the TTS settings.
594 */
595 private boolean hasLangPref() {
Jean-Baptiste Queru6e61b212010-04-14 10:40:48 -0700596 ContentResolver resolver = getContentResolver();
597 String language = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
598 if ((language == null) || (language.length() < 1)) {
599 return false;
600 }
601 String country = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
602 if (country == null) {
603 return false;
604 }
605 String variant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
606 if (variant == null) {
607 return false;
608 }
609 return true;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700610 }
611
612 /**
613 * (helper function for initDefaultLang() )
614 * Returns whether the current Locale is supported by this Settings screen
615 */
616 private boolean isCurrentLocSupported() {
617 String currentLocID = Locale.getDefault().getISO3Language() + LOCALE_DELIMITER
618 + Locale.getDefault().getISO3Country();
619 return (mDefaultLocPref.findIndexOfValue(currentLocID) > -1);
620 }
621
622 /**
623 * (helper function for initDefaultLang() )
624 * Sets the default language in TTS settings to be the current Locale.
625 * This should only be used after checking that the current Locale is supported.
626 */
627 private void useCurrentLocAsDefault() {
628 Locale currentLocale = Locale.getDefault();
629 ContentResolver resolver = getContentResolver();
630 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, currentLocale.getISO3Language());
631 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, currentLocale.getISO3Country());
632 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, currentLocale.getVariant());
633 }
634
635 /**
636 * (helper function for initDefaultLang() )
637 * Sets the default language in TTS settings to be one known to be supported
638 */
639 private void useSupportedLocAsDefault() {
640 ContentResolver resolver = getContentResolver();
641 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, DEFAULT_LANG_VAL);
642 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, DEFAULT_COUNTRY_VAL);
643 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, DEFAULT_VARIANT_VAL);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700644 }
645
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700646 private void loadEngines() {
Narayan Kamath3dbba082011-06-10 16:50:48 +0100647 List<EngineInfo> engines = mEnginesHelper.getEngines();
Narayan Kamathd5642f72011-06-14 18:17:40 +0100648 CharSequence entries[] = new CharSequence[engines.size()];
649 CharSequence values[] = new CharSequence[engines.size()];
Narayan Kamath62f153d2011-06-14 16:37:11 +0100650
Narayan Kamathd5642f72011-06-14 18:17:40 +0100651 final int count = engines.size();
652 for (int i = 0; i < count; ++i) {
653 final EngineInfo engine = engines.get(i);
654 entries[i] = engine.label;
655 values[i] = engine.name;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700656 }
Narayan Kamath62f153d2011-06-14 16:37:11 +0100657
Narayan Kamathd5642f72011-06-14 18:17:40 +0100658 mDefaultSynthPref.setEntries(entries);
659 mDefaultSynthPref.setEntryValues(values);
Charles Chenbe6e8272010-03-22 16:06:05 -0700660
661 // Set the selected engine based on the saved preference
662 String selectedEngine = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_SYNTH);
Charles Chen8c8185b2010-04-08 16:51:35 -0700663 int selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(selectedEngine);
Charles Chenbe6e8272010-03-22 16:06:05 -0700664 if (selectedEngineIndex == -1){
Narayan Kamath3dbba082011-06-10 16:50:48 +0100665 selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(
666 mEnginesHelper.getHighestRankedEngineName());
Charles Chenbe6e8272010-03-22 16:06:05 -0700667 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000668 if (selectedEngineIndex >= 0) {
669 mDefaultSynthPref.setValueIndex(selectedEngineIndex);
670 }
671 }
672
Narayan Kamathd5642f72011-06-14 18:17:40 +0100673 private void displayDataAlert(Preference pref, final String key) {
674 Log.v(TAG, "Displaying data alert for :" + key);
675 AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
676 builder.setTitle(android.R.string.dialog_alert_title);
677 builder.setIcon(android.R.drawable.ic_dialog_alert);
678 builder.setMessage(getActivity().getString(
679 R.string.tts_engine_security_warning, pref.getTitle()));
680 builder.setCancelable(true);
681 builder.setPositiveButton(android.R.string.ok,
682 new DialogInterface.OnClickListener() {
683 public void onClick(DialogInterface dialog, int which) {
684 updateDefaultEngine(key);
685 loadEngines();
686 initEngineSpecificSettings();
687 }
688 });
689 builder.setNegativeButton(android.R.string.cancel, null);
690
691 AlertDialog dialog = builder.create();
692 dialog.show();
693 }
694
695 private void updateDefaultEngine(String engine) {
696 Log.v(TAG, "Updating default synth to : " + engine);
697 if (mTts != null) {
698 try {
699 mTts.shutdown();
700 mTts = null;
701 } catch (Exception e) {
702 Log.e(TAG, "Error shutting down TTS engine" + e);
Narayan Kamath62f153d2011-06-14 16:37:11 +0100703 }
704 }
705
Narayan Kamathd5642f72011-06-14 18:17:40 +0100706 mTts = new TextToSpeech(getActivity().getApplicationContext(), this, engine);
707 mEnableDemo = false;
708 mVoicesMissing = false;
709 updateWidgetState();
710 checkVoiceData();
Narayan Kamath62f153d2011-06-14 16:37:11 +0100711
Narayan Kamathd5642f72011-06-14 18:17:40 +0100712 // Finally, persist this value to settings.
713 Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, engine);
714 // .. and update the UI.
715 mDefaultSynthPref.setValue(engine);
716
717 Log.v(TAG, "The default synth is now: " + engine);
Narayan Kamath62f153d2011-06-14 16:37:11 +0100718 }
719
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700720}