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