• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 
17 package com.android.tv.settings.system;
18 
19 import static com.android.tv.settings.util.InstrumentationUtils.logEntrySelected;
20 
21 import android.app.AlertDialog;
22 import android.app.tvsettings.TvSettingsEnums;
23 import android.content.ActivityNotFoundException;
24 import android.content.ContentResolver;
25 import android.content.Intent;
26 import android.os.Bundle;
27 import android.provider.Settings;
28 import android.speech.tts.TextToSpeech;
29 import android.speech.tts.TtsEngines;
30 import android.speech.tts.UtteranceProgressListener;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.widget.Checkable;
34 
35 import androidx.annotation.Keep;
36 import androidx.preference.ListPreference;
37 import androidx.preference.Preference;
38 import androidx.preference.PreferenceCategory;
39 
40 import com.android.internal.logging.nano.MetricsProto;
41 import com.android.tv.settings.R;
42 import com.android.tv.settings.SettingsPreferenceFragment;
43 
44 import java.util.ArrayList;
45 import java.util.HashMap;
46 import java.util.List;
47 import java.util.Locale;
48 import java.util.MissingResourceException;
49 import java.util.Objects;
50 import java.util.Set;
51 
52 /**
53  * Fragment for TextToSpeech settings
54  */
55 @Keep
56 public class TextToSpeechFragment extends SettingsPreferenceFragment implements
57         Preference.OnPreferenceChangeListener, Preference.OnPreferenceClickListener,
58         TtsEnginePreference.RadioButtonGroupState {
59     private static final String TAG = "TextToSpeechSettings";
60     private static final boolean DBG = false;
61 
62     /** Preference key for the engine settings preference */
63     private static final String KEY_ENGINE_SETTINGS = "tts_engine_settings";
64 
65     /** Preference key for the "play TTS example" preference. */
66     private static final String KEY_PLAY_EXAMPLE = "tts_play_example";
67 
68     /** Preference key for the TTS rate selection dialog. */
69     private static final String KEY_DEFAULT_RATE = "tts_default_rate";
70 
71     /** Preference key for the TTS status field. */
72     private static final String KEY_STATUS = "tts_status";
73 
74     /**
75      * Preference key for the engine selection preference.
76      */
77     private static final String KEY_ENGINE_PREFERENCE_SECTION =
78             "tts_engine_preference_section";
79 
80     /**
81      * These look like birth years, but they aren't mine. I'm much younger than this.
82      */
83     private static final int GET_SAMPLE_TEXT = 1983;
84     private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
85 
86     private PreferenceCategory mEnginePreferenceCategory;
87     private Preference mEngineSettingsPref;
88     private ListPreference mDefaultRatePref;
89     private Preference mPlayExample;
90     private Preference mEngineStatus;
91 
92     private int mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
93 
94     /**
95      * The currently selected engine.
96      */
97     private String mCurrentEngine;
98 
99     /**
100      * The engine checkbox that is currently checked. Saves us a bit of effort
101      * in deducing the right one from the currently selected engine.
102      */
103     private Checkable mCurrentChecked;
104 
105     /**
106      * The previously selected TTS engine. Useful for rollbacks if the users
107      * choice is not loaded or fails a voice integrity check.
108      */
109     private String mPreviousEngine;
110 
111     private TextToSpeech mTts = null;
112     private TtsEngines mEnginesHelper = null;
113 
114     private String mSampleText = null;
115 
116     /**
117      * Default locale used by selected TTS engine, null if not connected to any engine.
118      */
119     private Locale mCurrentDefaultLocale;
120 
121     /**
122      * List of available locals of selected TTS engine, as returned by
123      * {@link TextToSpeech.Engine#ACTION_CHECK_TTS_DATA} activity. If empty, then activity
124      * was not yet called.
125      */
126     private List<String> mAvailableStrLocals;
127 
128     /**
129      * The initialization listener used when we are initalizing the settings
130      * screen for the first time (as opposed to when a user changes his choice
131      * of engine).
132      */
133     private final TextToSpeech.OnInitListener mInitListener = new TextToSpeech.OnInitListener() {
134         @Override
135         public void onInit(int status) {
136             onInitEngine(status);
137         }
138     };
139 
140     /**
141      * The initialization listener used when the user changes his choice of
142      * engine (as opposed to when then screen is being initialized for the first
143      * time).
144      */
145     private final TextToSpeech.OnInitListener mUpdateListener = new TextToSpeech.OnInitListener() {
146         @Override
147         public void onInit(int status) {
148             onUpdateEngine(status);
149         }
150     };
151 
152     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)153     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
154         addPreferencesFromResource(R.xml.tts_settings);
155 
156         mEngineSettingsPref = findPreference(KEY_ENGINE_SETTINGS);
157 
158         mPlayExample = findPreference(KEY_PLAY_EXAMPLE);
159         mPlayExample.setOnPreferenceClickListener(this);
160         mPlayExample.setEnabled(false);
161 
162         mEnginePreferenceCategory = (PreferenceCategory) findPreference(
163                 KEY_ENGINE_PREFERENCE_SECTION);
164         mDefaultRatePref = (ListPreference) findPreference(KEY_DEFAULT_RATE);
165 
166         mEngineStatus = findPreference(KEY_STATUS);
167         updateEngineStatus(R.string.tts_status_checking);
168     }
169 
170     @Override
onCreate(Bundle savedInstanceState)171     public void onCreate(Bundle savedInstanceState) {
172         super.onCreate(savedInstanceState);
173 
174         getActivity().setVolumeControlStream(TextToSpeech.Engine.DEFAULT_STREAM);
175 
176         mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener);
177         mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
178 
179         setTtsUtteranceProgressListener();
180         initSettings();
181     }
182 
183     @Override
onResume()184     public void onResume() {
185         super.onResume();
186 
187         if (mTts == null || mCurrentDefaultLocale == null) {
188             return;
189         }
190         Locale ttsDefaultLocale = mTts.getDefaultLanguage();
191         if (!mCurrentDefaultLocale.equals(ttsDefaultLocale)) {
192             updateWidgetState(false);
193             checkDefaultLocale();
194         }
195     }
196 
setTtsUtteranceProgressListener()197     private void setTtsUtteranceProgressListener() {
198         if (mTts == null) {
199             return;
200         }
201         mTts.setOnUtteranceProgressListener(new UtteranceProgressListener() {
202             @Override
203             public void onStart(String utteranceId) {}
204 
205             @Override
206             public void onDone(String utteranceId) {}
207 
208             @Override
209             public void onError(String utteranceId) {
210                 Log.e(TAG, "Error while trying to synthesize sample text");
211             }
212         });
213     }
214 
215     @Override
onDestroy()216     public void onDestroy() {
217         super.onDestroy();
218         if (mTts != null) {
219             mTts.shutdown();
220             mTts = null;
221         }
222     }
223 
initSettings()224     private void initSettings() {
225         final ContentResolver resolver = getActivity().getContentResolver();
226 
227         // Set up the default rate.
228         try {
229             mDefaultRate = android.provider.Settings.Secure.getInt(resolver,
230                     Settings.Secure.TTS_DEFAULT_RATE);
231         } catch (Settings.SettingNotFoundException e) {
232             // Default rate setting not found, initialize it
233             mDefaultRate = TextToSpeech.Engine.DEFAULT_RATE;
234         }
235         mDefaultRatePref.setValue(String.valueOf(mDefaultRate));
236         mDefaultRatePref.setOnPreferenceChangeListener(this);
237 
238         mCurrentEngine = mTts.getCurrentEngine();
239 
240         mEnginePreferenceCategory.removeAll();
241 
242         List<TextToSpeech.EngineInfo> engines = mEnginesHelper.getEngines();
243         for (TextToSpeech.EngineInfo engine : engines) {
244             TtsEnginePreference enginePref =
245                     new TtsEnginePreference(getPreferenceManager().getContext(), engine,
246                     this);
247             mEnginePreferenceCategory.addPreference(enginePref);
248         }
249 
250         checkVoiceData(mCurrentEngine);
251     }
252 
253     /**
254      * Called when the TTS engine is initialized.
255      */
onInitEngine(int status)256     public void onInitEngine(int status) {
257         if (status == TextToSpeech.SUCCESS) {
258             if (DBG) Log.d(TAG, "TTS engine for settings screen initialized.");
259             checkDefaultLocale();
260         } else {
261             if (DBG) Log.d(TAG, "TTS engine for settings screen failed to initialize successfully.");
262             updateWidgetState(false);
263         }
264     }
265 
checkDefaultLocale()266     private void checkDefaultLocale() {
267         Locale defaultLocale = mTts.getDefaultLanguage();
268         if (defaultLocale == null) {
269             Log.e(TAG, "Failed to get default language from engine " + mCurrentEngine);
270             updateWidgetState(false);
271             updateEngineStatus(R.string.tts_status_not_supported);
272             return;
273         }
274 
275         // ISO-3166 alpha 3 country codes are out of spec. If we won't normalize,
276         // we may end up with English (USA)and German (DEU).
277         final Locale oldDefaultLocale = mCurrentDefaultLocale;
278         mCurrentDefaultLocale = mEnginesHelper.parseLocaleString(defaultLocale.toString());
279         if (!Objects.equals(oldDefaultLocale, mCurrentDefaultLocale)) {
280             mSampleText = null;
281         }
282 
283         mTts.setLanguage(defaultLocale);
284         if (evaluateDefaultLocale() && mSampleText == null) {
285             getSampleText();
286         }
287     }
288 
evaluateDefaultLocale()289     private boolean evaluateDefaultLocale() {
290         // Check if we are connected to the engine, and CHECK_VOICE_DATA returned list
291         // of available languages.
292         if (mCurrentDefaultLocale == null || mAvailableStrLocals == null) {
293             return false;
294         }
295 
296         boolean notInAvailableLangauges = true;
297         try {
298             // Check if language is listed in CheckVoices Action result as available voice.
299             String defaultLocaleStr = mCurrentDefaultLocale.getISO3Language();
300             if (!TextUtils.isEmpty(mCurrentDefaultLocale.getISO3Country())) {
301                 defaultLocaleStr += "-" + mCurrentDefaultLocale.getISO3Country();
302             }
303             if (!TextUtils.isEmpty(mCurrentDefaultLocale.getVariant())) {
304                 defaultLocaleStr += "-" + mCurrentDefaultLocale.getVariant();
305             }
306 
307             for (String loc : mAvailableStrLocals) {
308                 if (loc.equalsIgnoreCase(defaultLocaleStr)) {
309                     notInAvailableLangauges = false;
310                     break;
311                 }
312             }
313         } catch (MissingResourceException e) {
314             if (DBG) Log.wtf(TAG, "MissingResourceException", e);
315             updateEngineStatus(R.string.tts_status_not_supported);
316             updateWidgetState(false);
317             return false;
318         }
319 
320         int defaultAvailable = mTts.setLanguage(mCurrentDefaultLocale);
321         if (defaultAvailable == TextToSpeech.LANG_NOT_SUPPORTED ||
322                 defaultAvailable == TextToSpeech.LANG_MISSING_DATA ||
323                 notInAvailableLangauges) {
324             if (DBG) Log.d(TAG, "Default locale for this TTS engine is not supported.");
325             updateEngineStatus(R.string.tts_status_not_supported);
326             updateWidgetState(false);
327             return false;
328         } else {
329             if (isNetworkRequiredForSynthesis()) {
330                 updateEngineStatus(R.string.tts_status_requires_network);
331             } else {
332                 updateEngineStatus(R.string.tts_status_ok);
333             }
334             updateWidgetState(true);
335             return true;
336         }
337     }
338 
339     /**
340      * Ask the current default engine to return a string of sample text to be
341      * spoken to the user.
342      */
getSampleText()343     private void getSampleText() {
344         String currentEngine = mTts.getCurrentEngine();
345 
346         if (TextUtils.isEmpty(currentEngine)) currentEngine = mTts.getDefaultEngine();
347 
348         // TODO: This is currently a hidden private API. The intent extras
349         // and the intent action should be made public if we intend to make this
350         // a public API. We fall back to using a canned set of strings if this
351         // doesn't work.
352         Intent intent = new Intent(TextToSpeech.Engine.ACTION_GET_SAMPLE_TEXT);
353 
354         intent.putExtra("language", mCurrentDefaultLocale.getLanguage());
355         intent.putExtra("country", mCurrentDefaultLocale.getCountry());
356         intent.putExtra("variant", mCurrentDefaultLocale.getVariant());
357         intent.setPackage(currentEngine);
358 
359         try {
360             if (DBG) Log.d(TAG, "Getting sample text: " + intent.toUri(0));
361             startActivityForResult(intent, GET_SAMPLE_TEXT);
362         } catch (ActivityNotFoundException ex) {
363             Log.e(TAG, "Failed to get sample text, no activity found for " + intent + ")");
364         }
365     }
366 
367     /**
368      * Called when voice data integrity check returns
369      */
370     @Override
onActivityResult(int requestCode, int resultCode, Intent data)371     public void onActivityResult(int requestCode, int resultCode, Intent data) {
372         if (requestCode == GET_SAMPLE_TEXT) {
373             onSampleTextReceived(resultCode, data);
374         } else if (requestCode == VOICE_DATA_INTEGRITY_CHECK) {
375             onVoiceDataIntegrityCheckDone(data);
376         }
377     }
378 
getDefaultSampleString()379     private String getDefaultSampleString() {
380         if (mTts != null && mTts.getLanguage() != null) {
381             try {
382                 final String currentLang = mTts.getLanguage().getISO3Language();
383                 String[] strings = getActivity().getResources().getStringArray(
384                         R.array.tts_demo_strings);
385                 String[] langs = getActivity().getResources().getStringArray(
386                         R.array.tts_demo_string_langs);
387 
388                 for (int i = 0; i < strings.length; ++i) {
389                     if (langs[i].equals(currentLang)) {
390                         return strings[i];
391                     }
392                 }
393             } catch (MissingResourceException e) {
394                 if (DBG) Log.wtf(TAG, "MissingResourceException", e);
395                 // Ignore and fall back to default sample string
396             }
397         }
398         return getString(R.string.tts_default_sample_string);
399     }
400 
isNetworkRequiredForSynthesis()401     private boolean isNetworkRequiredForSynthesis() {
402         Set<String> features = mTts.getFeatures(mCurrentDefaultLocale);
403         return features != null &&
404                 features.contains(TextToSpeech.Engine.KEY_FEATURE_NETWORK_SYNTHESIS) &&
405                 !features.contains(TextToSpeech.Engine.KEY_FEATURE_EMBEDDED_SYNTHESIS);
406     }
407 
onSampleTextReceived(int resultCode, Intent data)408     private void onSampleTextReceived(int resultCode, Intent data) {
409         String sample = getDefaultSampleString();
410 
411         if (resultCode == TextToSpeech.LANG_AVAILABLE && data != null) {
412             if (data.getStringExtra("sampleText") != null) {
413                 sample = data.getStringExtra("sampleText");
414             }
415             if (DBG) Log.d(TAG, "Got sample text: " + sample);
416         } else {
417             if (DBG) Log.d(TAG, "Using default sample text :" + sample);
418         }
419 
420         mSampleText = sample;
421         if (mSampleText != null) {
422             updateWidgetState(true);
423         } else {
424             Log.e(TAG, "Did not have a sample string for the requested language. Using default");
425         }
426     }
427 
speakSampleText()428     private void speakSampleText() {
429         final boolean networkRequired = isNetworkRequiredForSynthesis();
430         if (!networkRequired ||
431                 mTts.isLanguageAvailable(mCurrentDefaultLocale) >= TextToSpeech.LANG_AVAILABLE) {
432             HashMap<String, String> params = new HashMap<>();
433             params.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "Sample");
434 
435             mTts.speak(mSampleText, TextToSpeech.QUEUE_FLUSH, params);
436         } else {
437             Log.w(TAG, "Network required for sample synthesis for requested language");
438             displayNetworkAlert();
439         }
440     }
441 
442     @Override
onPreferenceChange(Preference preference, Object objValue)443     public boolean onPreferenceChange(Preference preference, Object objValue) {
444         if (KEY_DEFAULT_RATE.equals(preference.getKey())) {
445             logEntrySelected(TvSettingsEnums.SYSTEM_A11Y_TTS_SPEECH_RATE);
446             // Default rate
447             mDefaultRate = Integer.parseInt((String) objValue);
448             try {
449                 android.provider.Settings.Secure.putInt(getActivity().getContentResolver(),
450                         Settings.Secure.TTS_DEFAULT_RATE, mDefaultRate);
451                 if (mTts != null) {
452                     mTts.setSpeechRate(mDefaultRate / 100.0f);
453                 }
454                 if (DBG) Log.d(TAG, "TTS default rate changed, now " + mDefaultRate);
455             } catch (NumberFormatException e) {
456                 Log.e(TAG, "could not persist default TTS rate setting", e);
457             }
458         }
459 
460         return true;
461     }
462 
463     /**
464      * Called when mPlayExample is clicked
465      */
466     @Override
onPreferenceClick(Preference preference)467     public boolean onPreferenceClick(Preference preference) {
468         if (preference == mPlayExample) {
469             logEntrySelected(TvSettingsEnums.SYSTEM_A11Y_TTS_LISTEN_EXAMPLE);
470             // Get the sample text from the TTS engine; onActivityResult will do
471             // the actual speaking
472             speakSampleText();
473             return true;
474         }
475 
476         return false;
477     }
478 
updateWidgetState(boolean enable)479     private void updateWidgetState(boolean enable) {
480         mPlayExample.setEnabled(enable);
481         mDefaultRatePref.setEnabled(enable);
482         mEngineStatus.setEnabled(enable);
483     }
484 
updateEngineStatus(int resourceId)485     private void updateEngineStatus(int resourceId) {
486         Locale locale = mCurrentDefaultLocale;
487         if (locale == null) {
488             locale = Locale.getDefault();
489         }
490         mEngineStatus.setSummary(getString(resourceId, locale.getDisplayName()));
491     }
492 
displayNetworkAlert()493     private void displayNetworkAlert() {
494         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
495         builder.setTitle(android.R.string.dialog_alert_title)
496                 .setMessage(getActivity().getString(R.string.tts_engine_network_required))
497                 .setCancelable(false)
498                 .setPositiveButton(android.R.string.ok, null);
499 
500         AlertDialog dialog = builder.create();
501         dialog.show();
502     }
503 
updateDefaultEngine(String engine)504     private void updateDefaultEngine(String engine) {
505         if (DBG) Log.d(TAG, "Updating default synth to : " + engine);
506 
507         // Disable the "play sample text" preference and the speech
508         // rate preference while the engine is being swapped.
509         updateWidgetState(false);
510         updateEngineStatus(R.string.tts_status_checking);
511 
512         // Keep track of the previous engine that was being used. So that
513         // we can reuse the previous engine.
514         //
515         // Note that if TextToSpeech#getCurrentEngine is not null, it means at
516         // the very least that we successfully bound to the engine service.
517         mPreviousEngine = mTts.getCurrentEngine();
518 
519         // Step 1: Shut down the existing TTS engine.
520         try {
521             mTts.shutdown();
522             mTts = null;
523         } catch (Exception e) {
524             Log.e(TAG, "Error shutting down TTS engine" + e);
525         }
526 
527         // Step 2: Connect to the new TTS engine.
528         // Step 3 is continued on #onUpdateEngine (below) which is called when
529         // the app binds successfully to the engine.
530         if (DBG) Log.d(TAG, "Updating engine : Attempting to connect to engine: " + engine);
531         mTts = new TextToSpeech(getActivity().getApplicationContext(), mUpdateListener, engine);
532         setTtsUtteranceProgressListener();
533     }
534 
535     /*
536      * Step 3: We have now bound to the TTS engine the user requested. We will
537      * attempt to check voice data for the engine if we successfully bound to it,
538      * or revert to the previous engine if we didn't.
539      */
onUpdateEngine(int status)540     public void onUpdateEngine(int status) {
541         if (status == TextToSpeech.SUCCESS) {
542             if (DBG) {
543                 Log.d(TAG, "Updating engine: Successfully bound to the engine: " +
544                         mTts.getCurrentEngine());
545             }
546             checkVoiceData(mTts.getCurrentEngine());
547         } else {
548             if (DBG) Log.d(TAG, "Updating engine: Failed to bind to engine, reverting.");
549             if (mPreviousEngine != null) {
550                 // This is guaranteed to at least bind, since mPreviousEngine would be
551                 // null if the previous bind to this engine failed.
552                 mTts = new TextToSpeech(getActivity().getApplicationContext(), mInitListener,
553                         mPreviousEngine);
554                 setTtsUtteranceProgressListener();
555             }
556             mPreviousEngine = null;
557         }
558     }
559 
560     /*
561      * Step 4: Check whether the voice data for the engine is ok.
562      */
checkVoiceData(String engine)563     private void checkVoiceData(String engine) {
564         Intent intent = new Intent(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
565         intent.setPackage(engine);
566         try {
567             if (DBG) Log.d(TAG, "Updating engine: Checking voice data: " + intent.toUri(0));
568             startActivityForResult(intent, VOICE_DATA_INTEGRITY_CHECK);
569         } catch (ActivityNotFoundException ex) {
570             Log.e(TAG, "Failed to check TTS data, no activity found for " + intent + ")");
571         }
572     }
573 
574     /*
575      * Step 5: The voice data check is complete.
576      */
onVoiceDataIntegrityCheckDone(Intent data)577     private void onVoiceDataIntegrityCheckDone(Intent data) {
578         final String engine = mTts.getCurrentEngine();
579 
580         if (engine == null) {
581             Log.e(TAG, "Voice data check complete, but no engine bound");
582             return;
583         }
584 
585         if (data == null){
586             Log.e(TAG, "Engine failed voice data integrity check (null return)" +
587                     mTts.getCurrentEngine());
588             return;
589         }
590 
591         android.provider.Settings.Secure.putString(getActivity().getContentResolver(),
592                 Settings.Secure.TTS_DEFAULT_SYNTH, engine);
593 
594         mAvailableStrLocals = data.getStringArrayListExtra(
595                 TextToSpeech.Engine.EXTRA_AVAILABLE_VOICES);
596         if (mAvailableStrLocals == null) {
597             Log.e(TAG, "Voice data check complete, but no available voices found");
598             // Set mAvailableStrLocals to empty list
599             mAvailableStrLocals = new ArrayList<>();
600         }
601         if (evaluateDefaultLocale()) {
602             getSampleText();
603         }
604 
605         final TextToSpeech.EngineInfo engineInfo = mEnginesHelper.getEngineInfo(engine);
606         TtsEngineSettingsFragment.prepareArgs(mEngineSettingsPref.getExtras(),
607                 engineInfo.name, engineInfo.label, data);
608     }
609 
610     @Override
getCurrentChecked()611     public Checkable getCurrentChecked() {
612         return mCurrentChecked;
613     }
614 
615     @Override
getCurrentKey()616     public String getCurrentKey() {
617         return mCurrentEngine;
618     }
619 
620     @Override
setCurrentChecked(Checkable current)621     public void setCurrentChecked(Checkable current) {
622         mCurrentChecked = current;
623     }
624 
625     @Override
setCurrentKey(String key)626     public void setCurrentKey(String key) {
627         mCurrentEngine = key;
628         updateDefaultEngine(mCurrentEngine);
629     }
630 
631     @Override
getMetricsCategory()632     public int getMetricsCategory() {
633         return MetricsProto.MetricsEvent.TTS_TEXT_TO_SPEECH;
634     }
635 
636     @Override
getPageId()637     protected int getPageId() {
638         return TvSettingsEnums.SYSTEM_A11Y_TTS;
639     }
640 }
641