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