diff options
Diffstat (limited to 'services/java/com/android/server/InputMethodManagerService.java')
-rw-r--r-- | services/java/com/android/server/InputMethodManagerService.java | 297 |
1 files changed, 230 insertions, 67 deletions
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 7d4faea..43c2292 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -27,6 +27,7 @@ import com.android.internal.view.IInputMethodManager; import com.android.internal.view.IInputMethodSession; import com.android.internal.view.InputBindResult; import com.android.server.EventLogTags; +import com.android.server.wm.WindowManagerService; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -91,7 +92,10 @@ import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import android.widget.ArrayAdapter; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.RadioButton; +import android.widget.Switch; import android.widget.TextView; import java.io.File; @@ -101,6 +105,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -113,7 +118,7 @@ import java.util.TreeMap; public class InputMethodManagerService extends IInputMethodManager.Stub implements ServiceConnection, Handler.Callback { static final boolean DEBUG = false; - static final String TAG = "InputManagerService"; + static final String TAG = "InputMethodManagerService"; static final int MSG_SHOW_IM_PICKER = 1; static final int MSG_SHOW_IM_SUBTYPE_PICKER = 2; @@ -133,6 +138,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub static final int MSG_UNBIND_METHOD = 3000; static final int MSG_BIND_METHOD = 3010; + static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000; + static final long TIME_TO_RECONNECT = 10*1000; static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20; @@ -142,6 +149,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; private static final String SUBTYPE_MODE_VOICE = "voice"; private static final String TAG_TRY_SUPPRESSING_IME_SWITCHER = "TrySuppressingImeSwitcher"; + private static final String TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = + "EnabledWhenDefaultIsNotAsciiCapable"; + private static final String TAG_ASCII_CAPABLE = "AsciiCapable"; final Context mContext; final Resources mRes; @@ -151,6 +161,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final IWindowManager mIWindowManager; final HandlerCaller mCaller; private final InputMethodFileManager mFileManager; + private final InputMethodAndSubtypeListManager mImListManager; + private final HardKeyboardListener mHardKeyboardListener; + private final WindowManagerService mWindowManagerService; final InputBindResult mNoBinding = new InputBindResult(null, null, -1); @@ -355,6 +368,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private AlertDialog.Builder mDialogBuilder; private AlertDialog mSwitchingDialog; + private View mSwitchingDialogTitleView; private InputMethodInfo[] mIms; private int[] mSubtypeIds; @@ -523,7 +537,31 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - public InputMethodManagerService(Context context) { + private class HardKeyboardListener + implements WindowManagerService.OnHardKeyboardStatusChangeListener { + @Override + public void onHardKeyboardStatusChange(boolean available, boolean enabled) { + mHandler.sendMessage(mHandler.obtainMessage( + MSG_HARD_KEYBOARD_SWITCH_CHANGED, available ? 1 : 0, enabled ? 1 : 0)); + } + + public void handleHardKeyboardStatusChange(boolean available, boolean enabled) { + if (DEBUG) { + Slog.w(TAG, "HardKeyboardStatusChanged: available = " + available + ", enabled = " + + enabled); + } + synchronized(mMethodMap) { + if (mSwitchingDialog != null && mSwitchingDialogTitleView != null + && mSwitchingDialog.isShowing()) { + mSwitchingDialogTitleView.findViewById( + com.android.internal.R.id.hard_keyboard_section).setVisibility( + available ? View.VISIBLE : View.GONE); + } + } + } + } + + public InputMethodManagerService(Context context, WindowManagerService windowManager) { mContext = context; mRes = context.getResources(); mHandler = new Handler(this); @@ -535,6 +573,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub handleMessage(msg); } }); + mWindowManagerService = windowManager; + mHardKeyboardListener = new HardKeyboardListener(); mImeSwitcherNotification = new Notification(); mImeSwitcherNotification.icon = com.android.internal.R.drawable.ic_notification_ime_default; @@ -552,8 +592,9 @@ public class InputMethodManagerService extends IInputMethodManager.Stub synchronized (mMethodMap) { mFileManager = new InputMethodFileManager(mMethodMap); } + mImListManager = new InputMethodAndSubtypeListManager(context, this); - (new MyPackageMonitor()).register(mContext, true); + (new MyPackageMonitor()).register(mContext, null, true); IntentFilter screenOnOffFilt = new IntentFilter(); screenOnOffFilt.addAction(Intent.ACTION_SCREEN_ON); @@ -627,6 +668,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub updateImeWindowStatusLocked(); mShowOngoingImeSwitcherForPhones = mRes.getBoolean( com.android.internal.R.bool.show_ongoing_ime_switcher); + if (mShowOngoingImeSwitcherForPhones) { + mWindowManagerService.setOnHardKeyboardStatusChangeListener( + mHardKeyboardListener); + } try { startInputInnerLocked(); } catch (RuntimeException e) { @@ -1126,6 +1171,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub private boolean needsToShowImeSwitchOngoingNotification() { if (!mShowOngoingImeSwitcherForPhones) return false; + if (isScreenLocked()) return false; synchronized (mMethodMap) { List<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked(); final int N = imis.size(); @@ -1735,6 +1781,19 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @Override + public boolean switchToNextInputMethod(IBinder token, boolean onlyCurrentIme) { + synchronized (mMethodMap) { + final ImeSubtypeListItem nextSubtype = mImListManager.getNextInputMethod( + onlyCurrentIme, mMethodMap.get(mCurMethodId), mCurrentSubtype); + if (nextSubtype == null) { + return false; + } + setInputMethodWithSubtypeId(token, nextSubtype.mImi.getId(), nextSubtype.mSubtypeId); + return true; + } + } + + @Override public InputMethodSubtype getLastInputMethodSubtype() { synchronized (mMethodMap) { final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); @@ -1974,6 +2033,12 @@ public class InputMethodManagerService extends IInputMethodManager.Stub Slog.w(TAG, "Client died receiving input method " + args.arg2); } return true; + + // -------------------------------------------------------------- + case MSG_HARD_KEYBOARD_SWITCH_CHANGED: + mHardKeyboardListener.handleHardKeyboardStatusChange( + msg.arg1 == 1, msg.arg2 == 1); + return true; } return false; } @@ -2129,15 +2194,18 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mContext.startActivity(intent); } + private boolean isScreenLocked() { + return mKeyguardManager != null + && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure(); + } private void showInputMethodMenuInternal(boolean showSubtypes) { if (DEBUG) Slog.v(TAG, "Show switching menu"); final Context context = mContext; final PackageManager pm = context.getPackageManager(); - final boolean isScreenLocked = mKeyguardManager != null - && mKeyguardManager.isKeyguardLocked() && mKeyguardManager.isKeyguardSecure(); + final boolean isScreenLocked = isScreenLocked(); - String lastInputMethodId = Settings.Secure.getString(context + final String lastInputMethodId = Settings.Secure.getString(context .getContentResolver(), Settings.Secure.DEFAULT_INPUT_METHOD); int lastInputMethodSubtypeId = getSelectedInputMethodSubtypeId(lastInputMethodId); if (DEBUG) Slog.v(TAG, "Current IME: " + lastInputMethodId); @@ -2151,60 +2219,16 @@ public class InputMethodManagerService extends IInputMethodManager.Stub hideInputMethodMenuLocked(); - final TreeMap<InputMethodInfo, List<InputMethodSubtype>> sortedImmis = - new TreeMap<InputMethodInfo, List<InputMethodSubtype>>( - new Comparator<InputMethodInfo>() { - @Override - public int compare(InputMethodInfo imi1, InputMethodInfo imi2) { - if (imi2 == null) return 0; - if (imi1 == null) return 1; - if (pm == null) { - return imi1.getId().compareTo(imi2.getId()); - } - CharSequence imiId1 = imi1.loadLabel(pm) + "/" + imi1.getId(); - CharSequence imiId2 = imi2.loadLabel(pm) + "/" + imi2.getId(); - return imiId1.toString().compareTo(imiId2.toString()); - } - }); - - sortedImmis.putAll(immis); - - final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>(); + final List<ImeSubtypeListItem> imList = + mImListManager.getSortedInputMethodAndSubtypeList( + showSubtypes, mInputShown, isScreenLocked); - for (InputMethodInfo imi : sortedImmis.keySet()) { - if (imi == null) continue; - List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi); - HashSet<String> enabledSubtypeSet = new HashSet<String>(); - for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) { - enabledSubtypeSet.add(String.valueOf(subtype.hashCode())); - } - ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi); - final CharSequence imeLabel = imi.loadLabel(pm); - if (showSubtypes && enabledSubtypeSet.size() > 0) { - final int subtypeCount = imi.getSubtypeCount(); - if (DEBUG) { - Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); - } - for (int j = 0; j < subtypeCount; ++j) { - final InputMethodSubtype subtype = imi.getSubtypeAt(j); - final String subtypeHashCode = String.valueOf(subtype.hashCode()); - // We show all enabled IMEs and subtypes when an IME is shown. - if (enabledSubtypeSet.contains(subtypeHashCode) - && ((mInputShown && !isScreenLocked) || !subtype.isAuxiliary())) { - final CharSequence subtypeLabel = - subtype.overridesImplicitlyEnabledSubtype() ? null - : subtype.getDisplayName(context, imi.getPackageName(), - imi.getServiceInfo().applicationInfo); - imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j)); - - // Removing this subtype from enabledSubtypeSet because we no longer - // need to add an entry of this subtype to imList to avoid duplicated - // entries. - enabledSubtypeSet.remove(subtypeHashCode); - } - } - } else { - imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID)); + if (lastInputMethodSubtypeId == NOT_A_SUBTYPE_ID) { + final InputMethodSubtype currentSubtype = getCurrentInputMethodSubtype(); + if (currentSubtype != null) { + final InputMethodInfo currentImi = mMethodMap.get(mCurMethodId); + lastInputMethodSubtypeId = + getSubtypeIdFromHashCode(currentImi, currentSubtype.hashCode()); } } @@ -2225,12 +2249,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } - final TypedArray a = context.obtainStyledAttributes(null, com.android.internal.R.styleable.DialogPreference, com.android.internal.R.attr.alertDialogStyle, 0); mDialogBuilder = new AlertDialog.Builder(context) - .setTitle(com.android.internal.R.string.select_input_method) .setOnCancelListener(new OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { @@ -2240,6 +2262,29 @@ public class InputMethodManagerService extends IInputMethodManager.Stub .setIcon(a.getDrawable( com.android.internal.R.styleable.DialogPreference_dialogTitle)); a.recycle(); + final LayoutInflater inflater = + (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + final View tv = inflater.inflate( + com.android.internal.R.layout.input_method_switch_dialog_title, null); + mDialogBuilder.setCustomTitle(tv); + + // Setup layout for a toggle switch of the hardware keyboard + mSwitchingDialogTitleView = tv; + mSwitchingDialogTitleView.findViewById( + com.android.internal.R.id.hard_keyboard_section).setVisibility( + mWindowManagerService.isHardKeyboardAvailable() ? + View.VISIBLE : View.GONE); + final Switch hardKeySwitch = ((Switch)mSwitchingDialogTitleView.findViewById( + com.android.internal.R.id.hard_keyboard_switch)); + hardKeySwitch.setChecked(mWindowManagerService.isHardKeyboardEnabled()); + hardKeySwitch.setOnCheckedChangeListener( + new OnCheckedChangeListener() { + @Override + public void onCheckedChanged( + CompoundButton buttonView, boolean isChecked) { + mWindowManagerService.setHardKeyboardEnabled(isChecked); + } + }); final ImeSubtypeListAdapter adapter = new ImeSubtypeListAdapter(context, com.android.internal.R.layout.simple_list_item_2_single_choice, imList, @@ -2517,7 +2562,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub final HashMap<String, InputMethodSubtype> applicableModeAndSubtypesMap = new HashMap<String, InputMethodSubtype>(); final int N = subtypes.size(); - boolean containsKeyboardSubtype = false; for (int i = 0; i < N; ++i) { // scan overriding implicitly enabled subtypes. InputMethodSubtype subtype = subtypes.get(i); @@ -2551,15 +2595,23 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (!systemLocale.equals(locale)) continue; } applicableModeAndSubtypesMap.put(mode, subtype); - if (!containsKeyboardSubtype - && SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())) { - containsKeyboardSubtype = true; - } } } + final InputMethodSubtype keyboardSubtype + = applicableModeAndSubtypesMap.get(SUBTYPE_MODE_KEYBOARD); final ArrayList<InputMethodSubtype> applicableSubtypes = new ArrayList<InputMethodSubtype>( applicableModeAndSubtypesMap.values()); - if (!containsKeyboardSubtype) { + if (keyboardSubtype != null && !keyboardSubtype.containsExtraValueKey(TAG_ASCII_CAPABLE)) { + for (int i = 0; i < N; ++i) { + final InputMethodSubtype subtype = subtypes.get(i); + final String mode = subtype.getMode(); + if (SUBTYPE_MODE_KEYBOARD.equals(mode) && subtype.containsExtraValueKey( + TAG_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)) { + applicableSubtypes.add(subtype); + } + } + } + if (keyboardSubtype == null) { InputMethodSubtype lastResortKeyboardSubtype = findLastResortApplicableSubtypeLocked( res, subtypes, SUBTYPE_MODE_KEYBOARD, systemLocale, true); if (lastResortKeyboardSubtype != null) { @@ -2812,6 +2864,117 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + private static class InputMethodAndSubtypeListManager { + private final Context mContext; + private final PackageManager mPm; + private final InputMethodManagerService mImms; + public InputMethodAndSubtypeListManager(Context context, InputMethodManagerService imms) { + mContext = context; + mPm = context.getPackageManager(); + mImms = imms; + } + + private final TreeMap<InputMethodInfo, List<InputMethodSubtype>> mSortedImmis = + new TreeMap<InputMethodInfo, List<InputMethodSubtype>>( + new Comparator<InputMethodInfo>() { + @Override + public int compare(InputMethodInfo imi1, InputMethodInfo imi2) { + if (imi2 == null) return 0; + if (imi1 == null) return 1; + if (mPm == null) { + return imi1.getId().compareTo(imi2.getId()); + } + CharSequence imiId1 = imi1.loadLabel(mPm) + "/" + imi1.getId(); + CharSequence imiId2 = imi2.loadLabel(mPm) + "/" + imi2.getId(); + return imiId1.toString().compareTo(imiId2.toString()); + } + }); + + public ImeSubtypeListItem getNextInputMethod( + boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype) { + if (imi == null) { + return null; + } + final List<ImeSubtypeListItem> imList = getSortedInputMethodAndSubtypeList(); + if (imList.size() <= 1) { + return null; + } + final int N = imList.size(); + final int currentSubtypeId = subtype != null + ? mImms.getSubtypeIdFromHashCode(imi, subtype.hashCode()) + : NOT_A_SUBTYPE_ID; + for (int i = 0; i < N; ++i) { + final ImeSubtypeListItem isli = imList.get(i); + if (isli.mImi.equals(imi) && isli.mSubtypeId == currentSubtypeId) { + if (!onlyCurrentIme) { + return imList.get((i + 1) % N); + } + for (int j = 0; j < N - 1; ++j) { + final ImeSubtypeListItem candidate = imList.get((i + j + 1) % N); + if (candidate.mImi.equals(imi)) { + return candidate; + } + } + return null; + } + } + return null; + } + + public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList() { + return getSortedInputMethodAndSubtypeList(true, false, false); + } + + public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(boolean showSubtypes, + boolean inputShown, boolean isScreenLocked) { + final ArrayList<ImeSubtypeListItem> imList = new ArrayList<ImeSubtypeListItem>(); + final HashMap<InputMethodInfo, List<InputMethodSubtype>> immis = + mImms.getExplicitlyOrImplicitlyEnabledInputMethodsAndSubtypeListLocked(); + if (immis == null || immis.size() == 0) { + return Collections.emptyList(); + } + mSortedImmis.clear(); + mSortedImmis.putAll(immis); + for (InputMethodInfo imi : mSortedImmis.keySet()) { + if (imi == null) continue; + List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList = immis.get(imi); + HashSet<String> enabledSubtypeSet = new HashSet<String>(); + for (InputMethodSubtype subtype: explicitlyOrImplicitlyEnabledSubtypeList) { + enabledSubtypeSet.add(String.valueOf(subtype.hashCode())); + } + ArrayList<InputMethodSubtype> subtypes = getSubtypes(imi); + final CharSequence imeLabel = imi.loadLabel(mPm); + if (showSubtypes && enabledSubtypeSet.size() > 0) { + final int subtypeCount = imi.getSubtypeCount(); + if (DEBUG) { + Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId()); + } + for (int j = 0; j < subtypeCount; ++j) { + final InputMethodSubtype subtype = imi.getSubtypeAt(j); + final String subtypeHashCode = String.valueOf(subtype.hashCode()); + // We show all enabled IMEs and subtypes when an IME is shown. + if (enabledSubtypeSet.contains(subtypeHashCode) + && ((inputShown && !isScreenLocked) || !subtype.isAuxiliary())) { + final CharSequence subtypeLabel = + subtype.overridesImplicitlyEnabledSubtype() ? null + : subtype.getDisplayName(mContext, imi.getPackageName(), + imi.getServiceInfo().applicationInfo); + imList.add(new ImeSubtypeListItem(imeLabel, subtypeLabel, imi, j)); + + // Removing this subtype from enabledSubtypeSet because we no longer + // need to add an entry of this subtype to imList to avoid duplicated + // entries. + enabledSubtypeSet.remove(subtypeHashCode); + } + } + } else { + imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID)); + } + } + return imList; + } + } + /** * Utility class for putting and getting settings for InputMethod * TODO: Move all putters and getters of settings to this class. |