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