Java tutorial
/* * Copyright (C) 2009 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.accessibilityservice; import static android.content.pm.PackageManager.FEATURE_FINGERPRINT; import android.annotation.IntDef; import android.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.Context; 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.Resources; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.hardware.fingerprint.FingerprintManager; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; import android.util.SparseArray; import android.util.TypedValue; import android.util.Xml; import android.view.View; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; import com.android.internal.R; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * This class describes an {@link AccessibilityService}. The system notifies an * {@link AccessibilityService} for {@link android.view.accessibility.AccessibilityEvent}s * according to the information encapsulated in this class. * * <div class="special reference"> * <h3>Developer Guides</h3> * <p>For more information about creating AccessibilityServices, read the * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> * developer guide.</p> * </div> * * @attr ref android.R.styleable#AccessibilityService_accessibilityEventTypes * @attr ref android.R.styleable#AccessibilityService_accessibilityFeedbackType * @attr ref android.R.styleable#AccessibilityService_accessibilityFlags * @attr ref android.R.styleable#AccessibilityService_canRequestEnhancedWebAccessibility * @attr ref android.R.styleable#AccessibilityService_canRequestFilterKeyEvents * @attr ref android.R.styleable#AccessibilityService_canRequestTouchExplorationMode * @attr ref android.R.styleable#AccessibilityService_canRetrieveWindowContent * @attr ref android.R.styleable#AccessibilityService_description * @attr ref android.R.styleable#AccessibilityService_summary * @attr ref android.R.styleable#AccessibilityService_notificationTimeout * @attr ref android.R.styleable#AccessibilityService_packageNames * @attr ref android.R.styleable#AccessibilityService_settingsActivity * @see AccessibilityService * @see android.view.accessibility.AccessibilityEvent * @see android.view.accessibility.AccessibilityManager */ public class AccessibilityServiceInfo implements Parcelable { private static final String TAG_ACCESSIBILITY_SERVICE = "accessibility-service"; /** * Capability: This accessibility service can retrieve the active window content. * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent */ public static final int CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT = 0x00000001; /** * Capability: This accessibility service can request touch exploration mode in which * touched items are spoken aloud and the UI can be explored via gestures. * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode */ public static final int CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION = 0x00000002; /** * @deprecated No longer used */ public static final int CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000004; /** * Capability: This accessibility service can request to filter the key event stream. * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents */ public static final int CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS = 0x00000008; /** * Capability: This accessibility service can control display magnification. * @see android.R.styleable#AccessibilityService_canControlMagnification */ public static final int CAPABILITY_CAN_CONTROL_MAGNIFICATION = 0x00000010; /** * Capability: This accessibility service can perform gestures. * @see android.R.styleable#AccessibilityService_canPerformGestures */ public static final int CAPABILITY_CAN_PERFORM_GESTURES = 0x00000020; /** * Capability: This accessibility service can capture gestures from the fingerprint sensor * @see android.R.styleable#AccessibilityService_canRequestFingerprintGestures */ public static final int CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES = 0x00000040; private static SparseArray<CapabilityInfo> sAvailableCapabilityInfos; /** * Denotes spoken feedback. */ public static final int FEEDBACK_SPOKEN = 0x0000001; /** * Denotes haptic feedback. */ public static final int FEEDBACK_HAPTIC = 0x0000002; /** * Denotes audible (not spoken) feedback. */ public static final int FEEDBACK_AUDIBLE = 0x0000004; /** * Denotes visual feedback. */ public static final int FEEDBACK_VISUAL = 0x0000008; /** * Denotes generic feedback. */ public static final int FEEDBACK_GENERIC = 0x0000010; /** * Denotes braille feedback. */ public static final int FEEDBACK_BRAILLE = 0x0000020; /** * Mask for all feedback types. * * @see #FEEDBACK_SPOKEN * @see #FEEDBACK_HAPTIC * @see #FEEDBACK_AUDIBLE * @see #FEEDBACK_VISUAL * @see #FEEDBACK_GENERIC * @see #FEEDBACK_BRAILLE */ public static final int FEEDBACK_ALL_MASK = 0xFFFFFFFF; /** * If an {@link AccessibilityService} is the default for a given type. * Default service is invoked only if no package specific one exists. In case of * more than one package specific service only the earlier registered is notified. */ public static final int DEFAULT = 0x0000001; /** * If this flag is set the system will regard views that are not important * for accessibility in addition to the ones that are important for accessibility. * That is, views that are marked as not important for accessibility via * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO} or * {@link View#IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS} and views that are * marked as potentially important for accessibility via * {@link View#IMPORTANT_FOR_ACCESSIBILITY_AUTO} for which the system has determined * that are not important for accessibility, are reported while querying the window * content and also the accessibility service will receive accessibility events from * them. * <p> * <strong>Note:</strong> For accessibility services targeting Android 4.1 (API level 16) or * higher, this flag has to be explicitly set for the system to regard views that are not * important for accessibility. For accessibility services targeting Android 4.0.4 (API level * 15) or lower, this flag is ignored and all views are regarded for accessibility purposes. * </p> * <p> * Usually views not important for accessibility are layout managers that do not * react to user actions, do not draw any content, and do not have any special * semantics in the context of the screen content. For example, a three by three * grid can be implemented as three horizontal linear layouts and one vertical, * or three vertical linear layouts and one horizontal, or one grid layout, etc. * In this context, the actual layout managers used to achieve the grid configuration * are not important; rather it is important that there are nine evenly distributed * elements. * </p> */ public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x0000002; /** * This flag requests that the system gets into touch exploration mode. * In this mode a single finger moving on the screen behaves as a mouse * pointer hovering over the user interface. The system will also detect * certain gestures performed on the touch screen and notify this service. * The system will enable touch exploration mode if there is at least one * accessibility service that has this flag set. Hence, clearing this * flag does not guarantee that the device will not be in touch exploration * mode since there may be another enabled service that requested it. * <p> * For accessibility services targeting Android 4.3 (API level 18) or higher * that want to set this flag have to declare this capability in their * meta-data by setting the attribute * {@link android.R.attr#canRequestTouchExplorationMode * canRequestTouchExplorationMode} to true. Otherwise, this flag will * be ignored. For how to declare the meta-data of a service refer to * {@value AccessibilityService#SERVICE_META_DATA}. * </p> * <p> * Services targeting Android 4.2.2 (API level 17) or lower will work * normally. In other words, the first time they are run, if this flag is * specified, a dialog is shown to the user to confirm enabling explore by * touch. * </p> * @see android.R.styleable#AccessibilityService_canRequestTouchExplorationMode */ public static final int FLAG_REQUEST_TOUCH_EXPLORATION_MODE = 0x0000004; /** * @deprecated No longer used */ public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 0x00000008; /** * This flag requests that the {@link AccessibilityNodeInfo}s obtained * by an {@link AccessibilityService} contain the id of the source view. * The source view id will be a fully qualified resource name of the * form "package:id/name", for example "foo.bar:id/my_list", and it is * useful for UI test automation. This flag is not set by default. */ public static final int FLAG_REPORT_VIEW_IDS = 0x00000010; /** * This flag requests from the system to filter key events. If this flag * is set the accessibility service will receive the key events before * applications allowing it implement global shortcuts. * <p> * Services that want to set this flag have to declare this capability * in their meta-data by setting the attribute {@link android.R.attr * #canRequestFilterKeyEvents canRequestFilterKeyEvents} to true, * otherwise this flag will be ignored. For how to declare the meta-data * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}. * </p> * @see android.R.styleable#AccessibilityService_canRequestFilterKeyEvents */ public static final int FLAG_REQUEST_FILTER_KEY_EVENTS = 0x00000020; /** * This flag indicates to the system that the accessibility service wants * to access content of all interactive windows. An interactive window is a * window that has input focus or can be touched by a sighted user when explore * by touch is not enabled. If this flag is not set your service will not receive * {@link android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED} * events, calling AccessibilityService{@link AccessibilityService#getWindows() * AccessibilityService.getWindows()} will return an empty list, and {@link * AccessibilityNodeInfo#getWindow() AccessibilityNodeInfo.getWindow()} will * return null. * <p> * Services that want to set this flag have to declare the capability * to retrieve window content in their meta-data by setting the attribute * {@link android.R.attr#canRetrieveWindowContent canRetrieveWindowContent} to * true, otherwise this flag will be ignored. For how to declare the meta-data * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}. * </p> * @see android.R.styleable#AccessibilityService_canRetrieveWindowContent */ public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040; /** * This flag requests that all audio tracks system-wide with * {@link android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY} be controlled by the * {@link android.media.AudioManager#STREAM_ACCESSIBILITY} volume. */ public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 0x00000080; /** * This flag indicates to the system that the accessibility service requests that an * accessibility button be shown within the system's navigation area, if available. */ public static final int FLAG_REQUEST_ACCESSIBILITY_BUTTON = 0x00000100; /** * This flag requests that all fingerprint gestures be sent to the accessibility service. * <p> * Services that want to set this flag have to declare the capability * to retrieve window content in their meta-data by setting the attribute * {@link android.R.attr#canRequestFingerprintGestures} to * true, otherwise this flag will be ignored. For how to declare the meta-data * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}. * </p> * * @see android.R.styleable#AccessibilityService_canRequestFingerprintGestures * @see AccessibilityService#getFingerprintGestureController() */ public static final int FLAG_REQUEST_FINGERPRINT_GESTURES = 0x00000200; /** {@hide} */ public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000; /** * The event types an {@link AccessibilityService} is interested in. * <p> * <strong>Can be dynamically set at runtime.</strong> * </p> * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_CLICKED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_LONG_CLICKED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_FOCUSED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SELECTED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_NOTIFICATION_STATE_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_START * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_EXPLORATION_GESTURE_END * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_ENTER * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_HOVER_EXIT * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_SCROLLED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_SELECTION_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_START * @see android.view.accessibility.AccessibilityEvent#TYPE_TOUCH_INTERACTION_END * @see android.view.accessibility.AccessibilityEvent#TYPE_ANNOUNCEMENT * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_START * @see android.view.accessibility.AccessibilityEvent#TYPE_GESTURE_DETECTION_END * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED * @see android.view.accessibility.AccessibilityEvent#TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY * @see android.view.accessibility.AccessibilityEvent#TYPE_WINDOWS_CHANGED */ public int eventTypes; /** * The package names an {@link AccessibilityService} is interested in. Setting * to <code>null</code> is equivalent to all packages. * <p> * <strong>Can be dynamically set at runtime.</strong> * </p> */ public String[] packageNames; /** @hide */ @IntDef(flag = true, prefix = { "FEEDBACK_" }, value = { FEEDBACK_AUDIBLE, FEEDBACK_GENERIC, FEEDBACK_HAPTIC, FEEDBACK_SPOKEN, FEEDBACK_VISUAL, FEEDBACK_BRAILLE }) @Retention(RetentionPolicy.SOURCE) public @interface FeedbackType { } /** * The feedback type an {@link AccessibilityService} provides. * <p> * <strong>Can be dynamically set at runtime.</strong> * </p> * @see #FEEDBACK_AUDIBLE * @see #FEEDBACK_GENERIC * @see #FEEDBACK_HAPTIC * @see #FEEDBACK_SPOKEN * @see #FEEDBACK_VISUAL * @see #FEEDBACK_BRAILLE */ @FeedbackType public int feedbackType; /** * The timeout, in milliseconds, after the most recent event of a given type before an * {@link AccessibilityService} is notified. * <p> * <strong>Can be dynamically set at runtime.</strong> * </p> * <p> * <strong>Note:</strong> The event notification timeout is useful to avoid propagating * events to the client too frequently since this is accomplished via an expensive * interprocess call. One can think of the timeout as a criteria to determine when * event generation has settled down. */ public long notificationTimeout; /** * This field represents a set of flags used for configuring an * {@link AccessibilityService}. * <p> * <strong>Can be dynamically set at runtime.</strong> * </p> * @see #DEFAULT * @see #FLAG_INCLUDE_NOT_IMPORTANT_VIEWS * @see #FLAG_REQUEST_TOUCH_EXPLORATION_MODE * @see #FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY * @see #FLAG_REQUEST_FILTER_KEY_EVENTS * @see #FLAG_REPORT_VIEW_IDS * @see #FLAG_RETRIEVE_INTERACTIVE_WINDOWS * @see #FLAG_ENABLE_ACCESSIBILITY_VOLUME * @see #FLAG_REQUEST_ACCESSIBILITY_BUTTON */ public int flags; /** * Whether or not the service has crashed and is awaiting restart. Only valid from {@link * android.view.accessibility.AccessibilityManager#getEnabledAccessibilityServiceList(int)}, * because that is populated from the internal list of running services. * * @hide */ public boolean crashed; /** * The component name the accessibility service. */ private ComponentName mComponentName; /** * The Service that implements this accessibility service component. */ private ResolveInfo mResolveInfo; /** * The accessibility service setting activity's name, used by the system * settings to launch the setting activity of this accessibility service. */ private String mSettingsActivityName; /** * Bit mask with capabilities of this service. */ private int mCapabilities; /** * Resource id of the summary of the accessibility service. */ private int mSummaryResId; /** * Non-localized summary of the accessibility service. */ private String mNonLocalizedSummary; /** * Resource id of the description of the accessibility service. */ private int mDescriptionResId; /** * Non localized description of the accessibility service. */ private String mNonLocalizedDescription; /** * Creates a new instance. */ public AccessibilityServiceInfo() { /* do nothing */ } /** * Creates a new instance. * * @param resolveInfo The service resolve info. * @param context Context for accessing resources. * @throws XmlPullParserException If a XML parsing error occurs. * @throws IOException If a XML parsing error occurs. * * @hide */ public AccessibilityServiceInfo(ResolveInfo resolveInfo, Context context) throws XmlPullParserException, IOException { ServiceInfo serviceInfo = resolveInfo.serviceInfo; mComponentName = new ComponentName(serviceInfo.packageName, serviceInfo.name); mResolveInfo = resolveInfo; XmlResourceParser parser = null; try { PackageManager packageManager = context.getPackageManager(); parser = serviceInfo.loadXmlMetaData(packageManager, AccessibilityService.SERVICE_META_DATA); if (parser == null) { return; } int type = 0; while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { type = parser.next(); } String nodeName = parser.getName(); if (!TAG_ACCESSIBILITY_SERVICE.equals(nodeName)) { throw new XmlPullParserException( "Meta-data does not start with" + TAG_ACCESSIBILITY_SERVICE + " tag"); } AttributeSet allAttributes = Xml.asAttributeSet(parser); Resources resources = packageManager.getResourcesForApplication(serviceInfo.applicationInfo); TypedArray asAttributes = resources.obtainAttributes(allAttributes, com.android.internal.R.styleable.AccessibilityService); eventTypes = asAttributes .getInt(com.android.internal.R.styleable.AccessibilityService_accessibilityEventTypes, 0); String packageNamez = asAttributes .getString(com.android.internal.R.styleable.AccessibilityService_packageNames); if (packageNamez != null) { packageNames = packageNamez.split("(\\s)*,(\\s)*"); } feedbackType = asAttributes .getInt(com.android.internal.R.styleable.AccessibilityService_accessibilityFeedbackType, 0); notificationTimeout = asAttributes .getInt(com.android.internal.R.styleable.AccessibilityService_notificationTimeout, 0); flags = asAttributes.getInt(com.android.internal.R.styleable.AccessibilityService_accessibilityFlags, 0); mSettingsActivityName = asAttributes .getString(com.android.internal.R.styleable.AccessibilityService_settingsActivity); if (asAttributes.getBoolean( com.android.internal.R.styleable.AccessibilityService_canRetrieveWindowContent, false)) { mCapabilities |= CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT; } if (asAttributes.getBoolean( com.android.internal.R.styleable.AccessibilityService_canRequestTouchExplorationMode, false)) { mCapabilities |= CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION; } if (asAttributes.getBoolean( com.android.internal.R.styleable.AccessibilityService_canRequestFilterKeyEvents, false)) { mCapabilities |= CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS; } if (asAttributes.getBoolean( com.android.internal.R.styleable.AccessibilityService_canControlMagnification, false)) { mCapabilities |= CAPABILITY_CAN_CONTROL_MAGNIFICATION; } if (asAttributes.getBoolean(com.android.internal.R.styleable.AccessibilityService_canPerformGestures, false)) { mCapabilities |= CAPABILITY_CAN_PERFORM_GESTURES; } if (asAttributes.getBoolean( com.android.internal.R.styleable.AccessibilityService_canRequestFingerprintGestures, false)) { mCapabilities |= CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES; } TypedValue peekedValue = asAttributes .peekValue(com.android.internal.R.styleable.AccessibilityService_description); if (peekedValue != null) { mDescriptionResId = peekedValue.resourceId; CharSequence nonLocalizedDescription = peekedValue.coerceToString(); if (nonLocalizedDescription != null) { mNonLocalizedDescription = nonLocalizedDescription.toString().trim(); } } peekedValue = asAttributes.peekValue(com.android.internal.R.styleable.AccessibilityService_summary); if (peekedValue != null) { mSummaryResId = peekedValue.resourceId; CharSequence nonLocalizedSummary = peekedValue.coerceToString(); if (nonLocalizedSummary != null) { mNonLocalizedSummary = nonLocalizedSummary.toString().trim(); } } asAttributes.recycle(); } catch (NameNotFoundException e) { throw new XmlPullParserException("Unable to create context for: " + serviceInfo.packageName); } finally { if (parser != null) { parser.close(); } } } /** * Updates the properties that an AccessibilitySerivice can change dynamically. * * @param other The info from which to update the properties. * * @hide */ public void updateDynamicallyConfigurableProperties(AccessibilityServiceInfo other) { eventTypes = other.eventTypes; packageNames = other.packageNames; feedbackType = other.feedbackType; notificationTimeout = other.notificationTimeout; flags = other.flags; } /** * @hide */ public void setComponentName(ComponentName component) { mComponentName = component; } /** * @hide */ public ComponentName getComponentName() { return mComponentName; } /** * The accessibility service id. * <p> * <strong>Generated by the system.</strong> * </p> * @return The id. */ public String getId() { return mComponentName.flattenToShortString(); } /** * The service {@link ResolveInfo}. * <p> * <strong>Generated by the system.</strong> * </p> * @return The info. */ public ResolveInfo getResolveInfo() { return mResolveInfo; } /** * The settings activity name. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The settings activity name. */ public String getSettingsActivityName() { return mSettingsActivityName; } /** * Whether this service can retrieve the current window's content. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return True if window content can be retrieved. * * @deprecated Use {@link #getCapabilities()}. */ public boolean getCanRetrieveWindowContent() { return (mCapabilities & CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT) != 0; } /** * Returns the bit mask of capabilities this accessibility service has such as * being able to retrieve the active window content, etc. * * @return The capability bit mask. * * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION * @see #CAPABILITY_CAN_PERFORM_GESTURES */ public int getCapabilities() { return mCapabilities; } /** * Sets the bit mask of capabilities this accessibility service has such as * being able to retrieve the active window content, etc. * * @param capabilities The capability bit mask. * * @see #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT * @see #CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION * @see #CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS * @see #CAPABILITY_CAN_CONTROL_MAGNIFICATION * @see #CAPABILITY_CAN_PERFORM_GESTURES * * @hide */ @UnsupportedAppUsage public void setCapabilities(int capabilities) { mCapabilities = capabilities; } /** * The localized summary of the accessibility service. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The localized summary if available, and {@code null} if a summary * has not been provided. */ public CharSequence loadSummary(PackageManager packageManager) { if (mSummaryResId == 0) { return mNonLocalizedSummary; } ServiceInfo serviceInfo = mResolveInfo.serviceInfo; CharSequence summary = packageManager.getText(serviceInfo.packageName, mSummaryResId, serviceInfo.applicationInfo); if (summary != null) { return summary.toString().trim(); } return null; } /** * Gets the non-localized description of the accessibility service. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The description. * * @deprecated Use {@link #loadDescription(PackageManager)}. */ public String getDescription() { return mNonLocalizedDescription; } /** * The localized description of the accessibility service. * <p> * <strong>Statically set from * {@link AccessibilityService#SERVICE_META_DATA meta-data}.</strong> * </p> * @return The localized description. */ public String loadDescription(PackageManager packageManager) { if (mDescriptionResId == 0) { return mNonLocalizedDescription; } ServiceInfo serviceInfo = mResolveInfo.serviceInfo; CharSequence description = packageManager.getText(serviceInfo.packageName, mDescriptionResId, serviceInfo.applicationInfo); if (description != null) { return description.toString().trim(); } return null; } /** {@hide} */ public boolean isDirectBootAware() { return ((flags & FLAG_FORCE_DIRECT_BOOT_AWARE) != 0) || mResolveInfo.serviceInfo.directBootAware; } /** * {@inheritDoc} */ public int describeContents() { return 0; } public void writeToParcel(Parcel parcel, int flagz) { parcel.writeInt(eventTypes); parcel.writeStringArray(packageNames); parcel.writeInt(feedbackType); parcel.writeLong(notificationTimeout); parcel.writeInt(flags); parcel.writeInt(crashed ? 1 : 0); parcel.writeParcelable(mComponentName, flagz); parcel.writeParcelable(mResolveInfo, 0); parcel.writeString(mSettingsActivityName); parcel.writeInt(mCapabilities); parcel.writeInt(mSummaryResId); parcel.writeString(mNonLocalizedSummary); parcel.writeInt(mDescriptionResId); parcel.writeString(mNonLocalizedDescription); } private void initFromParcel(Parcel parcel) { eventTypes = parcel.readInt(); packageNames = parcel.readStringArray(); feedbackType = parcel.readInt(); notificationTimeout = parcel.readLong(); flags = parcel.readInt(); crashed = parcel.readInt() != 0; mComponentName = parcel.readParcelable(this.getClass().getClassLoader()); mResolveInfo = parcel.readParcelable(null); mSettingsActivityName = parcel.readString(); mCapabilities = parcel.readInt(); mSummaryResId = parcel.readInt(); mNonLocalizedSummary = parcel.readString(); mDescriptionResId = parcel.readInt(); mNonLocalizedDescription = parcel.readString(); } @Override public int hashCode() { return 31 * 1 + ((mComponentName == null) ? 0 : mComponentName.hashCode()); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } AccessibilityServiceInfo other = (AccessibilityServiceInfo) obj; if (mComponentName == null) { if (other.mComponentName != null) { return false; } } else if (!mComponentName.equals(other.mComponentName)) { return false; } return true; } @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); appendEventTypes(stringBuilder, eventTypes); stringBuilder.append(", "); appendPackageNames(stringBuilder, packageNames); stringBuilder.append(", "); appendFeedbackTypes(stringBuilder, feedbackType); stringBuilder.append(", "); stringBuilder.append("notificationTimeout: ").append(notificationTimeout); stringBuilder.append(", "); appendFlags(stringBuilder, flags); stringBuilder.append(", "); stringBuilder.append("id: ").append(getId()); stringBuilder.append(", "); stringBuilder.append("resolveInfo: ").append(mResolveInfo); stringBuilder.append(", "); stringBuilder.append("settingsActivityName: ").append(mSettingsActivityName); stringBuilder.append(", "); stringBuilder.append("summary: ").append(mNonLocalizedSummary); stringBuilder.append(", "); appendCapabilities(stringBuilder, mCapabilities); return stringBuilder.toString(); } private static void appendFeedbackTypes(StringBuilder stringBuilder, @FeedbackType int feedbackTypes) { stringBuilder.append("feedbackTypes:"); stringBuilder.append("["); while (feedbackTypes != 0) { final int feedbackTypeBit = (1 << Integer.numberOfTrailingZeros(feedbackTypes)); stringBuilder.append(feedbackTypeToString(feedbackTypeBit)); feedbackTypes &= ~feedbackTypeBit; if (feedbackTypes != 0) { stringBuilder.append(", "); } } stringBuilder.append("]"); } private static void appendPackageNames(StringBuilder stringBuilder, String[] packageNames) { stringBuilder.append("packageNames:"); stringBuilder.append("["); if (packageNames != null) { final int packageNameCount = packageNames.length; for (int i = 0; i < packageNameCount; i++) { stringBuilder.append(packageNames[i]); if (i < packageNameCount - 1) { stringBuilder.append(", "); } } } stringBuilder.append("]"); } private static void appendEventTypes(StringBuilder stringBuilder, int eventTypes) { stringBuilder.append("eventTypes:"); stringBuilder.append("["); while (eventTypes != 0) { final int eventTypeBit = (1 << Integer.numberOfTrailingZeros(eventTypes)); stringBuilder.append(AccessibilityEvent.eventTypeToString(eventTypeBit)); eventTypes &= ~eventTypeBit; if (eventTypes != 0) { stringBuilder.append(", "); } } stringBuilder.append("]"); } private static void appendFlags(StringBuilder stringBuilder, int flags) { stringBuilder.append("flags:"); stringBuilder.append("["); while (flags != 0) { final int flagBit = (1 << Integer.numberOfTrailingZeros(flags)); stringBuilder.append(flagToString(flagBit)); flags &= ~flagBit; if (flags != 0) { stringBuilder.append(", "); } } stringBuilder.append("]"); } private static void appendCapabilities(StringBuilder stringBuilder, int capabilities) { stringBuilder.append("capabilities:"); stringBuilder.append("["); while (capabilities != 0) { final int capabilityBit = (1 << Integer.numberOfTrailingZeros(capabilities)); stringBuilder.append(capabilityToString(capabilityBit)); capabilities &= ~capabilityBit; if (capabilities != 0) { stringBuilder.append(", "); } } stringBuilder.append("]"); } /** * Returns the string representation of a feedback type. For example, * {@link #FEEDBACK_SPOKEN} is represented by the string FEEDBACK_SPOKEN. * * @param feedbackType The feedback type. * @return The string representation. */ public static String feedbackTypeToString(int feedbackType) { StringBuilder builder = new StringBuilder(); builder.append("["); while (feedbackType != 0) { final int feedbackTypeFlag = 1 << Integer.numberOfTrailingZeros(feedbackType); feedbackType &= ~feedbackTypeFlag; switch (feedbackTypeFlag) { case FEEDBACK_AUDIBLE: if (builder.length() > 1) { builder.append(", "); } builder.append("FEEDBACK_AUDIBLE"); break; case FEEDBACK_HAPTIC: if (builder.length() > 1) { builder.append(", "); } builder.append("FEEDBACK_HAPTIC"); break; case FEEDBACK_GENERIC: if (builder.length() > 1) { builder.append(", "); } builder.append("FEEDBACK_GENERIC"); break; case FEEDBACK_SPOKEN: if (builder.length() > 1) { builder.append(", "); } builder.append("FEEDBACK_SPOKEN"); break; case FEEDBACK_VISUAL: if (builder.length() > 1) { builder.append(", "); } builder.append("FEEDBACK_VISUAL"); break; case FEEDBACK_BRAILLE: if (builder.length() > 1) { builder.append(", "); } builder.append("FEEDBACK_BRAILLE"); break; } } builder.append("]"); return builder.toString(); } /** * Returns the string representation of a flag. For example, * {@link #DEFAULT} is represented by the string DEFAULT. * * @param flag The flag. * @return The string representation. */ public static String flagToString(int flag) { switch (flag) { case DEFAULT: return "DEFAULT"; case FLAG_INCLUDE_NOT_IMPORTANT_VIEWS: return "FLAG_INCLUDE_NOT_IMPORTANT_VIEWS"; case FLAG_REQUEST_TOUCH_EXPLORATION_MODE: return "FLAG_REQUEST_TOUCH_EXPLORATION_MODE"; case FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY: return "FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; case FLAG_REPORT_VIEW_IDS: return "FLAG_REPORT_VIEW_IDS"; case FLAG_REQUEST_FILTER_KEY_EVENTS: return "FLAG_REQUEST_FILTER_KEY_EVENTS"; case FLAG_RETRIEVE_INTERACTIVE_WINDOWS: return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS"; case FLAG_ENABLE_ACCESSIBILITY_VOLUME: return "FLAG_ENABLE_ACCESSIBILITY_VOLUME"; case FLAG_REQUEST_ACCESSIBILITY_BUTTON: return "FLAG_REQUEST_ACCESSIBILITY_BUTTON"; case FLAG_REQUEST_FINGERPRINT_GESTURES: return "FLAG_REQUEST_FINGERPRINT_GESTURES"; default: return null; } } /** * Returns the string representation of a capability. For example, * {@link #CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT} is represented * by the string CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT. * * @param capability The capability. * @return The string representation. */ public static String capabilityToString(int capability) { switch (capability) { case CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT: return "CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT"; case CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION: return "CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION"; case CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY: return "CAPABILITY_CAN_REQUEST_ENHANCED_WEB_ACCESSIBILITY"; case CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS: return "CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS"; case CAPABILITY_CAN_CONTROL_MAGNIFICATION: return "CAPABILITY_CAN_CONTROL_MAGNIFICATION"; case CAPABILITY_CAN_PERFORM_GESTURES: return "CAPABILITY_CAN_PERFORM_GESTURES"; case CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES: return "CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES"; default: return "UNKNOWN"; } } /** * @hide * @return The list of {@link CapabilityInfo} objects. * @deprecated The version that takes a context works better. */ public List<CapabilityInfo> getCapabilityInfos() { return getCapabilityInfos(null); } /** * @hide * @param context A valid context * @return The list of {@link CapabilityInfo} objects. */ public List<CapabilityInfo> getCapabilityInfos(Context context) { if (mCapabilities == 0) { return Collections.emptyList(); } int capabilities = mCapabilities; List<CapabilityInfo> capabilityInfos = new ArrayList<CapabilityInfo>(); SparseArray<CapabilityInfo> capabilityInfoSparseArray = getCapabilityInfoSparseArray(context); while (capabilities != 0) { final int capabilityBit = 1 << Integer.numberOfTrailingZeros(capabilities); capabilities &= ~capabilityBit; CapabilityInfo capabilityInfo = capabilityInfoSparseArray.get(capabilityBit); if (capabilityInfo != null) { capabilityInfos.add(capabilityInfo); } } return capabilityInfos; } private static SparseArray<CapabilityInfo> getCapabilityInfoSparseArray(Context context) { if (sAvailableCapabilityInfos == null) { sAvailableCapabilityInfos = new SparseArray<CapabilityInfo>(); sAvailableCapabilityInfos.put(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT, new CapabilityInfo(CAPABILITY_CAN_RETRIEVE_WINDOW_CONTENT, R.string.capability_title_canRetrieveWindowContent, R.string.capability_desc_canRetrieveWindowContent)); sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION, new CapabilityInfo(CAPABILITY_CAN_REQUEST_TOUCH_EXPLORATION, R.string.capability_title_canRequestTouchExploration, R.string.capability_desc_canRequestTouchExploration)); sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS, new CapabilityInfo(CAPABILITY_CAN_REQUEST_FILTER_KEY_EVENTS, R.string.capability_title_canRequestFilterKeyEvents, R.string.capability_desc_canRequestFilterKeyEvents)); sAvailableCapabilityInfos.put(CAPABILITY_CAN_CONTROL_MAGNIFICATION, new CapabilityInfo(CAPABILITY_CAN_CONTROL_MAGNIFICATION, R.string.capability_title_canControlMagnification, R.string.capability_desc_canControlMagnification)); sAvailableCapabilityInfos.put(CAPABILITY_CAN_PERFORM_GESTURES, new CapabilityInfo(CAPABILITY_CAN_PERFORM_GESTURES, R.string.capability_title_canPerformGestures, R.string.capability_desc_canPerformGestures)); if ((context == null) || fingerprintAvailable(context)) { sAvailableCapabilityInfos.put(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES, new CapabilityInfo(CAPABILITY_CAN_REQUEST_FINGERPRINT_GESTURES, R.string.capability_title_canCaptureFingerprintGestures, R.string.capability_desc_canCaptureFingerprintGestures)); } } return sAvailableCapabilityInfos; } private static boolean fingerprintAvailable(Context context) { return context.getPackageManager().hasSystemFeature(FEATURE_FINGERPRINT) && context.getSystemService(FingerprintManager.class).isHardwareDetected(); } /** * @hide */ public static final class CapabilityInfo { public final int capability; public final int titleResId; public final int descResId; public CapabilityInfo(int capability, int titleResId, int descResId) { this.capability = capability; this.titleResId = titleResId; this.descResId = descResId; } } /** * @see Parcelable.Creator */ public static final Parcelable.Creator<AccessibilityServiceInfo> CREATOR = new Parcelable.Creator<AccessibilityServiceInfo>() { public AccessibilityServiceInfo createFromParcel(Parcel parcel) { AccessibilityServiceInfo info = new AccessibilityServiceInfo(); info.initFromParcel(parcel); return info; } public AccessibilityServiceInfo[] newArray(int size) { return new AccessibilityServiceInfo[size]; } }; }