script-astra/Android/Sdk/sources/android-35/android/view/inputmethod/InputMethodInfo.java

1064 lines
44 KiB
Java
Raw Permalink Normal View History

2025-01-20 15:15:20 +00:00
/*
* Copyright (C) 2007-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package android.view.inputmethod;
import android.annotation.FlaggedApi;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.Resources.NotFoundException;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.icu.util.ULocale;
import android.inputmethodservice.InputMethodService;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.Printer;
import android.util.Slog;
import android.util.Xml;
import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* This class is used to specify meta information of an input method.
*
* <p>It should be defined in an XML resource file with an {@code <input-method>} element.
* For more information, see the guide to
* <a href="{@docRoot}guide/topics/text/creating-input-method.html">
* Creating an Input Method</a>.</p>
*
* @see InputMethodSubtype
*
* @attr ref android.R.styleable#InputMethod_settingsActivity
* @attr ref android.R.styleable#InputMethod_isDefault
* @attr ref android.R.styleable#InputMethod_supportsSwitchingToNextInputMethod
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestions
* @attr ref android.R.styleable#InputMethod_supportsInlineSuggestionsWithTouchExploration
* @attr ref android.R.styleable#InputMethod_suppressesSpellChecker
* @attr ref android.R.styleable#InputMethod_showInInputMethodPicker
* @attr ref android.R.styleable#InputMethod_configChanges
*/
public final class InputMethodInfo implements Parcelable {
/**
* {@link Intent#getAction() Intent action} for IME that
* {@link #supportsStylusHandwriting() supports stylus handwriting}.
*
* @see #createStylusHandwritingSettingsActivityIntent()
*/
public static final String ACTION_STYLUS_HANDWRITING_SETTINGS =
"android.view.inputmethod.action.STYLUS_HANDWRITING_SETTINGS";
/**
* {@link Intent#getAction() Intent action} for the IME language settings.
*
* @see #createImeLanguageSettingsActivityIntent()
*/
@FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP)
public static final String ACTION_IME_LANGUAGE_SETTINGS =
"android.view.inputmethod.action.IME_LANGUAGE_SETTINGS";
/**
* Maximal length of a component name
* @hide
*/
@TestApi
public static final int COMPONENT_NAME_MAX_LENGTH = 1000;
/**
* The maximum amount of IMEs that are loaded per package (in order).
* If a package contains more IMEs, they will be ignored and cannot be enabled.
* @hide
*/
@TestApi
@SuppressLint("MinMaxConstant")
public static final int MAX_IMES_PER_PACKAGE = 20;
static final String TAG = "InputMethodInfo";
/**
* The Service that implements this input method component.
*/
final ResolveInfo mService;
/**
* IME only supports VR mode.
*/
final boolean mIsVrOnly;
/**
* IME only supports virtual devices.
*/
final boolean mIsVirtualDeviceOnly;
/**
* The unique string Id to identify the input method. This is generated
* from the input method component.
*/
final String mId;
/**
* The input method setting activity's name, used by the system settings to
* launch the setting activity of this input method.
*/
final String mSettingsActivityName;
/**
* The input method language settings activity's name, used to
* launch the language settings activity of this input method.
*/
@Nullable
private final String mLanguageSettingsActivityName;
/**
* The resource in the input method's .apk that holds a boolean indicating
* whether it should be considered the default input method for this
* system. This is a resource ID instead of the final value so that it
* can change based on the configuration (in particular locale).
*/
final int mIsDefaultResId;
/**
* An array-like container of the subtypes.
*/
@UnsupportedAppUsage
private final InputMethodSubtypeArray mSubtypes;
private final boolean mIsAuxIme;
/**
* Caveat: mForceDefault must be false for production. This flag is only for test.
*/
private final boolean mForceDefault;
/**
* The flag whether this IME supports ways to switch to a next input method (e.g. globe key.)
*/
private final boolean mSupportsSwitchingToNextInputMethod;
/**
* The flag whether this IME supports inline suggestions.
*/
private final boolean mInlineSuggestionsEnabled;
/**
* The flag whether this IME supports inline suggestions when touch exploration is enabled.
*/
private final boolean mSupportsInlineSuggestionsWithTouchExploration;
/**
* The flag whether this IME suppresses spell checker.
*/
private final boolean mSuppressesSpellChecker;
/**
* The flag whether this IME should be shown as an option in the IME picker.
*/
private final boolean mShowInInputMethodPicker;
/**
* The flag for configurations IME assumes the responsibility for handling in
* {@link InputMethodService#onConfigurationChanged(Configuration)}}.
*/
private final int mHandledConfigChanges;
/**
* The flag whether this IME supports Handwriting using stylus input.
*/
private final boolean mSupportsStylusHandwriting;
/** The flag whether this IME supports connectionless stylus handwriting sessions. */
private final boolean mSupportsConnectionlessStylusHandwriting;
/**
* The stylus handwriting setting activity's name, used by the system settings to
* launch the stylus handwriting specific setting activity of this input method.
*/
private final String mStylusHandwritingSettingsActivityAttr;
/**
* @param service the {@link ResolveInfo} corresponds in which the IME is implemented.
* @return a unique ID to be returned by {@link #getId()}. We have used
* {@link ComponentName#flattenToShortString()} for this purpose (and it is already
* unrealistic to switch to a different scheme as it is already implicitly assumed in
* many places).
* @hide
*/
public static String computeId(@NonNull ResolveInfo service) {
final ServiceInfo si = service.serviceInfo;
return new ComponentName(si.packageName, si.name).flattenToShortString();
}
/**
* Constructor.
*
* @param context The Context in which we are parsing the input method.
* @param service The ResolveInfo returned from the package manager about
* this input method's component.
*/
public InputMethodInfo(Context context, ResolveInfo service)
throws XmlPullParserException, IOException {
this(context, service, null);
}
/**
* Constructor.
*
* @param context The Context in which we are parsing the input method.
* @param service The ResolveInfo returned from the package manager about
* this input method's component.
* @param additionalSubtypes additional subtypes being added to this InputMethodInfo
* @hide
*/
public InputMethodInfo(Context context, ResolveInfo service,
List<InputMethodSubtype> additionalSubtypes)
throws XmlPullParserException, IOException {
mService = service;
ServiceInfo si = service.serviceInfo;
mId = computeId(service);
boolean isAuxIme = true;
boolean supportsSwitchingToNextInputMethod = false; // false as default
boolean inlineSuggestionsEnabled = false; // false as default
boolean supportsInlineSuggestionsWithTouchExploration = false; // false as default
boolean suppressesSpellChecker = false; // false as default
boolean showInInputMethodPicker = true; // true as default
mForceDefault = false;
PackageManager pm = context.getPackageManager();
String settingsActivityComponent = null;
String languageSettingsActivityComponent = null;
String stylusHandwritingSettingsActivity = null;
boolean isVrOnly;
boolean isVirtualDeviceOnly;
int isDefaultResId = 0;
XmlResourceParser parser = null;
final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
try {
parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA);
if (parser == null) {
throw new XmlPullParserException("No "
+ InputMethod.SERVICE_META_DATA + " meta-data");
}
Resources res = pm.getResourcesForApplication(si.applicationInfo);
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
}
String nodeName = parser.getName();
if (!"input-method".equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data does not start with input-method tag");
}
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.InputMethod);
settingsActivityComponent = sa.getString(
com.android.internal.R.styleable.InputMethod_settingsActivity);
if (Flags.imeSwitcherRevamp()) {
languageSettingsActivityComponent = sa.getString(
com.android.internal.R.styleable.InputMethod_languageSettingsActivity);
}
if ((si.name != null && si.name.length() > COMPONENT_NAME_MAX_LENGTH)
|| (settingsActivityComponent != null
&& settingsActivityComponent.length()
> COMPONENT_NAME_MAX_LENGTH)
|| (languageSettingsActivityComponent != null
&& languageSettingsActivityComponent.length()
> COMPONENT_NAME_MAX_LENGTH)) {
throw new XmlPullParserException(
"Activity name exceeds maximum of 1000 characters");
}
isVrOnly = sa.getBoolean(com.android.internal.R.styleable.InputMethod_isVrOnly, false);
isVirtualDeviceOnly = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_isVirtualDeviceOnly, false);
isDefaultResId = sa.getResourceId(
com.android.internal.R.styleable.InputMethod_isDefault, 0);
supportsSwitchingToNextInputMethod = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsSwitchingToNextInputMethod,
false);
inlineSuggestionsEnabled = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsInlineSuggestions, false);
supportsInlineSuggestionsWithTouchExploration = sa.getBoolean(
com.android.internal.R.styleable
.InputMethod_supportsInlineSuggestionsWithTouchExploration, false);
suppressesSpellChecker = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_suppressesSpellChecker, false);
showInInputMethodPicker = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_showInInputMethodPicker, true);
mHandledConfigChanges = sa.getInt(
com.android.internal.R.styleable.InputMethod_configChanges, 0);
mSupportsStylusHandwriting = sa.getBoolean(
com.android.internal.R.styleable.InputMethod_supportsStylusHandwriting, false);
mSupportsConnectionlessStylusHandwriting = sa.getBoolean(
com.android.internal.R.styleable
.InputMethod_supportsConnectionlessStylusHandwriting, false);
stylusHandwritingSettingsActivity = sa.getString(
com.android.internal.R.styleable.InputMethod_stylusHandwritingSettingsActivity);
sa.recycle();
final int depth = parser.getDepth();
// Parse all subtypes
while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type == XmlPullParser.START_TAG) {
nodeName = parser.getName();
if (!"subtype".equals(nodeName)) {
throw new XmlPullParserException(
"Meta-data in input-method does not start with subtype tag");
}
final TypedArray a = res.obtainAttributes(
attrs, com.android.internal.R.styleable.InputMethod_Subtype);
String pkLanguageTag = a.getString(com.android.internal.R.styleable
.InputMethod_Subtype_physicalKeyboardHintLanguageTag);
String pkLayoutType = a.getString(com.android.internal.R.styleable
.InputMethod_Subtype_physicalKeyboardHintLayoutType);
final InputMethodSubtype subtype = new InputMethodSubtypeBuilder()
.setSubtypeNameResId(a.getResourceId(com.android.internal.R.styleable
.InputMethod_Subtype_label, 0))
.setSubtypeIconResId(a.getResourceId(com.android.internal.R.styleable
.InputMethod_Subtype_icon, 0))
.setPhysicalKeyboardHint(
pkLanguageTag == null ? null : new ULocale(pkLanguageTag),
pkLayoutType == null ? "" : pkLayoutType)
.setLanguageTag(a.getString(com.android.internal.R.styleable
.InputMethod_Subtype_languageTag))
.setSubtypeLocale(a.getString(com.android.internal.R.styleable
.InputMethod_Subtype_imeSubtypeLocale))
.setSubtypeMode(a.getString(com.android.internal.R.styleable
.InputMethod_Subtype_imeSubtypeMode))
.setSubtypeExtraValue(a.getString(com.android.internal.R.styleable
.InputMethod_Subtype_imeSubtypeExtraValue))
.setIsAuxiliary(a.getBoolean(com.android.internal.R.styleable
.InputMethod_Subtype_isAuxiliary, false))
.setOverridesImplicitlyEnabledSubtype(a.getBoolean(
com.android.internal.R.styleable
.InputMethod_Subtype_overridesImplicitlyEnabledSubtype, false))
.setSubtypeId(a.getInt(com.android.internal.R.styleable
.InputMethod_Subtype_subtypeId, 0 /* use Arrays.hashCode */))
.setIsAsciiCapable(a.getBoolean(com.android.internal.R.styleable
.InputMethod_Subtype_isAsciiCapable, false)).build();
a.recycle();
if (!subtype.isAuxiliary()) {
isAuxIme = false;
}
subtypes.add(subtype);
}
}
} catch (NameNotFoundException | IndexOutOfBoundsException | NumberFormatException e) {
throw new XmlPullParserException(
"Unable to create context for: " + si.packageName);
} finally {
if (parser != null) parser.close();
}
if (subtypes.size() == 0) {
isAuxIme = false;
}
if (additionalSubtypes != null) {
final int N = additionalSubtypes.size();
for (int i = 0; i < N; ++i) {
final InputMethodSubtype subtype = additionalSubtypes.get(i);
if (!subtypes.contains(subtype)) {
subtypes.add(subtype);
} else {
Slog.w(TAG, "Duplicated subtype definition found: "
+ subtype.getLocale() + ", " + subtype.getMode());
}
}
}
mSubtypes = new InputMethodSubtypeArray(subtypes);
mSettingsActivityName = settingsActivityComponent;
mLanguageSettingsActivityName = languageSettingsActivityComponent;
mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivity;
mIsDefaultResId = isDefaultResId;
mIsAuxIme = isAuxIme;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
mSupportsInlineSuggestionsWithTouchExploration =
supportsInlineSuggestionsWithTouchExploration;
mSuppressesSpellChecker = suppressesSpellChecker;
mShowInInputMethodPicker = showInInputMethodPicker;
mIsVrOnly = isVrOnly;
mIsVirtualDeviceOnly = isVirtualDeviceOnly;
}
/**
* @hide
*/
public InputMethodInfo(InputMethodInfo source) {
this(source, Collections.emptyList());
}
/**
* @hide
*/
public InputMethodInfo(@NonNull InputMethodInfo source,
@NonNull List<InputMethodSubtype> additionalSubtypes) {
mId = source.mId;
mSettingsActivityName = source.mSettingsActivityName;
mLanguageSettingsActivityName = source.mLanguageSettingsActivityName;
mIsDefaultResId = source.mIsDefaultResId;
mIsAuxIme = source.mIsAuxIme;
mSupportsSwitchingToNextInputMethod = source.mSupportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = source.mInlineSuggestionsEnabled;
mSupportsInlineSuggestionsWithTouchExploration =
source.mSupportsInlineSuggestionsWithTouchExploration;
mSuppressesSpellChecker = source.mSuppressesSpellChecker;
mShowInInputMethodPicker = source.mShowInInputMethodPicker;
mIsVrOnly = source.mIsVrOnly;
mIsVirtualDeviceOnly = source.mIsVirtualDeviceOnly;
mService = source.mService;
if (additionalSubtypes.isEmpty()) {
mSubtypes = source.mSubtypes;
} else {
final ArrayList<InputMethodSubtype> subtypes = source.mSubtypes.toList();
final int additionalSubtypeCount = additionalSubtypes.size();
for (int i = 0; i < additionalSubtypeCount; ++i) {
final InputMethodSubtype additionalSubtype = additionalSubtypes.get(i);
if (!subtypes.contains(additionalSubtype)) {
subtypes.add(additionalSubtype);
}
}
mSubtypes = new InputMethodSubtypeArray(subtypes);
}
mHandledConfigChanges = source.mHandledConfigChanges;
mSupportsStylusHandwriting = source.mSupportsStylusHandwriting;
mSupportsConnectionlessStylusHandwriting = source.mSupportsConnectionlessStylusHandwriting;
mForceDefault = source.mForceDefault;
mStylusHandwritingSettingsActivityAttr = source.mStylusHandwritingSettingsActivityAttr;
}
InputMethodInfo(Parcel source) {
mId = source.readString();
mSettingsActivityName = source.readString();
mLanguageSettingsActivityName = source.readString8();
mIsDefaultResId = source.readInt();
mIsAuxIme = source.readInt() == 1;
mSupportsSwitchingToNextInputMethod = source.readInt() == 1;
mInlineSuggestionsEnabled = source.readInt() == 1;
mSupportsInlineSuggestionsWithTouchExploration = source.readInt() == 1;
mSuppressesSpellChecker = source.readBoolean();
mShowInInputMethodPicker = source.readBoolean();
mIsVrOnly = source.readBoolean();
mIsVirtualDeviceOnly = source.readBoolean();
mService = ResolveInfo.CREATOR.createFromParcel(source);
mSubtypes = new InputMethodSubtypeArray(source);
mHandledConfigChanges = source.readInt();
mSupportsStylusHandwriting = source.readBoolean();
mSupportsConnectionlessStylusHandwriting = source.readBoolean();
mStylusHandwritingSettingsActivityAttr = source.readString8();
mForceDefault = false;
}
/**
* Temporary API for creating a built-in input method for test.
*/
public InputMethodInfo(String packageName, String className,
CharSequence label, String settingsActivity) {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* languageSettingsActivity */, null /* subtypes */,
0 /* isDefaultResId */, false /* forceDefault */,
true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */,
false /* supportsStylusHandwriting */,
false /* supportConnectionlessStylusHandwriting */,
null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
/**
* Test API for creating a built-in input method to verify stylus handwriting.
* @hide
*/
@TestApi
public InputMethodInfo(@NonNull String packageName, @NonNull String className,
@NonNull CharSequence label, @NonNull String settingsActivity,
boolean supportStylusHandwriting,
@NonNull String stylusHandwritingSettingsActivityAttr) {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* languageSettingsActivity */,
null /* subtypes */, 0 /* isDefaultResId */,
false /* forceDefault */, true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */,
supportStylusHandwriting, false /* supportConnectionlessStylusHandwriting */,
stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
}
/**
* Test API for creating a built-in input method to verify stylus handwriting.
* @hide
*/
@TestApi
public InputMethodInfo(@NonNull String packageName, @NonNull String className,
@NonNull CharSequence label, @NonNull String settingsActivity,
@NonNull String languageSettingsActivity, boolean supportStylusHandwriting,
@NonNull String stylusHandwritingSettingsActivityAttr) {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, languageSettingsActivity, null /* subtypes */,
0 /* isDefaultResId */, false /* forceDefault */,
true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */,
supportStylusHandwriting, false /* supportConnectionlessStylusHandwriting */,
stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
}
/**
* Test API for creating a built-in input method to verify stylus handwriting.
* @hide
*/
@TestApi
@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING)
public InputMethodInfo(@NonNull String packageName, @NonNull String className,
@NonNull CharSequence label, @NonNull String settingsActivity,
@NonNull String languageSettingsActivity, boolean supportStylusHandwriting,
boolean supportConnectionlessStylusHandwriting,
@NonNull String stylusHandwritingSettingsActivityAttr) {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, languageSettingsActivity, null /* subtypes */,
0 /* isDefaultResId */, false /* forceDefault */,
true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
false /* isVirtualDeviceOnly */, 0 /* handledConfigChanges */,
supportStylusHandwriting, supportConnectionlessStylusHandwriting,
stylusHandwritingSettingsActivityAttr, false /* inlineSuggestionsEnabled */);
}
/**
* Temporary API for creating a built-in input method for test.
* @hide
*/
@TestApi
public InputMethodInfo(@NonNull String packageName, @NonNull String className,
@NonNull CharSequence label, @NonNull String settingsActivity,
int handledConfigChanges) {
this(buildFakeResolveInfo(packageName, className, label), false /* isAuxIme */,
settingsActivity, null /* languageSettingsActivity */, null /* subtypes */,
0 /* isDefaultResId */, false /* forceDefault */,
true /* supportsSwitchingToNextInputMethod */,
false /* inlineSuggestionsEnabled */, false /* isVrOnly */,
false /* isVirtualDeviceOnly */, handledConfigChanges,
false /* supportsStylusHandwriting */,
false /* supportConnectionlessStylusHandwriting */,
null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
/**
* Temporary API for creating a built-in input method for test.
* @hide
*/
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme,
String settingsActivity, List<InputMethodSubtype> subtypes, int isDefaultResId,
boolean forceDefault) {
this(ri, isAuxIme, settingsActivity, null /* languageSettingsActivity */, subtypes,
isDefaultResId, forceDefault,
true /* supportsSwitchingToNextInputMethod */, false /* inlineSuggestionsEnabled */,
false /* isVrOnly */, false /* isVirtualDeviceOnly */, 0 /* handledconfigChanges */,
false /* supportsStylusHandwriting */,
false /* supportConnectionlessStylusHandwriting */,
null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
/**
* Temporary API for creating a built-in input method for test.
* @hide
*/
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
List<InputMethodSubtype> subtypes, int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean isVrOnly) {
this(ri, isAuxIme, settingsActivity, null /* languageSettingsActivity */, subtypes,
isDefaultResId, forceDefault,
supportsSwitchingToNextInputMethod, false /* inlineSuggestionsEnabled */, isVrOnly,
false /* isVirtualDeviceOnly */,
0 /* handledConfigChanges */, false /* supportsStylusHandwriting */,
false /* supportConnectionlessStylusHandwriting */,
null /* stylusHandwritingSettingsActivityAttr */,
false /* inlineSuggestionsEnabled */);
}
/**
* Temporary API for creating a built-in input method for test.
* @hide
*/
public InputMethodInfo(ResolveInfo ri, boolean isAuxIme, String settingsActivity,
@Nullable String languageSettingsActivity, List<InputMethodSubtype> subtypes,
int isDefaultResId, boolean forceDefault,
boolean supportsSwitchingToNextInputMethod, boolean inlineSuggestionsEnabled,
boolean isVrOnly, boolean isVirtualDeviceOnly, int handledConfigChanges,
boolean supportsStylusHandwriting, boolean supportsConnectionlessStylusHandwriting,
String stylusHandwritingSettingsActivityAttr,
boolean supportsInlineSuggestionsWithTouchExploration) {
final ServiceInfo si = ri.serviceInfo;
mService = ri;
mId = new ComponentName(si.packageName, si.name).flattenToShortString();
mSettingsActivityName = settingsActivity;
mLanguageSettingsActivityName = languageSettingsActivity;
mIsDefaultResId = isDefaultResId;
mIsAuxIme = isAuxIme;
mSubtypes = new InputMethodSubtypeArray(subtypes);
mForceDefault = forceDefault;
mSupportsSwitchingToNextInputMethod = supportsSwitchingToNextInputMethod;
mInlineSuggestionsEnabled = inlineSuggestionsEnabled;
mSupportsInlineSuggestionsWithTouchExploration =
supportsInlineSuggestionsWithTouchExploration;
mSuppressesSpellChecker = false;
mShowInInputMethodPicker = true;
mIsVrOnly = isVrOnly;
mIsVirtualDeviceOnly = isVirtualDeviceOnly;
mHandledConfigChanges = handledConfigChanges;
mSupportsStylusHandwriting = supportsStylusHandwriting;
mSupportsConnectionlessStylusHandwriting = supportsConnectionlessStylusHandwriting;
mStylusHandwritingSettingsActivityAttr = stylusHandwritingSettingsActivityAttr;
}
private static ResolveInfo buildFakeResolveInfo(String packageName, String className,
CharSequence label) {
ResolveInfo ri = new ResolveInfo();
ServiceInfo si = new ServiceInfo();
ApplicationInfo ai = new ApplicationInfo();
ai.packageName = packageName;
ai.enabled = true;
si.applicationInfo = ai;
si.enabled = true;
si.packageName = packageName;
si.name = className;
si.exported = true;
si.nonLocalizedLabel = label;
ri.serviceInfo = si;
return ri;
}
/**
* @return a unique ID for this input method, which is guaranteed to be the same as the result
* of {@code getComponent().flattenToShortString()}.
* @see ComponentName#unflattenFromString(String)
*/
public String getId() {
return mId;
}
/**
* Return the .apk package that implements this input method.
*/
public String getPackageName() {
return mService.serviceInfo.packageName;
}
/**
* Return the class name of the service component that implements
* this input method.
*/
public String getServiceName() {
return mService.serviceInfo.name;
}
/**
* Return the raw information about the Service implementing this
* input method. Do not modify the returned object.
*/
public ServiceInfo getServiceInfo() {
return mService.serviceInfo;
}
/**
* Return the component of the service that implements this input
* method.
*/
public ComponentName getComponent() {
return new ComponentName(mService.serviceInfo.packageName,
mService.serviceInfo.name);
}
/**
* Load the user-displayed label for this input method.
*
* @param pm Supply a PackageManager used to load the input method's
* resources.
*/
public CharSequence loadLabel(PackageManager pm) {
return mService.loadLabel(pm);
}
/**
* Load the user-displayed icon for this input method.
*
* @param pm Supply a PackageManager used to load the input method's
* resources.
*/
public Drawable loadIcon(PackageManager pm) {
return mService.loadIcon(pm);
}
/**
* Return the class name of an activity that provides a settings UI for
* the input method. You can launch this activity be starting it with
* an {@link android.content.Intent} whose action is MAIN and with an
* explicit {@link android.content.ComponentName}
* composed of {@link #getPackageName} and the class name returned here.
*
* <p>A null will be returned if there is no settings activity associated
* with the input method.</p>
* @see #createStylusHandwritingSettingsActivityIntent()
*/
public String getSettingsActivity() {
return mSettingsActivityName;
}
/**
* Returns true if IME supports VR mode only.
* @hide
*/
public boolean isVrOnly() {
return mIsVrOnly;
}
/**
* Returns true if IME supports only virtual devices.
* @hide
*/
@FlaggedApi(android.companion.virtual.flags.Flags.FLAG_VDM_CUSTOM_IME)
@SystemApi
public boolean isVirtualDeviceOnly() {
return mIsVirtualDeviceOnly;
}
/**
* Return the count of the subtypes of Input Method.
*/
public int getSubtypeCount() {
return mSubtypes.getCount();
}
/**
* Return the Input Method's subtype at the specified index.
*
* @param index the index of the subtype to return.
*/
public InputMethodSubtype getSubtypeAt(int index) {
return mSubtypes.get(index);
}
/**
* Return the resource identifier of a resource inside of this input
* method's .apk that determines whether it should be considered a
* default input method for the system.
*/
public int getIsDefaultResourceId() {
return mIsDefaultResId;
}
/**
* Return whether or not this ime is a default ime or not.
* @hide
*/
@UnsupportedAppUsage
public boolean isDefault(Context context) {
if (mForceDefault) {
return true;
}
try {
if (getIsDefaultResourceId() == 0) {
return false;
}
final Resources res = context.createPackageContext(getPackageName(), 0).getResources();
return res.getBoolean(getIsDefaultResourceId());
} catch (NameNotFoundException | NotFoundException e) {
return false;
}
}
/**
* Returns the bit mask of kinds of configuration changes that this IME
* can handle itself (without being restarted by the system).
*
* @attr ref android.R.styleable#InputMethod_configChanges
*/
@ActivityInfo.Config
public int getConfigChanges() {
return mHandledConfigChanges;
}
/**
* Returns if IME supports handwriting using stylus input.
* @attr ref android.R.styleable#InputMethod_supportsStylusHandwriting
* @see #createStylusHandwritingSettingsActivityIntent()
*/
public boolean supportsStylusHandwriting() {
return mSupportsStylusHandwriting;
}
/**
* Returns whether the IME supports connectionless stylus handwriting sessions.
*
* @attr ref android.R.styleable#InputMethod_supportsConnectionlessStylusHandwriting
*/
@FlaggedApi(Flags.FLAG_CONNECTIONLESS_HANDWRITING)
public boolean supportsConnectionlessStylusHandwriting() {
return mSupportsConnectionlessStylusHandwriting;
}
/**
* Returns {@link Intent} for stylus handwriting settings activity with
* {@link Intent#getAction() Intent action} {@link #ACTION_STYLUS_HANDWRITING_SETTINGS}
* if IME {@link #supportsStylusHandwriting() supports stylus handwriting}, else
* <code>null</code> if there are no associated settings for stylus handwriting / handwriting
* is not supported or if
* {@link android.R.styleable#InputMethod_stylusHandwritingSettingsActivity} is not defined.
*
* <p>To launch stylus settings, use this method to get the {@link android.content.Intent} to
* launch the stylus handwriting settings activity.</p>
* <p>e.g.<pre><code>startActivity(createStylusHandwritingSettingsActivityIntent());</code>
* </pre></p>
*
* @attr ref R.styleable#InputMethod_stylusHandwritingSettingsActivity
* @see #getSettingsActivity()
* @see #supportsStylusHandwriting()
*/
@Nullable
public Intent createStylusHandwritingSettingsActivityIntent() {
if (TextUtils.isEmpty(mStylusHandwritingSettingsActivityAttr)
|| !mSupportsStylusHandwriting) {
return null;
}
// TODO(b/210039666): consider returning null if component is not enabled.
return new Intent(ACTION_STYLUS_HANDWRITING_SETTINGS).setComponent(
new ComponentName(getServiceInfo().packageName,
mStylusHandwritingSettingsActivityAttr));
}
/**
* Returns {@link Intent} for IME language settings activity with
* {@link Intent#getAction() Intent action} {@link #ACTION_IME_LANGUAGE_SETTINGS},
* else <code>null</code> if
* {@link android.R.styleable#InputMethod_languageSettingsActivity} is not defined.
*
* <p>To launch IME language settings, use this method to get the {@link Intent} to launch
* the IME language settings activity.</p>
* <p>e.g.<pre><code>startActivity(createImeLanguageSettingsActivityIntent());</code></pre></p>
*
* @attr ref R.styleable#InputMethod_languageSettingsActivity
*/
@FlaggedApi(android.view.inputmethod.Flags.FLAG_IME_SWITCHER_REVAMP)
@Nullable
public Intent createImeLanguageSettingsActivityIntent() {
if (TextUtils.isEmpty(mLanguageSettingsActivityName)) {
return null;
}
return new Intent(ACTION_IME_LANGUAGE_SETTINGS).setComponent(
new ComponentName(getServiceInfo().packageName,
mLanguageSettingsActivityName)
);
}
public void dump(Printer pw, String prefix) {
pw.println(prefix + "mId=" + mId
+ " mSettingsActivityName=" + mSettingsActivityName
+ " mLanguageSettingsActivityName=" + mLanguageSettingsActivityName
+ " mIsVrOnly=" + mIsVrOnly
+ " mIsVirtualDeviceOnly=" + mIsVirtualDeviceOnly
+ " mSupportsSwitchingToNextInputMethod=" + mSupportsSwitchingToNextInputMethod
+ " mInlineSuggestionsEnabled=" + mInlineSuggestionsEnabled
+ " mSupportsInlineSuggestionsWithTouchExploration="
+ mSupportsInlineSuggestionsWithTouchExploration
+ " mSuppressesSpellChecker=" + mSuppressesSpellChecker
+ " mShowInInputMethodPicker=" + mShowInInputMethodPicker
+ " mSupportsStylusHandwriting=" + mSupportsStylusHandwriting
+ " mSupportsConnectionlessStylusHandwriting="
+ mSupportsConnectionlessStylusHandwriting
+ " mStylusHandwritingSettingsActivityAttr="
+ mStylusHandwritingSettingsActivityAttr);
pw.println(prefix + "mIsDefaultResId=0x"
+ Integer.toHexString(mIsDefaultResId));
pw.println(prefix + "Service:");
mService.dump(pw, prefix + " ");
pw.println(prefix + "InputMethodSubtype array: count=" + mSubtypes.getCount());
mSubtypes.dump(pw, prefix + " ");
}
@Override
public String toString() {
return "InputMethodInfo{" + mId
+ ", settings: " + mSettingsActivityName
+ ", languageSettings: " + mLanguageSettingsActivityName
+ "}";
}
/**
* Used to test whether the given parameter object is an
* {@link InputMethodInfo} and its Id is the same to this one.
*
* @return true if the given parameter object is an
* {@link InputMethodInfo} and its Id is the same to this one.
*/
@Override
public boolean equals(@Nullable Object o) {
if (o == this) return true;
if (o == null) return false;
if (!(o instanceof InputMethodInfo)) return false;
InputMethodInfo obj = (InputMethodInfo) o;
return mId.equals(obj.mId);
}
@Override
public int hashCode() {
return mId.hashCode();
}
/**
* @hide
* @return {@code true} if the IME is a trusted system component (e.g. pre-installed)
*/
public boolean isSystem() {
return (mService.serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
}
/**
* @hide
*/
public boolean isAuxiliaryIme() {
return mIsAuxIme;
}
/**
* @return true if this input method supports ways to switch to a next input method.
* @hide
*/
public boolean supportsSwitchingToNextInputMethod() {
return mSupportsSwitchingToNextInputMethod;
}
/**
* @return true if this input method supports inline suggestions.
* @hide
*/
public boolean isInlineSuggestionsEnabled() {
return mInlineSuggestionsEnabled;
}
/**
* Returns {@code true} if this input method supports inline suggestions when touch exploration
* is enabled.
* @hide
*/
public boolean supportsInlineSuggestionsWithTouchExploration() {
return mSupportsInlineSuggestionsWithTouchExploration;
}
/**
* Return {@code true} if this input method suppresses spell checker.
*/
public boolean suppressesSpellChecker() {
return mSuppressesSpellChecker;
}
/**
* Returns {@code true} if this input method should be shown in menus for selecting an Input
* Method, such as the system Input Method Picker. This is {@code false} if the IME is intended
* to be accessed programmatically.
*/
public boolean shouldShowInInputMethodPicker() {
return mShowInInputMethodPicker;
}
/**
* Used to package this object into a {@link Parcel}.
*
* @param dest The {@link Parcel} to be written.
* @param flags The flags used for parceling.
*/
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mId);
dest.writeString(mSettingsActivityName);
dest.writeString8(mLanguageSettingsActivityName);
dest.writeInt(mIsDefaultResId);
dest.writeInt(mIsAuxIme ? 1 : 0);
dest.writeInt(mSupportsSwitchingToNextInputMethod ? 1 : 0);
dest.writeInt(mInlineSuggestionsEnabled ? 1 : 0);
dest.writeInt(mSupportsInlineSuggestionsWithTouchExploration ? 1 : 0);
dest.writeBoolean(mSuppressesSpellChecker);
dest.writeBoolean(mShowInInputMethodPicker);
dest.writeBoolean(mIsVrOnly);
dest.writeBoolean(mIsVirtualDeviceOnly);
mService.writeToParcel(dest, flags);
mSubtypes.writeToParcel(dest);
dest.writeInt(mHandledConfigChanges);
dest.writeBoolean(mSupportsStylusHandwriting);
dest.writeBoolean(mSupportsConnectionlessStylusHandwriting);
dest.writeString8(mStylusHandwritingSettingsActivityAttr);
}
/**
* Used to make this class parcelable.
*/
public static final @android.annotation.NonNull Parcelable.Creator<InputMethodInfo> CREATOR
= new Parcelable.Creator<InputMethodInfo>() {
@Override
public InputMethodInfo createFromParcel(Parcel source) {
return new InputMethodInfo(source);
}
@Override
public InputMethodInfo[] newArray(int size) {
return new InputMethodInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
}