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.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 
     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
    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 
    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                 .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 
    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 
    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 {
    261         String getCurrentKey();
    262         Checkable getCurrentChecked();
    263 
    264         void setCurrentKey(String key);
    265         void setCurrentChecked(Checkable current);
    266     }
    267 
    268 }
    269