• 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.preference.PreferenceActivity;
26 import android.speech.tts.TextToSpeech.EngineInfo;
27 import android.util.Log;
28 import android.view.View;
29 import android.view.ViewGroup;
30 import android.widget.Checkable;
31 import android.widget.CompoundButton;
32 import android.widget.RadioButton;
33 
34 
35 import com.android.settings.R;
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 PreferenceActivity mPreferenceActivity;
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, PreferenceActivity prefActivity)97     public TtsEnginePreference(Context context, EngineInfo info, RadioButtonGroupState state,
98             PreferenceActivity prefActivity) {
99         super(context);
100         setLayoutResource(R.layout.preference_tts_engine);
101 
102         mSharedState = state;
103         mPreferenceActivity = 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                 // PreferenceActivity.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                 mPreferenceActivity.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         builder.setIconAttribute(android.R.attr.alertDialogIcon);
203         builder.setMessage(getContext().getString(
204                 R.string.tts_engine_security_warning, mEngineInfo.label));
205         builder.setCancelable(true);
206         builder.setPositiveButton(android.R.string.ok, positiveOnClickListener);
207         builder.setNegativeButton(android.R.string.cancel, negativeOnClickListener);
208 
209         AlertDialog dialog = builder.create();
210         dialog.show();
211     }
212 
213 
onRadioButtonClicked(final CompoundButton buttonView, boolean isChecked)214     private void onRadioButtonClicked(final CompoundButton buttonView,
215             boolean isChecked) {
216         if (mPreventRadioButtonCallbacks ||
217                 (mSharedState.getCurrentChecked() == buttonView)) {
218             return;
219         }
220 
221         if (isChecked) {
222             // Should we alert user? if that's true, delay making engine current one.
223             if (shouldDisplayDataAlert()) {
224                 displayDataAlert(new DialogInterface.OnClickListener() {
225                     @Override
226                     public void onClick(DialogInterface dialog, int which) {
227                         makeCurrentEngine(buttonView);
228                     }
229                 },new DialogInterface.OnClickListener() {
230                     @Override
231                     public void onClick(DialogInterface dialog, int which) {
232                         // Undo the click.
233                         buttonView.setChecked(false);
234                     }
235                 });
236             } else {
237                 // Privileged engine, set it current
238                 makeCurrentEngine(buttonView);
239             }
240         } else {
241             mSettingsIcon.setEnabled(false);
242         }
243     }
244 
makeCurrentEngine(Checkable current)245     private void makeCurrentEngine(Checkable current) {
246         if (mSharedState.getCurrentChecked() != null) {
247             mSharedState.getCurrentChecked().setChecked(false);
248         }
249         mSharedState.setCurrentChecked(current);
250         mSharedState.setCurrentKey(getKey());
251         callChangeListener(mSharedState.getCurrentKey());
252         mSettingsIcon.setEnabled(true);
253     }
254 
255 
256     /**
257      * Holds all state that is common to this group of radio buttons, such
258      * as the currently selected key and the currently checked compound button.
259      * (which corresponds to this key).
260      */
261     public interface RadioButtonGroupState {
getCurrentKey()262         String getCurrentKey();
getCurrentChecked()263         Checkable getCurrentChecked();
264 
setCurrentKey(String key)265         void setCurrentKey(String key);
setCurrentChecked(Checkable current)266         void setCurrentChecked(Checkable current);
267     }
268 
269 }
270