1 /* 2 * Copyright (C) 2014 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.voice; 18 19 import android.app.AlertDialog; 20 import android.content.ComponentName; 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.Intent; 24 import android.preference.Preference; 25 import android.util.Log; 26 import android.view.View; 27 import android.view.ViewGroup; 28 import android.widget.Checkable; 29 import android.widget.CompoundButton; 30 import android.widget.RadioButton; 31 32 33 import com.android.settings.R; 34 import com.android.settings.Utils; 35 36 public final class VoiceInputPreference extends Preference { 37 38 private static final String TAG = "VoiceInputPreference"; 39 40 private final CharSequence mLabel; 41 42 private final CharSequence mAppLabel; 43 44 private final CharSequence mAlertText; 45 46 private final ComponentName mSettingsComponent; 47 48 /** 49 * The shared radio button state, which button is checked etc. 50 */ 51 private final RadioButtonGroupState mSharedState; 52 53 /** 54 * When true, the change callbacks on the radio button will not 55 * fire. 56 */ 57 private volatile boolean mPreventRadioButtonCallbacks; 58 59 private View mSettingsIcon; 60 private RadioButton mRadioButton; 61 62 private final CompoundButton.OnCheckedChangeListener mRadioChangeListener = 63 new CompoundButton.OnCheckedChangeListener() { 64 @Override 65 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 66 onRadioButtonClicked(buttonView, isChecked); 67 } 68 }; 69 70 public VoiceInputPreference(Context context, VoiceInputHelper.BaseInfo info, 71 CharSequence summary, CharSequence alertText, RadioButtonGroupState state) { 72 super(context); 73 setLayoutResource(R.layout.preference_tts_engine); 74 75 mSharedState = state; 76 mLabel = info.label; 77 mAppLabel = info.appLabel; 78 mAlertText = alertText; 79 mSettingsComponent = info.settings; 80 mPreventRadioButtonCallbacks = false; 81 82 setKey(info.key); 83 setTitle(info.label); 84 setSummary(summary); 85 } 86 87 @Override 88 public View getView(View convertView, ViewGroup parent) { 89 if (mSharedState == null) { 90 throw new IllegalStateException("Call to getView() before a call to" + 91 "setSharedState()"); 92 } 93 94 View view = super.getView(convertView, parent); 95 final RadioButton rb = (RadioButton) view.findViewById(R.id.tts_engine_radiobutton); 96 rb.setOnCheckedChangeListener(mRadioChangeListener); 97 98 boolean isChecked = getKey().equals(mSharedState.getCurrentKey()); 99 if (isChecked) { 100 mSharedState.setCurrentChecked(rb); 101 } 102 103 mPreventRadioButtonCallbacks = true; 104 rb.setChecked(isChecked); 105 mPreventRadioButtonCallbacks = false; 106 107 mRadioButton = rb; 108 109 View textLayout = view.findViewById(R.id.tts_engine_pref_text); 110 textLayout.setOnClickListener(new View.OnClickListener() { 111 @Override 112 public void onClick(View v) { 113 if (!rb.isChecked()) { 114 onRadioButtonClicked(rb, true); 115 } 116 } 117 }); 118 119 mSettingsIcon = view.findViewById(R.id.tts_engine_settings); 120 mSettingsIcon.setOnClickListener(new View.OnClickListener() { 121 @Override 122 public void onClick(View v) { 123 Intent intent = new Intent(Intent.ACTION_MAIN); 124 intent.setComponent(mSettingsComponent); 125 getContext().startActivity(new Intent(intent)); 126 } 127 }); 128 updateCheckedState(isChecked); 129 130 return view; 131 } 132 133 private boolean shouldDisplayAlert() { 134 return mAlertText != null; 135 } 136 137 private void displayAlert( 138 final DialogInterface.OnClickListener positiveOnClickListener, 139 final DialogInterface.OnClickListener negativeOnClickListener) { 140 Log.i(TAG, "Displaying data alert for :" + getKey()); 141 142 AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); 143 String msg = String.format(getContext().getResources().getConfiguration().locale, 144 mAlertText.toString(), mAppLabel); 145 builder.setTitle(android.R.string.dialog_alert_title) 146 .setMessage(msg) 147 .setCancelable(true) 148 .setPositiveButton(android.R.string.ok, positiveOnClickListener) 149 .setNegativeButton(android.R.string.cancel, negativeOnClickListener) 150 .setOnCancelListener(new DialogInterface.OnCancelListener() { 151 @Override public void onCancel(DialogInterface dialog) { 152 negativeOnClickListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE); 153 } 154 }); 155 156 AlertDialog dialog = builder.create(); 157 dialog.show(); 158 } 159 160 public void doClick() { 161 mRadioButton.performClick(); 162 } 163 164 void updateCheckedState(boolean isChecked) { 165 if (mSettingsComponent != null) { 166 mSettingsIcon.setVisibility(View.VISIBLE); 167 if (isChecked) { 168 mSettingsIcon.setEnabled(true); 169 mSettingsIcon.setAlpha(1); 170 } else { 171 mSettingsIcon.setEnabled(false); 172 mSettingsIcon.setAlpha(Utils.DISABLED_ALPHA); 173 } 174 } else { 175 mSettingsIcon.setVisibility(View.GONE); 176 } 177 } 178 179 void onRadioButtonClicked(final CompoundButton buttonView, boolean isChecked) { 180 if (mPreventRadioButtonCallbacks) { 181 return; 182 } 183 if (mSharedState.getCurrentChecked() == buttonView) { 184 updateCheckedState(isChecked); 185 return; 186 } 187 188 if (isChecked) { 189 // Should we alert user? if that's true, delay making engine current one. 190 if (shouldDisplayAlert()) { 191 displayAlert(new DialogInterface.OnClickListener() { 192 @Override 193 public void onClick(DialogInterface dialog, int which) { 194 makeCurrentChecked(buttonView); 195 } 196 }, new DialogInterface.OnClickListener() { 197 @Override 198 public void onClick(DialogInterface dialog, int which) { 199 // Undo the click. 200 buttonView.setChecked(false); 201 } 202 } 203 ); 204 } else { 205 // Privileged engine, set it current 206 makeCurrentChecked(buttonView); 207 } 208 } else { 209 updateCheckedState(isChecked); 210 } 211 } 212 213 void makeCurrentChecked(Checkable current) { 214 if (mSharedState.getCurrentChecked() != null) { 215 mSharedState.getCurrentChecked().setChecked(false); 216 } 217 mSharedState.setCurrentChecked(current); 218 mSharedState.setCurrentKey(getKey()); 219 updateCheckedState(true); 220 callChangeListener(mSharedState.getCurrentKey()); 221 current.setChecked(true); 222 } 223 224 /** 225 * Holds all state that is common to this group of radio buttons, such 226 * as the currently selected key and the currently checked compound button. 227 * (which corresponds to this key). 228 */ 229 public interface RadioButtonGroupState { 230 String getCurrentKey(); 231 Checkable getCurrentChecked(); 232 233 void setCurrentKey(String key); 234 void setCurrentChecked(Checkable current); 235 } 236 } 237