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