Home | History | Annotate | Download | only in tts
      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 
     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
    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 
    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 
    190     private boolean shouldDisplayDataAlert() {
    191         return !mEngineInfo.system;
    192     }
    193 
    194 
    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 
    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 
    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 {
    262         String getCurrentKey();
    263         Checkable getCurrentChecked();
    264 
    265         void setCurrentKey(String key);
    266         void setCurrentChecked(Checkable current);
    267     }
    268 
    269 }
    270