| /* |
| * Copyright (C) 2011 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.accessibility; |
| |
| import android.accessibilityservice.IAccessibilityServiceConnection; |
| import android.graphics.Rect; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.RemoteException; |
| import android.text.TextUtils; |
| import android.util.SparseArray; |
| import android.util.SparseIntArray; |
| import android.view.View; |
| |
| /** |
| * This class represents a node of the screen content. From the point of |
| * view of an accessibility service the screen content is presented as tree |
| * of accessibility nodes. |
| * |
| * TODO(svertoslavganov): Update the documentation, add sample, and describe |
| * the security policy. |
| */ |
| public class AccessibilityNodeInfo implements Parcelable { |
| |
| private static final boolean DEBUG = false; |
| |
| // Actions. |
| |
| /** |
| * Action that focuses the node. |
| */ |
| public static final int ACTION_FOCUS = 0x00000001; |
| |
| /** |
| * Action that unfocuses the node. |
| */ |
| public static final int ACTION_CLEAR_FOCUS = 0x00000002; |
| |
| /** |
| * Action that selects the node. |
| */ |
| public static final int ACTION_SELECT = 0x00000004; |
| |
| /** |
| * Action that unselects the node. |
| */ |
| public static final int ACTION_CLEAR_SELECTION = 0x00000008; |
| |
| // Boolean attributes. |
| |
| private static final int PROPERTY_CHECKABLE = 0x00000001; |
| |
| private static final int PROPERTY_CHECKED = 0x00000002; |
| |
| private static final int PROPERTY_FOCUSABLE = 0x00000004; |
| |
| private static final int PROPERTY_FOCUSED = 0x00000008; |
| |
| private static final int PROPERTY_SELECTED = 0x00000010; |
| |
| private static final int PROPERTY_CLICKABLE = 0x00000020; |
| |
| private static final int PROPERTY_LONG_CLICKABLE = 0x00000040; |
| |
| private static final int PROPERTY_ENABLED = 0x00000080; |
| |
| private static final int PROPERTY_PASSWORD = 0x00000100; |
| |
| // Readable representations - lazily initialized. |
| private static SparseArray<String> sActionSymbolicNames; |
| |
| // Housekeeping. |
| private static final int MAX_POOL_SIZE = 50; |
| private static final Object sPoolLock = new Object(); |
| private static AccessibilityNodeInfo sPool; |
| private static int sPoolSize; |
| private AccessibilityNodeInfo mNext; |
| private boolean mIsInPool; |
| private boolean mSealed; |
| |
| // Data. |
| private int mAccessibilityViewId = View.NO_ID; |
| private int mAccessibilityWindowId = View.NO_ID; |
| private int mParentAccessibilityViewId = View.NO_ID; |
| private int mBooleanProperties; |
| private final Rect mBounds = new Rect(); |
| |
| private CharSequence mPackageName; |
| private CharSequence mClassName; |
| private CharSequence mText; |
| private CharSequence mContentDescription; |
| |
| private final SparseIntArray mChildAccessibilityIds = new SparseIntArray(); |
| private int mActions; |
| |
| private IAccessibilityServiceConnection mConnection; |
| |
| /** |
| * Hide constructor from clients. |
| */ |
| private AccessibilityNodeInfo() { |
| /* do nothing */ |
| } |
| |
| /** |
| * Sets the source. |
| * |
| * @param source The info source. |
| */ |
| public void setSource(View source) { |
| enforceNotSealed(); |
| mAccessibilityViewId = source.getAccessibilityViewId(); |
| mAccessibilityWindowId = source.getAccessibilityWindowId(); |
| } |
| |
| /** |
| * Gets the id of the window from which the info comes from. |
| * |
| * @return The window id. |
| */ |
| public int getAccessibilityWindowId() { |
| return mAccessibilityWindowId; |
| } |
| |
| /** |
| * Gets the number of children. |
| * |
| * @return The child count. |
| */ |
| public int getChildCount() { |
| return mChildAccessibilityIds.size(); |
| } |
| |
| /** |
| * Get the child at given index. |
| * <p> |
| * <strong> |
| * It is a client responsibility to recycle the received info by |
| * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating |
| * of multiple instances. |
| * </strong> |
| * </p> |
| * @param index The child index. |
| * @return The child node. |
| * |
| * @throws IllegalStateException If called outside of an AccessibilityService. |
| * |
| */ |
| public AccessibilityNodeInfo getChild(int index) { |
| enforceSealed(); |
| final int childAccessibilityViewId = mChildAccessibilityIds.get(index); |
| try { |
| return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, |
| childAccessibilityViewId); |
| } catch (RemoteException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Adds a child. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param child The child. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void addChild(View child) { |
| enforceNotSealed(); |
| final int childAccessibilityViewId = child.getAccessibilityViewId(); |
| final int index = mChildAccessibilityIds.size(); |
| mChildAccessibilityIds.put(index, childAccessibilityViewId); |
| } |
| |
| /** |
| * Gets the actions that can be performed on the node. |
| * |
| * @return The bit mask of with actions. |
| * |
| * @see AccessibilityNodeInfo#ACTION_FOCUS |
| * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS |
| * @see AccessibilityNodeInfo#ACTION_SELECT |
| * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION |
| */ |
| public int getActions() { |
| return mActions; |
| } |
| |
| /** |
| * Adds an action that can be performed on the node. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param action The action. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void addAction(int action) { |
| enforceNotSealed(); |
| mActions |= action; |
| } |
| |
| /** |
| * Performs an action on the node. |
| * <p> |
| * Note: An action can be performed only if the request is made |
| * from an {@link android.accessibilityservice.AccessibilityService}. |
| * </p> |
| * @param action The action to perform. |
| * @return True if the action was performed. |
| * |
| * @throws IllegalStateException If called outside of an AccessibilityService. |
| */ |
| public boolean performAction(int action) { |
| enforceSealed(); |
| try { |
| return mConnection.performAccessibilityAction(mAccessibilityWindowId, |
| mAccessibilityViewId, action); |
| } catch (RemoteException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Gets the unique id identifying this node's parent. |
| * <p> |
| * <strong> |
| * It is a client responsibility to recycle the received info by |
| * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating |
| * of multiple instances. |
| * </strong> |
| * </p> |
| * @return The node's patent id. |
| */ |
| public AccessibilityNodeInfo getParent() { |
| enforceSealed(); |
| try { |
| return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, |
| mParentAccessibilityViewId); |
| } catch (RemoteException e) { |
| return null; |
| } |
| } |
| |
| /** |
| * Sets the parent. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param parent The parent. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setParent(View parent) { |
| enforceNotSealed(); |
| mParentAccessibilityViewId = parent.getAccessibilityViewId(); |
| } |
| |
| /** |
| * Gets the node bounds in parent coordinates. |
| * |
| * @param outBounds The output node bounds. |
| */ |
| public void getBounds(Rect outBounds) { |
| outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom); |
| } |
| |
| /** |
| * Sets the node bounds in parent coordinates. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param bounds The node bounds. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setBounds(Rect bounds) { |
| enforceNotSealed(); |
| mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom); |
| } |
| |
| /** |
| * Gets whether this node is checkable. |
| * |
| * @return True if the node is checkable. |
| */ |
| public boolean isCheckable() { |
| return getBooleanProperty(PROPERTY_CHECKABLE); |
| } |
| |
| /** |
| * Sets whether this node is checkable. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param checkable True if the node is checkable. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setCheckable(boolean checkable) { |
| setBooleanProperty(PROPERTY_CHECKABLE, checkable); |
| } |
| |
| /** |
| * Gets whether this node is checked. |
| * |
| * @return True if the node is checked. |
| */ |
| public boolean isChecked() { |
| return getBooleanProperty(PROPERTY_CHECKED); |
| } |
| |
| /** |
| * Sets whether this node is checked. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param checked True if the node is checked. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setChecked(boolean checked) { |
| setBooleanProperty(PROPERTY_CHECKED, checked); |
| } |
| |
| /** |
| * Gets whether this node is focusable. |
| * |
| * @return True if the node is focusable. |
| */ |
| public boolean isFocusable() { |
| return getBooleanProperty(PROPERTY_FOCUSABLE); |
| } |
| |
| /** |
| * Sets whether this node is focusable. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param focusable True if the node is focusable. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setFocusable(boolean focusable) { |
| setBooleanProperty(PROPERTY_FOCUSABLE, focusable); |
| } |
| |
| /** |
| * Gets whether this node is focused. |
| * |
| * @return True if the node is focused. |
| */ |
| public boolean isFocused() { |
| return getBooleanProperty(PROPERTY_FOCUSED); |
| } |
| |
| /** |
| * Sets whether this node is focused. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param focused True if the node is focused. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setFocused(boolean focused) { |
| setBooleanProperty(PROPERTY_FOCUSED, focused); |
| } |
| |
| /** |
| * Gets whether this node is selected. |
| * |
| * @return True if the node is selected. |
| */ |
| public boolean isSelected() { |
| return getBooleanProperty(PROPERTY_SELECTED); |
| } |
| |
| /** |
| * Sets whether this node is selected. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param selected True if the node is selected. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setSelected(boolean selected) { |
| setBooleanProperty(PROPERTY_SELECTED, selected); |
| } |
| |
| /** |
| * Gets whether this node is clickable. |
| * |
| * @return True if the node is clickable. |
| */ |
| public boolean isClickable() { |
| return getBooleanProperty(PROPERTY_CLICKABLE); |
| } |
| |
| /** |
| * Sets whether this node is clickable. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param clickable True if the node is clickable. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setClickable(boolean clickable) { |
| setBooleanProperty(PROPERTY_CLICKABLE, clickable); |
| } |
| |
| /** |
| * Gets whether this node is long clickable. |
| * |
| * @return True if the node is long clickable. |
| */ |
| public boolean isLongClickable() { |
| return getBooleanProperty(PROPERTY_LONG_CLICKABLE); |
| } |
| |
| /** |
| * Sets whether this node is long clickable. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param longClickable True if the node is long clickable. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setLongClickable(boolean longClickable) { |
| setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable); |
| } |
| |
| /** |
| * Gets whether this node is enabled. |
| * |
| * @return True if the node is enabled. |
| */ |
| public boolean isEnabled() { |
| return getBooleanProperty(PROPERTY_ENABLED); |
| } |
| |
| /** |
| * Sets whether this node is enabled. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param enabled True if the node is enabled. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setEnabled(boolean enabled) { |
| setBooleanProperty(PROPERTY_ENABLED, enabled); |
| } |
| |
| /** |
| * Gets whether this node is a password. |
| * |
| * @return True if the node is a password. |
| */ |
| public boolean isPassword() { |
| return getBooleanProperty(PROPERTY_PASSWORD); |
| } |
| |
| /** |
| * Sets whether this node is a password. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param password True if the node is a password. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setPassword(boolean password) { |
| setBooleanProperty(PROPERTY_PASSWORD, password); |
| } |
| |
| /** |
| * Gets the package this node comes from. |
| * |
| * @return The package name. |
| */ |
| public CharSequence getPackageName() { |
| return mPackageName; |
| } |
| |
| /** |
| * Sets the package this node comes from. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param packageName The package name. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setPackageName(CharSequence packageName) { |
| enforceNotSealed(); |
| mPackageName = packageName; |
| } |
| |
| /** |
| * Gets the class this node comes from. |
| * |
| * @return The class name. |
| */ |
| public CharSequence getClassName() { |
| return mClassName; |
| } |
| |
| /** |
| * Sets the class this node comes from. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param className The class name. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setClassName(CharSequence className) { |
| enforceNotSealed(); |
| mClassName = className; |
| } |
| |
| /** |
| * Gets the text of this node. |
| * |
| * @return The text. |
| */ |
| public CharSequence getText() { |
| return mText; |
| } |
| |
| /** |
| * Sets the text of this node. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param text The text. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setText(CharSequence text) { |
| enforceNotSealed(); |
| mText = text; |
| } |
| |
| /** |
| * Gets the content description of this node. |
| * |
| * @return The content description. |
| */ |
| public CharSequence getContentDescription() { |
| return mContentDescription; |
| } |
| |
| /** |
| * Sets the content description of this node. |
| * <p> |
| * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. |
| * This class is made immutable before being delivered to an AccessibilityService. |
| * </p> |
| * @param contentDescription The content description. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setContentDescription(CharSequence contentDescription) { |
| enforceNotSealed(); |
| mContentDescription = contentDescription; |
| } |
| |
| /** |
| * Gets the value of a boolean property. |
| * |
| * @param property The property. |
| * @return The value. |
| */ |
| private boolean getBooleanProperty(int property) { |
| return (mBooleanProperties & property) != 0; |
| } |
| |
| /** |
| * Sets a boolean property. |
| * |
| * @param property The property. |
| * @param value The value. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| private void setBooleanProperty(int property, boolean value) { |
| enforceNotSealed(); |
| if (value) { |
| mBooleanProperties |= property; |
| } else { |
| mBooleanProperties &= ~property; |
| } |
| } |
| |
| /** |
| * Sets the connection for interacting with the system. |
| * |
| * @param connection The client token. |
| * |
| * @hide |
| */ |
| public final void setConnection(IAccessibilityServiceConnection connection) { |
| mConnection = connection; |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** |
| * Sets if this instance is sealed. |
| * |
| * @param sealed Whether is sealed. |
| * |
| * @hide |
| */ |
| public void setSealed(boolean sealed) { |
| mSealed = sealed; |
| } |
| |
| /** |
| * Gets if this instance is sealed. |
| * |
| * @return Whether is sealed. |
| * |
| * @hide |
| */ |
| public boolean isSealed() { |
| return mSealed; |
| } |
| |
| /** |
| * Enforces that this instance is sealed. |
| * |
| * @throws IllegalStateException If this instance is not sealed. |
| * |
| * @hide |
| */ |
| protected void enforceSealed() { |
| if (!isSealed()) { |
| throw new IllegalStateException("Cannot perform this " |
| + "action on a not sealed instance."); |
| } |
| } |
| |
| /** |
| * Enforces that this instance is not sealed. |
| * |
| * @throws IllegalStateException If this instance is sealed. |
| * |
| * @hide |
| */ |
| protected void enforceNotSealed() { |
| if (isSealed()) { |
| throw new IllegalStateException("Cannot perform this " |
| + "action on an sealed instance."); |
| } |
| } |
| |
| /** |
| * Returns a cached instance if such is available otherwise a new one |
| * and sets the source. |
| * |
| * @return An instance. |
| * |
| * @see #setSource(View) |
| */ |
| public static AccessibilityNodeInfo obtain(View source) { |
| AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); |
| info.setSource(source); |
| return info; |
| } |
| |
| /** |
| * Returns a cached instance if such is available otherwise a new one. |
| * |
| * @return An instance. |
| */ |
| public static AccessibilityNodeInfo obtain() { |
| synchronized (sPoolLock) { |
| if (sPool != null) { |
| AccessibilityNodeInfo info = sPool; |
| sPool = sPool.mNext; |
| sPoolSize--; |
| info.mNext = null; |
| info.mIsInPool = false; |
| return info; |
| } |
| return new AccessibilityNodeInfo(); |
| } |
| } |
| |
| /** |
| * Return an instance back to be reused. |
| * <p> |
| * <b>Note: You must not touch the object after calling this function.</b> |
| * |
| * @throws IllegalStateException If the info is already recycled. |
| */ |
| public void recycle() { |
| if (mIsInPool) { |
| throw new IllegalStateException("Info already recycled!"); |
| } |
| clear(); |
| synchronized (sPoolLock) { |
| if (sPoolSize <= MAX_POOL_SIZE) { |
| mNext = sPool; |
| sPool = this; |
| mIsInPool = true; |
| sPoolSize++; |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| * <p> |
| * <b>Note: After the instance is written to a parcel it is recycled. |
| * You must not touch the object after calling this function.</b> |
| * </p> |
| */ |
| public void writeToParcel(Parcel parcel, int flags) { |
| if (mConnection == null) { |
| parcel.writeInt(0); |
| } else { |
| parcel.writeInt(1); |
| parcel.writeStrongBinder(mConnection.asBinder()); |
| } |
| parcel.writeInt(isSealed() ? 1 : 0); |
| parcel.writeInt(mAccessibilityViewId); |
| parcel.writeInt(mAccessibilityWindowId); |
| parcel.writeInt(mParentAccessibilityViewId); |
| |
| SparseIntArray childIds = mChildAccessibilityIds; |
| final int childIdsSize = childIds.size(); |
| parcel.writeInt(childIdsSize); |
| for (int i = 0; i < childIdsSize; i++) { |
| parcel.writeInt(childIds.valueAt(i)); |
| } |
| |
| parcel.writeInt(mBounds.top); |
| parcel.writeInt(mBounds.bottom); |
| parcel.writeInt(mBounds.left); |
| parcel.writeInt(mBounds.right); |
| |
| parcel.writeInt(mActions); |
| |
| parcel.writeInt(mBooleanProperties); |
| |
| TextUtils.writeToParcel(mPackageName, parcel, flags); |
| TextUtils.writeToParcel(mClassName, parcel, flags); |
| TextUtils.writeToParcel(mText, parcel, flags); |
| TextUtils.writeToParcel(mContentDescription, parcel, flags); |
| |
| // Since instances of this class are fetched via synchronous i.e. blocking |
| // calls in IPCs and we always recycle as soon as the instance is marshaled. |
| recycle(); |
| } |
| |
| /** |
| * Creates a new instance from a {@link Parcel}. |
| * |
| * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. |
| */ |
| private void initFromParcel(Parcel parcel) { |
| if (parcel.readInt() == 1) { |
| mConnection = IAccessibilityServiceConnection.Stub.asInterface( |
| parcel.readStrongBinder()); |
| } |
| mSealed = (parcel.readInt() == 1); |
| mAccessibilityViewId = parcel.readInt(); |
| mAccessibilityWindowId = parcel.readInt(); |
| mParentAccessibilityViewId = parcel.readInt(); |
| |
| SparseIntArray childIds = mChildAccessibilityIds; |
| final int childrenSize = parcel.readInt(); |
| for (int i = 0; i < childrenSize; i++) { |
| final int childId = parcel.readInt(); |
| childIds.put(i, childId); |
| } |
| |
| mBounds.top = parcel.readInt(); |
| mBounds.bottom = parcel.readInt(); |
| mBounds.left = parcel.readInt(); |
| mBounds.right = parcel.readInt(); |
| |
| mActions = parcel.readInt(); |
| |
| mBooleanProperties = parcel.readInt(); |
| |
| mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); |
| } |
| |
| /** |
| * Clears the state of this instance. |
| */ |
| private void clear() { |
| mSealed = false; |
| mConnection = null; |
| mAccessibilityViewId = View.NO_ID; |
| mParentAccessibilityViewId = View.NO_ID; |
| mChildAccessibilityIds.clear(); |
| mBounds.set(0, 0, 0, 0); |
| mBooleanProperties = 0; |
| mPackageName = null; |
| mClassName = null; |
| mText = null; |
| mContentDescription = null; |
| mActions = 0; |
| } |
| |
| /** |
| * Gets the human readable action symbolic name. |
| * |
| * @param action The action. |
| * @return The symbolic name. |
| */ |
| private static String getActionSymbolicName(int action) { |
| SparseArray<String> actionSymbolicNames = sActionSymbolicNames; |
| if (actionSymbolicNames == null) { |
| actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>(); |
| actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS"); |
| actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS"); |
| actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT"); |
| actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT"); |
| } |
| return actionSymbolicNames.get(action); |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| if (this == object) { |
| return true; |
| } |
| if (object == null) { |
| return false; |
| } |
| if (getClass() != object.getClass()) { |
| return false; |
| } |
| AccessibilityNodeInfo other = (AccessibilityNodeInfo) object; |
| if (mAccessibilityViewId != other.mAccessibilityViewId) { |
| return false; |
| } |
| if (mAccessibilityWindowId != other.mAccessibilityWindowId) { |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + mAccessibilityViewId; |
| result = prime * result + mAccessibilityWindowId; |
| return result; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| builder.append(super.toString()); |
| |
| if (DEBUG) { |
| builder.append("; accessibilityId: " + mAccessibilityViewId); |
| builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId); |
| SparseIntArray childIds = mChildAccessibilityIds; |
| builder.append("; childAccessibilityIds: ["); |
| for (int i = 0, count = childIds.size(); i < count; i++) { |
| builder.append(childIds.valueAt(i)); |
| if (i < count - 1) { |
| builder.append(", "); |
| } |
| } |
| builder.append("]"); |
| } |
| |
| builder.append("; bounds: " + mBounds); |
| |
| builder.append("; packageName: ").append(mPackageName); |
| builder.append("; className: ").append(mClassName); |
| builder.append("; text: ").append(mText); |
| builder.append("; contentDescription: ").append(mContentDescription); |
| |
| builder.append("; checkable: ").append(isCheckable()); |
| builder.append("; checked: ").append(isChecked()); |
| builder.append("; focusable: ").append(isFocusable()); |
| builder.append("; focused: ").append(isFocused()); |
| builder.append("; selected: ").append(isSelected()); |
| builder.append("; clickable: ").append(isClickable()); |
| builder.append("; longClickable: ").append(isLongClickable()); |
| builder.append("; enabled: ").append(isEnabled()); |
| builder.append("; password: ").append(isPassword()); |
| |
| builder.append("; ["); |
| |
| for (int actionBits = mActions; actionBits != 0;) { |
| final int action = 1 << Integer.numberOfTrailingZeros(actionBits); |
| actionBits &= ~action; |
| builder.append(getActionSymbolicName(action)); |
| if (actionBits != 0) { |
| builder.append(", "); |
| } |
| } |
| |
| builder.append("]"); |
| |
| return builder.toString(); |
| } |
| |
| /** |
| * @see Parcelable.Creator |
| */ |
| public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = |
| new Parcelable.Creator<AccessibilityNodeInfo>() { |
| public AccessibilityNodeInfo createFromParcel(Parcel parcel) { |
| AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); |
| info.initFromParcel(parcel); |
| return info; |
| } |
| |
| public AccessibilityNodeInfo[] newArray(int size) { |
| return new AccessibilityNodeInfo[size]; |
| } |
| }; |
| } |