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