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