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