• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 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.settings.tts;
18 
19 import android.app.AlertDialog;
20 import android.content.Context;
21 import android.content.DialogInterface;
22 import android.content.Intent;
23 import android.os.Bundle;
24 import android.preference.Preference;
25 import android.speech.tts.TextToSpeech.EngineInfo;
26 import android.util.Log;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.Checkable;
30 import android.widget.CompoundButton;
31 import android.widget.RadioButton;
32 
33 
34 import com.android.settings.R;
35 import com.android.settings.SettingsActivity;
36 import com.android.settings.Utils;
37 
38 
39 public class TtsEnginePreference extends Preference {
40 
41     private static final String TAG = "TtsEnginePreference";
42 
43     /**
44      * Key for the name of the TTS engine passed in to the engine
45      * settings fragment {@link TtsEngineSettingsFragment}.
46      */
47     static final String FRAGMENT_ARGS_NAME = "name";
48 
49     /**
50      * Key for the label of the TTS engine passed in to the engine
51      * settings fragment. This is used as the title of the fragment
52      * {@link TtsEngineSettingsFragment}.
53      */
54     static final String FRAGMENT_ARGS_LABEL = "label";
55 
56     /**
57      * Key for the voice data data passed in to the engine settings
58      * fragmetn {@link TtsEngineSettingsFragment}.
59      */
60     static final String FRAGMENT_ARGS_VOICES = "voices";
61 
62     /**
63      * The preference activity that owns this preference. Required
64      * for instantiating the engine specific settings screen.
65      */
66     private final SettingsActivity mSettingsActivity;
67 
68     /**
69      * The engine information for the engine this preference represents.
70      * Contains it's name, label etc. which are used for display.
71      */
72     private final EngineInfo mEngineInfo;
73 
74     /**
75      * The shared radio button state, which button is checked etc.
76      */
77     private final RadioButtonGroupState mSharedState;
78 
79     /**
80      * When true, the change callbacks on the radio button will not
81      * fire.
82      */
83     private volatile boolean mPreventRadioButtonCallbacks;
84 
85     private View mSettingsIcon;
86     private RadioButton mRadioButton;
87     private Intent mVoiceCheckData;
88 
89     private final CompoundButton.OnCheckedChangeListener mRadioChangeListener =
90         new CompoundButton.OnCheckedChangeListener() {
91             @Override
92             public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
93                 onRadioButtonClicked(buttonView, isChecked);
94             }
95         };
96 
TtsEnginePreference(Context context, EngineInfo info, RadioButtonGroupState state, SettingsActivity prefActivity)97     public TtsEnginePreference(Context context, EngineInfo info, RadioButtonGroupState state,
98             SettingsActivity prefActivity) {
99         super(context);
100         setLayoutResource(R.layout.preference_tts_engine);
101 
102         mSharedState = state;
103         mSettingsActivity = prefActivity;
104         mEngineInfo = info;
105         mPreventRadioButtonCallbacks = false;
106 
107         setKey(mEngineInfo.name);
108         setTitle(mEngineInfo.label);
109     }
110 
111     @Override
getView(View convertView, ViewGroup parent)112     public View getView(View convertView, ViewGroup parent) {
113         if (mSharedState == null) {
114             throw new IllegalStateException("Call to getView() before a call to" +
115                     "setSharedState()");
116         }
117 
118         View view = super.getView(convertView, parent);
119         final RadioButton rb = (RadioButton) view.findViewById(R.id.tts_engine_radiobutton);
120         rb.setOnCheckedChangeListener(mRadioChangeListener);
121 
122         boolean isChecked = getKey().equals(mSharedState.getCurrentKey());
123         if (isChecked) {
124             mSharedState.setCurrentChecked(rb);
125         }
126 
127         mPreventRadioButtonCallbacks = true;
128         rb.setChecked(isChecked);
129         mPreventRadioButtonCallbacks = false;
130 
131         mRadioButton = rb;
132 
133         View textLayout = view.findViewById(R.id.tts_engine_pref_text);
134         textLayout.setOnClickListener(new View.OnClickListener() {
135             @Override
136             public void onClick(View v) {
137                 onRadioButtonClicked(rb, !rb.isChecked());
138             }
139         });
140 
141         mSettingsIcon = view.findViewById(R.id.tts_engine_settings);
142         // Will be enabled only the engine has passed the voice check, and
143         // is currently enabled.
144         mSettingsIcon.setEnabled(isChecked && mVoiceCheckData != null);
145         if (!isChecked) {
146             mSettingsIcon.setAlpha(Utils.DISABLED_ALPHA);
147         }
148         mSettingsIcon.setOnClickListener(new View.OnClickListener() {
149             @Override
150             public void onClick(View v) {
151                 Bundle args = new Bundle();
152                 args.putString(FRAGMENT_ARGS_NAME, mEngineInfo.name);
153                 args.putString(FRAGMENT_ARGS_LABEL, mEngineInfo.label);
154                 if (mVoiceCheckData != null) {
155                     args.putParcelable(FRAGMENT_ARGS_VOICES, mVoiceCheckData);
156                 }
157 
158                 // Note that we use this instead of the (easier to use)
159                 // SettingsActivity.startPreferenceFragment because the
160                 // title will not be updated correctly in the fragment
161                 // breadcrumb since it isn't inflated from the XML layout.
162                 mSettingsActivity.startPreferencePanel(
163                         TtsEngineSettingsFragment.class.getName(),
164                         args, 0, mEngineInfo.label, null, 0);
165             }
166         });
167 
168         if (mVoiceCheckData != null) {
169             mSettingsIcon.setEnabled(mRadioButton.isChecked());
170         }
171 
172         return view;
173     }
174 
setVoiceDataDetails(Intent data)175     public void setVoiceDataDetails(Intent data) {
176         mVoiceCheckData = data;
177         // This might end up running before getView aboive, in which
178         // case mSettingsIcon && mRadioButton will be null. In this case
179         // getView will set the right values.
180         if (mSettingsIcon != null && mRadioButton != null) {
181             if (mRadioButton.isChecked()) {
182                 mSettingsIcon.setEnabled(true);
183             } else {
184                 mSettingsIcon.setEnabled(false);
185                 mSettingsIcon.setAlpha(Utils.DISABLED_ALPHA);
186             }
187         }
188     }
189 
shouldDisplayDataAlert()190     private boolean shouldDisplayDataAlert() {
191         return !mEngineInfo.system;
192     }
193 
194 
displayDataAlert( DialogInterface.OnClickListener positiveOnClickListener, DialogInterface.OnClickListener negativeOnClickListener)195     private void displayDataAlert(
196             DialogInterface.OnClickListener positiveOnClickListener,
197             DialogInterface.OnClickListener negativeOnClickListener) {
198         Log.i(TAG, "Displaying data alert for :" + mEngineInfo.name);
199 
200         AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
201         builder.setTitle(android.R.string.dialog_alert_title)
202                 .setMessage(getContext().getString(
203                         R.string.tts_engine_security_warning, mEngineInfo.label))
204                 .setCancelable(true)
205                 .setPositiveButton(android.R.string.ok, positiveOnClickListener)
206                 .setNegativeButton(android.R.string.cancel, negativeOnClickListener);
207 
208         AlertDialog dialog = builder.create();
209         dialog.show();
210     }
211 
212 
onRadioButtonClicked(final CompoundButton buttonView, boolean isChecked)213     private void onRadioButtonClicked(final CompoundButton buttonView,
214             boolean isChecked) {
215         if (mPreventRadioButtonCallbacks ||
216                 (mSharedState.getCurrentChecked() == buttonView)) {
217             return;
218         }
219 
220         if (isChecked) {
221             // Should we alert user? if that's true, delay making engine current one.
222             if (shouldDisplayDataAlert()) {
223                 displayDataAlert(new DialogInterface.OnClickListener() {
224                     @Override
225                     public void onClick(DialogInterface dialog, int which) {
226                         makeCurrentEngine(buttonView);
227                     }
228                 },new DialogInterface.OnClickListener() {
229                     @Override
230                     public void onClick(DialogInterface dialog, int which) {
231                         // Undo the click.
232                         buttonView.setChecked(false);
233                     }
234                 });
235             } else {
236                 // Privileged engine, set it current
237                 makeCurrentEngine(buttonView);
238             }
239         } else {
240             mSettingsIcon.setEnabled(false);
241         }
242     }
243 
makeCurrentEngine(Checkable current)244     private void makeCurrentEngine(Checkable current) {
245         if (mSharedState.getCurrentChecked() != null) {
246             mSharedState.getCurrentChecked().setChecked(false);
247         }
248         mSharedState.setCurrentChecked(current);
249         mSharedState.setCurrentKey(getKey());
250         callChangeListener(mSharedState.getCurrentKey());
251         mSettingsIcon.setEnabled(true);
252     }
253 
254 
255     /**
256      * Holds all state that is common to this group of radio buttons, such
257      * as the currently selected key and the currently checked compound button.
258      * (which corresponds to this key).
259      */
260     public interface RadioButtonGroupState {
getCurrentKey()261         String getCurrentKey();
getCurrentChecked()262         Checkable getCurrentChecked();
263 
setCurrentKey(String key)264         void setCurrentKey(String key);
setCurrentChecked(Checkable current)265         void setCurrentChecked(Checkable current);
266     }
267 
268 }
269