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