/* * 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); } }