blob: fe2dbe32e752085600da35228df14be479114674 [file] [log] [blame]
Jean-Michel Trivied29a652009-06-05 18:37:29 -07001/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.settings;
18
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070019import static android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070020import static android.provider.Settings.Secure.TTS_DEFAULT_LANG;
21import static android.provider.Settings.Secure.TTS_DEFAULT_RATE;
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -070022import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070023import static android.provider.Settings.Secure.TTS_DEFAULT_VARIANT;
Charles Chenf47cce02010-03-17 17:33:23 -070024import static android.provider.Settings.Secure.TTS_ENABLED_PLUGINS;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070025import static android.provider.Settings.Secure.TTS_USE_DEFAULTS;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070026
Charles Chen0a0eb5f2010-03-16 20:09:17 -070027import android.app.AlertDialog;
Bjorn Bringertc7762972011-03-11 16:52:51 +000028import android.content.ActivityNotFoundException;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070029import android.content.ContentResolver;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070030import android.content.Context;
Charles Chen0a0eb5f2010-03-16 20:09:17 -070031import android.content.DialogInterface;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070032import android.content.Intent;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070033import android.content.pm.PackageManager;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070034import android.os.Bundle;
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070035import android.preference.CheckBoxPreference;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070036import android.preference.ListPreference;
37import android.preference.Preference;
Charles Chen0a0eb5f2010-03-16 20:09:17 -070038import android.preference.PreferenceGroup;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070039import android.provider.Settings;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070040import android.provider.Settings.SettingNotFoundException;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070041import android.speech.tts.TextToSpeech;
Bjorn Bringertc7762972011-03-11 16:52:51 +000042import android.text.TextUtils;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070043import android.util.Log;
44
Charles Chenc8298712010-02-10 13:58:23 -080045import java.util.ArrayList;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070046import java.util.List;
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -070047import java.util.Locale;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070048import java.util.StringTokenizer;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070049
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -070050public class TextToSpeechSettings extends SettingsPreferenceFragment implements
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070051 Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
52 TextToSpeech.OnInitListener {
Jean-Michel Trivied29a652009-06-05 18:37:29 -070053
Bjorn Bringertc7762972011-03-11 16:52:51 +000054 private static final boolean DBG = true;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070055 private static final String TAG = "TextToSpeechSettings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070056
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070057 private static final String KEY_TTS_PLAY_EXAMPLE = "tts_play_example";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070058 private static final String KEY_TTS_INSTALL_DATA = "tts_install_data";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070059 private static final String KEY_TTS_USE_DEFAULT = "toggle_use_default_tts_settings";
Jean-Michel Trivied29a652009-06-05 18:37:29 -070060 private static final String KEY_TTS_DEFAULT_RATE = "tts_default_rate";
Jean-Michel Trivi80368622009-06-09 19:29:27 -070061 private static final String KEY_TTS_DEFAULT_LANG = "tts_default_lang";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070062 private static final String KEY_TTS_DEFAULT_COUNTRY = "tts_default_country";
63 private static final String KEY_TTS_DEFAULT_VARIANT = "tts_default_variant";
Charles Chen5dbc74a2009-12-07 12:08:13 -080064 private static final String KEY_TTS_DEFAULT_SYNTH = "tts_default_synth";
Bjorn Bringertc7762972011-03-11 16:52:51 +000065 private static final String KEY_TTS_ENGINES = "tts_engines_section";
Charles Chen0a0eb5f2010-03-16 20:09:17 -070066
67 private static final String KEY_PLUGIN_ENABLED_PREFIX = "ENABLED_";
68 private static final String KEY_PLUGIN_SETTINGS_PREFIX = "SETTINGS_";
69
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070070 // TODO move default Locale values to TextToSpeech.Engine
71 private static final String DEFAULT_LANG_VAL = "eng";
72 private static final String DEFAULT_COUNTRY_VAL = "USA";
73 private static final String DEFAULT_VARIANT_VAL = "";
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070074
75 private static final String LOCALE_DELIMITER = "-";
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070076
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070077 private Preference mPlayExample = null;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070078 private Preference mInstallData = null;
Jean-Michel Trivied29a652009-06-05 18:37:29 -070079 private CheckBoxPreference mUseDefaultPref = null;
80 private ListPreference mDefaultRatePref = null;
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070081 private ListPreference mDefaultLocPref = null;
Charles Chen5dbc74a2009-12-07 12:08:13 -080082 private ListPreference mDefaultSynthPref = null;
Bjorn Bringertc7762972011-03-11 16:52:51 +000083 private PreferenceGroup mEnginesGroup;
84
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -070085 private String mDefaultLanguage = null;
86 private String mDefaultCountry = null;
87 private String mDefaultLocVariant = null;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070088 private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
89
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -070090 // Index of the current string to use for the demo.
91 private int mDemoStringIndex = 0;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070092
93 private boolean mEnableDemo = false;
Charles Chenc8298712010-02-10 13:58:23 -080094 private boolean mVoicesMissing = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070095
96 private TextToSpeech mTts = null;
Charles Chencf31e652010-04-07 14:26:31 -070097 private boolean mTtsStarted = false;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -070098
99 /**
100 * Request code (arbitrary value) for voice data check through
101 * startActivityForResult.
102 */
103 private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
Charles Chen4df6c792010-01-22 11:17:40 -0800104 private static final int GET_SAMPLE_TEXT = 1983;
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700105
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700106 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700107 public void onCreate(Bundle savedInstanceState) {
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700108 super.onCreate(savedInstanceState);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700109 addPreferencesFromResource(R.xml.tts_settings);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700110
Bjorn Bringertc7762972011-03-11 16:52:51 +0000111 getActivity().setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
Jean-Michel Trivi6dde8962009-08-11 09:06:58 -0700112
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700113 mEnableDemo = false;
Charles Chencf31e652010-04-07 14:26:31 -0700114 mTtsStarted = false;
Charles Chenbe6e8272010-03-22 16:06:05 -0700115
Charles Chen8c8185b2010-04-08 16:51:35 -0700116 Locale currentLocale = Locale.getDefault();
117 mDefaultLanguage = currentLocale.getISO3Language();
118 mDefaultCountry = currentLocale.getISO3Country();
119 mDefaultLocVariant = currentLocale.getVariant();
120
Bjorn Bringertc7762972011-03-11 16:52:51 +0000121 mPlayExample = findPreference(KEY_TTS_PLAY_EXAMPLE);
122 mPlayExample.setOnPreferenceClickListener(this);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700123
Bjorn Bringertc7762972011-03-11 16:52:51 +0000124 mInstallData = findPreference(KEY_TTS_INSTALL_DATA);
125 mInstallData.setOnPreferenceClickListener(this);
126
127 mUseDefaultPref = (CheckBoxPreference) findPreference(KEY_TTS_USE_DEFAULT);
128 mDefaultSynthPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_SYNTH);
129 mDefaultRatePref = (ListPreference) findPreference(KEY_TTS_DEFAULT_RATE);
130 mDefaultLocPref = (ListPreference) findPreference(KEY_TTS_DEFAULT_LANG);
131
132 mEnginesGroup = (PreferenceGroup) findPreference(KEY_TTS_ENGINES);
133
134 mTts = new TextToSpeech(getActivity().getApplicationContext(), this);
135
136 initDefaultSettings();
137 addEngineSpecificSettings();
138 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700139
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700140 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700141 public void onStart() {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700142 super.onStart();
Charles Chencf31e652010-04-07 14:26:31 -0700143 if (mTtsStarted){
144 // whenever we return to this screen, we don't know the state of the
145 // system, so we have to recheck that we can play the demo, or it must be disabled.
146 // TODO make the TTS service listen to "changes in the system", i.e. sd card un/mount
Charles Chencf31e652010-04-07 14:26:31 -0700147 updateWidgetState();
148 checkVoiceData();
149 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700150 }
151
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700152 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700153 public void onDestroy() {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700154 super.onDestroy();
155 if (mTts != null) {
156 mTts.shutdown();
157 }
158 }
159
Charles Chen8c8185b2010-04-08 16:51:35 -0700160 @Override
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700161 public void onPause() {
Charles Chen8c8185b2010-04-08 16:51:35 -0700162 super.onPause();
163 if ((mDefaultRatePref != null) && (mDefaultRatePref.getDialog() != null)) {
164 mDefaultRatePref.getDialog().dismiss();
165 }
166 if ((mDefaultLocPref != null) && (mDefaultLocPref.getDialog() != null)) {
167 mDefaultLocPref.getDialog().dismiss();
168 }
169 if ((mDefaultSynthPref != null) && (mDefaultSynthPref.getDialog() != null)) {
170 mDefaultSynthPref.getDialog().dismiss();
171 }
172 }
173
Bjorn Bringertc7762972011-03-11 16:52:51 +0000174 private void addEngineSpecificSettings() {
175 Context context = getActivity();
176 List<TextToSpeech.EngineInfo> engines = mTts.getEngines();
177 for (TextToSpeech.EngineInfo engine : engines) {
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700178 String prefKey = "";
Bjorn Bringertc7762972011-03-11 16:52:51 +0000179 final String engineName = engine.name;
180 if (!engineName.equals(TextToSpeech.Engine.DEFAULT_ENGINE)) {
181 CheckBoxPreference enablePref = new CheckBoxPreference(context);
182 prefKey = KEY_PLUGIN_ENABLED_PREFIX + engineName;
183 enablePref.setKey(prefKey);
184 enablePref.setTitle(engine.label);
Bjorn Bringertdf92f2e2011-04-19 14:37:49 +0100185 enablePref.setOnPreferenceClickListener(this);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000186 mEnginesGroup.addPreference(enablePref);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700187 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000188 if (engineHasSettings(engineName)) {
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700189 Preference pref = new Preference(context);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000190 prefKey = KEY_PLUGIN_SETTINGS_PREFIX + engineName;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700191 pref.setKey(prefKey);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000192 pref.setTitle(engine.label);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700193 CharSequence settingsLabel = getResources().getString(
Bjorn Bringertc7762972011-03-11 16:52:51 +0000194 R.string.tts_engine_name_settings, engine.label);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700195 pref.setSummary(settingsLabel);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000196 // TODO: add a new API for this
197// pref.setOnPreferenceClickListener(new OnPreferenceClickListener(){
198// public boolean onPreferenceClick(Preference preference){
199// Intent i = new Intent();
200// i.setClassName(pluginPackageName,
201// pluginPackageName + ".EngineSettings");
202// startActivity(i);
203// return true;
204// }
205// });
206 mEnginesGroup.addPreference(pref);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700207 }
208 }
209 }
210
Bjorn Bringertc7762972011-03-11 16:52:51 +0000211 private boolean engineHasSettings(String enginePackageName) {
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700212 PackageManager pm = getPackageManager();
213 Intent i = new Intent();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000214 i.setClassName(enginePackageName, enginePackageName + ".EngineSettings");
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700215 if (pm.resolveActivity(i, PackageManager.MATCH_DEFAULT_ONLY) != null){
216 return true;
217 }
218 return false;
219 }
220
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700221 private void initDefaultSettings() {
222 ContentResolver resolver = getContentResolver();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700223
224 // Find the default TTS values in the settings, initialize and store the
225 // settings if they are not found.
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700226
227 // "Use Defaults"
Bjorn Bringertc7762972011-03-11 16:52:51 +0000228 int useDefault = Settings.Secure.getInt(resolver, TTS_USE_DEFAULTS,
229 TextToSpeech.Engine.USE_DEFAULTS);
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700230 mUseDefaultPref.setChecked(useDefault == 1);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700231 mUseDefaultPref.setOnPreferenceChangeListener(this);
232
Charles Chen5dbc74a2009-12-07 12:08:13 -0800233 // Default synthesis engine
Charles Chen5dbc74a2009-12-07 12:08:13 -0800234 loadEngines();
235 mDefaultSynthPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700236
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700237 // Default rate
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700238 try {
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700239 mDefaultRate = Settings.Secure.getInt(resolver, TTS_DEFAULT_RATE);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700240 } catch (SettingNotFoundException e) {
241 // default rate setting not found, initialize it
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700242 mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
243 Settings.Secure.putInt(resolver, TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700244 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700245 mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700246 mDefaultRatePref.setOnPreferenceChangeListener(this);
247
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700248 // Default language / country / variant : these three values map to a single ListPref
249 // representing the matching Locale
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700250 initDefaultLang();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700251 mDefaultLocPref.setOnPreferenceChangeListener(this);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700252 }
253
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700254 /**
255 * Ask the current default engine to launch the matching CHECK_TTS_DATA activity
256 * to check the required TTS files are properly installed.
257 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700258 private void checkVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000259 String defaultEngine = mTts.getDefaultEngine();
260 if (TextUtils.isEmpty(defaultEngine)) return;
261 Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
262 intent.setPackage(defaultEngine);
263 try {
264 Log.v(TAG, "Checking voice data: " + intent.toUri(0));
265 startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
266 } catch (ActivityNotFoundException ex) {
267 Log.e(TAG, "Failed to check TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700268 }
269 }
270
271
272 /**
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700273 * Ask the current default engine to launch the matching INSTALL_TTS_DATA activity
274 * so the required TTS files are properly installed.
275 */
276 private void installVoiceData() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000277 String defaultEngine = mTts.getDefaultEngine();
278 if (TextUtils.isEmpty(defaultEngine)) return;
279 Intent intent = new Intent(TextToSpeech.Engine.ACTION_INSTALL_TTS_DATA);
Jean-Michel Trivi58ea43a2009-09-09 15:13:38 -0700280 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000281 intent.setPackage(defaultEngine);
282 try {
283 Log.v(TAG, "Installing voice data: " + intent.toUri(0));
284 startActivity(intent);
285 } catch (ActivityNotFoundException ex) {
286 Log.e(TAG, "Failed to install TTS data, no acitivty found for " + intent + ")");
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700287 }
288 }
289
Charles Chen4df6c792010-01-22 11:17:40 -0800290 /**
291 * Ask the current default engine to return a string of sample text to be
292 * spoken to the user.
293 */
294 private void getSampleText() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000295 String defaultEngine = mTts.getDefaultEngine();
296 if (TextUtils.isEmpty(defaultEngine)) return;
297 Intent intent = new Intent(TextToSpeech.Engine.ACTION_GET_SAMPLE_TEXT);
Charles Chen4df6c792010-01-22 11:17:40 -0800298 intent.putExtra("language", mDefaultLanguage);
299 intent.putExtra("country", mDefaultCountry);
300 intent.putExtra("variant", mDefaultLocVariant);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000301 intent.setPackage(defaultEngine);
302 try {
303 Log.v(TAG, "Getting sample text: " + intent.toUri(0));
304 startActivityForResult(intent, GET_SAMPLE_TEXT);
305 } catch (ActivityNotFoundException ex) {
306 Log.e(TAG, "Failed to get sample text, no acitivty found for " + intent + ")");
Charles Chen4df6c792010-01-22 11:17:40 -0800307 }
308 }
309
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700310 /**
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700311 * Called when the TTS engine is initialized.
312 */
313 public void onInit(int status) {
Jean-Michel Trivi387dc0c2009-07-28 15:13:35 -0700314 if (status == TextToSpeech.SUCCESS) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700315 mEnableDemo = true;
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800316 if (mDefaultLanguage == null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800317 mDefaultLanguage = Locale.getDefault().getISO3Language();
318 }
Jean-Michel Trivi7330a882010-02-17 12:50:44 -0800319 if (mDefaultCountry == null) {
320 mDefaultCountry = Locale.getDefault().getISO3Country();
321 }
322 if (mDefaultLocVariant == null) {
323 mDefaultLocVariant = new String();
324 }
Charles Chencf3998b2010-02-11 18:13:42 -0800325 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Charles Chencf31e652010-04-07 14:26:31 -0700326 updateWidgetState();
327 checkVoiceData();
328 mTtsStarted = true;
329 Log.v(TAG, "TTS engine for settings screen initialized.");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700330 } else {
331 Log.v(TAG, "TTS engine for settings screen failed to initialize successfully.");
332 mEnableDemo = false;
333 }
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700334 updateWidgetState();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700335 }
336
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700337 /**
338 * Called when voice data integrity check returns
339 */
Daisuke Miyakawa49a305e2010-09-13 17:52:13 -0700340 @Override
341 public void onActivityResult(int requestCode, int resultCode, Intent data) {
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700342 if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000343 onVoiceDataIntegrityCheckDone(data);
Charles Chen4df6c792010-01-22 11:17:40 -0800344 } else if (requestCode == GET_SAMPLE_TEXT) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000345 onSampleTextReceived(resultCode, data);
346 }
347 }
348
349 private void onVoiceDataIntegrityCheckDone(Intent data) {
350 if (data == null){
351 Log.e(TAG, "TTS data check failed data = null");
352 // The CHECK_TTS_DATA activity for the plugin did not run properly;
353 // disable the preview and install controls and return.
354 mEnableDemo = false;
355 mVoicesMissing = false;
356 updateWidgetState();
357 return;
358 }
359 Log.v(TAG, "TTS data check completed, data = " + data.toUri(0));
360 ArrayList<String> available =
361 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
362 ArrayList<String> unavailable =
363 data.getStringArrayListExtra(TextToSpeech.Engine.EXTRA_UNAVAILABLE_VOICES);
364 if (available == null || unavailable == null){
365 Log.e(TAG, "TTS data check failed (available == == null)");
366 // The CHECK_TTS_DATA activity for the plugin did not run properly;
367 // disable the preview and install controls and return.
368 mEnableDemo = false;
369 mVoicesMissing = false;
370 updateWidgetState();
371 return;
372 }
373 if (available.size() > 0){
374 if (mTts == null) {
375 mTts = new TextToSpeech(getActivity(), this);
Charles Chen4df6c792010-01-22 11:17:40 -0800376 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000377
378 updateDefaultLocPref(available);
379
380 mEnableDemo = true;
381 // Make sure that the default language can be used.
382 int languageResult = mTts.setLanguage(
383 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
384 if (languageResult < TextToSpeech.LANG_AVAILABLE){
385 Locale currentLocale = Locale.getDefault();
386 mDefaultLanguage = currentLocale.getISO3Language();
387 mDefaultCountry = currentLocale.getISO3Country();
388 mDefaultLocVariant = currentLocale.getVariant();
389 languageResult = mTts.setLanguage(
390 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
391 // If the default Locale isn't supported, just choose the first available
392 // language so that there is at least something.
393 if (languageResult < TextToSpeech.LANG_AVAILABLE){
394 parseLocaleInfo(mDefaultLocPref.getEntryValues()[0].toString());
395 mTts.setLanguage(
396 new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
397 }
398 ContentResolver resolver = getContentResolver();
399 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
400 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
401 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
402 }
403 } else {
404 mEnableDemo = false;
405 }
406
407 if (unavailable.size() > 0){
408 mVoicesMissing = true;
409 } else {
410 mVoicesMissing = false;
411 }
412
413 updateWidgetState();
414 }
415
416 private void updateDefaultLocPref(ArrayList<String> availableLangs) {
417 CharSequence[] entries = new CharSequence[availableLangs.size()];
418 CharSequence[] entryValues = new CharSequence[availableLangs.size()];
419 int selectedLanguageIndex = -1;
420 String selectedLanguagePref = mDefaultLanguage;
421 if (mDefaultCountry.length() > 0) {
422 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
423 mDefaultCountry;
424 }
425 if (mDefaultLocVariant.length() > 0) {
426 selectedLanguagePref = selectedLanguagePref + LOCALE_DELIMITER +
427 mDefaultLocVariant;
428 }
429 for (int i = 0; i < availableLangs.size(); i++) {
430 String[] langCountryVariant = availableLangs.get(i).split("-");
431 Locale loc = null;
432 if (langCountryVariant.length == 1){
433 loc = new Locale(langCountryVariant[0]);
434 } else if (langCountryVariant.length == 2){
435 loc = new Locale(langCountryVariant[0], langCountryVariant[1]);
436 } else if (langCountryVariant.length == 3){
437 loc = new Locale(langCountryVariant[0], langCountryVariant[1],
438 langCountryVariant[2]);
439 }
440 if (loc != null){
441 entries[i] = loc.getDisplayName();
442 entryValues[i] = availableLangs.get(i);
443 if (entryValues[i].equals(selectedLanguagePref)) {
444 selectedLanguageIndex = i;
445 }
446 }
447 }
448 mDefaultLocPref.setEntries(entries);
449 mDefaultLocPref.setEntryValues(entryValues);
450 if (selectedLanguageIndex > -1) {
451 mDefaultLocPref.setValueIndex(selectedLanguageIndex);
452 }
453 }
454
455 private void onSampleTextReceived(int resultCode, Intent data) {
456 if (resultCode == TextToSpeech.LANG_AVAILABLE) {
457 String sample = getActivity().getString(R.string.tts_demo);
458 if (data != null && data.getStringExtra("sampleText") != null) {
459 sample = data.getStringExtra("sampleText");
460 }
461 Log.v(TAG, "Got sample text: " + sample);
462 if (mTts != null) {
463 mTts.speak(sample, TextToSpeech.QUEUE_FLUSH, null);
464 }
465 } else {
466 // TODO: Display an error here to the user.
467 Log.e(TAG, "Did not have a sample string for the requested language");
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700468 }
469 }
470
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700471 public boolean onPreferenceChange(Preference preference, Object objValue) {
472 if (KEY_TTS_USE_DEFAULT.equals(preference.getKey())) {
473 // "Use Defaults"
Bjorn Bringertc7762972011-03-11 16:52:51 +0000474 int value = ((Boolean) objValue) ? 1 : 0;
475 Settings.Secure.putInt(getContentResolver(), TTS_USE_DEFAULTS, value);
476 Log.i(TAG, "TTS 'use default' settings changed, now " + value);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700477 } else if (KEY_TTS_DEFAULT_RATE.equals(preference.getKey())) {
478 // Default rate
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700479 mDefaultRate = Integer.parseInt((String) objValue);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700480 try {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000481 Settings.Secure.putInt(getContentResolver(), TTS_DEFAULT_RATE, mDefaultRate);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700482 if (mTts != null) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000483 mTts.setSpeechRate(mDefaultRate / 100.0f);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700484 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000485 Log.v(TAG, "TTS default rate changed, now " + mDefaultRate);
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700486 } catch (NumberFormatException e) {
487 Log.e(TAG, "could not persist default TTS rate setting", e);
488 }
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700489 } else if (KEY_TTS_DEFAULT_LANG.equals(preference.getKey())) {
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700490 // Default locale
491 ContentResolver resolver = getContentResolver();
492 parseLocaleInfo((String) objValue);
493 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, mDefaultLanguage);
494 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, mDefaultCountry);
495 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, mDefaultLocVariant);
496 Log.v(TAG, "TTS default lang/country/variant set to "
497 + mDefaultLanguage + "/" + mDefaultCountry + "/" + mDefaultLocVariant);
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700498 if (mTts != null) {
Charles Chencf3998b2010-02-11 18:13:42 -0800499 mTts.setLanguage(new Locale(mDefaultLanguage, mDefaultCountry, mDefaultLocVariant));
Jean-Michel Trivi628431d2009-07-17 16:52:54 -0700500 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700501 int newIndex = mDefaultLocPref.findIndexOfValue((String)objValue);
Bjorn Bringertc7762972011-03-11 16:52:51 +0000502 Log.v(TAG, " selected is " + newIndex);
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700503 mDemoStringIndex = newIndex > -1 ? newIndex : 0;
Charles Chen5dbc74a2009-12-07 12:08:13 -0800504 } else if (KEY_TTS_DEFAULT_SYNTH.equals(preference.getKey())) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000505 String defaultEng = objValue.toString();
506 Settings.Secure.putString(getContentResolver(), TTS_DEFAULT_SYNTH, defaultEng);
Charles Chen5dbc74a2009-12-07 12:08:13 -0800507 if (mTts != null) {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000508 mTts.setEngineByPackageName(defaultEng);
Charles Chencf3998b2010-02-11 18:13:42 -0800509 mEnableDemo = false;
510 mVoicesMissing = false;
511 updateWidgetState();
512 checkVoiceData();
Charles Chen5dbc74a2009-12-07 12:08:13 -0800513 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000514 Log.v(TAG, "The default synth is: " + objValue.toString());
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700515 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700516
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700517 return true;
518 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700519
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700520
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700521 /**
522 * Called when mPlayExample or mInstallData is clicked
523 */
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700524 public boolean onPreferenceClick(Preference preference) {
525 if (preference == mPlayExample) {
Charles Chen4df6c792010-01-22 11:17:40 -0800526 // Get the sample text from the TTS engine; onActivityResult will do
527 // the actual speaking
528 getSampleText();
Jean-Michel Trivi74e565d2009-06-18 18:44:52 -0700529 return true;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000530 } else if (preference == mInstallData) {
Jean-Michel Trivi2acc02e2009-06-25 10:03:43 -0700531 installVoiceData();
532 // quit this activity so it needs to be restarted after installation of the voice data
533 finish();
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700534 return true;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000535 } else if (preference.getKey().startsWith(KEY_PLUGIN_ENABLED_PREFIX)) {
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700536 final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
Bjorn Bringertc7762972011-03-11 16:52:51 +0000537 if (chkPref.isChecked()) {
538 chkPref.setChecked(false);
539 AlertDialog d = (new AlertDialog.Builder(getActivity()))
540 .setTitle(android.R.string.dialog_alert_title)
541 .setIcon(android.R.drawable.ic_dialog_alert)
542 .setMessage(
543 getActivity().getString(R.string.tts_engine_security_warning,
544 chkPref.getTitle()))
545 .setCancelable(true)
546 .setPositiveButton(android.R.string.ok,
547 new DialogInterface.OnClickListener() {
548 public void onClick(DialogInterface dialog, int which) {
549 chkPref.setChecked(true);
550 loadEngines();
551 }
552 })
553 .setNegativeButton(android.R.string.cancel,
554 new DialogInterface.OnClickListener() {
555 public void onClick(DialogInterface dialog, int which) {
556 }
557 })
558 .create();
559 d.show();
560 } else {
561 loadEngines();
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700562 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000563 return true;
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700564 }
565 return false;
566 }
567
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700568 private void updateWidgetState() {
569 mPlayExample.setEnabled(mEnableDemo);
570 mUseDefaultPref.setEnabled(mEnableDemo);
571 mDefaultRatePref.setEnabled(mEnableDemo);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700572 mDefaultLocPref.setEnabled(mEnableDemo);
573
Charles Chenc8298712010-02-10 13:58:23 -0800574 mInstallData.setEnabled(mVoicesMissing);
Jean-Michel Trivi1e6a45a2009-06-22 16:03:40 -0700575 }
576
577
578 private void parseLocaleInfo(String locale) {
579 StringTokenizer tokenizer = new StringTokenizer(locale, LOCALE_DELIMITER);
580 mDefaultLanguage = "";
581 mDefaultCountry = "";
582 mDefaultLocVariant = "";
583 if (tokenizer.hasMoreTokens()) {
584 mDefaultLanguage = tokenizer.nextToken().trim();
585 }
586 if (tokenizer.hasMoreTokens()) {
587 mDefaultCountry = tokenizer.nextToken().trim();
588 }
589 if (tokenizer.hasMoreTokens()) {
590 mDefaultLocVariant = tokenizer.nextToken().trim();
591 }
592 }
593
594
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700595 /**
596 * Initialize the default language in the UI and in the preferences.
597 * After this method has been invoked, the default language is a supported Locale.
598 */
599 private void initDefaultLang() {
600 // if there isn't already a default language preference
601 if (!hasLangPref()) {
602 // if the current Locale is supported
603 if (isCurrentLocSupported()) {
604 // then use the current Locale as the default language
605 useCurrentLocAsDefault();
606 } else {
607 // otherwise use a default supported Locale as the default language
608 useSupportedLocAsDefault();
609 }
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700610 }
611
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700612 // Update the language preference list with the default language and the matching
613 // demo string (at this stage there is a default language pref)
614 ContentResolver resolver = getContentResolver();
615 mDefaultLanguage = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
Charles Chen681d0b82010-04-12 12:06:30 -0700616 mDefaultCountry = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
617 mDefaultLocVariant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700618
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700619 // update the demo string
620 mDemoStringIndex = mDefaultLocPref.findIndexOfValue(mDefaultLanguage + LOCALE_DELIMITER
621 + mDefaultCountry);
Charles Chen8a37e612010-02-04 15:52:30 -0800622 if (mDemoStringIndex > -1){
623 mDefaultLocPref.setValueIndex(mDemoStringIndex);
624 }
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700625 }
626
627 /**
628 * (helper function for initDefaultLang() )
629 * Returns whether there is a default language in the TTS settings.
630 */
631 private boolean hasLangPref() {
Jean-Baptiste Queru6e61b212010-04-14 10:40:48 -0700632 ContentResolver resolver = getContentResolver();
633 String language = Settings.Secure.getString(resolver, TTS_DEFAULT_LANG);
634 if ((language == null) || (language.length() < 1)) {
635 return false;
636 }
637 String country = Settings.Secure.getString(resolver, TTS_DEFAULT_COUNTRY);
638 if (country == null) {
639 return false;
640 }
641 String variant = Settings.Secure.getString(resolver, TTS_DEFAULT_VARIANT);
642 if (variant == null) {
643 return false;
644 }
645 return true;
Jean-Michel Trivie8e23db2009-09-02 15:15:33 -0700646 }
647
648 /**
649 * (helper function for initDefaultLang() )
650 * Returns whether the current Locale is supported by this Settings screen
651 */
652 private boolean isCurrentLocSupported() {
653 String currentLocID = Locale.getDefault().getISO3Language() + LOCALE_DELIMITER
654 + Locale.getDefault().getISO3Country();
655 return (mDefaultLocPref.findIndexOfValue(currentLocID) > -1);
656 }
657
658 /**
659 * (helper function for initDefaultLang() )
660 * Sets the default language in TTS settings to be the current Locale.
661 * This should only be used after checking that the current Locale is supported.
662 */
663 private void useCurrentLocAsDefault() {
664 Locale currentLocale = Locale.getDefault();
665 ContentResolver resolver = getContentResolver();
666 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, currentLocale.getISO3Language());
667 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, currentLocale.getISO3Country());
668 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, currentLocale.getVariant());
669 }
670
671 /**
672 * (helper function for initDefaultLang() )
673 * Sets the default language in TTS settings to be one known to be supported
674 */
675 private void useSupportedLocAsDefault() {
676 ContentResolver resolver = getContentResolver();
677 Settings.Secure.putString(resolver, TTS_DEFAULT_LANG, DEFAULT_LANG_VAL);
678 Settings.Secure.putString(resolver, TTS_DEFAULT_COUNTRY, DEFAULT_COUNTRY_VAL);
679 Settings.Secure.putString(resolver, TTS_DEFAULT_VARIANT, DEFAULT_VARIANT_VAL);
Jean-Michel Trivi44fbbea2009-07-06 14:04:54 -0700680 }
681
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700682 private void loadEngines() {
Bjorn Bringertc7762972011-03-11 16:52:51 +0000683 List<TextToSpeech.EngineInfo> engines = mTts.getEngines();
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700684 ArrayList<CharSequence> entries = new ArrayList<CharSequence>();
685 ArrayList<CharSequence> values = new ArrayList<CharSequence>();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000686 StringBuilder enabledEngines = new StringBuilder();
687
688 for (TextToSpeech.EngineInfo engine : engines) {
689 Log.v(TAG, "Engine: " + engine);
690 if (isEngineEnabled(engine.name)) {
691 entries.add(engine.label);
692 values.add(engine.name);
693 if (enabledEngines.length() > 0) enabledEngines.append(' ');
694 enabledEngines.append(engine.name);
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700695 }
696 }
Charles Chenf47cce02010-03-17 17:33:23 -0700697 ContentResolver resolver = getContentResolver();
Bjorn Bringertc7762972011-03-11 16:52:51 +0000698 Settings.Secure.putString(resolver, TTS_ENABLED_PLUGINS, enabledEngines.toString());
Charles Chen5dbc74a2009-12-07 12:08:13 -0800699
Charles Chen0a0eb5f2010-03-16 20:09:17 -0700700 CharSequence entriesArray[] = new CharSequence[entries.size()];
701 CharSequence valuesArray[] = new CharSequence[values.size()];
702
Charles Chen8c8185b2010-04-08 16:51:35 -0700703 mDefaultSynthPref.setEntries(entries.toArray(entriesArray));
704 mDefaultSynthPref.setEntryValues(values.toArray(valuesArray));
Charles Chenbe6e8272010-03-22 16:06:05 -0700705
706 // Set the selected engine based on the saved preference
707 String selectedEngine = Settings.Secure.getString(getContentResolver(), TTS_DEFAULT_SYNTH);
Charles Chen8c8185b2010-04-08 16:51:35 -0700708 int selectedEngineIndex = mDefaultSynthPref.findIndexOfValue(selectedEngine);
Charles Chenbe6e8272010-03-22 16:06:05 -0700709 if (selectedEngineIndex == -1){
Bjorn Bringertc7762972011-03-11 16:52:51 +0000710 selectedEngineIndex =
711 mDefaultSynthPref.findIndexOfValue(TextToSpeech.Engine.DEFAULT_ENGINE);
Charles Chenbe6e8272010-03-22 16:06:05 -0700712 }
Bjorn Bringertc7762972011-03-11 16:52:51 +0000713 if (selectedEngineIndex >= 0) {
714 mDefaultSynthPref.setValueIndex(selectedEngineIndex);
715 }
716 }
717
718 private boolean isEngineEnabled(String engineName) {
719 if (engineName.equals(TextToSpeech.Engine.DEFAULT_ENGINE)) return true;
720 String enginePref = KEY_PLUGIN_ENABLED_PREFIX + engineName;
721 return getPreferenceManager().getSharedPreferences().getBoolean(enginePref, false);
Charles Chen5dbc74a2009-12-07 12:08:13 -0800722 }
Charles Chen5dbc74a2009-12-07 12:08:13 -0800723
Jean-Michel Trivied29a652009-06-05 18:37:29 -0700724}