| /* | |
| * Copyright (C) 2013 The Android Open Source Project | |
| * | |
| * Licensed under the Apache License, Version 2.0 (the "License"); | |
| * you may not use this file except in compliance with the License. | |
| * You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, software | |
| * distributed under the License is distributed on an "AS IS" BASIS, | |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| * See the License for the specific language governing permissions and | |
| * limitations under the License. | |
| */ | |
| package com.android.settings.inputmethod; | |
| import android.app.ActivityManagerNative; | |
| import android.content.Context; | |
| import android.os.RemoteException; | |
| import android.util.Log; | |
| import android.util.Slog; | |
| import android.view.inputmethod.InputMethodInfo; | |
| import android.view.inputmethod.InputMethodManager; | |
| import android.view.inputmethod.InputMethodSubtype; | |
| import com.android.internal.inputmethod.InputMethodUtils; | |
| import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings; | |
| import java.util.ArrayList; | |
| import java.util.HashMap; | |
| import java.util.HashSet; | |
| import java.util.List; | |
| import java.util.Locale; | |
| /** | |
| * This class is a wrapper for InputMethodSettings. You need to refresh internal states | |
| * manually on some events when "InputMethodInfo"s and "InputMethodSubtype"s can be | |
| * changed. | |
| */ | |
| // TODO: Consolidate this with {@link InputMethodAndSubtypeUtil}. | |
| class InputMethodSettingValuesWrapper { | |
| private static final String TAG = InputMethodSettingValuesWrapper.class.getSimpleName(); | |
| private static volatile InputMethodSettingValuesWrapper sInstance; | |
| private final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>(); | |
| private final HashMap<String, InputMethodInfo> mMethodMap = new HashMap<>(); | |
| private final InputMethodSettings mSettings; | |
| private final InputMethodManager mImm; | |
| private final HashSet<InputMethodInfo> mAsciiCapableEnabledImis = new HashSet<>(); | |
| static InputMethodSettingValuesWrapper getInstance(Context context) { | |
| if (sInstance == null) { | |
| synchronized (TAG) { | |
| if (sInstance == null) { | |
| sInstance = new InputMethodSettingValuesWrapper(context); | |
| } | |
| } | |
| } | |
| return sInstance; | |
| } | |
| private static int getDefaultCurrentUserId() { | |
| try { | |
| return ActivityManagerNative.getDefault().getCurrentUser().id; | |
| } catch (RemoteException e) { | |
| Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); | |
| } | |
| return 0; | |
| } | |
| // Ensure singleton | |
| private InputMethodSettingValuesWrapper(Context context) { | |
| mSettings = new InputMethodSettings(context.getResources(), context.getContentResolver(), | |
| mMethodMap, mMethodList, getDefaultCurrentUserId(), false /* copyOnWrite */); | |
| mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); | |
| refreshAllInputMethodAndSubtypes(); | |
| } | |
| void refreshAllInputMethodAndSubtypes() { | |
| synchronized (mMethodMap) { | |
| mMethodList.clear(); | |
| mMethodMap.clear(); | |
| final List<InputMethodInfo> imms = mImm.getInputMethodList(); | |
| mMethodList.addAll(imms); | |
| for (InputMethodInfo imi : imms) { | |
| mMethodMap.put(imi.getId(), imi); | |
| } | |
| updateAsciiCapableEnabledImis(); | |
| } | |
| } | |
| // TODO: Add a cts to ensure at least one AsciiCapableSubtypeEnabledImis exist | |
| private void updateAsciiCapableEnabledImis() { | |
| synchronized (mMethodMap) { | |
| mAsciiCapableEnabledImis.clear(); | |
| final List<InputMethodInfo> enabledImis = mSettings.getEnabledInputMethodListLocked(); | |
| for (final InputMethodInfo imi : enabledImis) { | |
| final int subtypeCount = imi.getSubtypeCount(); | |
| for (int i = 0; i < subtypeCount; ++i) { | |
| final InputMethodSubtype subtype = imi.getSubtypeAt(i); | |
| if (InputMethodUtils.SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode()) | |
| && subtype.isAsciiCapable()) { | |
| mAsciiCapableEnabledImis.add(imi); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| List<InputMethodInfo> getInputMethodList() { | |
| synchronized (mMethodMap) { | |
| return mMethodList; | |
| } | |
| } | |
| CharSequence getCurrentInputMethodName(Context context) { | |
| synchronized (mMethodMap) { | |
| final InputMethodInfo imi = mMethodMap.get(mSettings.getSelectedInputMethod()); | |
| if (imi == null) { | |
| Log.w(TAG, "Invalid selected imi: " + mSettings.getSelectedInputMethod()); | |
| return ""; | |
| } | |
| final InputMethodSubtype subtype = mImm.getCurrentInputMethodSubtype(); | |
| return InputMethodUtils.getImeAndSubtypeDisplayName(context, imi, subtype); | |
| } | |
| } | |
| boolean isAlwaysCheckedIme(InputMethodInfo imi, Context context) { | |
| final boolean isEnabled = isEnabledImi(imi); | |
| synchronized (mMethodMap) { | |
| if (mSettings.getEnabledInputMethodListLocked().size() <= 1 && isEnabled) { | |
| return true; | |
| } | |
| } | |
| final int enabledValidSystemNonAuxAsciiCapableImeCount = | |
| getEnabledValidSystemNonAuxAsciiCapableImeCount(context); | |
| if (enabledValidSystemNonAuxAsciiCapableImeCount > 1) { | |
| return false; | |
| } | |
| if (enabledValidSystemNonAuxAsciiCapableImeCount == 1 && !isEnabled) { | |
| return false; | |
| } | |
| if (!InputMethodUtils.isSystemIme(imi)) { | |
| return false; | |
| } | |
| return isValidSystemNonAuxAsciiCapableIme(imi, context); | |
| } | |
| private int getEnabledValidSystemNonAuxAsciiCapableImeCount(Context context) { | |
| int count = 0; | |
| final List<InputMethodInfo> enabledImis; | |
| synchronized (mMethodMap) { | |
| enabledImis = mSettings.getEnabledInputMethodListLocked(); | |
| } | |
| for (final InputMethodInfo imi : enabledImis) { | |
| if (isValidSystemNonAuxAsciiCapableIme(imi, context)) { | |
| ++count; | |
| } | |
| } | |
| if (count == 0) { | |
| Log.w(TAG, "No \"enabledValidSystemNonAuxAsciiCapableIme\"s found."); | |
| } | |
| return count; | |
| } | |
| boolean isEnabledImi(InputMethodInfo imi) { | |
| final List<InputMethodInfo> enabledImis; | |
| synchronized (mMethodMap) { | |
| enabledImis = mSettings.getEnabledInputMethodListLocked(); | |
| } | |
| for (final InputMethodInfo tempImi : enabledImis) { | |
| if (tempImi.getId().equals(imi.getId())) { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi, Context context) { | |
| if (imi.isAuxiliaryIme()) { | |
| return false; | |
| } | |
| final Locale systemLocale = context.getResources().getConfiguration().locale; | |
| if (InputMethodUtils.isSystemImeThatHasSubtypeOf(imi, context, | |
| true /* checkDefaultAttribute */, systemLocale, false /* checkCountry */, | |
| InputMethodUtils.SUBTYPE_MODE_ANY)) { | |
| return true; | |
| } | |
| if (mAsciiCapableEnabledImis.isEmpty()) { | |
| Log.w(TAG, "ascii capable subtype enabled imi not found. Fall back to English" | |
| + " Keyboard subtype."); | |
| return InputMethodUtils.containsSubtypeOf(imi, Locale.ENGLISH, false /* checkCountry */, | |
| InputMethodUtils.SUBTYPE_MODE_KEYBOARD); | |
| } | |
| return mAsciiCapableEnabledImis.contains(imi); | |
| } | |
| } |