1 /* 2 * Copyright (C) 2007-2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package android.view.inputmethod; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SuppressLint; 22 import android.annotation.TestApi; 23 import android.compat.annotation.UnsupportedAppUsage; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.pm.ActivityInfo; 28 import android.content.pm.ApplicationInfo; 29 import android.content.pm.PackageManager; 30 import android.content.pm.PackageManager.NameNotFoundException; 31 import android.content.pm.ResolveInfo; 32 import android.content.pm.ServiceInfo; 33 import android.content.res.Configuration; 34 import android.content.res.Resources; 35 import android.content.res.Resources.NotFoundException; 36 import android.content.res.TypedArray; 37 import android.content.res.XmlResourceParser; 38 import android.graphics.drawable.Drawable; 39 import android.icu.util.ULocale; 40 import android.inputmethodservice.InputMethodService; 41 import android.os.Parcel; 42 import android.os.Parcelable; 43 import android.text.TextUtils; 44 import android.util.AttributeSet; 45 import android.util.Printer; 46 import android.util.Slog; 47 import android.util.Xml; 48 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; 49 50 import org.xmlpull.v1.XmlPullParser; 51 import org.xmlpull.v1.XmlPullParserException; 52 53 import java.io.IOException; 54 import java.util.ArrayList; 55 import java.util.List; 56 57 /** 58 * This class is used to specify meta information of an input method. 59 * 60 * <p>It should be defined in an XML resource file with an {@code <input-method>} element. 61 * For more information, see the guide to 62 * <a href="{@docRoot}guide/topics/text/creating-input-method.html"> 63 * Creating an Input Method</a>.</p> 64 * 65 * @see InputMethodSubtype 66 * 67 * @attr ref android.R.styleable#InputMethod_settingsActivity 68 * @attr ref android.R.styleable#InputMethod_isDefault 69 * @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod 70 * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions 71 * @attr ref android.R.styleable#InputMethod_supportsInlineSuggestionsWithTouchExploration 72 * @attr ref android.R.styleable#InputMethod_suppressesSpellChecker 73 * @attr ref android.R.styleable#InputMethod_showInInputMethodPicker 74 * @attr ref android.R.styleable#InputMethod_configChanges 75 */ 76 public final class InputMethodInfo implements Parcelable { 77 78 /** 79 * {@link Intent#getAction() Intent action} for IME that 80 * {@link #supportsStylusHandwriting() supports stylus handwriting}. 81 * 82 * @see #createStylusHandwritingSettingsActivityIntent(). 83 */ 84 public static final String ACTION_STYLUS_HANDWRITING_SETTINGS = 85 "android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS"; 86 87 /** 88 * Maximal length of a component name 89 * @hide 90 */ 91 @TestApi 92 public static final int COMPONENT_NAME_MAX_LENGTH = 1000; 93 94 /** 95 * The maximum amount of IMEs that are loaded per package (in order). 96 * If a package contains more IMEs, they will be ignored and cannot be enabled. 97 * @hide 98 */ 99 @TestApi 100 @SuppressLint("MinMaxConstant") 101 public static final int MAX_IMES_PER_PACKAGE = 20; 102 103 static final String TAG = "InputMethodInfo"; 104 105 /** 106 * The Service that implements this input method component. 107 */ 108 final ResolveInfo mService; 109 110 /** 111 * IME only supports VR mode. 112 */ 113 final boolean mIsVrOnly; 114 115 /** 116 * The unique string Id to identify the input method. This is generated 117 * from the input method component. 118 */ 119 final String mId; 120 121 /** 122 * The input method setting activity's name, used by the system settings to 123 * launch the setting activity of this input method. 124 */ 125 final String mSettingsActivityName; 126 127 /** 128 * The resource in the input method's .apk that holds a boolean indicating 129 * whether it should be considered the default input method for this 130 * system. This is a resource ID instead of the final value so that it 131 * can change based on the configuration (in particular locale). 132 */ 133 final int mIsDefaultResId; 134 135 /** 136 * An array-like container of the subtypes. 137 */ 138 @UnsupportedAppUsage 139 private final InputMethodSubtypeArray mSubtypes; 140 141 private final boolean mIsAuxIme; 142 143 /** 144 * Caveat: mForceDefault must be false for production. This flag is only for test. 145 */ 146 private final boolean mForceDefault; 147 148 /** 149 * The flag whether this IME supports ways to switch to a next input method (e.g. globe key.) 150 */ 151 private final boolean mSupportsSwitchingToNextInputMethod; 152 153 /** 154 * The flag whether this IME supports inline suggestions. 155 */ 156 private final boolean mInlineSuggestionsEnabled; 157 158 /** 159 * The flag whether this IME supports inline suggestions when touch exploration is enabled. 160 */ 161 private final boolean mSupportsInlineSuggestionsWithTouchExploration; 162 163 /** 164 * The flag whether this IME suppresses spell checker. 165 */ 166 private final boolean mSuppressesSpellChecker; 167 168 /** 169 * The flag whether this IME should be shown as an option in the IME picker. 170 */ 171 private final boolean mShowInInputMethodPicker; 172 173 /** 174 * The flag for configurations IME assumes the responsibility for handling in 175 * {@link InputMethodService#onConfigurationChanged(Configuration)}}. 176 */ 177 private final int mHandledConfigChanges; 178 179 /** 180 * The flag whether this IME supports Handwriting using stylus input. 181 */ 182 private final boolean mSupportsStylusHandwriting; 183 184 /** 185 * The stylus handwriting setting activity's name, used by the system settings to 186 * launch the stylus handwriting specific setting activity of this input method. 187 */ 188 private final String mStylusHandwritingSettingsActivityAttr; 189 190 /** 191 * @param service the {@link ResolveInfo} corresponds in which the IME is implemented. 192 * @return a unique ID to be returned by {@link #getId()}. We have used 193 * {@link ComponentName#flattenToShortString()} for this purpose (and it is already 194 * unrealistic to switch to a different scheme as it is already implicitly assumed in 195 * many places). 196 * @hide 197 */ computeId(@onNull ResolveInfo service)198 public static String computeId(@NonNull ResolveInfo service) { 199 final ServiceInfo si = service.serviceInfo; 200 return new ComponentName(si.packageName, si.name).flattenToShortString(); 201 } 202 203 /** 204 * Constructor. 205 * 206 * @param context The Context in which we are parsing the input method. 207 * @param service The ResolveInfo returned from the package manager about 208 * this input method's component. 209 */ InputMethodInfo(Context context, ResolveInfo service)210 public InputMethodInfo(Context context, ResolveInfo service) 211 throws XmlPullParserException, IOException { 212 this(context, service, null); 213 } 214 215 /** 216 * Constructor. 217 * 218 * @param context The Context in which we are parsing the input method. 219 * @param service The ResolveInfo returned from the package manager about 220 * this input method's component. 221 * @param additionalSubtypes additional subtypes being added to this InputMethodInfo 222 * @hide 223 */ InputMethodInfo(Context context, ResolveInfo service, List<InputMethodSubtype> additionalSubtypes)224 public InputMethodInfo(Context context, ResolveInfo service, 225 List<InputMethodSubtype> additionalSubtypes) 226 throws XmlPullParserException, IOException { 227 mService = service; 228 ServiceInfo si = service.serviceInfo; 229 mId = computeId(service); 230 boolean isAuxIme = true; 231 boolean supportsSwitchingToNextInputMethod = false; // false as default 232 boolean inlineSuggestionsEnabled = false; // false as default 233 boolean supportsInlineSuggestionsWithTouchExploration = false; // false as default 234 boolean suppressesSpellChecker = false; // false as default 235 boolean showInInputMethodPicker = true; // true as default 236 mForceDefault = false; 237 238 PackageManager pm = context.getPackageManager(); 239 String settingsActivityComponent = null; 240 String stylusHandwritingSettingsActivity = null; 241 boolean isVrOnly; 242 int isDefaultResId = 0; 243 244 XmlResourceParser parser = null; 245 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 246 try { 247 parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA); 248 if (parser == null) { 249 throw new XmlPullParserException("No " 250 + InputMethod.SERVICE_META_DATA + " meta-data"); 251 } 252 253 Resources res = pm.getResourcesForApplication(si.applicationInfo); 254 255 AttributeSet attrs = Xml.asAttributeSet(parser); 256 257 int type; 258 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 259 && type != XmlPullParser.START_TAG) { 260 } 261 262 String nodeName = parser.getName(); 263 if (!"input-method".equals(nodeName)) { 264 throw new XmlPullParserException( 265 "Meta-data does not start with input-method tag"); 266 } 267 268 TypedArray sa = res.obtainAttributes(attrs, 269 com.android.internal.R.styleable.InputMethod); 270 settingsActivityComponent = sa.getString( 271 com.android.internal.R.styleable.InputMethod_settingsActivity); 272 if ((si.name != null && si.name.length() > COMPONENT_NAME_MAX_LENGTH) || ( 273 settingsActivityComponent != null 274 && settingsActivityComponent.length() > COMPONENT_NAME_MAX_LENGTH)) { 275 throw new XmlPullParserException( 276 "Activity name exceeds maximum of 1000 characters"); 277 } 278 279 isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false); 280 isDefaultResId = sa.getResourceId( 281 com.android.internal.R.styleable.InputMethod_isDefault, 0); 282 supportsSwitchingToNextInputMethod = sa.getBoolean( 283 com.android.internal.R.styleable.InputMethod_supportsSwitchingToNextInputMethod, 284 false); 285 inlineSuggestionsEnabled = sa.getBoolean( 286 com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false); 287 supportsInlineSuggestionsWithTouchExploration = sa.getBoolean( 288 com.android.internal.R.styleable 289 .InputMethod_supportsInlineSuggestionsWithTouchExploration, false); 290 suppressesSpellChecker = sa.getBoolean( 291 com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false); 292 showInInputMethodPicker = sa.getBoolean( 293 com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true); 294 mHandledConfigChanges = sa.getInt( 295 com.android.internal.R.styleable.InputMethod_configChanges, 0); 296 mSupportsStylusHandwriting = sa.getBoolean( 297 com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false); 298 stylusHandwritingSettingsActivity = sa.getString( 299 com.android.internal.R.styleable.InputMethod_stylusHandwritingSettingsActivity); 300 sa.recycle(); 301 302 final int depth = parser.getDepth(); 303 // Parse all subtypes 304 while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth) 305 && type != XmlPullParser.END_DOCUMENT) { 306 if (type == XmlPullParser.START_TAG) { 307 nodeName = parser.getName(); 308 if (!"subtype".equals(nodeName)) { 309 throw new XmlPullParserException( 310 "Meta-data in input-method does not start with subtype tag"); 311 } 312 final TypedArray a = res.obtainAttributes( 313 attrs, com.android.internal.R.styleable.InputMethod_Subtype); 314 String pkLanguageTag = a.getString(com.android.internal.R.styleable 315 .InputMethod_Subtype_physicalKeyboardHintLanguageTag); 316 String pkLayoutType = a.getString(com.android.internal.R.styleable 317 .InputMethod_Subtype_physicalKeyboardHintLayoutType); 318 final InputMethodSubtype subtype = new InputMethodSubtypeBuilder() 319 .setSubtypeNameResId(a.getResourceId(com.android.internal.R.styleable 320 .InputMethod_Subtype_label, 0)) 321 .setSubtypeIconResId(a.getResourceId(com.android.internal.R.styleable 322 .InputMethod_Subtype_icon, 0)) 323 .setPhysicalKeyboardHint( 324 pkLanguageTag == null ? null : new ULocale(pkLanguageTag), 325 pkLayoutType == null ? "" : pkLayoutType) 326 .setLanguageTag(a.getString(com.android.internal.R.styleable 327 .InputMethod_Subtype_languageTag)) 328 .setSubtypeLocale(a.getString(com.android.internal.R.styleable 329 .InputMethod_Subtype_imeSubtypeLocale)) 330 .setSubtypeMode(a.getString(com.android.internal.R.styleable 331 .InputMethod_Subtype_imeSubtypeMode)) 332 .setSubtypeExtraValue(a.getString(com.android.internal.R.styleable 333 .InputMethod_Subtype_imeSubtypeExtraValue)) 334 .setIsAuxiliary(a.getBoolean(com.android.internal.R.styleable 335 .InputMethod_Subtype_isAuxiliary, false)) 336 .setOverridesImplicitlyEnabledSubtype(a.getBoolean( 337 com.android.internal.R.styleable 338 .InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false)) 339 .setSubtypeId(a.getInt(com.android.internal.R.styleable 340 .InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */)) 341 .setIsAsciiCapable(a.getBoolean(com.android.internal.R.styleable 342 .InputMethod_Subtype_isAsciiCapable, false)).build(); 343 a.recycle(); 344 if (!subtype.isAuxiliary()) { 345 isAuxIme = false; 346 } 347 subtypes.add(subtype); 348 } 349 } 350 } catch (NameNotFoundException | IndexOutOfBoundsException | NumberFormatException e) { 351 throw new XmlPullParserException( 352 "Unable to create context for: " + si.packageName); 353 } finally { 354 if (parser != null) parser.close(); 355 } 356 357 if (subtypes.size() == 0) { 358 isAuxIme = false; 359 } 360 361 if (additionalSubtypes != null) { 362 final int N = additionalSubtypes.size(); 363 for (int i = 0; i < N; ++i) { 364 final InputMethodSubtype subtype = additionalSubtypes.get(i); 365 if (!subtypes.contains(subtype)) { 366 subtypes.add(subtype); 367 } else { 368 Slog.w(TAG, "Duplicated subtype definition found: " 369 + subtype.getLocale() + ", " + subtype.getMode()); 370 } 371 } 372 } 373 mSubtypes = new InputMethodSubtypeArray(subtypes); 374 mSettingsActivityName = settingsActivityComponent; 375 mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivity; 376 mIsDefaultResId = isDefaultResId; 377 mIsAuxIme = isAuxIme; 378 mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; 379 mInlineSuggestionsEnabled = inlineSuggestionsEnabled; 380 mSupportsInlineSuggestionsWithTouchExploration = 381 supportsInlineSuggestionsWithTouchExploration; 382 mSuppressesSpellChecker = suppressesSpellChecker; 383 mShowInInputMethodPicker = showInInputMethodPicker; 384 mIsVrOnly = isVrOnly; 385 } 386 387 /** 388 * @hide 389 */ InputMethodInfo(InputMethodInfo source)390 public InputMethodInfo(InputMethodInfo source) { 391 mId = source.mId; 392 mSettingsActivityName = source.mSettingsActivityName; 393 mIsDefaultResId = source.mIsDefaultResId; 394 mIsAuxIme = source.mIsAuxIme; 395 mSupportsSwitchingToNextInputMethod = source.mSupportsSwitchingToNextInputMethod; 396 mInlineSuggestionsEnabled = source.mInlineSuggestionsEnabled; 397 mSupportsInlineSuggestionsWithTouchExploration = 398 source.mSupportsInlineSuggestionsWithTouchExploration; 399 mSuppressesSpellChecker = source.mSuppressesSpellChecker; 400 mShowInInputMethodPicker = source.mShowInInputMethodPicker; 401 mIsVrOnly = source.mIsVrOnly; 402 mService = source.mService; 403 mSubtypes = source.mSubtypes; 404 mHandledConfigChanges = source.mHandledConfigChanges; 405 mSupportsStylusHandwriting = source.mSupportsStylusHandwriting; 406 mForceDefault = source.mForceDefault; 407 mStylusHandwritingSettingsActivityAttr = source.mStylusHandwritingSettingsActivityAttr; 408 } 409 InputMethodInfo(Parcel source)410 InputMethodInfo(Parcel source) { 411 mId = source.readString(); 412 mSettingsActivityName = source.readString(); 413 mIsDefaultResId = source.readInt(); 414 mIsAuxIme = source.readInt() == 1; 415 mSupportsSwitchingToNextInputMethod = source.readInt() == 1; 416 mInlineSuggestionsEnabled = source.readInt() == 1; 417 mSupportsInlineSuggestionsWithTouchExploration = source.readInt() == 1; 418 mSuppressesSpellChecker = source.readBoolean(); 419 mShowInInputMethodPicker = source.readBoolean(); 420 mIsVrOnly = source.readBoolean(); 421 mService = ResolveInfo.CREATOR.createFromParcel(source); 422 mSubtypes = new InputMethodSubtypeArray(source); 423 mHandledConfigChanges = source.readInt(); 424 mSupportsStylusHandwriting = source.readBoolean(); 425 mStylusHandwritingSettingsActivityAttr = source.readString8(); 426 mForceDefault = false; 427 } 428 429 /** 430 * Temporary API for creating a built-in input method for test. 431 */ InputMethodInfo(String packageName, String className, CharSequence label, String settingsActivity)432 public InputMethodInfo(String packageName, String className, 433 CharSequence label, String settingsActivity) { 434 this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, 435 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, 436 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, 437 false /* inlineSuggestionsEnabled */, false /* isVrOnly */, 438 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */, 439 null /* stylusHandwritingSettingsActivityAttr */, 440 false /* inlineSuggestionsEnabled */); 441 } 442 443 /** 444 * Test API for creating a built-in input method to verify stylus handwriting. 445 * @hide 446 */ 447 @TestApi InputMethodInfo(@onNull String packageName, @NonNull String className, @NonNull CharSequence label, @NonNull String settingsActivity, boolean supportStylusHandwriting, @NonNull String stylusHandwritingSettingsActivityAttr)448 public InputMethodInfo(@NonNull String packageName, @NonNull String className, 449 @NonNull CharSequence label, @NonNull String settingsActivity, 450 boolean supportStylusHandwriting, 451 @NonNull String stylusHandwritingSettingsActivityAttr) { 452 this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, 453 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, 454 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, 455 false /* inlineSuggestionsEnabled */, false /* isVrOnly */, 456 0 /* handledConfigChanges */, supportStylusHandwriting, 457 stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */); 458 } 459 460 /** 461 * Temporary API for creating a built-in input method for test. 462 * @hide 463 */ 464 @TestApi InputMethodInfo(@onNull String packageName, @NonNull String className, @NonNull CharSequence label, @NonNull String settingsActivity, int handledConfigChanges)465 public InputMethodInfo(@NonNull String packageName, @NonNull String className, 466 @NonNull CharSequence label, @NonNull String settingsActivity, 467 int handledConfigChanges) { 468 this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */, 469 settingsActivity, null /* subtypes */, 0 /* isDefaultResId */, 470 false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */, 471 false /* inlineSuggestionsEnabled */, false /* isVrOnly */, handledConfigChanges, 472 false /* supportsStylusHandwriting */, 473 null /* stylusHandwritingSettingsActivityAttr */, 474 false /* inlineSuggestionsEnabled */); 475 } 476 477 /** 478 * Temporary API for creating a built-in input method for test. 479 * @hide 480 */ InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault)481 public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, 482 String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, 483 boolean forceDefault) { 484 this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, 485 true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */, 486 false /* isVrOnly */, 0 /* handledconfigChanges */, 487 false /* supportsStylusHandwriting */, 488 null /* stylusHandwritingSettingsActivityAttr */, 489 false /* inlineSuggestionsEnabled */); 490 } 491 492 /** 493 * Temporary API for creating a built-in input method for test. 494 * @hide 495 */ InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean isVrOnly)496 public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, 497 List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, 498 boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) { 499 this(ri, isAuxIme, settingsActivity, subtypes, isDefaultResId, forceDefault, 500 supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly, 501 0 /* handledConfigChanges */, false /* supportsStylusHandwriting */, 502 null /* stylusHandwritingSettingsActivityAttr */, 503 false /* inlineSuggestionsEnabled */); 504 } 505 506 /** 507 * Temporary API for creating a built-in input method for test. 508 * @hide 509 */ InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled, boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting, String stylusHandwritingSettingsActivityAttr, boolean supportsInlineSuggestionsWithTouchExploration)510 public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity, 511 List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault, 512 boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled, 513 boolean isVrOnly, int handledConfigChanges, boolean supportsStylusHandwriting, 514 String stylusHandwritingSettingsActivityAttr, 515 boolean supportsInlineSuggestionsWithTouchExploration) { 516 final ServiceInfo si = ri.serviceInfo; 517 mService = ri; 518 mId = new ComponentName(si.packageName, si.name).flattenToShortString(); 519 mSettingsActivityName = settingsActivity; 520 mIsDefaultResId = isDefaultResId; 521 mIsAuxIme = isAuxIme; 522 mSubtypes = new InputMethodSubtypeArray(subtypes); 523 mForceDefault = forceDefault; 524 mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod; 525 mInlineSuggestionsEnabled = inlineSuggestionsEnabled; 526 mSupportsInlineSuggestionsWithTouchExploration = 527 supportsInlineSuggestionsWithTouchExploration; 528 mSuppressesSpellChecker = false; 529 mShowInInputMethodPicker = true; 530 mIsVrOnly = isVrOnly; 531 mHandledConfigChanges = handledConfigChanges; 532 mSupportsStylusHandwriting = supportsStylusHandwriting; 533 mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivityAttr; 534 } 535 buildFakeResolveInfo(String packageName, String className, CharSequence label)536 private static ResolveInfo buildFakeResolveInfo(String packageName, String className, 537 CharSequence label) { 538 ResolveInfo ri = new ResolveInfo(); 539 ServiceInfo si = new ServiceInfo(); 540 ApplicationInfo ai = new ApplicationInfo(); 541 ai.packageName = packageName; 542 ai.enabled = true; 543 si.applicationInfo = ai; 544 si.enabled = true; 545 si.packageName = packageName; 546 si.name = className; 547 si.exported = true; 548 si.nonLocalizedLabel = label; 549 ri.serviceInfo = si; 550 return ri; 551 } 552 553 /** 554 * @return a unique ID for this input method, which is guaranteed to be the same as the result 555 * of {@code getComponent().flattenToShortString()}. 556 * @see ComponentName#unflattenFromString(String) 557 */ getId()558 public String getId() { 559 return mId; 560 } 561 562 /** 563 * Return the .apk package that implements this input method. 564 */ getPackageName()565 public String getPackageName() { 566 return mService.serviceInfo.packageName; 567 } 568 569 /** 570 * Return the class name of the service component that implements 571 * this input method. 572 */ getServiceName()573 public String getServiceName() { 574 return mService.serviceInfo.name; 575 } 576 577 /** 578 * Return the raw information about the Service implementing this 579 * input method. Do not modify the returned object. 580 */ getServiceInfo()581 public ServiceInfo getServiceInfo() { 582 return mService.serviceInfo; 583 } 584 585 /** 586 * Return the component of the service that implements this input 587 * method. 588 */ getComponent()589 public ComponentName getComponent() { 590 return new ComponentName(mService.serviceInfo.packageName, 591 mService.serviceInfo.name); 592 } 593 594 /** 595 * Load the user-displayed label for this input method. 596 * 597 * @param pm Supply a PackageManager used to load the input method's 598 * resources. 599 */ loadLabel(PackageManager pm)600 public CharSequence loadLabel(PackageManager pm) { 601 return mService.loadLabel(pm); 602 } 603 604 /** 605 * Load the user-displayed icon for this input method. 606 * 607 * @param pm Supply a PackageManager used to load the input method's 608 * resources. 609 */ loadIcon(PackageManager pm)610 public Drawable loadIcon(PackageManager pm) { 611 return mService.loadIcon(pm); 612 } 613 614 /** 615 * Return the class name of an activity that provides a settings UI for 616 * the input method. You can launch this activity be starting it with 617 * an {@link android.content.Intent} whose action is MAIN and with an 618 * explicit {@link android.content.ComponentName} 619 * composed of {@link #getPackageName} and the class name returned here. 620 * 621 * <p>A null will be returned if there is no settings activity associated 622 * with the input method.</p> 623 * @see #createStylusHandwritingSettingsActivityIntent() 624 */ getSettingsActivity()625 public String getSettingsActivity() { 626 return mSettingsActivityName; 627 } 628 629 /** 630 * Returns true if IME supports VR mode only. 631 * @hide 632 */ isVrOnly()633 public boolean isVrOnly() { 634 return mIsVrOnly; 635 } 636 637 /** 638 * Return the count of the subtypes of Input Method. 639 */ getSubtypeCount()640 public int getSubtypeCount() { 641 return mSubtypes.getCount(); 642 } 643 644 /** 645 * Return the Input Method's subtype at the specified index. 646 * 647 * @param index the index of the subtype to return. 648 */ getSubtypeAt(int index)649 public InputMethodSubtype getSubtypeAt(int index) { 650 return mSubtypes.get(index); 651 } 652 653 /** 654 * Return the resource identifier of a resource inside of this input 655 * method's .apk that determines whether it should be considered a 656 * default input method for the system. 657 */ getIsDefaultResourceId()658 public int getIsDefaultResourceId() { 659 return mIsDefaultResId; 660 } 661 662 /** 663 * Return whether or not this ime is a default ime or not. 664 * @hide 665 */ 666 @UnsupportedAppUsage isDefault(Context context)667 public boolean isDefault(Context context) { 668 if (mForceDefault) { 669 return true; 670 } 671 try { 672 if (getIsDefaultResourceId() == 0) { 673 return false; 674 } 675 final Resources res = context.createPackageContext(getPackageName(), 0).getResources(); 676 return res.getBoolean(getIsDefaultResourceId()); 677 } catch (NameNotFoundException | NotFoundException e) { 678 return false; 679 } 680 } 681 682 /** 683 * Returns the bit mask of kinds of configuration changes that this IME 684 * can handle itself (without being restarted by the system). 685 * 686 * @attr ref android.R.styleable#InputMethod_configChanges 687 */ 688 @ActivityInfo.Config getConfigChanges()689 public int getConfigChanges() { 690 return mHandledConfigChanges; 691 } 692 693 /** 694 * Returns if IME supports handwriting using stylus input. 695 * @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting 696 * @see #createStylusHandwritingSettingsActivityIntent() 697 */ supportsStylusHandwriting()698 public boolean supportsStylusHandwriting() { 699 return mSupportsStylusHandwriting; 700 } 701 702 /** 703 * Returns {@link Intent} for stylus handwriting settings activity with 704 * {@link Intent#getAction() Intent action} {@link #ACTION_STYLUS_HANDWRITING_SETTINGS} 705 * if IME {@link #supportsStylusHandwriting() supports stylus handwriting}, else 706 * <code>null</code> if there are no associated settings for stylus handwriting / handwriting 707 * is not supported or if 708 * {@link android.R.styleable#InputMethod_stylusHandwritingSettingsActivity} is not defined. 709 * 710 * <p>To launch stylus settings, use this method to get the {@link android.content.Intent} to 711 * launch the stylus handwriting settings activity.</p> 712 * <p>e.g.<pre><code>startActivity(createStylusHandwritingSettingsActivityIntent());</code> 713 * </pre></p> 714 * 715 * @attr ref R.styleable#InputMethod_stylusHandwritingSettingsActivity 716 * @see #getSettingsActivity() 717 * @see #supportsStylusHandwriting() 718 */ 719 @Nullable createStylusHandwritingSettingsActivityIntent()720 public Intent createStylusHandwritingSettingsActivityIntent() { 721 if (TextUtils.isEmpty(mStylusHandwritingSettingsActivityAttr) 722 || !mSupportsStylusHandwriting) { 723 return null; 724 } 725 // TODO(b/210039666): consider returning null if component is not enabled. 726 return new Intent(ACTION_STYLUS_HANDWRITING_SETTINGS).setComponent( 727 new ComponentName(getServiceInfo().packageName, 728 mStylusHandwritingSettingsActivityAttr)); 729 } 730 dump(Printer pw, String prefix)731 public void dump(Printer pw, String prefix) { 732 pw.println(prefix + "mId=" + mId 733 + " mSettingsActivityName=" + mSettingsActivityName 734 + " mIsVrOnly=" + mIsVrOnly 735 + " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod 736 + " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled 737 + " mSupportsInlineSuggestionsWithTouchExploration=" 738 + mSupportsInlineSuggestionsWithTouchExploration 739 + " mSuppressesSpellChecker=" + mSuppressesSpellChecker 740 + " mShowInInputMethodPicker=" + mShowInInputMethodPicker 741 + " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting 742 + " mStylusHandwritingSettingsActivityAttr=" 743 + mStylusHandwritingSettingsActivityAttr); 744 pw.println(prefix + "mIsDefaultResId=0x" 745 + Integer.toHexString(mIsDefaultResId)); 746 pw.println(prefix + "Service:"); 747 mService.dump(pw, prefix + " "); 748 } 749 750 @Override toString()751 public String toString() { 752 return "InputMethodInfo{" + mId 753 + ", settings: " 754 + mSettingsActivityName + "}"; 755 } 756 757 /** 758 * Used to test whether the given parameter object is an 759 * {@link InputMethodInfo} and its Id is the same to this one. 760 * 761 * @return true if the given parameter object is an 762 * {@link InputMethodInfo} and its Id is the same to this one. 763 */ 764 @Override equals(@ullable Object o)765 public boolean equals(@Nullable Object o) { 766 if (o == this) return true; 767 if (o == null) return false; 768 769 if (!(o instanceof InputMethodInfo)) return false; 770 771 InputMethodInfo obj = (InputMethodInfo) o; 772 return mId.equals(obj.mId); 773 } 774 775 @Override hashCode()776 public int hashCode() { 777 return mId.hashCode(); 778 } 779 780 /** 781 * @hide 782 * @return {@code true} if the IME is a trusted system component (e.g. pre-installed) 783 */ isSystem()784 public boolean isSystem() { 785 return (mService.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0; 786 } 787 788 /** 789 * @hide 790 */ isAuxiliaryIme()791 public boolean isAuxiliaryIme() { 792 return mIsAuxIme; 793 } 794 795 /** 796 * @return true if this input method supports ways to switch to a next input method. 797 * @hide 798 */ supportsSwitchingToNextInputMethod()799 public boolean supportsSwitchingToNextInputMethod() { 800 return mSupportsSwitchingToNextInputMethod; 801 } 802 803 /** 804 * @return true if this input method supports inline suggestions. 805 * @hide 806 */ isInlineSuggestionsEnabled()807 public boolean isInlineSuggestionsEnabled() { 808 return mInlineSuggestionsEnabled; 809 } 810 811 /** 812 * Returns {@code true} if this input method supports inline suggestions when touch exploration 813 * is enabled. 814 * @hide 815 */ supportsInlineSuggestionsWithTouchExploration()816 public boolean supportsInlineSuggestionsWithTouchExploration() { 817 return mSupportsInlineSuggestionsWithTouchExploration; 818 } 819 820 /** 821 * Return {@code true} if this input method suppresses spell checker. 822 */ suppressesSpellChecker()823 public boolean suppressesSpellChecker() { 824 return mSuppressesSpellChecker; 825 } 826 827 /** 828 * Returns {@code true} if this input method should be shown in menus for selecting an Input 829 * Method, such as the system Input Method Picker. This is {@code false} if the IME is intended 830 * to be accessed programmatically. 831 */ shouldShowInInputMethodPicker()832 public boolean shouldShowInInputMethodPicker() { 833 return mShowInInputMethodPicker; 834 } 835 836 /** 837 * Used to package this object into a {@link Parcel}. 838 * 839 * @param dest The {@link Parcel} to be written. 840 * @param flags The flags used for parceling. 841 */ 842 @Override writeToParcel(Parcel dest, int flags)843 public void writeToParcel(Parcel dest, int flags) { 844 dest.writeString(mId); 845 dest.writeString(mSettingsActivityName); 846 dest.writeInt(mIsDefaultResId); 847 dest.writeInt(mIsAuxIme ? 1 : 0); 848 dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0); 849 dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0); 850 dest.writeInt(mSupportsInlineSuggestionsWithTouchExploration ? 1 : 0); 851 dest.writeBoolean(mSuppressesSpellChecker); 852 dest.writeBoolean(mShowInInputMethodPicker); 853 dest.writeBoolean(mIsVrOnly); 854 mService.writeToParcel(dest, flags); 855 mSubtypes.writeToParcel(dest); 856 dest.writeInt(mHandledConfigChanges); 857 dest.writeBoolean(mSupportsStylusHandwriting); 858 dest.writeString8(mStylusHandwritingSettingsActivityAttr); 859 } 860 861 /** 862 * Used to make this class parcelable. 863 */ 864 public static final @android.annotation.NonNull Parcelable.Creator<InputMethodInfo> CREATOR 865 = new Parcelable.Creator<InputMethodInfo>() { 866 @Override 867 public InputMethodInfo createFromParcel(Parcel source) { 868 return new InputMethodInfo(source); 869 } 870 871 @Override 872 public InputMethodInfo[] newArray(int size) { 873 return new InputMethodInfo[size]; 874 } 875 }; 876 877 @Override describeContents()878 public int describeContents() { 879 return 0; 880 } 881 } 882