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.inputmethod; 18 19 import com.android.settings.R; 20 import com.android.settings.SettingsPreferenceFragment; 21 22 import android.app.AlertDialog; 23 import android.app.Fragment; 24 import android.content.ActivityNotFoundException; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.res.Configuration; 28 import android.os.Bundle; 29 import android.preference.CheckBoxPreference; 30 import android.preference.PreferenceActivity; 31 import android.provider.Settings; 32 import android.text.TextUtils; 33 import android.util.Log; 34 import android.view.View; 35 import android.view.View.OnClickListener; 36 import android.view.View.OnLongClickListener; 37 import android.view.inputmethod.InputMethodInfo; 38 import android.view.inputmethod.InputMethodManager; 39 import android.view.inputmethod.InputMethodSubtype; 40 import android.widget.ImageView; 41 import android.widget.TextView; 42 import android.widget.Toast; 43 44 import java.util.Comparator; 45 import java.util.List; 46 47 public class InputMethodPreference extends CheckBoxPreference 48 implements Comparator<InputMethodPreference> { 49 private static final String TAG = InputMethodPreference.class.getSimpleName(); 50 private static final float DISABLED_ALPHA = 0.4f; 51 private final SettingsPreferenceFragment mFragment; 52 private final InputMethodInfo mImi; 53 private final InputMethodManager mImm; 54 private final Intent mSettingsIntent; 55 private final boolean mAlwaysChecked; 56 private final boolean mIsSystemIme; 57 58 private AlertDialog mDialog = null; 59 private ImageView mInputMethodSettingsButton; 60 private TextView mTitleText; 61 private TextView mSummaryText; 62 private View mInputMethodPref; 63 64 private final OnClickListener mPrefOnclickListener = new OnClickListener() { 65 @Override 66 public void onClick(View arg0) { 67 if (!isEnabled()) { 68 return; 69 } 70 if (isChecked()) { 71 setChecked(false, true /* save */); 72 } else { 73 if (mIsSystemIme) { 74 setChecked(true, true /* save */); 75 } else { 76 showSecurityWarnDialog(mImi, InputMethodPreference.this); 77 } 78 } 79 } 80 }; 81 82 public InputMethodPreference(SettingsPreferenceFragment fragment, Intent settingsIntent, 83 InputMethodManager imm, InputMethodInfo imi, int imiCount) { 84 super(fragment.getActivity(), null, R.style.InputMethodPreferenceStyle); 85 setLayoutResource(R.layout.preference_inputmethod); 86 setWidgetLayoutResource(R.layout.preference_inputmethod_widget); 87 mFragment = fragment; 88 mSettingsIntent = settingsIntent; 89 mImm = imm; 90 mImi = imi; 91 updateSummary(); 92 mAlwaysChecked = InputMethodAndSubtypeUtil.isAlwaysCheckedIme( 93 imi, fragment.getActivity(), imiCount); 94 mIsSystemIme = InputMethodAndSubtypeUtil.isSystemIme(imi); 95 if (mAlwaysChecked) { 96 setEnabled(false); 97 } 98 } 99 100 @Override 101 protected void onBindView(View view) { 102 super.onBindView(view); 103 mInputMethodPref = view.findViewById(R.id.inputmethod_pref); 104 mInputMethodPref.setOnClickListener(mPrefOnclickListener); 105 mInputMethodSettingsButton = (ImageView)view.findViewById(R.id.inputmethod_settings); 106 mTitleText = (TextView)view.findViewById(android.R.id.title); 107 mSummaryText = (TextView)view.findViewById(android.R.id.summary); 108 final boolean hasSubtypes = mImi.getSubtypeCount() > 1; 109 final String imiId = mImi.getId(); 110 if (hasSubtypes) { 111 mInputMethodPref.setOnLongClickListener(new OnLongClickListener() { 112 @Override 113 public boolean onLongClick(View arg0) { 114 final Bundle bundle = new Bundle(); 115 bundle.putString(Settings.EXTRA_INPUT_METHOD_ID, imiId); 116 startFragment(mFragment, InputMethodAndSubtypeEnabler.class.getName(), 117 0, bundle); 118 return true; 119 } 120 }); 121 } 122 123 if (mSettingsIntent != null) { 124 mInputMethodSettingsButton.setOnClickListener( 125 new OnClickListener() { 126 @Override 127 public void onClick(View arg0) { 128 try { 129 mFragment.startActivity(mSettingsIntent); 130 } catch (ActivityNotFoundException e) { 131 Log.d(TAG, "IME's Settings Activity Not Found: " + e); 132 final String msg = mFragment.getString( 133 R.string.failed_to_open_app_settings_toast, 134 mImi.loadLabel( 135 mFragment.getActivity().getPackageManager())); 136 Toast.makeText( 137 mFragment.getActivity(), msg, Toast.LENGTH_LONG).show(); 138 } 139 } 140 }); 141 } 142 if (hasSubtypes) { 143 final OnLongClickListener listener = new OnLongClickListener() { 144 @Override 145 public boolean onLongClick(View arg0) { 146 final Bundle bundle = new Bundle(); 147 bundle.putString(Settings.EXTRA_INPUT_METHOD_ID, imiId); 148 startFragment(mFragment, InputMethodAndSubtypeEnabler.class.getName(), 149 0, bundle); 150 return true; 151 } 152 }; 153 mInputMethodSettingsButton.setOnLongClickListener(listener); 154 } 155 if (mSettingsIntent == null) { 156 mInputMethodSettingsButton.setVisibility(View.GONE); 157 } else { 158 updatePreferenceViews(); 159 } 160 } 161 162 @Override 163 public void setEnabled(boolean enabled) { 164 super.setEnabled(enabled); 165 updatePreferenceViews(); 166 } 167 168 private void updatePreferenceViews() { 169 final boolean checked = isChecked(); 170 if (mInputMethodSettingsButton != null) { 171 mInputMethodSettingsButton.setEnabled(checked); 172 mInputMethodSettingsButton.setClickable(checked); 173 mInputMethodSettingsButton.setFocusable(checked); 174 if (!checked) { 175 mInputMethodSettingsButton.setAlpha(DISABLED_ALPHA); 176 } 177 } 178 if (mTitleText != null) { 179 mTitleText.setEnabled(true); 180 } 181 if (mSummaryText != null) { 182 mSummaryText.setEnabled(checked); 183 } 184 if (mInputMethodPref != null) { 185 mInputMethodPref.setEnabled(true); 186 mInputMethodPref.setLongClickable(checked); 187 final boolean enabled = isEnabled(); 188 mInputMethodPref.setOnClickListener(enabled ? mPrefOnclickListener : null); 189 if (!enabled) { 190 mInputMethodPref.setBackgroundColor(0); 191 } 192 } 193 } 194 195 public static boolean startFragment( 196 Fragment fragment, String fragmentClass, int requestCode, Bundle extras) { 197 if (fragment.getActivity() instanceof PreferenceActivity) { 198 PreferenceActivity preferenceActivity = (PreferenceActivity)fragment.getActivity(); 199 preferenceActivity.startPreferencePanel(fragmentClass, extras, 0, null, fragment, 200 requestCode); 201 return true; 202 } else { 203 Log.w(TAG, "Parent isn't PreferenceActivity, thus there's no way to launch the " 204 + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode 205 + ")"); 206 return false; 207 } 208 } 209 210 public String getSummaryString() { 211 final StringBuilder builder = new StringBuilder(); 212 final List<InputMethodSubtype> subtypes = mImm.getEnabledInputMethodSubtypeList(mImi, true); 213 for (InputMethodSubtype subtype : subtypes) { 214 if (builder.length() > 0) { 215 builder.append(", "); 216 } 217 final CharSequence subtypeLabel = subtype.getDisplayName(mFragment.getActivity(), 218 mImi.getPackageName(), mImi.getServiceInfo().applicationInfo); 219 builder.append(subtypeLabel); 220 } 221 return builder.toString(); 222 } 223 224 public void updateSummary() { 225 final String summary = getSummaryString(); 226 if (TextUtils.isEmpty(summary)) { 227 return; 228 } 229 setSummary(summary); 230 } 231 232 /** 233 * Sets the checkbox state and optionally saves the settings. 234 * @param checked whether to check the box 235 * @param save whether to save IME settings 236 */ 237 public void setChecked(boolean checked, boolean save) { 238 super.setChecked(checked); 239 if (save) { 240 saveImeSettings(); 241 } 242 updateSummary(); 243 } 244 245 @Override 246 public void setChecked(boolean checked) { 247 setChecked(checked, false); 248 } 249 250 private void showSecurityWarnDialog(InputMethodInfo imi, final InputMethodPreference chkPref) { 251 if (mDialog != null && mDialog.isShowing()) { 252 mDialog.dismiss(); 253 } 254 mDialog = (new AlertDialog.Builder(mFragment.getActivity())) 255 .setTitle(android.R.string.dialog_alert_title) 256 .setIcon(android.R.drawable.ic_dialog_alert) 257 .setCancelable(true) 258 .setPositiveButton(android.R.string.ok, 259 new DialogInterface.OnClickListener() { 260 @Override 261 public void onClick(DialogInterface dialog, int which) { 262 chkPref.setChecked(true, true); 263 } 264 }) 265 .setNegativeButton(android.R.string.cancel, 266 new DialogInterface.OnClickListener() { 267 @Override 268 public void onClick(DialogInterface dialog, int which) { 269 } 270 }) 271 .create(); 272 mDialog.setMessage(mFragment.getResources().getString(R.string.ime_security_warning, 273 imi.getServiceInfo().applicationInfo.loadLabel( 274 mFragment.getActivity().getPackageManager()))); 275 mDialog.show(); 276 } 277 278 @Override 279 public int compare(InputMethodPreference arg0, InputMethodPreference arg1) { 280 if (arg0.isEnabled() == arg0.isEnabled()) { 281 return arg0.mImi.getId().compareTo(arg1.mImi.getId()); 282 } else { 283 // Prefer system IMEs 284 return arg0.isEnabled() ? 1 : -1; 285 } 286 } 287 288 private void saveImeSettings() { 289 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( 290 mFragment, mFragment.getActivity().getContentResolver(), mImm.getInputMethodList(), 291 mFragment.getResources().getConfiguration().keyboard 292 == Configuration.KEYBOARD_QWERTY); 293 } 294 } 295