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.content.Context; 20 import android.content.DialogInterface; 21 import android.speech.tts.TextToSpeech.EngineInfo; 22 import android.util.Log; 23 import android.widget.Checkable; 24 import android.widget.CompoundButton; 25 import android.widget.RadioButton; 26 27 import androidx.appcompat.app.AlertDialog; 28 import androidx.preference.Preference; 29 import androidx.preference.PreferenceViewHolder; 30 31 import com.android.settings.R; 32 33 import androidx.annotation.VisibleForTesting; 34 35 36 public class TtsEnginePreference extends Preference { 37 38 private static final String TAG = "TtsEnginePreference"; 39 40 /** 41 * The engine information for the engine this preference represents. 42 * Contains it's name, label etc. which are used for display. 43 */ 44 private final EngineInfo mEngineInfo; 45 46 /** 47 * The shared radio button state, which button is checked etc. 48 */ 49 private final RadioButtonGroupState mSharedState; 50 private RadioButton mRadioButton; 51 52 /** 53 * When true, the change callbacks on the radio button will not 54 * fire. 55 */ 56 private volatile boolean mPreventRadioButtonCallbacks; 57 58 private final CompoundButton.OnCheckedChangeListener mRadioChangeListener = 59 new CompoundButton.OnCheckedChangeListener() { 60 @Override 61 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 62 onRadioButtonClicked(buttonView, isChecked); 63 } 64 }; 65 TtsEnginePreference(Context context, EngineInfo info, RadioButtonGroupState state)66 public TtsEnginePreference(Context context, EngineInfo info, RadioButtonGroupState state) { 67 super(context); 68 69 setWidgetLayoutResource(R.layout.preference_widget_radiobutton); 70 setLayoutResource(R.layout.preference_radio); 71 setIconSpaceReserved(false); 72 73 mSharedState = state; 74 mEngineInfo = info; 75 mPreventRadioButtonCallbacks = false; 76 77 setKey(mEngineInfo.name); 78 setTitle(mEngineInfo.label); 79 } 80 81 @Override onBindViewHolder(PreferenceViewHolder view)82 public void onBindViewHolder(PreferenceViewHolder view) { 83 super.onBindViewHolder(view); 84 85 if (mSharedState == null) { 86 throw new IllegalStateException("Call to getView() before a call to" + 87 "setSharedState()"); 88 } 89 90 final RadioButton rb = view.itemView.findViewById(android.R.id.checkbox); 91 rb.setOnCheckedChangeListener(mRadioChangeListener); 92 93 boolean isChecked = getKey().equals(mSharedState.getCurrentKey()); 94 if (isChecked) { 95 mSharedState.setCurrentChecked(rb); 96 } 97 98 mPreventRadioButtonCallbacks = true; 99 rb.setChecked(isChecked); 100 mPreventRadioButtonCallbacks = false; 101 mRadioButton = rb; 102 } 103 104 @Override onClick()105 public void onClick() { 106 mRadioButton.setChecked(true); 107 } 108 shouldDisplayDataAlert()109 private boolean shouldDisplayDataAlert() { 110 return !mEngineInfo.system; 111 } 112 113 displayDataAlert( DialogInterface.OnClickListener positiveOnClickListener, DialogInterface.OnClickListener negativeOnClickListener)114 private void displayDataAlert( 115 DialogInterface.OnClickListener positiveOnClickListener, 116 DialogInterface.OnClickListener negativeOnClickListener) { 117 Log.i(TAG, "Displaying data alert for :" + mEngineInfo.name); 118 119 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 120 builder.setTitle(android.R.string.dialog_alert_title) 121 .setMessage(getContext().getString( 122 R.string.tts_engine_security_warning, mEngineInfo.label)) 123 .setCancelable(true) 124 .setPositiveButton(android.R.string.ok, positiveOnClickListener) 125 .setNegativeButton(android.R.string.cancel, negativeOnClickListener); 126 127 AlertDialog dialog = builder.create(); 128 dialog.show(); 129 } 130 131 onRadioButtonClicked(final CompoundButton buttonView, boolean isChecked)132 private void onRadioButtonClicked(final CompoundButton buttonView, 133 boolean isChecked) { 134 if (mPreventRadioButtonCallbacks || 135 (mSharedState.getCurrentChecked() == buttonView)) { 136 return; 137 } 138 139 if (isChecked) { 140 // Should we alert user? if that's true, delay making engine current one. 141 if (shouldDisplayDataAlert()) { 142 displayDataAlert(new DialogInterface.OnClickListener() { 143 @Override 144 public void onClick(DialogInterface dialog, int which) { 145 makeCurrentEngine(buttonView); 146 } 147 }, new DialogInterface.OnClickListener() { 148 @Override 149 public void onClick(DialogInterface dialog, int which) { 150 // Undo the click. 151 buttonView.setChecked(false); 152 } 153 }); 154 } else { 155 // Privileged engine, set it current 156 makeCurrentEngine(buttonView); 157 } 158 } 159 } 160 makeCurrentEngine(Checkable current)161 private void makeCurrentEngine(Checkable current) { 162 if (mSharedState.getCurrentChecked() != null) { 163 mSharedState.getCurrentChecked().setChecked(false); 164 } 165 mSharedState.setCurrentChecked(current); 166 mSharedState.setCurrentKey(getKey()); 167 callChangeListener(mSharedState.getCurrentKey()); 168 } 169 170 171 /** 172 * Holds all state that is common to this group of radio buttons, such 173 * as the currently selected key and the currently checked compound button. 174 * (which corresponds to this key). 175 */ 176 public interface RadioButtonGroupState { getCurrentKey()177 String getCurrentKey(); 178 getCurrentChecked()179 Checkable getCurrentChecked(); 180 setCurrentKey(String key)181 void setCurrentKey(String key); 182 setCurrentChecked(Checkable current)183 void setCurrentChecked(Checkable current); 184 } 185 186 } 187