Home | History | Annotate | Download | only in inputmethod
      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.internal.inputmethod.InputMethodUtils;
     20 import com.android.settings.R;
     21 import com.android.settings.SettingsPreferenceFragment;
     22 import com.android.settings.Utils;
     23 
     24 import android.app.AlertDialog;
     25 import android.app.Fragment;
     26 import android.content.ActivityNotFoundException;
     27 import android.content.Context;
     28 import android.content.DialogInterface;
     29 import android.content.Intent;
     30 import android.content.res.Configuration;
     31 import android.os.Bundle;
     32 import android.preference.CheckBoxPreference;
     33 import android.preference.Preference;
     34 import android.preference.PreferenceActivity;
     35 import android.provider.Settings;
     36 import android.text.TextUtils;
     37 import android.util.Log;
     38 import android.view.View;
     39 import android.view.View.OnClickListener;
     40 import android.view.View.OnLongClickListener;
     41 import android.view.inputmethod.InputMethodInfo;
     42 import android.view.inputmethod.InputMethodManager;
     43 import android.view.inputmethod.InputMethodSubtype;
     44 import android.widget.ImageView;
     45 import android.widget.TextView;
     46 import android.widget.Toast;
     47 
     48 import java.text.Collator;
     49 import java.util.List;
     50 
     51 public class InputMethodPreference extends CheckBoxPreference {
     52     private static final String TAG = InputMethodPreference.class.getSimpleName();
     53     private final SettingsPreferenceFragment mFragment;
     54     private final InputMethodInfo mImi;
     55     private final InputMethodManager mImm;
     56     private final boolean mIsValidSystemNonAuxAsciiCapableIme;
     57     private final Intent mSettingsIntent;
     58     private final boolean mIsSystemIme;
     59     private final Collator mCollator;
     60 
     61     private AlertDialog mDialog = null;
     62     private ImageView mInputMethodSettingsButton;
     63     private TextView mTitleText;
     64     private TextView mSummaryText;
     65     private View mInputMethodPref;
     66     private OnPreferenceChangeListener mOnImePreferenceChangeListener;
     67 
     68     private final OnClickListener mPrefOnclickListener = new OnClickListener() {
     69         @Override
     70         public void onClick(View arg0) {
     71             if (!isEnabled()) {
     72                 return;
     73             }
     74             if (isChecked()) {
     75                 setChecked(false, true /* save */);
     76             } else {
     77                 if (mIsSystemIme) {
     78                     setChecked(true, true /* save */);
     79                 } else {
     80                     showSecurityWarnDialog(mImi, InputMethodPreference.this);
     81                 }
     82             }
     83         }
     84     };
     85 
     86     public InputMethodPreference(SettingsPreferenceFragment fragment, Intent settingsIntent,
     87             InputMethodManager imm, InputMethodInfo imi) {
     88         super(fragment.getActivity(), null, R.style.InputMethodPreferenceStyle);
     89         setLayoutResource(R.layout.preference_inputmethod);
     90         setWidgetLayoutResource(R.layout.preference_inputmethod_widget);
     91         mFragment = fragment;
     92         mSettingsIntent = settingsIntent;
     93         mImm = imm;
     94         mImi = imi;
     95         mIsSystemIme = InputMethodUtils.isSystemIme(imi);
     96         mCollator = Collator.getInstance(fragment.getResources().getConfiguration().locale);
     97         final Context context = fragment.getActivity();
     98         mIsValidSystemNonAuxAsciiCapableIme = InputMethodSettingValuesWrapper
     99                 .getInstance(context).isValidSystemNonAuxAsciiCapableIme(imi, context);
    100         updatePreferenceViews();
    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         }
    161         updatePreferenceViews();
    162     }
    163 
    164     @Override
    165     public void setEnabled(boolean enabled) {
    166         super.setEnabled(enabled);
    167         updatePreferenceViews();
    168     }
    169 
    170     public void updatePreferenceViews() {
    171         final boolean isAlwaysChecked =
    172                 InputMethodSettingValuesWrapper.getInstance(getContext()).isAlwaysCheckedIme(
    173                         mImi, getContext());
    174         if (isAlwaysChecked) {
    175             super.setChecked(true);
    176             super.setEnabled(false);
    177         } else {
    178             super.setEnabled(true);
    179         }
    180         final boolean checked = isChecked();
    181         if (mInputMethodSettingsButton != null) {
    182             mInputMethodSettingsButton.setEnabled(checked);
    183             mInputMethodSettingsButton.setClickable(checked);
    184             mInputMethodSettingsButton.setFocusable(checked);
    185             if (!checked) {
    186                 mInputMethodSettingsButton.setAlpha(Utils.DISABLED_ALPHA);
    187             }
    188         }
    189         if (mTitleText != null) {
    190             mTitleText.setEnabled(true);
    191         }
    192         if (mSummaryText != null) {
    193             mSummaryText.setEnabled(checked);
    194         }
    195         if (mInputMethodPref != null) {
    196             mInputMethodPref.setEnabled(true);
    197             mInputMethodPref.setLongClickable(checked);
    198             final boolean enabled = isEnabled();
    199             mInputMethodPref.setOnClickListener(enabled ? mPrefOnclickListener : null);
    200             if (!enabled) {
    201                 mInputMethodPref.setBackgroundColor(0);
    202             }
    203         }
    204         updateSummary();
    205     }
    206 
    207     public static boolean startFragment(
    208             Fragment fragment, String fragmentClass, int requestCode, Bundle extras) {
    209         if (fragment.getActivity() instanceof PreferenceActivity) {
    210             PreferenceActivity preferenceActivity = (PreferenceActivity)fragment.getActivity();
    211             preferenceActivity.startPreferencePanel(fragmentClass, extras, 0, null, fragment,
    212                     requestCode);
    213             return true;
    214         } else {
    215             Log.w(TAG, "Parent isn't PreferenceActivity, thus there's no way to launch the "
    216                     + "given Fragment (name: " + fragmentClass + ", requestCode: " + requestCode
    217                     + ")");
    218             return false;
    219         }
    220     }
    221 
    222     private String getSummaryString() {
    223         final StringBuilder builder = new StringBuilder();
    224         final List<InputMethodSubtype> subtypes = mImm.getEnabledInputMethodSubtypeList(mImi, true);
    225         for (InputMethodSubtype subtype : subtypes) {
    226             if (builder.length() > 0) {
    227                 builder.append(", ");
    228             }
    229             final CharSequence subtypeLabel = subtype.getDisplayName(mFragment.getActivity(),
    230                     mImi.getPackageName(), mImi.getServiceInfo().applicationInfo);
    231             builder.append(subtypeLabel);
    232         }
    233         return builder.toString();
    234     }
    235 
    236     private void updateSummary() {
    237         final String summary = getSummaryString();
    238         if (TextUtils.isEmpty(summary)) {
    239             return;
    240         }
    241         setSummary(summary);
    242     }
    243 
    244     /**
    245      * Sets the checkbox state and optionally saves the settings.
    246      * @param checked whether to check the box
    247      * @param save whether to save IME settings
    248      */
    249     private void setChecked(boolean checked, boolean save) {
    250         final boolean wasChecked = isChecked();
    251         super.setChecked(checked);
    252         if (save) {
    253             saveImeSettings();
    254             if (wasChecked != checked && mOnImePreferenceChangeListener != null) {
    255                 mOnImePreferenceChangeListener.onPreferenceChange(this, checked);
    256             }
    257         }
    258     }
    259 
    260     public void setOnImePreferenceChangeListener(OnPreferenceChangeListener listener) {
    261         mOnImePreferenceChangeListener = listener;
    262     }
    263 
    264     private void showSecurityWarnDialog(InputMethodInfo imi, final InputMethodPreference chkPref) {
    265         if (mDialog != null && mDialog.isShowing()) {
    266             mDialog.dismiss();
    267         }
    268         mDialog = (new AlertDialog.Builder(mFragment.getActivity()))
    269                 .setTitle(android.R.string.dialog_alert_title)
    270                 .setIconAttribute(android.R.attr.alertDialogIcon)
    271                 .setCancelable(true)
    272                 .setPositiveButton(android.R.string.ok,
    273                         new DialogInterface.OnClickListener() {
    274                     @Override
    275                     public void onClick(DialogInterface dialog, int which) {
    276                         chkPref.setChecked(true, true);
    277                     }
    278                 })
    279                 .setNegativeButton(android.R.string.cancel,
    280                         new DialogInterface.OnClickListener() {
    281                     @Override
    282                     public void onClick(DialogInterface dialog, int which) {
    283                     }
    284                 })
    285                 .create();
    286         mDialog.setMessage(mFragment.getResources().getString(R.string.ime_security_warning,
    287                 imi.getServiceInfo().applicationInfo.loadLabel(
    288                         mFragment.getActivity().getPackageManager())));
    289         mDialog.show();
    290     }
    291 
    292     @Override
    293     public int compareTo(Preference p) {
    294         if (!(p instanceof InputMethodPreference)) {
    295             return super.compareTo(p);
    296         }
    297         final InputMethodPreference imp = (InputMethodPreference) p;
    298         final boolean priority0 = mIsSystemIme && mIsValidSystemNonAuxAsciiCapableIme;
    299         final boolean priority1 = imp.mIsSystemIme && imp.mIsValidSystemNonAuxAsciiCapableIme;
    300         if (priority0 == priority1) {
    301             final CharSequence t0 = getTitle();
    302             final CharSequence t1 = imp.getTitle();
    303             if (TextUtils.isEmpty(t0)) {
    304                 return 1;
    305             }
    306             if (TextUtils.isEmpty(t1)) {
    307                 return -1;
    308             }
    309             return mCollator.compare(t0.toString(), t1.toString());
    310         }
    311         // Prefer always checked system IMEs
    312         return priority0 ? -1 : 1;
    313     }
    314 
    315     private void saveImeSettings() {
    316         InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
    317                 mFragment, mFragment.getActivity().getContentResolver(), mImm.getInputMethodList(),
    318                 mFragment.getResources().getConfiguration().keyboard
    319                         == Configuration.KEYBOARD_QWERTY);
    320     }
    321 }
    322