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.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