• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.settings.tts;
2 
3 import static android.provider.Settings.Secure.TTS_DEFAULT_SYNTH;
4 
5 import android.app.settings.SettingsEnums;
6 import android.content.Context;
7 import android.os.Bundle;
8 import android.provider.SearchIndexableResource;
9 import android.speech.tts.TextToSpeech;
10 import android.speech.tts.TextToSpeech.EngineInfo;
11 import android.speech.tts.TtsEngines;
12 import android.util.Log;
13 import android.widget.Checkable;
14 
15 import androidx.preference.PreferenceCategory;
16 
17 import com.android.settings.R;
18 import com.android.settings.SettingsPreferenceFragment;
19 import com.android.settings.search.BaseSearchIndexProvider;
20 import com.android.settings.search.Indexable;
21 import com.android.settings.tts.TtsEnginePreference.RadioButtonGroupState;
22 import com.android.settingslib.search.SearchIndexable;
23 
24 import java.util.Arrays;
25 import java.util.List;
26 
27 @SearchIndexable
28 public class TtsEnginePreferenceFragment extends SettingsPreferenceFragment
29         implements RadioButtonGroupState {
30     private static final String TAG = "TtsEnginePrefFragment";
31 
32     private static final int VOICE_DATA_INTEGRITY_CHECK = 1977;
33 
34     /** The currently selected engine. */
35     private String mCurrentEngine;
36 
37     /**
38      * The engine checkbox that is currently checked. Saves us a bit of effort in deducing the right
39      * one from the currently selected engine.
40      */
41     private Checkable mCurrentChecked;
42 
43     /**
44      * The previously selected TTS engine. Useful for rollbacks if the users choice is not loaded or
45      * fails a voice integrity check.
46      */
47     private String mPreviousEngine;
48 
49     private PreferenceCategory mEnginePreferenceCategory;
50 
51     private TextToSpeech mTts = null;
52     private TtsEngines mEnginesHelper = null;
53 
54     @Override
onCreate(Bundle savedInstanceState)55     public void onCreate(Bundle savedInstanceState) {
56         super.onCreate(savedInstanceState);
57         addPreferencesFromResource(R.xml.tts_engine_picker);
58 
59         mEnginePreferenceCategory =
60                 (PreferenceCategory) findPreference("tts_engine_preference_category");
61         mEnginesHelper = new TtsEngines(getActivity().getApplicationContext());
62 
63         mTts = new TextToSpeech(getActivity().getApplicationContext(), null);
64 
65         initSettings();
66     }
67 
68     @Override
getMetricsCategory()69     public int getMetricsCategory() {
70         return SettingsEnums.TTS_ENGINE_SETTINGS;
71     }
72 
73     @Override
onDestroy()74     public void onDestroy() {
75         super.onDestroy();
76         if (mTts != null) {
77             mTts.shutdown();
78             mTts = null;
79         }
80     }
81 
initSettings()82     private void initSettings() {
83         if (mTts != null) {
84             mCurrentEngine = mTts.getCurrentEngine();
85         }
86 
87         mEnginePreferenceCategory.removeAll();
88 
89         List<EngineInfo> engines = mEnginesHelper.getEngines();
90         for (EngineInfo engine : engines) {
91             TtsEnginePreference enginePref =
92                     new TtsEnginePreference(getPrefContext(), engine, this);
93             mEnginePreferenceCategory.addPreference(enginePref);
94         }
95     }
96 
97     @Override
getCurrentChecked()98     public Checkable getCurrentChecked() {
99         return mCurrentChecked;
100     }
101 
102     @Override
getCurrentKey()103     public String getCurrentKey() {
104         return mCurrentEngine;
105     }
106 
107     @Override
setCurrentChecked(Checkable current)108     public void setCurrentChecked(Checkable current) {
109         mCurrentChecked = current;
110     }
111 
112     /**
113      * The initialization listener used when the user changes his choice of engine (as opposed to
114      * when then screen is being initialized for the first time).
115      */
116     private final TextToSpeech.OnInitListener mUpdateListener =
117             new TextToSpeech.OnInitListener() {
118                 @Override
119                 public void onInit(int status) {
120                     onUpdateEngine(status);
121                 }
122             };
123 
updateDefaultEngine(String engine)124     private void updateDefaultEngine(String engine) {
125         Log.d(TAG, "Updating default synth to : " + engine);
126 
127         // Keep track of the previous engine that was being used. So that
128         // we can reuse the previous engine.
129         //
130         // Note that if TextToSpeech#getCurrentEngine is not null, it means at
131         // the very least that we successfully bound to the engine service.
132         mPreviousEngine = mTts.getCurrentEngine();
133 
134         // Step 1: Shut down the existing TTS engine.
135         Log.i(TAG, "Shutting down current tts engine");
136         if (mTts != null) {
137             try {
138                 mTts.shutdown();
139                 mTts = null;
140             } catch (Exception e) {
141                 Log.e(TAG, "Error shutting down TTS engine" + e);
142             }
143         }
144 
145         // Step 2: Connect to the new TTS engine.
146         // Step 3 is continued on #onUpdateEngine (below) which is called when
147         // the app binds successfully to the engine.
148         Log.i(TAG, "Updating engine : Attempting to connect to engine: " + engine);
149         mTts = new TextToSpeech(getActivity().getApplicationContext(), mUpdateListener, engine);
150         Log.i(TAG, "Success");
151     }
152 
153     /**
154      * Step 3: We have now bound to the TTS engine the user requested. We will attempt to check
155      * voice data for the engine if we successfully bound to it, or revert to the previous engine if
156      * we didn't.
157      */
onUpdateEngine(int status)158     public void onUpdateEngine(int status) {
159         if (status == TextToSpeech.SUCCESS) {
160             Log.d(
161 
162                     TAG,
163                     "Updating engine: Successfully bound to the engine: "
164                             + mTts.getCurrentEngine());
165             android.provider.Settings.Secure.putString(
166                     getContentResolver(), TTS_DEFAULT_SYNTH, mTts.getCurrentEngine());
167         } else {
168             Log.d(TAG, "Updating engine: Failed to bind to engine, reverting.");
169             if (mPreviousEngine != null) {
170                 // This is guaranteed to at least bind, since mPreviousEngine would be
171                 // null if the previous bind to this engine failed.
172                 mTts =
173                         new TextToSpeech(
174                                 getActivity().getApplicationContext(), null, mPreviousEngine);
175             }
176             mPreviousEngine = null;
177         }
178     }
179 
180     @Override
setCurrentKey(String key)181     public void setCurrentKey(String key) {
182         mCurrentEngine = key;
183         updateDefaultEngine(mCurrentEngine);
184     }
185 
186     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
187             new BaseSearchIndexProvider() {
188                 @Override
189                 public List<SearchIndexableResource> getXmlResourcesToIndex(
190                         Context context, boolean enabled) {
191                     final SearchIndexableResource sir = new SearchIndexableResource(context);
192                     sir.xmlResId = R.xml.tts_engine_picker;
193                     return Arrays.asList(sir);
194                 }
195             };
196 }
197