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.server.inputmethod;
18 
19 import android.annotation.Nullable;
20 import android.content.Context;
21 import android.content.pm.PackageManager;
22 import android.text.TextUtils;
23 import android.util.ArraySet;
24 import android.util.Log;
25 import android.util.Printer;
26 import android.util.Slog;
27 import android.view.inputmethod.InputMethodInfo;
28 import android.view.inputmethod.InputMethodSubtype;
29 
30 import com.android.internal.annotations.VisibleForTesting;
31 import com.android.server.inputmethod.InputMethodUtils.InputMethodSettings;
32 
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.List;
36 import java.util.Locale;
37 import java.util.Objects;
38 
39 /**
40  * InputMethodSubtypeSwitchingController controls the switching behavior of the subtypes.
41  *
42  * <p>This class is designed to be used from and only from {@link InputMethodManagerService} by
43  * using {@link InputMethodManagerService#mMethodMap} as a global lock.</p>
44  */
45 final class InputMethodSubtypeSwitchingController {
46     private static final String TAG = InputMethodSubtypeSwitchingController.class.getSimpleName();
47     private static final boolean DEBUG = false;
48     private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
49 
50     public static class ImeSubtypeListItem implements Comparable<ImeSubtypeListItem> {
51         public final CharSequence mImeName;
52         public final CharSequence mSubtypeName;
53         public final InputMethodInfo mImi;
54         public final int mSubtypeId;
55         public final boolean mIsSystemLocale;
56         public final boolean mIsSystemLanguage;
57 
ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName, InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale)58         public ImeSubtypeListItem(CharSequence imeName, CharSequence subtypeName,
59                 InputMethodInfo imi, int subtypeId, String subtypeLocale, String systemLocale) {
60             mImeName = imeName;
61             mSubtypeName = subtypeName;
62             mImi = imi;
63             mSubtypeId = subtypeId;
64             if (TextUtils.isEmpty(subtypeLocale)) {
65                 mIsSystemLocale = false;
66                 mIsSystemLanguage = false;
67             } else {
68                 mIsSystemLocale = subtypeLocale.equals(systemLocale);
69                 if (mIsSystemLocale) {
70                     mIsSystemLanguage = true;
71                 } else {
72                     // TODO: Use Locale#getLanguage or Locale#toLanguageTag
73                     final String systemLanguage = parseLanguageFromLocaleString(systemLocale);
74                     final String subtypeLanguage = parseLanguageFromLocaleString(subtypeLocale);
75                     mIsSystemLanguage = systemLanguage.length() >= 2 &&
76                             systemLanguage.equals(subtypeLanguage);
77                 }
78             }
79         }
80 
81         /**
82          * Returns the language component of a given locale string.
83          * TODO: Use {@link Locale#getLanguage()} instead.
84          */
parseLanguageFromLocaleString(final String locale)85         private static String parseLanguageFromLocaleString(final String locale) {
86             final int idx = locale.indexOf('_');
87             if (idx < 0) {
88                 return locale;
89             } else {
90                 return locale.substring(0, idx);
91             }
92         }
93 
compareNullableCharSequences(@ullable CharSequence c1, @Nullable CharSequence c2)94         private static int compareNullableCharSequences(@Nullable CharSequence c1,
95                 @Nullable CharSequence c2) {
96             // For historical reasons, an empty text needs to put at the last.
97             final boolean empty1 = TextUtils.isEmpty(c1);
98             final boolean empty2 = TextUtils.isEmpty(c2);
99             if (empty1 || empty2) {
100                 return (empty1 ? 1 : 0) - (empty2 ? 1 : 0);
101             }
102             return c1.toString().compareTo(c2.toString());
103         }
104 
105         /**
106          * Compares this object with the specified object for order. The fields of this class will
107          * be compared in the following order.
108          * <ol>
109          *   <li>{@link #mImeName}</li>
110          *   <li>{@link #mIsSystemLocale}</li>
111          *   <li>{@link #mIsSystemLanguage}</li>
112          *   <li>{@link #mSubtypeName}</li>
113          *   <li>{@link #mImi} with {@link InputMethodInfo#getId()}</li>
114          * </ol>
115          * Note: this class has a natural ordering that is inconsistent with {@link #equals(Object).
116          * This method doesn't compare {@link #mSubtypeId} but {@link #equals(Object)} does.
117          *
118          * @param other the object to be compared.
119          * @return a negative integer, zero, or positive integer as this object is less than, equal
120          *         to, or greater than the specified <code>other</code> object.
121          */
122         @Override
compareTo(ImeSubtypeListItem other)123         public int compareTo(ImeSubtypeListItem other) {
124             int result = compareNullableCharSequences(mImeName, other.mImeName);
125             if (result != 0) {
126                 return result;
127             }
128             // Subtype that has the same locale of the system's has higher priority.
129             result = (mIsSystemLocale ? -1 : 0) - (other.mIsSystemLocale ? -1 : 0);
130             if (result != 0) {
131                 return result;
132             }
133             // Subtype that has the same language of the system's has higher priority.
134             result = (mIsSystemLanguage ? -1 : 0) - (other.mIsSystemLanguage ? -1 : 0);
135             if (result != 0) {
136                 return result;
137             }
138             result = compareNullableCharSequences(mSubtypeName, other.mSubtypeName);
139             if (result != 0) {
140                 return result;
141             }
142             return mImi.getId().compareTo(other.mImi.getId());
143         }
144 
145         @Override
toString()146         public String toString() {
147             return "ImeSubtypeListItem{"
148                     + "mImeName=" + mImeName
149                     + " mSubtypeName=" + mSubtypeName
150                     + " mSubtypeId=" + mSubtypeId
151                     + " mIsSystemLocale=" + mIsSystemLocale
152                     + " mIsSystemLanguage=" + mIsSystemLanguage
153                     + "}";
154         }
155 
156         @Override
equals(Object o)157         public boolean equals(Object o) {
158             if (o == this) {
159                 return true;
160             }
161             if (o instanceof ImeSubtypeListItem) {
162                 final ImeSubtypeListItem that = (ImeSubtypeListItem)o;
163                 return Objects.equals(this.mImi, that.mImi) && this.mSubtypeId == that.mSubtypeId;
164             }
165             return false;
166         }
167     }
168 
169     private static class InputMethodAndSubtypeList {
170         private final Context mContext;
171         // Used to load label
172         private final PackageManager mPm;
173         private final String mSystemLocaleStr;
174         private final InputMethodSettings mSettings;
175 
InputMethodAndSubtypeList(Context context, InputMethodSettings settings)176         public InputMethodAndSubtypeList(Context context, InputMethodSettings settings) {
177             mContext = context;
178             mSettings = settings;
179             mPm = context.getPackageManager();
180             final Locale locale = context.getResources().getConfiguration().locale;
181             mSystemLocaleStr = locale != null ? locale.toString() : "";
182         }
183 
getSortedInputMethodAndSubtypeList( boolean includeAuxiliarySubtypes, boolean isScreenLocked)184         public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeList(
185                 boolean includeAuxiliarySubtypes, boolean isScreenLocked) {
186             final ArrayList<InputMethodInfo> imis = mSettings.getEnabledInputMethodListLocked();
187             if (imis.isEmpty()) {
188                 return Collections.emptyList();
189             }
190             if (isScreenLocked && includeAuxiliarySubtypes) {
191                 if (DEBUG) {
192                     Slog.w(TAG, "Auxiliary subtypes are not allowed to be shown in lock screen.");
193                 }
194                 includeAuxiliarySubtypes = false;
195             }
196             final ArrayList<ImeSubtypeListItem> imList = new ArrayList<>();
197             final int numImes = imis.size();
198             for (int i = 0; i < numImes; ++i) {
199                 final InputMethodInfo imi = imis.get(i);
200                 final List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypeList =
201                         mSettings.getEnabledInputMethodSubtypeListLocked(mContext, imi, true);
202                 final ArraySet<String> enabledSubtypeSet = new ArraySet<>();
203                 for (InputMethodSubtype subtype : explicitlyOrImplicitlyEnabledSubtypeList) {
204                     enabledSubtypeSet.add(String.valueOf(subtype.hashCode()));
205                 }
206                 final CharSequence imeLabel = imi.loadLabel(mPm);
207                 if (enabledSubtypeSet.size() > 0) {
208                     final int subtypeCount = imi.getSubtypeCount();
209                     if (DEBUG) {
210                         Slog.v(TAG, "Add subtypes: " + subtypeCount + ", " + imi.getId());
211                     }
212                     for (int j = 0; j < subtypeCount; ++j) {
213                         final InputMethodSubtype subtype = imi.getSubtypeAt(j);
214                         final String subtypeHashCode = String.valueOf(subtype.hashCode());
215                         // We show all enabled IMEs and subtypes when an IME is shown.
216                         if (enabledSubtypeSet.contains(subtypeHashCode)
217                                 && (includeAuxiliarySubtypes || !subtype.isAuxiliary())) {
218                             final CharSequence subtypeLabel =
219                                     subtype.overridesImplicitlyEnabledSubtype() ? null : subtype
220                                             .getDisplayName(mContext, imi.getPackageName(),
221                                                     imi.getServiceInfo().applicationInfo);
222                             imList.add(new ImeSubtypeListItem(imeLabel,
223                                     subtypeLabel, imi, j, subtype.getLocale(), mSystemLocaleStr));
224 
225                             // Removing this subtype from enabledSubtypeSet because we no
226                             // longer need to add an entry of this subtype to imList to avoid
227                             // duplicated entries.
228                             enabledSubtypeSet.remove(subtypeHashCode);
229                         }
230                     }
231                 } else {
232                     imList.add(new ImeSubtypeListItem(imeLabel, null, imi, NOT_A_SUBTYPE_ID, null,
233                             mSystemLocaleStr));
234                 }
235             }
236             Collections.sort(imList);
237             return imList;
238         }
239     }
240 
calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype)241     private static int calculateSubtypeId(InputMethodInfo imi, InputMethodSubtype subtype) {
242         return subtype != null ? InputMethodUtils.getSubtypeIdFromHashCode(imi,
243                 subtype.hashCode()) : NOT_A_SUBTYPE_ID;
244     }
245 
246     private static class StaticRotationList {
247         private final List<ImeSubtypeListItem> mImeSubtypeList;
StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList)248         public StaticRotationList(final List<ImeSubtypeListItem> imeSubtypeList) {
249             mImeSubtypeList = imeSubtypeList;
250         }
251 
252         /**
253          * Returns the index of the specified input method and subtype in the given list.
254          * @param imi The {@link InputMethodInfo} to be searched.
255          * @param subtype The {@link InputMethodSubtype} to be searched. null if the input method
256          * does not have a subtype.
257          * @return The index in the given list. -1 if not found.
258          */
getIndex(InputMethodInfo imi, InputMethodSubtype subtype)259         private int getIndex(InputMethodInfo imi, InputMethodSubtype subtype) {
260             final int currentSubtypeId = calculateSubtypeId(imi, subtype);
261             final int N = mImeSubtypeList.size();
262             for (int i = 0; i < N; ++i) {
263                 final ImeSubtypeListItem isli = mImeSubtypeList.get(i);
264                 // Skip until the current IME/subtype is found.
265                 if (imi.equals(isli.mImi) && isli.mSubtypeId == currentSubtypeId) {
266                     return i;
267                 }
268             }
269             return -1;
270         }
271 
getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype)272         public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
273                 InputMethodInfo imi, InputMethodSubtype subtype) {
274             if (imi == null) {
275                 return null;
276             }
277             if (mImeSubtypeList.size() <= 1) {
278                 return null;
279             }
280             final int currentIndex = getIndex(imi, subtype);
281             if (currentIndex < 0) {
282                 return null;
283             }
284             final int N = mImeSubtypeList.size();
285             for (int offset = 1; offset < N; ++offset) {
286                 // Start searching the next IME/subtype from the next of the current index.
287                 final int candidateIndex = (currentIndex + offset) % N;
288                 final ImeSubtypeListItem candidate = mImeSubtypeList.get(candidateIndex);
289                 // Skip if searching inside the current IME only, but the candidate is not
290                 // the current IME.
291                 if (onlyCurrentIme && !imi.equals(candidate.mImi)) {
292                     continue;
293                 }
294                 return candidate;
295             }
296             return null;
297         }
298 
dump(final Printer pw, final String prefix)299         protected void dump(final Printer pw, final String prefix) {
300             final int N = mImeSubtypeList.size();
301             for (int i = 0; i < N; ++i) {
302                 final int rank = i;
303                 final ImeSubtypeListItem item = mImeSubtypeList.get(i);
304                 pw.println(prefix + "rank=" + rank + " item=" + item);
305             }
306         }
307     }
308 
309     private static class DynamicRotationList {
310         private static final String TAG = DynamicRotationList.class.getSimpleName();
311         private final List<ImeSubtypeListItem> mImeSubtypeList;
312         private final int[] mUsageHistoryOfSubtypeListItemIndex;
313 
DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems)314         private DynamicRotationList(final List<ImeSubtypeListItem> imeSubtypeListItems) {
315             mImeSubtypeList = imeSubtypeListItems;
316             mUsageHistoryOfSubtypeListItemIndex = new int[mImeSubtypeList.size()];
317             final int N = mImeSubtypeList.size();
318             for (int i = 0; i < N; i++) {
319                 mUsageHistoryOfSubtypeListItemIndex[i] = i;
320             }
321         }
322 
323         /**
324          * Returns the index of the specified object in
325          * {@link #mUsageHistoryOfSubtypeListItemIndex}.
326          * <p>We call the index of {@link #mUsageHistoryOfSubtypeListItemIndex} as "Usage Rank"
327          * so as not to be confused with the index in {@link #mImeSubtypeList}.
328          * @return -1 when the specified item doesn't belong to {@link #mImeSubtypeList} actually.
329          */
getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype)330         private int getUsageRank(final InputMethodInfo imi, InputMethodSubtype subtype) {
331             final int currentSubtypeId = calculateSubtypeId(imi, subtype);
332             final int N = mUsageHistoryOfSubtypeListItemIndex.length;
333             for (int usageRank = 0; usageRank < N; usageRank++) {
334                 final int subtypeListItemIndex = mUsageHistoryOfSubtypeListItemIndex[usageRank];
335                 final ImeSubtypeListItem subtypeListItem =
336                         mImeSubtypeList.get(subtypeListItemIndex);
337                 if (subtypeListItem.mImi.equals(imi) &&
338                         subtypeListItem.mSubtypeId == currentSubtypeId) {
339                     return usageRank;
340                 }
341             }
342             // Not found in the known IME/Subtype list.
343             return -1;
344         }
345 
onUserAction(InputMethodInfo imi, InputMethodSubtype subtype)346         public void onUserAction(InputMethodInfo imi, InputMethodSubtype subtype) {
347             final int currentUsageRank = getUsageRank(imi, subtype);
348             // Do nothing if currentUsageRank == -1 (not found), or currentUsageRank == 0
349             if (currentUsageRank <= 0) {
350                 return;
351             }
352             final int currentItemIndex = mUsageHistoryOfSubtypeListItemIndex[currentUsageRank];
353             System.arraycopy(mUsageHistoryOfSubtypeListItemIndex, 0,
354                     mUsageHistoryOfSubtypeListItemIndex, 1, currentUsageRank);
355             mUsageHistoryOfSubtypeListItemIndex[0] = currentItemIndex;
356         }
357 
getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype)358         public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme,
359                 InputMethodInfo imi, InputMethodSubtype subtype) {
360             int currentUsageRank = getUsageRank(imi, subtype);
361             if (currentUsageRank < 0) {
362                 if (DEBUG) {
363                     Slog.d(TAG, "IME/subtype is not found: " + imi.getId() + ", " + subtype);
364                 }
365                 return null;
366             }
367             final int N = mUsageHistoryOfSubtypeListItemIndex.length;
368             for (int i = 1; i < N; i++) {
369                 final int subtypeListItemRank = (currentUsageRank + i) % N;
370                 final int subtypeListItemIndex =
371                         mUsageHistoryOfSubtypeListItemIndex[subtypeListItemRank];
372                 final ImeSubtypeListItem subtypeListItem =
373                         mImeSubtypeList.get(subtypeListItemIndex);
374                 if (onlyCurrentIme && !imi.equals(subtypeListItem.mImi)) {
375                     continue;
376                 }
377                 return subtypeListItem;
378             }
379             return null;
380         }
381 
dump(final Printer pw, final String prefix)382         protected void dump(final Printer pw, final String prefix) {
383             for (int i = 0; i < mUsageHistoryOfSubtypeListItemIndex.length; ++i) {
384                 final int rank = mUsageHistoryOfSubtypeListItemIndex[i];
385                 final ImeSubtypeListItem item = mImeSubtypeList.get(i);
386                 pw.println(prefix + "rank=" + rank + " item=" + item);
387             }
388         }
389     }
390 
391     @VisibleForTesting
392     public static class ControllerImpl {
393         private final DynamicRotationList mSwitchingAwareRotationList;
394         private final StaticRotationList mSwitchingUnawareRotationList;
395 
createFrom(final ControllerImpl currentInstance, final List<ImeSubtypeListItem> sortedEnabledItems)396         public static ControllerImpl createFrom(final ControllerImpl currentInstance,
397                 final List<ImeSubtypeListItem> sortedEnabledItems) {
398             DynamicRotationList switchingAwareRotationList = null;
399             {
400                 final List<ImeSubtypeListItem> switchingAwareImeSubtypes =
401                         filterImeSubtypeList(sortedEnabledItems,
402                                 true /* supportsSwitchingToNextInputMethod */);
403                 if (currentInstance != null &&
404                         currentInstance.mSwitchingAwareRotationList != null &&
405                         Objects.equals(currentInstance.mSwitchingAwareRotationList.mImeSubtypeList,
406                                 switchingAwareImeSubtypes)) {
407                     // Can reuse the current instance.
408                     switchingAwareRotationList = currentInstance.mSwitchingAwareRotationList;
409                 }
410                 if (switchingAwareRotationList == null) {
411                     switchingAwareRotationList = new DynamicRotationList(switchingAwareImeSubtypes);
412                 }
413             }
414 
415             StaticRotationList switchingUnawareRotationList = null;
416             {
417                 final List<ImeSubtypeListItem> switchingUnawareImeSubtypes = filterImeSubtypeList(
418                         sortedEnabledItems, false /* supportsSwitchingToNextInputMethod */);
419                 if (currentInstance != null &&
420                         currentInstance.mSwitchingUnawareRotationList != null &&
421                         Objects.equals(
422                                 currentInstance.mSwitchingUnawareRotationList.mImeSubtypeList,
423                                 switchingUnawareImeSubtypes)) {
424                     // Can reuse the current instance.
425                     switchingUnawareRotationList = currentInstance.mSwitchingUnawareRotationList;
426                 }
427                 if (switchingUnawareRotationList == null) {
428                     switchingUnawareRotationList =
429                             new StaticRotationList(switchingUnawareImeSubtypes);
430                 }
431             }
432 
433             return new ControllerImpl(switchingAwareRotationList, switchingUnawareRotationList);
434         }
435 
ControllerImpl(final DynamicRotationList switchingAwareRotationList, final StaticRotationList switchingUnawareRotationList)436         private ControllerImpl(final DynamicRotationList switchingAwareRotationList,
437                 final StaticRotationList switchingUnawareRotationList) {
438             mSwitchingAwareRotationList = switchingAwareRotationList;
439             mSwitchingUnawareRotationList = switchingUnawareRotationList;
440         }
441 
getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype)442         public ImeSubtypeListItem getNextInputMethod(boolean onlyCurrentIme, InputMethodInfo imi,
443                 InputMethodSubtype subtype) {
444             if (imi == null) {
445                 return null;
446             }
447             if (imi.supportsSwitchingToNextInputMethod()) {
448                 return mSwitchingAwareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
449                         subtype);
450             } else {
451                 return mSwitchingUnawareRotationList.getNextInputMethodLocked(onlyCurrentIme, imi,
452                         subtype);
453             }
454         }
455 
onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype)456         public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
457             if (imi == null) {
458                 return;
459             }
460             if (imi.supportsSwitchingToNextInputMethod()) {
461                 mSwitchingAwareRotationList.onUserAction(imi, subtype);
462             }
463         }
464 
filterImeSubtypeList( final List<ImeSubtypeListItem> items, final boolean supportsSwitchingToNextInputMethod)465         private static List<ImeSubtypeListItem> filterImeSubtypeList(
466                 final List<ImeSubtypeListItem> items,
467                 final boolean supportsSwitchingToNextInputMethod) {
468             final ArrayList<ImeSubtypeListItem> result = new ArrayList<>();
469             final int ALL_ITEMS_COUNT = items.size();
470             for (int i = 0; i < ALL_ITEMS_COUNT; i++) {
471                 final ImeSubtypeListItem item = items.get(i);
472                 if (item.mImi.supportsSwitchingToNextInputMethod() ==
473                         supportsSwitchingToNextInputMethod) {
474                     result.add(item);
475                 }
476             }
477             return result;
478         }
479 
dump(final Printer pw)480         protected void dump(final Printer pw) {
481             pw.println("    mSwitchingAwareRotationList:");
482             mSwitchingAwareRotationList.dump(pw, "      ");
483             pw.println("    mSwitchingUnawareRotationList:");
484             mSwitchingUnawareRotationList.dump(pw, "      ");
485         }
486     }
487 
488     private final InputMethodSettings mSettings;
489     private InputMethodAndSubtypeList mSubtypeList;
490     private ControllerImpl mController;
491 
InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context)492     private InputMethodSubtypeSwitchingController(InputMethodSettings settings, Context context) {
493         mSettings = settings;
494         resetCircularListLocked(context);
495     }
496 
createInstanceLocked( InputMethodSettings settings, Context context)497     public static InputMethodSubtypeSwitchingController createInstanceLocked(
498             InputMethodSettings settings, Context context) {
499         return new InputMethodSubtypeSwitchingController(settings, context);
500     }
501 
onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype)502     public void onUserActionLocked(InputMethodInfo imi, InputMethodSubtype subtype) {
503         if (mController == null) {
504             if (DEBUG) {
505                 Log.e(TAG, "mController shouldn't be null.");
506             }
507             return;
508         }
509         mController.onUserActionLocked(imi, subtype);
510     }
511 
resetCircularListLocked(Context context)512     public void resetCircularListLocked(Context context) {
513         mSubtypeList = new InputMethodAndSubtypeList(context, mSettings);
514         mController = ControllerImpl.createFrom(mController,
515                 mSubtypeList.getSortedInputMethodAndSubtypeList(
516                         false /* includeAuxiliarySubtypes */, false /* isScreenLocked */));
517     }
518 
getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi, InputMethodSubtype subtype)519     public ImeSubtypeListItem getNextInputMethodLocked(boolean onlyCurrentIme, InputMethodInfo imi,
520             InputMethodSubtype subtype) {
521         if (mController == null) {
522             if (DEBUG) {
523                 Log.e(TAG, "mController shouldn't be null.");
524             }
525             return null;
526         }
527         return mController.getNextInputMethod(onlyCurrentIme, imi, subtype);
528     }
529 
getSortedInputMethodAndSubtypeListLocked( boolean includingAuxiliarySubtypes, boolean isScreenLocked)530     public List<ImeSubtypeListItem> getSortedInputMethodAndSubtypeListLocked(
531             boolean includingAuxiliarySubtypes, boolean isScreenLocked) {
532         return mSubtypeList.getSortedInputMethodAndSubtypeList(
533                 includingAuxiliarySubtypes, isScreenLocked);
534     }
535 
dump(final Printer pw)536     public void dump(final Printer pw) {
537         if (mController != null) {
538             mController.dump(pw);
539         } else {
540             pw.println("    mController=null");
541         }
542     }
543 }
544