• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 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.internal.inputmethod.InputMethodUtils;
20 import com.android.internal.inputmethod.InputMethodUtils.InputMethodSettings;
21 
22 import android.app.ActivityManagerNative;
23 import android.content.Context;
24 import android.os.RemoteException;
25 import android.util.Log;
26 import android.util.Slog;
27 import android.view.inputmethod.InputMethodInfo;
28 import android.view.inputmethod.InputMethodManager;
29 import android.view.inputmethod.InputMethodSubtype;
30 
31 import java.util.ArrayList;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.List;
35 import java.util.Locale;
36 
37 /**
38  * This class is a wrapper for InputMethodSettings. You need to refresh internal states
39  * manually on some events when "InputMethodInfo"s and "InputMethodSubtype"s can be
40  * changed.
41  */
42 public class InputMethodSettingValuesWrapper {
43     private static final String TAG = InputMethodSettingValuesWrapper.class.getSimpleName();
44     private static final Locale ENGLISH_LOCALE = new Locale("en");
45 
46     private static volatile InputMethodSettingValuesWrapper sInstance;
47     private final ArrayList<InputMethodInfo> mMethodList = new ArrayList<InputMethodInfo>();
48     private final HashMap<String, InputMethodInfo> mMethodMap =
49             new HashMap<String, InputMethodInfo>();
50     private final InputMethodSettings mSettings;
51     private final InputMethodManager mImm;
52     private final HashSet<InputMethodInfo> mAsciiCapableEnabledImis =
53             new HashSet<InputMethodInfo>();
54 
getInstance(Context context)55     public static InputMethodSettingValuesWrapper getInstance(Context context) {
56         if (sInstance == null) {
57             synchronized(TAG) {
58                 if (sInstance == null) {
59                     sInstance = new InputMethodSettingValuesWrapper(context);
60                 }
61             }
62         }
63         return sInstance;
64     }
65 
getDefaultCurrentUserId()66     private static int getDefaultCurrentUserId() {
67         try {
68             return ActivityManagerNative.getDefault().getCurrentUser().id;
69         } catch (RemoteException e) {
70             Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e);
71         }
72         return 0;
73     }
74 
75     // Ensure singleton
InputMethodSettingValuesWrapper(Context context)76     private InputMethodSettingValuesWrapper(Context context) {
77         mSettings =
78                 new InputMethodSettings(context.getResources(), context.getContentResolver(),
79                         mMethodMap, mMethodList, getDefaultCurrentUserId());
80         mImm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
81         refreshAllInputMethodAndSubtypes();
82     }
83 
refreshAllInputMethodAndSubtypes()84     public void refreshAllInputMethodAndSubtypes() {
85         synchronized (mMethodMap) {
86             mMethodList.clear();
87             mMethodMap.clear();
88             final List<InputMethodInfo> imms = mImm.getInputMethodList();
89             mMethodList.addAll(imms);
90             for (InputMethodInfo imi : imms) {
91                 mMethodMap.put(imi.getId(), imi);
92             }
93             updateAsciiCapableEnabledImis();
94         }
95     }
96 
97     // TODO: Add a cts to ensure at least one AsciiCapableSubtypeEnabledImis exist
updateAsciiCapableEnabledImis()98     private void updateAsciiCapableEnabledImis() {
99         synchronized (mMethodMap) {
100             mAsciiCapableEnabledImis.clear();
101             final List<InputMethodInfo> enabledImis = mSettings.getEnabledInputMethodListLocked();
102             for (final InputMethodInfo imi : enabledImis) {
103                 final int subtypeCount = imi.getSubtypeCount();
104                 for (int i = 0; i < subtypeCount; ++i) {
105                     final InputMethodSubtype subtype = imi.getSubtypeAt(i);
106                     if (InputMethodUtils.SUBTYPE_MODE_KEYBOARD.equalsIgnoreCase(subtype.getMode())
107                             && subtype.isAsciiCapable()) {
108                         mAsciiCapableEnabledImis.add(imi);
109                         break;
110                     }
111                 }
112             }
113         }
114     }
115 
getInputMethodList()116     public List<InputMethodInfo> getInputMethodList() {
117         synchronized (mMethodMap) {
118             return mMethodList;
119         }
120     }
121 
getCurrentInputMethodName(Context context)122     public CharSequence getCurrentInputMethodName(Context context) {
123         synchronized (mMethodMap) {
124             final InputMethodInfo imi = mMethodMap.get(mSettings.getSelectedInputMethod());
125             if (imi == null) {
126                 Log.w(TAG, "Invalid selected imi: " + mSettings.getSelectedInputMethod());
127                 return "";
128             }
129             final InputMethodSubtype subtype = mImm.getCurrentInputMethodSubtype();
130             return InputMethodUtils.getImeAndSubtypeDisplayName(context, imi, subtype);
131         }
132     }
133 
isAlwaysCheckedIme(InputMethodInfo imi, Context context)134     public boolean isAlwaysCheckedIme(InputMethodInfo imi, Context context) {
135         final boolean isEnabled = isEnabledImi(imi);
136         synchronized (mMethodMap) {
137             if (mSettings.getEnabledInputMethodListLocked().size() <= 1 && isEnabled) {
138                 return true;
139             }
140         }
141 
142         final int enabledValidSystemNonAuxAsciiCapableImeCount =
143                 getEnabledValidSystemNonAuxAsciiCapableImeCount(context);
144         if (enabledValidSystemNonAuxAsciiCapableImeCount > 1) {
145             return false;
146         }
147 
148         if (enabledValidSystemNonAuxAsciiCapableImeCount == 1 && !isEnabled) {
149             return false;
150         }
151 
152         if (!InputMethodUtils.isSystemIme(imi)) {
153             return false;
154         }
155         return isValidSystemNonAuxAsciiCapableIme(imi, context);
156     }
157 
getEnabledValidSystemNonAuxAsciiCapableImeCount(Context context)158     private int getEnabledValidSystemNonAuxAsciiCapableImeCount(Context context) {
159         int count = 0;
160         final List<InputMethodInfo> enabledImis;
161         synchronized(mMethodMap) {
162             enabledImis = mSettings.getEnabledInputMethodListLocked();
163         }
164         for (final InputMethodInfo imi : enabledImis) {
165             if (isValidSystemNonAuxAsciiCapableIme(imi, context)) {
166                 ++count;
167             }
168         }
169         if (count == 0) {
170             Log.w(TAG, "No \"enabledValidSystemNonAuxAsciiCapableIme\"s found.");
171         }
172         return count;
173     }
174 
isEnabledImi(InputMethodInfo imi)175     private boolean isEnabledImi(InputMethodInfo imi) {
176         final List<InputMethodInfo> enabledImis;
177         synchronized(mMethodMap) {
178             enabledImis = mSettings.getEnabledInputMethodListLocked();
179         }
180         for (final InputMethodInfo tempImi : enabledImis) {
181             if (tempImi.getId().equals(imi.getId())) {
182                 return true;
183             }
184         }
185         return false;
186     }
187 
isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi, Context context)188     public boolean isValidSystemNonAuxAsciiCapableIme(InputMethodInfo imi,
189             Context context) {
190         if (imi.isAuxiliaryIme()) {
191             return false;
192         }
193         if (InputMethodUtils.isValidSystemDefaultIme(true /* isSystemReady */, imi, context)) {
194             return true;
195         }
196         if (mAsciiCapableEnabledImis.isEmpty()) {
197             Log.w(TAG, "ascii capable subtype enabled imi not found. Fall back to English"
198                     + " Keyboard subtype.");
199             return InputMethodUtils.containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(),
200                     InputMethodUtils.SUBTYPE_MODE_KEYBOARD);
201         }
202         return mAsciiCapableEnabledImis.contains(imi);
203     }
204 }
205