Home | History | Annotate | Download | only in accessibility
      1 /*
      2  * Copyright (C) 2011 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view.accessibility;
     18 
     19 import android.accessibilityservice.AccessibilityServiceInfo;
     20 import android.annotation.Nullable;
     21 import android.graphics.Rect;
     22 import android.os.Bundle;
     23 import android.os.Parcel;
     24 import android.os.Parcelable;
     25 import android.text.InputType;
     26 import android.text.TextUtils;
     27 import android.util.ArraySet;
     28 import android.util.LongArray;
     29 import android.util.Pools.SynchronizedPool;
     30 import android.view.View;
     31 
     32 import java.util.ArrayList;
     33 import java.util.Collections;
     34 import java.util.List;
     35 
     36 /**
     37  * This class represents a node of the window content as well as actions that
     38  * can be requested from its source. From the point of view of an
     39  * {@link android.accessibilityservice.AccessibilityService} a window content is
     40  * presented as tree of accessibility node info which may or may not map one-to-one
     41  * to the view hierarchy. In other words, a custom view is free to report itself as
     42  * a tree of accessibility node info.
     43  * </p>
     44  * <p>
     45  * Once an accessibility node info is delivered to an accessibility service it is
     46  * made immutable and calling a state mutation method generates an error.
     47  * </p>
     48  * <p>
     49  * Please refer to {@link android.accessibilityservice.AccessibilityService} for
     50  * details about how to obtain a handle to window content as a tree of accessibility
     51  * node info as well as familiarizing with the security model.
     52  * </p>
     53  * <div class="special reference">
     54  * <h3>Developer Guides</h3>
     55  * <p>For more information about making applications accessible, read the
     56  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
     57  * developer guide.</p>
     58  * </div>
     59  *
     60  * @see android.accessibilityservice.AccessibilityService
     61  * @see AccessibilityEvent
     62  * @see AccessibilityManager
     63  */
     64 public class AccessibilityNodeInfo implements Parcelable {
     65 
     66     private static final boolean DEBUG = false;
     67 
     68     /** @hide */
     69     public static final int UNDEFINED_CONNECTION_ID = -1;
     70 
     71     /** @hide */
     72     public static final int UNDEFINED_SELECTION_INDEX = -1;
     73 
     74     /** @hide */
     75     public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE;
     76 
     77     /** @hide */
     78     public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
     79 
     80     /** @hide */
     81     public static final int ACTIVE_WINDOW_ID = UNDEFINED_ITEM_ID;
     82 
     83     /** @hide */
     84     public static final int ANY_WINDOW_ID = -2;
     85 
     86     /** @hide */
     87     public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
     88 
     89     /** @hide */
     90     public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
     91 
     92     /** @hide */
     93     public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
     94 
     95     /** @hide */
     96     public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
     97 
     98     /** @hide */
     99     public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
    100 
    101     // Actions.
    102 
    103     /**
    104      * Action that gives input focus to the node.
    105      */
    106     public static final int ACTION_FOCUS =  0x00000001;
    107 
    108     /**
    109      * Action that clears input focus of the node.
    110      */
    111     public static final int ACTION_CLEAR_FOCUS = 0x00000002;
    112 
    113     /**
    114      * Action that selects the node.
    115      */
    116     public static final int ACTION_SELECT = 0x00000004;
    117 
    118     /**
    119      * Action that deselects the node.
    120      */
    121     public static final int ACTION_CLEAR_SELECTION = 0x00000008;
    122 
    123     /**
    124      * Action that clicks on the node info.
    125      */
    126     public static final int ACTION_CLICK = 0x00000010;
    127 
    128     /**
    129      * Action that long clicks on the node.
    130      */
    131     public static final int ACTION_LONG_CLICK = 0x00000020;
    132 
    133     /**
    134      * Action that gives accessibility focus to the node.
    135      */
    136     public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
    137 
    138     /**
    139      * Action that clears accessibility focus of the node.
    140      */
    141     public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
    142 
    143     /**
    144      * Action that requests to go to the next entity in this node's text
    145      * at a given movement granularity. For example, move to the next character,
    146      * word, etc.
    147      * <p>
    148      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
    149      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
    150      * <strong>Example:</strong> Move to the previous character and do not extend selection.
    151      * <code><pre><p>
    152      *   Bundle arguments = new Bundle();
    153      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
    154      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
    155      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
    156      *           false);
    157      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
    158      * </code></pre></p>
    159      * </p>
    160      *
    161      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
    162      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
    163      *
    164      * @see #setMovementGranularities(int)
    165      * @see #getMovementGranularities()
    166      *
    167      * @see #MOVEMENT_GRANULARITY_CHARACTER
    168      * @see #MOVEMENT_GRANULARITY_WORD
    169      * @see #MOVEMENT_GRANULARITY_LINE
    170      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
    171      * @see #MOVEMENT_GRANULARITY_PAGE
    172      */
    173     public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
    174 
    175     /**
    176      * Action that requests to go to the previous entity in this node's text
    177      * at a given movement granularity. For example, move to the next character,
    178      * word, etc.
    179      * <p>
    180      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
    181      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
    182      * <strong>Example:</strong> Move to the next character and do not extend selection.
    183      * <code><pre><p>
    184      *   Bundle arguments = new Bundle();
    185      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
    186      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
    187      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
    188      *           false);
    189      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
    190      *           arguments);
    191      * </code></pre></p>
    192      * </p>
    193      *
    194      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
    195      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
    196      *
    197      * @see #setMovementGranularities(int)
    198      * @see #getMovementGranularities()
    199      *
    200      * @see #MOVEMENT_GRANULARITY_CHARACTER
    201      * @see #MOVEMENT_GRANULARITY_WORD
    202      * @see #MOVEMENT_GRANULARITY_LINE
    203      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
    204      * @see #MOVEMENT_GRANULARITY_PAGE
    205      */
    206     public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
    207 
    208     /**
    209      * Action to move to the next HTML element of a given type. For example, move
    210      * to the BUTTON, INPUT, TABLE, etc.
    211      * <p>
    212      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
    213      * <strong>Example:</strong>
    214      * <code><pre><p>
    215      *   Bundle arguments = new Bundle();
    216      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
    217      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
    218      * </code></pre></p>
    219      * </p>
    220      */
    221     public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
    222 
    223     /**
    224      * Action to move to the previous HTML element of a given type. For example, move
    225      * to the BUTTON, INPUT, TABLE, etc.
    226      * <p>
    227      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
    228      * <strong>Example:</strong>
    229      * <code><pre><p>
    230      *   Bundle arguments = new Bundle();
    231      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
    232      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
    233      * </code></pre></p>
    234      * </p>
    235      */
    236     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
    237 
    238     /**
    239      * Action to scroll the node content forward.
    240      */
    241     public static final int ACTION_SCROLL_FORWARD = 0x00001000;
    242 
    243     /**
    244      * Action to scroll the node content backward.
    245      */
    246     public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
    247 
    248     /**
    249      * Action to copy the current selection to the clipboard.
    250      */
    251     public static final int ACTION_COPY = 0x00004000;
    252 
    253     /**
    254      * Action to paste the current clipboard content.
    255      */
    256     public static final int ACTION_PASTE = 0x00008000;
    257 
    258     /**
    259      * Action to cut the current selection and place it to the clipboard.
    260      */
    261     public static final int ACTION_CUT = 0x00010000;
    262 
    263     /**
    264      * Action to set the selection. Performing this action with no arguments
    265      * clears the selection.
    266      * <p>
    267      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SELECTION_START_INT},
    268      * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
    269      * <strong>Example:</strong>
    270      * <code><pre><p>
    271      *   Bundle arguments = new Bundle();
    272      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
    273      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
    274      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
    275      * </code></pre></p>
    276      * </p>
    277      *
    278      * @see #ACTION_ARGUMENT_SELECTION_START_INT
    279      * @see #ACTION_ARGUMENT_SELECTION_END_INT
    280      */
    281     public static final int ACTION_SET_SELECTION = 0x00020000;
    282 
    283     /**
    284      * Action to expand an expandable node.
    285      */
    286     public static final int ACTION_EXPAND = 0x00040000;
    287 
    288     /**
    289      * Action to collapse an expandable node.
    290      */
    291     public static final int ACTION_COLLAPSE = 0x00080000;
    292 
    293     /**
    294      * Action to dismiss a dismissable node.
    295      */
    296     public static final int ACTION_DISMISS = 0x00100000;
    297 
    298     /**
    299      * Action that sets the text of the node. Performing the action without argument, using <code>
    300      * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
    301      * cursor at the end of text.
    302      * <p>
    303      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
    304      * <strong>Example:</strong>
    305      * <code><pre><p>
    306      *   Bundle arguments = new Bundle();
    307      *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
    308      *       "android");
    309      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
    310      * </code></pre></p>
    311      */
    312     public static final int ACTION_SET_TEXT = 0x00200000;
    313 
    314     private static final int LAST_LEGACY_STANDARD_ACTION = ACTION_SET_TEXT;
    315 
    316     /**
    317      * Mask to see if the value is larger than the largest ACTION_ constant
    318      */
    319     private static final int ACTION_TYPE_MASK = 0xFF000000;
    320 
    321     // Action arguments
    322 
    323     /**
    324      * Argument for which movement granularity to be used when traversing the node text.
    325      * <p>
    326      * <strong>Type:</strong> int<br>
    327      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
    328      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
    329      * </p>
    330      *
    331      * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
    332      * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
    333      */
    334     public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
    335             "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
    336 
    337     /**
    338      * Argument for which HTML element to get moving to the next/previous HTML element.
    339      * <p>
    340      * <strong>Type:</strong> String<br>
    341      * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
    342      *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
    343      * </p>
    344      *
    345      * @see #ACTION_NEXT_HTML_ELEMENT
    346      * @see #ACTION_PREVIOUS_HTML_ELEMENT
    347      */
    348     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
    349             "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
    350 
    351     /**
    352      * Argument for whether when moving at granularity to extend the selection
    353      * or to move it otherwise.
    354      * <p>
    355      * <strong>Type:</strong> boolean<br>
    356      * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
    357      * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
    358      * </p>
    359      *
    360      * @see #ACTION_NEXT_AT_MOVEMENT_GRANULARITY
    361      * @see #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
    362      */
    363     public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
    364             "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
    365 
    366     /**
    367      * Argument for specifying the selection start.
    368      * <p>
    369      * <strong>Type:</strong> int<br>
    370      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
    371      * </p>
    372      *
    373      * @see #ACTION_SET_SELECTION
    374      */
    375     public static final String ACTION_ARGUMENT_SELECTION_START_INT =
    376             "ACTION_ARGUMENT_SELECTION_START_INT";
    377 
    378     /**
    379      * Argument for specifying the selection end.
    380      * <p>
    381      * <strong>Type:</strong> int<br>
    382      * <strong>Actions:</strong> {@link #ACTION_SET_SELECTION}
    383      * </p>
    384      *
    385      * @see #ACTION_SET_SELECTION
    386      */
    387     public static final String ACTION_ARGUMENT_SELECTION_END_INT =
    388             "ACTION_ARGUMENT_SELECTION_END_INT";
    389 
    390     /**
    391      * Argument for specifying the text content to set
    392      * <p>
    393      * <strong>Type:</strong> CharSequence<br>
    394      * <strong>Actions:</strong> {@link #ACTION_SET_TEXT}
    395      * </p>
    396      *
    397      * @see #ACTION_SET_TEXT
    398      */
    399     public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
    400             "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
    401 
    402     // Focus types
    403 
    404     /**
    405      * The input focus.
    406      */
    407     public static final int FOCUS_INPUT = 1;
    408 
    409     /**
    410      * The accessibility focus.
    411      */
    412     public static final int FOCUS_ACCESSIBILITY = 2;
    413 
    414     // Movement granularities
    415 
    416     /**
    417      * Movement granularity bit for traversing the text of a node by character.
    418      */
    419     public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
    420 
    421     /**
    422      * Movement granularity bit for traversing the text of a node by word.
    423      */
    424     public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
    425 
    426     /**
    427      * Movement granularity bit for traversing the text of a node by line.
    428      */
    429     public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
    430 
    431     /**
    432      * Movement granularity bit for traversing the text of a node by paragraph.
    433      */
    434     public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
    435 
    436     /**
    437      * Movement granularity bit for traversing the text of a node by page.
    438      */
    439     public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
    440 
    441     // Boolean attributes.
    442 
    443     private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001;
    444 
    445     private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002;
    446 
    447     private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004;
    448 
    449     private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008;
    450 
    451     private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010;
    452 
    453     private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020;
    454 
    455     private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040;
    456 
    457     private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080;
    458 
    459     private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100;
    460 
    461     private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200;
    462 
    463     private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
    464 
    465     private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800;
    466 
    467     private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000;
    468 
    469     private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00002000;
    470 
    471     private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00004000;
    472 
    473     private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00008000;
    474 
    475     private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
    476 
    477     /**
    478      * Bits that provide the id of a virtual descendant of a view.
    479      */
    480     private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
    481 
    482     /**
    483      * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
    484      * virtual descendant of a view. Such a descendant does not exist in the view
    485      * hierarchy and is only reported via the accessibility APIs.
    486      */
    487     private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
    488 
    489     /**
    490      * Gets the accessibility view id which identifies a View in the view three.
    491      *
    492      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
    493      * @return The accessibility view id part of the node id.
    494      *
    495      * @hide
    496      */
    497     public static int getAccessibilityViewId(long accessibilityNodeId) {
    498         return (int) accessibilityNodeId;
    499     }
    500 
    501     /**
    502      * Gets the virtual descendant id which identifies an imaginary view in a
    503      * containing View.
    504      *
    505      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
    506      * @return The virtual view id part of the node id.
    507      *
    508      * @hide
    509      */
    510     public static int getVirtualDescendantId(long accessibilityNodeId) {
    511         return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK)
    512                 >> VIRTUAL_DESCENDANT_ID_SHIFT);
    513     }
    514 
    515     /**
    516      * Makes a node id by shifting the <code>virtualDescendantId</code>
    517      * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
    518      * the bitwise or with the <code>accessibilityViewId</code>.
    519      *
    520      * @param accessibilityViewId A View accessibility id.
    521      * @param virtualDescendantId A virtual descendant id.
    522      * @return The node id.
    523      *
    524      * @hide
    525      */
    526     public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
    527         // We changed the value for undefined node to positive due to wrong
    528         // global id composition (two 32-bin ints into one 64-bit long) but
    529         // the value used for the host node provider view has id -1 so we
    530         // remap it here.
    531         if (virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
    532             virtualDescendantId = UNDEFINED_ITEM_ID;
    533         }
    534         return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
    535     }
    536 
    537     // Housekeeping.
    538     private static final int MAX_POOL_SIZE = 50;
    539     private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
    540             new SynchronizedPool<AccessibilityNodeInfo>(MAX_POOL_SIZE);
    541 
    542     private boolean mSealed;
    543 
    544     // Data.
    545     private int mWindowId = UNDEFINED_ITEM_ID;
    546     private long mSourceNodeId = ROOT_NODE_ID;
    547     private long mParentNodeId = ROOT_NODE_ID;
    548     private long mLabelForId = ROOT_NODE_ID;
    549     private long mLabeledById = ROOT_NODE_ID;
    550     private long mTraversalBefore = ROOT_NODE_ID;
    551     private long mTraversalAfter = ROOT_NODE_ID;
    552 
    553     private int mBooleanProperties;
    554     private final Rect mBoundsInParent = new Rect();
    555     private final Rect mBoundsInScreen = new Rect();
    556 
    557     private CharSequence mPackageName;
    558     private CharSequence mClassName;
    559     private CharSequence mText;
    560     private CharSequence mError;
    561     private CharSequence mContentDescription;
    562     private String mViewIdResourceName;
    563 
    564     private LongArray mChildNodeIds;
    565     private ArrayList<AccessibilityAction> mActions;
    566 
    567     private int mMaxTextLength = -1;
    568     private int mMovementGranularities;
    569 
    570     private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
    571     private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
    572     private int mInputType = InputType.TYPE_NULL;
    573     private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
    574 
    575     private Bundle mExtras;
    576 
    577     private int mConnectionId = UNDEFINED_CONNECTION_ID;
    578 
    579     private RangeInfo mRangeInfo;
    580     private CollectionInfo mCollectionInfo;
    581     private CollectionItemInfo mCollectionItemInfo;
    582 
    583     /**
    584      * Hide constructor from clients.
    585      */
    586     private AccessibilityNodeInfo() {
    587         /* do nothing */
    588     }
    589 
    590     /**
    591      * Sets the source.
    592      * <p>
    593      *   <strong>Note:</strong> Cannot be called from an
    594      *   {@link android.accessibilityservice.AccessibilityService}.
    595      *   This class is made immutable before being delivered to an AccessibilityService.
    596      * </p>
    597      *
    598      * @param source The info source.
    599      */
    600     public void setSource(View source) {
    601         setSource(source, UNDEFINED_ITEM_ID);
    602     }
    603 
    604     /**
    605      * Sets the source to be a virtual descendant of the given <code>root</code>.
    606      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
    607      * is set as the source.
    608      * <p>
    609      * A virtual descendant is an imaginary View that is reported as a part of the view
    610      * hierarchy for accessibility purposes. This enables custom views that draw complex
    611      * content to report themselves as a tree of virtual views, thus conveying their
    612      * logical structure.
    613      * </p>
    614      * <p>
    615      *   <strong>Note:</strong> Cannot be called from an
    616      *   {@link android.accessibilityservice.AccessibilityService}.
    617      *   This class is made immutable before being delivered to an AccessibilityService.
    618      * </p>
    619      *
    620      * @param root The root of the virtual subtree.
    621      * @param virtualDescendantId The id of the virtual descendant.
    622      */
    623     public void setSource(View root, int virtualDescendantId) {
    624         enforceNotSealed();
    625         mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID;
    626         final int rootAccessibilityViewId =
    627             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
    628         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    629     }
    630 
    631     /**
    632      * Find the view that has the specified focus type. The search starts from
    633      * the view represented by this node info.
    634      *
    635      * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
    636      *         {@link #FOCUS_ACCESSIBILITY}.
    637      * @return The node info of the focused view or null.
    638      *
    639      * @see #FOCUS_INPUT
    640      * @see #FOCUS_ACCESSIBILITY
    641      */
    642     public AccessibilityNodeInfo findFocus(int focus) {
    643         enforceSealed();
    644         enforceValidFocusType(focus);
    645         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    646             return null;
    647         }
    648         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
    649                 mSourceNodeId, focus);
    650     }
    651 
    652     /**
    653      * Searches for the nearest view in the specified direction that can take
    654      * the input focus.
    655      *
    656      * @param direction The direction. Can be one of:
    657      *     {@link View#FOCUS_DOWN},
    658      *     {@link View#FOCUS_UP},
    659      *     {@link View#FOCUS_LEFT},
    660      *     {@link View#FOCUS_RIGHT},
    661      *     {@link View#FOCUS_FORWARD},
    662      *     {@link View#FOCUS_BACKWARD}.
    663      *
    664      * @return The node info for the view that can take accessibility focus.
    665      */
    666     public AccessibilityNodeInfo focusSearch(int direction) {
    667         enforceSealed();
    668         enforceValidFocusDirection(direction);
    669         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    670             return null;
    671         }
    672         return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
    673                 mSourceNodeId, direction);
    674     }
    675 
    676     /**
    677      * Gets the id of the window from which the info comes from.
    678      *
    679      * @return The window id.
    680      */
    681     public int getWindowId() {
    682         return mWindowId;
    683     }
    684 
    685     /**
    686      * Refreshes this info with the latest state of the view it represents.
    687      * <p>
    688      * <strong>Note:</strong> If this method returns false this info is obsolete
    689      * since it represents a view that is no longer in the view tree and should
    690      * be recycled.
    691      * </p>
    692      *
    693      * @param bypassCache Whether to bypass the cache.
    694      * @return Whether the refresh succeeded.
    695      *
    696      * @hide
    697      */
    698     public boolean refresh(boolean bypassCache) {
    699         enforceSealed();
    700         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    701             return false;
    702         }
    703         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    704         AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId(
    705                 mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0);
    706         if (refreshedInfo == null) {
    707             return false;
    708         }
    709         init(refreshedInfo);
    710         refreshedInfo.recycle();
    711         return true;
    712     }
    713 
    714     /**
    715      * Refreshes this info with the latest state of the view it represents.
    716      * <p>
    717      * <strong>Note:</strong> If this method returns false this info is obsolete
    718      * since it represents a view that is no longer in the view tree and should
    719      * be recycled.
    720      * </p>
    721      * @return Whether the refresh succeeded.
    722      */
    723     public boolean refresh() {
    724         return refresh(true);
    725     }
    726 
    727     /**
    728      * Returns the array containing the IDs of this node's children.
    729      *
    730      * @hide
    731      */
    732     public LongArray getChildNodeIds() {
    733         return mChildNodeIds;
    734     }
    735 
    736     /**
    737      * Returns the id of the child at the specified index.
    738      *
    739      * @throws IndexOutOfBoundsException when index &lt; 0 || index &gt;=
    740      *             getChildCount()
    741      * @hide
    742      */
    743     public long getChildId(int index) {
    744         if (mChildNodeIds == null) {
    745             throw new IndexOutOfBoundsException();
    746         }
    747         return mChildNodeIds.get(index);
    748     }
    749 
    750     /**
    751      * Gets the number of children.
    752      *
    753      * @return The child count.
    754      */
    755     public int getChildCount() {
    756         return mChildNodeIds == null ? 0 : mChildNodeIds.size();
    757     }
    758 
    759     /**
    760      * Get the child at given index.
    761      * <p>
    762      *   <strong>Note:</strong> It is a client responsibility to recycle the
    763      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    764      *     to avoid creating of multiple instances.
    765      * </p>
    766      *
    767      * @param index The child index.
    768      * @return The child node.
    769      *
    770      * @throws IllegalStateException If called outside of an AccessibilityService.
    771      *
    772      */
    773     public AccessibilityNodeInfo getChild(int index) {
    774         enforceSealed();
    775         if (mChildNodeIds == null) {
    776             return null;
    777         }
    778         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    779             return null;
    780         }
    781         final long childId = mChildNodeIds.get(index);
    782         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    783         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
    784                 childId, false, FLAG_PREFETCH_DESCENDANTS);
    785     }
    786 
    787     /**
    788      * Adds a child.
    789      * <p>
    790      * <strong>Note:</strong> Cannot be called from an
    791      * {@link android.accessibilityservice.AccessibilityService}.
    792      * This class is made immutable before being delivered to an AccessibilityService.
    793      * </p>
    794      *
    795      * @param child The child.
    796      *
    797      * @throws IllegalStateException If called from an AccessibilityService.
    798      */
    799     public void addChild(View child) {
    800         addChildInternal(child, UNDEFINED_ITEM_ID, true);
    801     }
    802 
    803     /**
    804      * Unchecked version of {@link #addChild(View)} that does not verify
    805      * uniqueness. For framework use only.
    806      *
    807      * @hide
    808      */
    809     public void addChildUnchecked(View child) {
    810         addChildInternal(child, UNDEFINED_ITEM_ID, false);
    811     }
    812 
    813     /**
    814      * Removes a child. If the child was not previously added to the node,
    815      * calling this method has no effect.
    816      * <p>
    817      * <strong>Note:</strong> Cannot be called from an
    818      * {@link android.accessibilityservice.AccessibilityService}.
    819      * This class is made immutable before being delivered to an AccessibilityService.
    820      * </p>
    821      *
    822      * @param child The child.
    823      * @return true if the child was present
    824      *
    825      * @throws IllegalStateException If called from an AccessibilityService.
    826      */
    827     public boolean removeChild(View child) {
    828         return removeChild(child, UNDEFINED_ITEM_ID);
    829     }
    830 
    831     /**
    832      * Adds a virtual child which is a descendant of the given <code>root</code>.
    833      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
    834      * is added as a child.
    835      * <p>
    836      * A virtual descendant is an imaginary View that is reported as a part of the view
    837      * hierarchy for accessibility purposes. This enables custom views that draw complex
    838      * content to report them selves as a tree of virtual views, thus conveying their
    839      * logical structure.
    840      * </p>
    841      *
    842      * @param root The root of the virtual subtree.
    843      * @param virtualDescendantId The id of the virtual child.
    844      */
    845     public void addChild(View root, int virtualDescendantId) {
    846         addChildInternal(root, virtualDescendantId, true);
    847     }
    848 
    849     private void addChildInternal(View root, int virtualDescendantId, boolean checked) {
    850         enforceNotSealed();
    851         if (mChildNodeIds == null) {
    852             mChildNodeIds = new LongArray();
    853         }
    854         final int rootAccessibilityViewId =
    855             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
    856         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    857         // If we're checking uniqueness and the ID already exists, abort.
    858         if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) {
    859             return;
    860         }
    861         mChildNodeIds.add(childNodeId);
    862     }
    863 
    864     /**
    865      * Removes a virtual child which is a descendant of the given
    866      * <code>root</code>. If the child was not previously added to the node,
    867      * calling this method has no effect.
    868      *
    869      * @param root The root of the virtual subtree.
    870      * @param virtualDescendantId The id of the virtual child.
    871      * @return true if the child was present
    872      * @see #addChild(View, int)
    873      */
    874     public boolean removeChild(View root, int virtualDescendantId) {
    875         enforceNotSealed();
    876         final LongArray childIds = mChildNodeIds;
    877         if (childIds == null) {
    878             return false;
    879         }
    880         final int rootAccessibilityViewId =
    881                 (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
    882         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    883         final int index = childIds.indexOf(childNodeId);
    884         if (index < 0) {
    885             return false;
    886         }
    887         childIds.remove(index);
    888         return true;
    889     }
    890 
    891     /**
    892      * Gets the actions that can be performed on the node.
    893      */
    894     public List<AccessibilityAction> getActionList() {
    895         if (mActions == null) {
    896             return Collections.emptyList();
    897         }
    898 
    899         return mActions;
    900     }
    901 
    902     /**
    903      * Gets the actions that can be performed on the node.
    904      *
    905      * @return The bit mask of with actions.
    906      *
    907      * @see AccessibilityNodeInfo#ACTION_FOCUS
    908      * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
    909      * @see AccessibilityNodeInfo#ACTION_SELECT
    910      * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
    911      * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
    912      * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
    913      * @see AccessibilityNodeInfo#ACTION_CLICK
    914      * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
    915      * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
    916      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
    917      * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
    918      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
    919      * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
    920      * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
    921      *
    922      * @deprecated Use {@link #getActionList()}.
    923      */
    924     @Deprecated
    925     public int getActions() {
    926         int returnValue = 0;
    927 
    928         if (mActions == null) {
    929             return returnValue;
    930         }
    931 
    932         final int actionSize = mActions.size();
    933         for (int i = 0; i < actionSize; i++) {
    934             int actionId = mActions.get(i).getId();
    935             if (actionId <= LAST_LEGACY_STANDARD_ACTION) {
    936                 returnValue |= actionId;
    937             }
    938         }
    939 
    940         return returnValue;
    941     }
    942 
    943     /**
    944      * Adds an action that can be performed on the node.
    945      * <p>
    946      * To add a standard action use the static constants on {@link AccessibilityAction}.
    947      * To add a custom action create a new {@link AccessibilityAction} by passing in a
    948      * resource id from your application as the action id and an optional label that
    949      * describes the action. To override one of the standard actions use as the action
    950      * id of a standard action id such as {@link #ACTION_CLICK} and an optional label that
    951      * describes the action.
    952      * </p>
    953      * <p>
    954      *   <strong>Note:</strong> Cannot be called from an
    955      *   {@link android.accessibilityservice.AccessibilityService}.
    956      *   This class is made immutable before being delivered to an AccessibilityService.
    957      * </p>
    958      *
    959      * @param action The action.
    960      *
    961      * @throws IllegalStateException If called from an AccessibilityService.
    962      */
    963     public void addAction(AccessibilityAction action) {
    964         enforceNotSealed();
    965 
    966         if (action == null) {
    967             return;
    968         }
    969 
    970         if (mActions == null) {
    971             mActions = new ArrayList<AccessibilityAction>();
    972         }
    973 
    974         mActions.remove(action);
    975         mActions.add(action);
    976     }
    977 
    978     /**
    979      * Adds an action that can be performed on the node.
    980      * <p>
    981      *   <strong>Note:</strong> Cannot be called from an
    982      *   {@link android.accessibilityservice.AccessibilityService}.
    983      *   This class is made immutable before being delivered to an AccessibilityService.
    984      * </p>
    985      *
    986      * @param action The action.
    987      *
    988      * @throws IllegalStateException If called from an AccessibilityService.
    989      * @throws IllegalArgumentException If the argument is not one of the standard actions.
    990      *
    991      * @deprecated This has been deprecated for {@link #addAction(AccessibilityAction)}
    992      */
    993     @Deprecated
    994     public void addAction(int action) {
    995         enforceNotSealed();
    996 
    997         if ((action & ACTION_TYPE_MASK) != 0) {
    998             throw new IllegalArgumentException("Action is not a combination of the standard " +
    999                     "actions: " + action);
   1000         }
   1001 
   1002         addLegacyStandardActions(action);
   1003     }
   1004 
   1005     /**
   1006      * Removes an action that can be performed on the node. If the action was
   1007      * not already added to the node, calling this method has no effect.
   1008      * <p>
   1009      *   <strong>Note:</strong> Cannot be called from an
   1010      *   {@link android.accessibilityservice.AccessibilityService}.
   1011      *   This class is made immutable before being delivered to an AccessibilityService.
   1012      * </p>
   1013      *
   1014      * @param action The action to be removed.
   1015      *
   1016      * @throws IllegalStateException If called from an AccessibilityService.
   1017      * @deprecated Use {@link #removeAction(AccessibilityAction)}
   1018      */
   1019     @Deprecated
   1020     public void removeAction(int action) {
   1021         enforceNotSealed();
   1022 
   1023         removeAction(getActionSingleton(action));
   1024     }
   1025 
   1026     /**
   1027      * Removes an action that can be performed on the node. If the action was
   1028      * not already added to the node, calling this method has no effect.
   1029      * <p>
   1030      *   <strong>Note:</strong> Cannot be called from an
   1031      *   {@link android.accessibilityservice.AccessibilityService}.
   1032      *   This class is made immutable before being delivered to an AccessibilityService.
   1033      * </p>
   1034      *
   1035      * @param action The action to be removed.
   1036      * @return The action removed from the list of actions.
   1037      *
   1038      * @throws IllegalStateException If called from an AccessibilityService.
   1039      */
   1040     public boolean removeAction(AccessibilityAction action) {
   1041         enforceNotSealed();
   1042 
   1043         if (mActions == null || action == null) {
   1044             return false;
   1045         }
   1046 
   1047         return mActions.remove(action);
   1048     }
   1049 
   1050     /**
   1051      * Gets the node before which this one is visited during traversal. A screen-reader
   1052      * must visit the content of this node before the content of the one it precedes.
   1053      *
   1054      * @return The succeeding node if such or <code>null</code>.
   1055      *
   1056      * @see #setTraversalBefore(android.view.View)
   1057      * @see #setTraversalBefore(android.view.View, int)
   1058      */
   1059     public AccessibilityNodeInfo getTraversalBefore() {
   1060         enforceSealed();
   1061         return getNodeForAccessibilityId(mTraversalBefore);
   1062     }
   1063 
   1064     /**
   1065      * Sets the view before whose node this one should be visited during traversal. A
   1066      * screen-reader must visit the content of this node before the content of the one
   1067      * it precedes.
   1068      * <p>
   1069      *   <strong>Note:</strong> Cannot be called from an
   1070      *   {@link android.accessibilityservice.AccessibilityService}.
   1071      *   This class is made immutable before being delivered to an AccessibilityService.
   1072      * </p>
   1073      *
   1074      * @param view The view providing the preceding node.
   1075      *
   1076      * @see #getTraversalBefore()
   1077      */
   1078     public void setTraversalBefore(View view) {
   1079         setTraversalBefore(view, UNDEFINED_ITEM_ID);
   1080     }
   1081 
   1082     /**
   1083      * Sets the node before which this one is visited during traversal. A screen-reader
   1084      * must visit the content of this node before the content of the one it precedes.
   1085      * The successor is a virtual descendant of the given <code>root</code>. If
   1086      * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set
   1087      * as the successor.
   1088      * <p>
   1089      * A virtual descendant is an imaginary View that is reported as a part of the view
   1090      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1091      * content to report them selves as a tree of virtual views, thus conveying their
   1092      * logical structure.
   1093      * </p>
   1094      * <p>
   1095      *   <strong>Note:</strong> Cannot be called from an
   1096      *   {@link android.accessibilityservice.AccessibilityService}.
   1097      *   This class is made immutable before being delivered to an AccessibilityService.
   1098      * </p>
   1099      *
   1100      * @param root The root of the virtual subtree.
   1101      * @param virtualDescendantId The id of the virtual descendant.
   1102      */
   1103     public void setTraversalBefore(View root, int virtualDescendantId) {
   1104         enforceNotSealed();
   1105         final int rootAccessibilityViewId = (root != null)
   1106                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1107         mTraversalBefore = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1108     }
   1109 
   1110     /**
   1111      * Gets the node after which this one is visited in accessibility traversal.
   1112      * A screen-reader must visit the content of the other node before the content
   1113      * of this one.
   1114      *
   1115      * @return The succeeding node if such or <code>null</code>.
   1116      *
   1117      * @see #setTraversalAfter(android.view.View)
   1118      * @see #setTraversalAfter(android.view.View, int)
   1119      */
   1120     public AccessibilityNodeInfo getTraversalAfter() {
   1121         enforceSealed();
   1122         return getNodeForAccessibilityId(mTraversalAfter);
   1123     }
   1124 
   1125     /**
   1126      * Sets the view whose node is visited after this one in accessibility traversal.
   1127      * A screen-reader must visit the content of the other node before the content
   1128      * of this one.
   1129      * <p>
   1130      *   <strong>Note:</strong> Cannot be called from an
   1131      *   {@link android.accessibilityservice.AccessibilityService}.
   1132      *   This class is made immutable before being delivered to an AccessibilityService.
   1133      * </p>
   1134      *
   1135      * @param view The previous view.
   1136      *
   1137      * @see #getTraversalAfter()
   1138      */
   1139     public void setTraversalAfter(View view) {
   1140         setTraversalAfter(view, UNDEFINED_ITEM_ID);
   1141     }
   1142 
   1143     /**
   1144      * Sets the node after which this one is visited in accessibility traversal.
   1145      * A screen-reader must visit the content of the other node before the content
   1146      * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID}
   1147      * the root is set as the predecessor.
   1148      * <p>
   1149      * A virtual descendant is an imaginary View that is reported as a part of the view
   1150      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1151      * content to report them selves as a tree of virtual views, thus conveying their
   1152      * logical structure.
   1153      * </p>
   1154      * <p>
   1155      *   <strong>Note:</strong> Cannot be called from an
   1156      *   {@link android.accessibilityservice.AccessibilityService}.
   1157      *   This class is made immutable before being delivered to an AccessibilityService.
   1158      * </p>
   1159      *
   1160      * @param root The root of the virtual subtree.
   1161      * @param virtualDescendantId The id of the virtual descendant.
   1162      */
   1163     public void setTraversalAfter(View root, int virtualDescendantId) {
   1164         enforceNotSealed();
   1165         final int rootAccessibilityViewId = (root != null)
   1166                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1167         mTraversalAfter = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1168     }
   1169 
   1170     /**
   1171      * Sets the maximum text length, or -1 for no limit.
   1172      * <p>
   1173      * Typically used to indicate that an editable text field has a limit on
   1174      * the number of characters entered.
   1175      * <p>
   1176      * <strong>Note:</strong> Cannot be called from an
   1177      * {@link android.accessibilityservice.AccessibilityService}.
   1178      * This class is made immutable before being delivered to an AccessibilityService.
   1179      *
   1180      * @param max The maximum text length.
   1181      * @see #getMaxTextLength()
   1182      *
   1183      * @throws IllegalStateException If called from an AccessibilityService.
   1184      */
   1185     public void setMaxTextLength(int max) {
   1186         enforceNotSealed();
   1187         mMaxTextLength = max;
   1188     }
   1189 
   1190     /**
   1191      * Returns the maximum text length for this node.
   1192      *
   1193      * @return The maximum text length, or -1 for no limit.
   1194      * @see #setMaxTextLength(int)
   1195      */
   1196     public int getMaxTextLength() {
   1197         return mMaxTextLength;
   1198     }
   1199 
   1200     /**
   1201      * Sets the movement granularities for traversing the text of this node.
   1202      * <p>
   1203      *   <strong>Note:</strong> Cannot be called from an
   1204      *   {@link android.accessibilityservice.AccessibilityService}.
   1205      *   This class is made immutable before being delivered to an AccessibilityService.
   1206      * </p>
   1207      *
   1208      * @param granularities The bit mask with granularities.
   1209      *
   1210      * @throws IllegalStateException If called from an AccessibilityService.
   1211      */
   1212     public void setMovementGranularities(int granularities) {
   1213         enforceNotSealed();
   1214         mMovementGranularities = granularities;
   1215     }
   1216 
   1217     /**
   1218      * Gets the movement granularities for traversing the text of this node.
   1219      *
   1220      * @return The bit mask with granularities.
   1221      */
   1222     public int getMovementGranularities() {
   1223         return mMovementGranularities;
   1224     }
   1225 
   1226     /**
   1227      * Performs an action on the node.
   1228      * <p>
   1229      *   <strong>Note:</strong> An action can be performed only if the request is made
   1230      *   from an {@link android.accessibilityservice.AccessibilityService}.
   1231      * </p>
   1232      *
   1233      * @param action The action to perform.
   1234      * @return True if the action was performed.
   1235      *
   1236      * @throws IllegalStateException If called outside of an AccessibilityService.
   1237      */
   1238     public boolean performAction(int action) {
   1239         enforceSealed();
   1240         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1241             return false;
   1242         }
   1243         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1244         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
   1245                 action, null);
   1246     }
   1247 
   1248     /**
   1249      * Performs an action on the node.
   1250      * <p>
   1251      *   <strong>Note:</strong> An action can be performed only if the request is made
   1252      *   from an {@link android.accessibilityservice.AccessibilityService}.
   1253      * </p>
   1254      *
   1255      * @param action The action to perform.
   1256      * @param arguments A bundle with additional arguments.
   1257      * @return True if the action was performed.
   1258      *
   1259      * @throws IllegalStateException If called outside of an AccessibilityService.
   1260      */
   1261     public boolean performAction(int action, Bundle arguments) {
   1262         enforceSealed();
   1263         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1264             return false;
   1265         }
   1266         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1267         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
   1268                 action, arguments);
   1269     }
   1270 
   1271     /**
   1272      * Finds {@link AccessibilityNodeInfo}s by text. The match is case
   1273      * insensitive containment. The search is relative to this info i.e.
   1274      * this info is the root of the traversed tree.
   1275      *
   1276      * <p>
   1277      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1278      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1279      *     to avoid creating of multiple instances.
   1280      * </p>
   1281      *
   1282      * @param text The searched text.
   1283      * @return A list of node info.
   1284      */
   1285     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
   1286         enforceSealed();
   1287         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1288             return Collections.emptyList();
   1289         }
   1290         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1291         return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
   1292                 text);
   1293     }
   1294 
   1295     /**
   1296      * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource
   1297      * name where a fully qualified id is of the from "package:id/id_resource_name".
   1298      * For example, if the target application's package is "foo.bar" and the id
   1299      * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
   1300      *
   1301      * <p>
   1302      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1303      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1304      *     to avoid creating of multiple instances.
   1305      * </p>
   1306      * <p>
   1307      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
   1308      *   and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo}
   1309      *   the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
   1310      *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
   1311      * </p>
   1312      *
   1313      * @param viewId The fully qualified resource name of the view id to find.
   1314      * @return A list of node info.
   1315      */
   1316     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) {
   1317         enforceSealed();
   1318         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1319             return Collections.emptyList();
   1320         }
   1321         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1322         return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId,
   1323                 viewId);
   1324     }
   1325 
   1326     /**
   1327      * Gets the window to which this node belongs.
   1328      *
   1329      * @return The window.
   1330      *
   1331      * @see android.accessibilityservice.AccessibilityService#getWindows()
   1332      */
   1333     public AccessibilityWindowInfo getWindow() {
   1334         enforceSealed();
   1335         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1336             return null;
   1337         }
   1338         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1339         return client.getWindow(mConnectionId, mWindowId);
   1340     }
   1341 
   1342     /**
   1343      * Gets the parent.
   1344      * <p>
   1345      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1346      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1347      *     to avoid creating of multiple instances.
   1348      * </p>
   1349      *
   1350      * @return The parent.
   1351      */
   1352     public AccessibilityNodeInfo getParent() {
   1353         enforceSealed();
   1354         return getNodeForAccessibilityId(mParentNodeId);
   1355     }
   1356 
   1357     /**
   1358      * @return The parent node id.
   1359      *
   1360      * @hide
   1361      */
   1362     public long getParentNodeId() {
   1363         return mParentNodeId;
   1364     }
   1365 
   1366     /**
   1367      * Sets the parent.
   1368      * <p>
   1369      *   <strong>Note:</strong> Cannot be called from an
   1370      *   {@link android.accessibilityservice.AccessibilityService}.
   1371      *   This class is made immutable before being delivered to an AccessibilityService.
   1372      * </p>
   1373      *
   1374      * @param parent The parent.
   1375      *
   1376      * @throws IllegalStateException If called from an AccessibilityService.
   1377      */
   1378     public void setParent(View parent) {
   1379         setParent(parent, UNDEFINED_ITEM_ID);
   1380     }
   1381 
   1382     /**
   1383      * Sets the parent to be a virtual descendant of the given <code>root</code>.
   1384      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
   1385      * is set as the parent.
   1386      * <p>
   1387      * A virtual descendant is an imaginary View that is reported as a part of the view
   1388      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1389      * content to report them selves as a tree of virtual views, thus conveying their
   1390      * logical structure.
   1391      * </p>
   1392      * <p>
   1393      *   <strong>Note:</strong> Cannot be called from an
   1394      *   {@link android.accessibilityservice.AccessibilityService}.
   1395      *   This class is made immutable before being delivered to an AccessibilityService.
   1396      * </p>
   1397      *
   1398      * @param root The root of the virtual subtree.
   1399      * @param virtualDescendantId The id of the virtual descendant.
   1400      */
   1401     public void setParent(View root, int virtualDescendantId) {
   1402         enforceNotSealed();
   1403         final int rootAccessibilityViewId =
   1404             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1405         mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1406     }
   1407 
   1408     /**
   1409      * Gets the node bounds in parent coordinates.
   1410      *
   1411      * @param outBounds The output node bounds.
   1412      */
   1413     public void getBoundsInParent(Rect outBounds) {
   1414         outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
   1415                 mBoundsInParent.right, mBoundsInParent.bottom);
   1416     }
   1417 
   1418     /**
   1419      * Sets the node bounds in parent coordinates.
   1420      * <p>
   1421      *   <strong>Note:</strong> Cannot be called from an
   1422      *   {@link android.accessibilityservice.AccessibilityService}.
   1423      *   This class is made immutable before being delivered to an AccessibilityService.
   1424      * </p>
   1425      *
   1426      * @param bounds The node bounds.
   1427      *
   1428      * @throws IllegalStateException If called from an AccessibilityService.
   1429      */
   1430     public void setBoundsInParent(Rect bounds) {
   1431         enforceNotSealed();
   1432         mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
   1433     }
   1434 
   1435     /**
   1436      * Gets the node bounds in screen coordinates.
   1437      *
   1438      * @param outBounds The output node bounds.
   1439      */
   1440     public void getBoundsInScreen(Rect outBounds) {
   1441         outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
   1442                 mBoundsInScreen.right, mBoundsInScreen.bottom);
   1443     }
   1444 
   1445     /**
   1446      * Sets the node bounds in screen coordinates.
   1447      * <p>
   1448      *   <strong>Note:</strong> Cannot be called from an
   1449      *   {@link android.accessibilityservice.AccessibilityService}.
   1450      *   This class is made immutable before being delivered to an AccessibilityService.
   1451      * </p>
   1452      *
   1453      * @param bounds The node bounds.
   1454      *
   1455      * @throws IllegalStateException If called from an AccessibilityService.
   1456      */
   1457     public void setBoundsInScreen(Rect bounds) {
   1458         enforceNotSealed();
   1459         mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
   1460     }
   1461 
   1462     /**
   1463      * Gets whether this node is checkable.
   1464      *
   1465      * @return True if the node is checkable.
   1466      */
   1467     public boolean isCheckable() {
   1468         return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE);
   1469     }
   1470 
   1471     /**
   1472      * Sets whether this node is checkable.
   1473      * <p>
   1474      *   <strong>Note:</strong> Cannot be called from an
   1475      *   {@link android.accessibilityservice.AccessibilityService}.
   1476      *   This class is made immutable before being delivered to an AccessibilityService.
   1477      * </p>
   1478      *
   1479      * @param checkable True if the node is checkable.
   1480      *
   1481      * @throws IllegalStateException If called from an AccessibilityService.
   1482      */
   1483     public void setCheckable(boolean checkable) {
   1484         setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable);
   1485     }
   1486 
   1487     /**
   1488      * Gets whether this node is checked.
   1489      *
   1490      * @return True if the node is checked.
   1491      */
   1492     public boolean isChecked() {
   1493         return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED);
   1494     }
   1495 
   1496     /**
   1497      * Sets whether this node is checked.
   1498      * <p>
   1499      *   <strong>Note:</strong> Cannot be called from an
   1500      *   {@link android.accessibilityservice.AccessibilityService}.
   1501      *   This class is made immutable before being delivered to an AccessibilityService.
   1502      * </p>
   1503      *
   1504      * @param checked True if the node is checked.
   1505      *
   1506      * @throws IllegalStateException If called from an AccessibilityService.
   1507      */
   1508     public void setChecked(boolean checked) {
   1509         setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked);
   1510     }
   1511 
   1512     /**
   1513      * Gets whether this node is focusable.
   1514      *
   1515      * @return True if the node is focusable.
   1516      */
   1517     public boolean isFocusable() {
   1518         return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
   1519     }
   1520 
   1521     /**
   1522      * Sets whether this node is focusable.
   1523      * <p>
   1524      *   <strong>Note:</strong> Cannot be called from an
   1525      *   {@link android.accessibilityservice.AccessibilityService}.
   1526      *   This class is made immutable before being delivered to an AccessibilityService.
   1527      * </p>
   1528      *
   1529      * @param focusable True if the node is focusable.
   1530      *
   1531      * @throws IllegalStateException If called from an AccessibilityService.
   1532      */
   1533     public void setFocusable(boolean focusable) {
   1534         setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
   1535     }
   1536 
   1537     /**
   1538      * Gets whether this node is focused.
   1539      *
   1540      * @return True if the node is focused.
   1541      */
   1542     public boolean isFocused() {
   1543         return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
   1544     }
   1545 
   1546     /**
   1547      * Sets whether this node is focused.
   1548      * <p>
   1549      *   <strong>Note:</strong> Cannot be called from an
   1550      *   {@link android.accessibilityservice.AccessibilityService}.
   1551      *   This class is made immutable before being delivered to an AccessibilityService.
   1552      * </p>
   1553      *
   1554      * @param focused True if the node is focused.
   1555      *
   1556      * @throws IllegalStateException If called from an AccessibilityService.
   1557      */
   1558     public void setFocused(boolean focused) {
   1559         setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
   1560     }
   1561 
   1562     /**
   1563      * Sets whether this node is visible to the user.
   1564      *
   1565      * @return Whether the node is visible to the user.
   1566      */
   1567     public boolean isVisibleToUser() {
   1568         return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER);
   1569     }
   1570 
   1571     /**
   1572      * Sets whether this node is visible to the user.
   1573      * <p>
   1574      *   <strong>Note:</strong> Cannot be called from an
   1575      *   {@link android.accessibilityservice.AccessibilityService}.
   1576      *   This class is made immutable before being delivered to an AccessibilityService.
   1577      * </p>
   1578      *
   1579      * @param visibleToUser Whether the node is visible to the user.
   1580      *
   1581      * @throws IllegalStateException If called from an AccessibilityService.
   1582      */
   1583     public void setVisibleToUser(boolean visibleToUser) {
   1584         setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser);
   1585     }
   1586 
   1587     /**
   1588      * Gets whether this node is accessibility focused.
   1589      *
   1590      * @return True if the node is accessibility focused.
   1591      */
   1592     public boolean isAccessibilityFocused() {
   1593         return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
   1594     }
   1595 
   1596     /**
   1597      * Sets whether this node is accessibility focused.
   1598      * <p>
   1599      *   <strong>Note:</strong> Cannot be called from an
   1600      *   {@link android.accessibilityservice.AccessibilityService}.
   1601      *   This class is made immutable before being delivered to an AccessibilityService.
   1602      * </p>
   1603      *
   1604      * @param focused True if the node is accessibility focused.
   1605      *
   1606      * @throws IllegalStateException If called from an AccessibilityService.
   1607      */
   1608     public void setAccessibilityFocused(boolean focused) {
   1609         setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused);
   1610     }
   1611 
   1612     /**
   1613      * Gets whether this node is selected.
   1614      *
   1615      * @return True if the node is selected.
   1616      */
   1617     public boolean isSelected() {
   1618         return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED);
   1619     }
   1620 
   1621     /**
   1622      * Sets whether this node is selected.
   1623      * <p>
   1624      *   <strong>Note:</strong> Cannot be called from an
   1625      *   {@link android.accessibilityservice.AccessibilityService}.
   1626      *   This class is made immutable before being delivered to an AccessibilityService.
   1627      * </p>
   1628      *
   1629      * @param selected True if the node is selected.
   1630      *
   1631      * @throws IllegalStateException If called from an AccessibilityService.
   1632      */
   1633     public void setSelected(boolean selected) {
   1634         setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected);
   1635     }
   1636 
   1637     /**
   1638      * Gets whether this node is clickable.
   1639      *
   1640      * @return True if the node is clickable.
   1641      */
   1642     public boolean isClickable() {
   1643         return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
   1644     }
   1645 
   1646     /**
   1647      * Sets whether this node is clickable.
   1648      * <p>
   1649      *   <strong>Note:</strong> Cannot be called from an
   1650      *   {@link android.accessibilityservice.AccessibilityService}.
   1651      *   This class is made immutable before being delivered to an AccessibilityService.
   1652      * </p>
   1653      *
   1654      * @param clickable True if the node is clickable.
   1655      *
   1656      * @throws IllegalStateException If called from an AccessibilityService.
   1657      */
   1658     public void setClickable(boolean clickable) {
   1659         setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
   1660     }
   1661 
   1662     /**
   1663      * Gets whether this node is long clickable.
   1664      *
   1665      * @return True if the node is long clickable.
   1666      */
   1667     public boolean isLongClickable() {
   1668         return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
   1669     }
   1670 
   1671     /**
   1672      * Sets whether this node is long clickable.
   1673      * <p>
   1674      *   <strong>Note:</strong> Cannot be called from an
   1675      *   {@link android.accessibilityservice.AccessibilityService}.
   1676      *   This class is made immutable before being delivered to an AccessibilityService.
   1677      * </p>
   1678      *
   1679      * @param longClickable True if the node is long clickable.
   1680      *
   1681      * @throws IllegalStateException If called from an AccessibilityService.
   1682      */
   1683     public void setLongClickable(boolean longClickable) {
   1684         setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
   1685     }
   1686 
   1687     /**
   1688      * Gets whether this node is enabled.
   1689      *
   1690      * @return True if the node is enabled.
   1691      */
   1692     public boolean isEnabled() {
   1693         return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED);
   1694     }
   1695 
   1696     /**
   1697      * Sets whether this node is enabled.
   1698      * <p>
   1699      *   <strong>Note:</strong> Cannot be called from an
   1700      *   {@link android.accessibilityservice.AccessibilityService}.
   1701      *   This class is made immutable before being delivered to an AccessibilityService.
   1702      * </p>
   1703      *
   1704      * @param enabled True if the node is enabled.
   1705      *
   1706      * @throws IllegalStateException If called from an AccessibilityService.
   1707      */
   1708     public void setEnabled(boolean enabled) {
   1709         setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled);
   1710     }
   1711 
   1712     /**
   1713      * Gets whether this node is a password.
   1714      *
   1715      * @return True if the node is a password.
   1716      */
   1717     public boolean isPassword() {
   1718         return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD);
   1719     }
   1720 
   1721     /**
   1722      * Sets whether this node is a password.
   1723      * <p>
   1724      *   <strong>Note:</strong> Cannot be called from an
   1725      *   {@link android.accessibilityservice.AccessibilityService}.
   1726      *   This class is made immutable before being delivered to an AccessibilityService.
   1727      * </p>
   1728      *
   1729      * @param password True if the node is a password.
   1730      *
   1731      * @throws IllegalStateException If called from an AccessibilityService.
   1732      */
   1733     public void setPassword(boolean password) {
   1734         setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password);
   1735     }
   1736 
   1737     /**
   1738      * Gets if the node is scrollable.
   1739      *
   1740      * @return True if the node is scrollable, false otherwise.
   1741      */
   1742     public boolean isScrollable() {
   1743         return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
   1744     }
   1745 
   1746     /**
   1747      * Sets if the node is scrollable.
   1748      * <p>
   1749      *   <strong>Note:</strong> Cannot be called from an
   1750      *   {@link android.accessibilityservice.AccessibilityService}.
   1751      *   This class is made immutable before being delivered to an AccessibilityService.
   1752      * </p>
   1753      *
   1754      * @param scrollable True if the node is scrollable, false otherwise.
   1755      *
   1756      * @throws IllegalStateException If called from an AccessibilityService.
   1757      */
   1758     public void setScrollable(boolean scrollable) {
   1759         setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
   1760     }
   1761 
   1762     /**
   1763      * Gets if the node is editable.
   1764      *
   1765      * @return True if the node is editable, false otherwise.
   1766      */
   1767     public boolean isEditable() {
   1768         return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE);
   1769     }
   1770 
   1771     /**
   1772      * Sets whether this node is editable.
   1773      * <p>
   1774      *   <strong>Note:</strong> Cannot be called from an
   1775      *   {@link android.accessibilityservice.AccessibilityService}.
   1776      *   This class is made immutable before being delivered to an AccessibilityService.
   1777      * </p>
   1778      *
   1779      * @param editable True if the node is editable.
   1780      *
   1781      * @throws IllegalStateException If called from an AccessibilityService.
   1782      */
   1783     public void setEditable(boolean editable) {
   1784         setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable);
   1785     }
   1786 
   1787     /**
   1788      * Gets the collection info if the node is a collection. A collection
   1789      * child is always a collection item.
   1790      *
   1791      * @return The collection info.
   1792      */
   1793     public CollectionInfo getCollectionInfo() {
   1794         return mCollectionInfo;
   1795     }
   1796 
   1797     /**
   1798      * Sets the collection info if the node is a collection. A collection
   1799      * child is always a collection item.
   1800      * <p>
   1801      *   <strong>Note:</strong> Cannot be called from an
   1802      *   {@link android.accessibilityservice.AccessibilityService}.
   1803      *   This class is made immutable before being delivered to an AccessibilityService.
   1804      * </p>
   1805      *
   1806      * @param collectionInfo The collection info.
   1807      */
   1808     public void setCollectionInfo(CollectionInfo collectionInfo) {
   1809         enforceNotSealed();
   1810         mCollectionInfo = collectionInfo;
   1811     }
   1812 
   1813     /**
   1814      * Gets the collection item info if the node is a collection item. A collection
   1815      * item is always a child of a collection.
   1816      *
   1817      * @return The collection item info.
   1818      */
   1819     public CollectionItemInfo getCollectionItemInfo() {
   1820         return mCollectionItemInfo;
   1821     }
   1822 
   1823     /**
   1824      * Sets the collection item info if the node is a collection item. A collection
   1825      * item is always a child of a collection.
   1826      * <p>
   1827      *   <strong>Note:</strong> Cannot be called from an
   1828      *   {@link android.accessibilityservice.AccessibilityService}.
   1829      *   This class is made immutable before being delivered to an AccessibilityService.
   1830      * </p>
   1831      */
   1832     public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) {
   1833         enforceNotSealed();
   1834         mCollectionItemInfo = collectionItemInfo;
   1835     }
   1836 
   1837     /**
   1838      * Gets the range info if this node is a range.
   1839      *
   1840      * @return The range.
   1841      */
   1842     public RangeInfo getRangeInfo() {
   1843         return mRangeInfo;
   1844     }
   1845 
   1846     /**
   1847      * Sets the range info if this node is a range.
   1848      * <p>
   1849      *   <strong>Note:</strong> Cannot be called from an
   1850      *   {@link android.accessibilityservice.AccessibilityService}.
   1851      *   This class is made immutable before being delivered to an AccessibilityService.
   1852      * </p>
   1853      *
   1854      * @param rangeInfo The range info.
   1855      */
   1856     public void setRangeInfo(RangeInfo rangeInfo) {
   1857         enforceNotSealed();
   1858         mRangeInfo = rangeInfo;
   1859     }
   1860 
   1861     /**
   1862      * Gets if the content of this node is invalid. For example,
   1863      * a date is not well-formed.
   1864      *
   1865      * @return If the node content is invalid.
   1866      */
   1867     public boolean isContentInvalid() {
   1868         return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID);
   1869     }
   1870 
   1871     /**
   1872      * Sets if the content of this node is invalid. For example,
   1873      * a date is not well-formed.
   1874      * <p>
   1875      *   <strong>Note:</strong> Cannot be called from an
   1876      *   {@link android.accessibilityservice.AccessibilityService}.
   1877      *   This class is made immutable before being delivered to an AccessibilityService.
   1878      * </p>
   1879      *
   1880      * @param contentInvalid If the node content is invalid.
   1881      */
   1882     public void setContentInvalid(boolean contentInvalid) {
   1883         setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid);
   1884     }
   1885 
   1886     /**
   1887      * Gets the node's live region mode.
   1888      * <p>
   1889      * A live region is a node that contains information that is important for
   1890      * the user and when it changes the user should be notified. For example,
   1891      * in a login screen with a TextView that displays an "incorrect password"
   1892      * notification, that view should be marked as a live region with mode
   1893      * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}.
   1894      * <p>
   1895      * It is the responsibility of the accessibility service to monitor
   1896      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating
   1897      * changes to live region nodes and their children.
   1898      *
   1899      * @return The live region mode, or
   1900      *         {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
   1901      *         live region.
   1902      * @see android.view.View#getAccessibilityLiveRegion()
   1903      */
   1904     public int getLiveRegion() {
   1905         return mLiveRegion;
   1906     }
   1907 
   1908     /**
   1909      * Sets the node's live region mode.
   1910      * <p>
   1911      * <strong>Note:</strong> Cannot be called from an
   1912      * {@link android.accessibilityservice.AccessibilityService}. This class is
   1913      * made immutable before being delivered to an AccessibilityService.
   1914      *
   1915      * @param mode The live region mode, or
   1916      *        {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
   1917      *        live region.
   1918      * @see android.view.View#setAccessibilityLiveRegion(int)
   1919      */
   1920     public void setLiveRegion(int mode) {
   1921         enforceNotSealed();
   1922         mLiveRegion = mode;
   1923     }
   1924 
   1925     /**
   1926      * Gets if the node is a multi line editable text.
   1927      *
   1928      * @return True if the node is multi line.
   1929      */
   1930     public boolean isMultiLine() {
   1931         return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE);
   1932     }
   1933 
   1934     /**
   1935      * Sets if the node is a multi line editable text.
   1936      * <p>
   1937      *   <strong>Note:</strong> Cannot be called from an
   1938      *   {@link android.accessibilityservice.AccessibilityService}.
   1939      *   This class is made immutable before being delivered to an AccessibilityService.
   1940      * </p>
   1941      *
   1942      * @param multiLine True if the node is multi line.
   1943      */
   1944     public void setMultiLine(boolean multiLine) {
   1945         setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine);
   1946     }
   1947 
   1948     /**
   1949      * Gets if this node opens a popup or a dialog.
   1950      *
   1951      * @return If the the node opens a popup.
   1952      */
   1953     public boolean canOpenPopup() {
   1954         return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP);
   1955     }
   1956 
   1957     /**
   1958      * Sets if this node opens a popup or a dialog.
   1959      * <p>
   1960      *   <strong>Note:</strong> Cannot be called from an
   1961      *   {@link android.accessibilityservice.AccessibilityService}.
   1962      *   This class is made immutable before being delivered to an AccessibilityService.
   1963      * </p>
   1964      *
   1965      * @param opensPopup If the the node opens a popup.
   1966      */
   1967     public void setCanOpenPopup(boolean opensPopup) {
   1968         enforceNotSealed();
   1969         setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup);
   1970     }
   1971 
   1972     /**
   1973      * Gets if the node can be dismissed.
   1974      *
   1975      * @return If the node can be dismissed.
   1976      */
   1977     public boolean isDismissable() {
   1978         return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
   1979     }
   1980 
   1981     /**
   1982      * Sets if the node can be dismissed.
   1983      * <p>
   1984      *   <strong>Note:</strong> Cannot be called from an
   1985      *   {@link android.accessibilityservice.AccessibilityService}.
   1986      *   This class is made immutable before being delivered to an AccessibilityService.
   1987      * </p>
   1988      *
   1989      * @param dismissable If the node can be dismissed.
   1990      */
   1991     public void setDismissable(boolean dismissable) {
   1992         setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
   1993     }
   1994 
   1995     /**
   1996      * Gets the package this node comes from.
   1997      *
   1998      * @return The package name.
   1999      */
   2000     public CharSequence getPackageName() {
   2001         return mPackageName;
   2002     }
   2003 
   2004     /**
   2005      * Sets the package this node comes from.
   2006      * <p>
   2007      *   <strong>Note:</strong> Cannot be called from an
   2008      *   {@link android.accessibilityservice.AccessibilityService}.
   2009      *   This class is made immutable before being delivered to an AccessibilityService.
   2010      * </p>
   2011      *
   2012      * @param packageName The package name.
   2013      *
   2014      * @throws IllegalStateException If called from an AccessibilityService.
   2015      */
   2016     public void setPackageName(CharSequence packageName) {
   2017         enforceNotSealed();
   2018         mPackageName = packageName;
   2019     }
   2020 
   2021     /**
   2022      * Gets the class this node comes from.
   2023      *
   2024      * @return The class name.
   2025      */
   2026     public CharSequence getClassName() {
   2027         return mClassName;
   2028     }
   2029 
   2030     /**
   2031      * Sets the class this node comes from.
   2032      * <p>
   2033      *   <strong>Note:</strong> Cannot be called from an
   2034      *   {@link android.accessibilityservice.AccessibilityService}.
   2035      *   This class is made immutable before being delivered to an AccessibilityService.
   2036      * </p>
   2037      *
   2038      * @param className The class name.
   2039      *
   2040      * @throws IllegalStateException If called from an AccessibilityService.
   2041      */
   2042     public void setClassName(CharSequence className) {
   2043         enforceNotSealed();
   2044         mClassName = className;
   2045     }
   2046 
   2047     /**
   2048      * Gets the text of this node.
   2049      *
   2050      * @return The text.
   2051      */
   2052     public CharSequence getText() {
   2053         return mText;
   2054     }
   2055 
   2056     /**
   2057      * Sets the text of this node.
   2058      * <p>
   2059      *   <strong>Note:</strong> Cannot be called from an
   2060      *   {@link android.accessibilityservice.AccessibilityService}.
   2061      *   This class is made immutable before being delivered to an AccessibilityService.
   2062      * </p>
   2063      *
   2064      * @param text The text.
   2065      *
   2066      * @throws IllegalStateException If called from an AccessibilityService.
   2067      */
   2068     public void setText(CharSequence text) {
   2069         enforceNotSealed();
   2070         mText = text;
   2071     }
   2072 
   2073     /**
   2074      * Sets the error text of this node.
   2075      * <p>
   2076      *   <strong>Note:</strong> Cannot be called from an
   2077      *   {@link android.accessibilityservice.AccessibilityService}.
   2078      *   This class is made immutable before being delivered to an AccessibilityService.
   2079      * </p>
   2080      *
   2081      * @param error The error text.
   2082      *
   2083      * @throws IllegalStateException If called from an AccessibilityService.
   2084      */
   2085     public void setError(CharSequence error) {
   2086         enforceNotSealed();
   2087         mError = error;
   2088     }
   2089 
   2090     /**
   2091      * Gets the error text of this node.
   2092      *
   2093      * @return The error text.
   2094      */
   2095     public CharSequence getError() {
   2096         return mError;
   2097     }
   2098 
   2099     /**
   2100      * Gets the content description of this node.
   2101      *
   2102      * @return The content description.
   2103      */
   2104     public CharSequence getContentDescription() {
   2105         return mContentDescription;
   2106     }
   2107 
   2108     /**
   2109      * Sets the content description of this node.
   2110      * <p>
   2111      *   <strong>Note:</strong> Cannot be called from an
   2112      *   {@link android.accessibilityservice.AccessibilityService}.
   2113      *   This class is made immutable before being delivered to an AccessibilityService.
   2114      * </p>
   2115      *
   2116      * @param contentDescription The content description.
   2117      *
   2118      * @throws IllegalStateException If called from an AccessibilityService.
   2119      */
   2120     public void setContentDescription(CharSequence contentDescription) {
   2121         enforceNotSealed();
   2122         mContentDescription = contentDescription;
   2123     }
   2124 
   2125     /**
   2126      * Sets the view for which the view represented by this info serves as a
   2127      * label for accessibility purposes.
   2128      *
   2129      * @param labeled The view for which this info serves as a label.
   2130      */
   2131     public void setLabelFor(View labeled) {
   2132         setLabelFor(labeled, UNDEFINED_ITEM_ID);
   2133     }
   2134 
   2135     /**
   2136      * Sets the view for which the view represented by this info serves as a
   2137      * label for accessibility purposes. If <code>virtualDescendantId</code>
   2138      * is {@link View#NO_ID} the root is set as the labeled.
   2139      * <p>
   2140      * A virtual descendant is an imaginary View that is reported as a part of the view
   2141      * hierarchy for accessibility purposes. This enables custom views that draw complex
   2142      * content to report themselves as a tree of virtual views, thus conveying their
   2143      * logical structure.
   2144      * </p>
   2145      * <p>
   2146      *   <strong>Note:</strong> Cannot be called from an
   2147      *   {@link android.accessibilityservice.AccessibilityService}.
   2148      *   This class is made immutable before being delivered to an AccessibilityService.
   2149      * </p>
   2150      *
   2151      * @param root The root whose virtual descendant serves as a label.
   2152      * @param virtualDescendantId The id of the virtual descendant.
   2153      */
   2154     public void setLabelFor(View root, int virtualDescendantId) {
   2155         enforceNotSealed();
   2156         final int rootAccessibilityViewId = (root != null)
   2157                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   2158         mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   2159     }
   2160 
   2161     /**
   2162      * Gets the node info for which the view represented by this info serves as
   2163      * a label for accessibility purposes.
   2164      * <p>
   2165      *   <strong>Note:</strong> It is a client responsibility to recycle the
   2166      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   2167      *     to avoid creating of multiple instances.
   2168      * </p>
   2169      *
   2170      * @return The labeled info.
   2171      */
   2172     public AccessibilityNodeInfo getLabelFor() {
   2173         enforceSealed();
   2174         return getNodeForAccessibilityId(mLabelForId);
   2175     }
   2176 
   2177     /**
   2178      * Sets the view which serves as the label of the view represented by
   2179      * this info for accessibility purposes.
   2180      *
   2181      * @param label The view that labels this node's source.
   2182      */
   2183     public void setLabeledBy(View label) {
   2184         setLabeledBy(label, UNDEFINED_ITEM_ID);
   2185     }
   2186 
   2187     /**
   2188      * Sets the view which serves as the label of the view represented by
   2189      * this info for accessibility purposes. If <code>virtualDescendantId</code>
   2190      * is {@link View#NO_ID} the root is set as the label.
   2191      * <p>
   2192      * A virtual descendant is an imaginary View that is reported as a part of the view
   2193      * hierarchy for accessibility purposes. This enables custom views that draw complex
   2194      * content to report themselves as a tree of virtual views, thus conveying their
   2195      * logical structure.
   2196      * </p>
   2197      * <p>
   2198      *   <strong>Note:</strong> Cannot be called from an
   2199      *   {@link android.accessibilityservice.AccessibilityService}.
   2200      *   This class is made immutable before being delivered to an AccessibilityService.
   2201      * </p>
   2202      *
   2203      * @param root The root whose virtual descendant labels this node's source.
   2204      * @param virtualDescendantId The id of the virtual descendant.
   2205      */
   2206     public void setLabeledBy(View root, int virtualDescendantId) {
   2207         enforceNotSealed();
   2208         final int rootAccessibilityViewId = (root != null)
   2209                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   2210         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   2211     }
   2212 
   2213     /**
   2214      * Gets the node info which serves as the label of the view represented by
   2215      * this info for accessibility purposes.
   2216      * <p>
   2217      *   <strong>Note:</strong> It is a client responsibility to recycle the
   2218      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   2219      *     to avoid creating of multiple instances.
   2220      * </p>
   2221      *
   2222      * @return The label.
   2223      */
   2224     public AccessibilityNodeInfo getLabeledBy() {
   2225         enforceSealed();
   2226         return getNodeForAccessibilityId(mLabeledById);
   2227     }
   2228 
   2229     /**
   2230      * Sets the fully qualified resource name of the source view's id.
   2231      *
   2232      * <p>
   2233      *   <strong>Note:</strong> Cannot be called from an
   2234      *   {@link android.accessibilityservice.AccessibilityService}.
   2235      *   This class is made immutable before being delivered to an AccessibilityService.
   2236      * </p>
   2237      *
   2238      * @param viewIdResName The id resource name.
   2239      */
   2240     public void setViewIdResourceName(String viewIdResName) {
   2241         enforceNotSealed();
   2242         mViewIdResourceName = viewIdResName;
   2243     }
   2244 
   2245     /**
   2246      * Gets the fully qualified resource name of the source view's id.
   2247      *
   2248      * <p>
   2249      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
   2250      *   and in order to report the source view id of an {@link AccessibilityNodeInfo} the
   2251      *   client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
   2252      *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
   2253      * </p>
   2254 
   2255      * @return The id resource name.
   2256      */
   2257     public String getViewIdResourceName() {
   2258         return mViewIdResourceName;
   2259     }
   2260 
   2261     /**
   2262      * Gets the text selection start.
   2263      *
   2264      * @return The text selection start if there is selection or -1.
   2265      */
   2266     public int getTextSelectionStart() {
   2267         return mTextSelectionStart;
   2268     }
   2269 
   2270     /**
   2271      * Gets the text selection end.
   2272      *
   2273      * @return The text selection end if there is selection or -1.
   2274      */
   2275     public int getTextSelectionEnd() {
   2276         return mTextSelectionEnd;
   2277     }
   2278 
   2279     /**
   2280      * Sets the text selection start and end.
   2281      * <p>
   2282      *   <strong>Note:</strong> Cannot be called from an
   2283      *   {@link android.accessibilityservice.AccessibilityService}.
   2284      *   This class is made immutable before being delivered to an AccessibilityService.
   2285      * </p>
   2286      *
   2287      * @param start The text selection start.
   2288      * @param end The text selection end.
   2289      *
   2290      * @throws IllegalStateException If called from an AccessibilityService.
   2291      */
   2292     public void setTextSelection(int start, int end) {
   2293         enforceNotSealed();
   2294         mTextSelectionStart = start;
   2295         mTextSelectionEnd = end;
   2296     }
   2297 
   2298     /**
   2299      * Gets the input type of the source as defined by {@link InputType}.
   2300      *
   2301      * @return The input type.
   2302      */
   2303     public int getInputType() {
   2304         return mInputType;
   2305     }
   2306 
   2307     /**
   2308      * Sets the input type of the source as defined by {@link InputType}.
   2309      * <p>
   2310      *   <strong>Note:</strong> Cannot be called from an
   2311      *   {@link android.accessibilityservice.AccessibilityService}.
   2312      *   This class is made immutable before being delivered to an
   2313      *   AccessibilityService.
   2314      * </p>
   2315      *
   2316      * @param inputType The input type.
   2317      *
   2318      * @throws IllegalStateException If called from an AccessibilityService.
   2319      */
   2320     public void setInputType(int inputType) {
   2321         enforceNotSealed();
   2322         mInputType = inputType;
   2323     }
   2324 
   2325     /**
   2326      * Gets an optional bundle with extra data. The bundle
   2327      * is lazily created and never <code>null</code>.
   2328      * <p>
   2329      * <strong>Note:</strong> It is recommended to use the package
   2330      * name of your application as a prefix for the keys to avoid
   2331      * collisions which may confuse an accessibility service if the
   2332      * same key has different meaning when emitted from different
   2333      * applications.
   2334      * </p>
   2335      *
   2336      * @return The bundle.
   2337      */
   2338     public Bundle getExtras() {
   2339         if (mExtras == null) {
   2340             mExtras = new Bundle();
   2341         }
   2342         return mExtras;
   2343     }
   2344 
   2345     /**
   2346      * Gets the value of a boolean property.
   2347      *
   2348      * @param property The property.
   2349      * @return The value.
   2350      */
   2351     private boolean getBooleanProperty(int property) {
   2352         return (mBooleanProperties & property) != 0;
   2353     }
   2354 
   2355     /**
   2356      * Sets a boolean property.
   2357      *
   2358      * @param property The property.
   2359      * @param value The value.
   2360      *
   2361      * @throws IllegalStateException If called from an AccessibilityService.
   2362      */
   2363     private void setBooleanProperty(int property, boolean value) {
   2364         enforceNotSealed();
   2365         if (value) {
   2366             mBooleanProperties |= property;
   2367         } else {
   2368             mBooleanProperties &= ~property;
   2369         }
   2370     }
   2371 
   2372     /**
   2373      * Sets the unique id of the IAccessibilityServiceConnection over which
   2374      * this instance can send requests to the system.
   2375      *
   2376      * @param connectionId The connection id.
   2377      *
   2378      * @hide
   2379      */
   2380     public void setConnectionId(int connectionId) {
   2381         enforceNotSealed();
   2382         mConnectionId = connectionId;
   2383     }
   2384 
   2385     /**
   2386      * {@inheritDoc}
   2387      */
   2388     @Override
   2389     public int describeContents() {
   2390         return 0;
   2391     }
   2392 
   2393     /**
   2394      * Gets the id of the source node.
   2395      *
   2396      * @return The id.
   2397      *
   2398      * @hide
   2399      */
   2400     public long getSourceNodeId() {
   2401         return mSourceNodeId;
   2402     }
   2403 
   2404     /**
   2405      * Sets if this instance is sealed.
   2406      *
   2407      * @param sealed Whether is sealed.
   2408      *
   2409      * @hide
   2410      */
   2411     public void setSealed(boolean sealed) {
   2412         mSealed = sealed;
   2413     }
   2414 
   2415     /**
   2416      * Gets if this instance is sealed.
   2417      *
   2418      * @return Whether is sealed.
   2419      *
   2420      * @hide
   2421      */
   2422     public boolean isSealed() {
   2423         return mSealed;
   2424     }
   2425 
   2426     /**
   2427      * Enforces that this instance is sealed.
   2428      *
   2429      * @throws IllegalStateException If this instance is not sealed.
   2430      *
   2431      * @hide
   2432      */
   2433     protected void enforceSealed() {
   2434         if (!isSealed()) {
   2435             throw new IllegalStateException("Cannot perform this "
   2436                     + "action on a not sealed instance.");
   2437         }
   2438     }
   2439 
   2440     private void enforceValidFocusDirection(int direction) {
   2441         switch (direction) {
   2442             case View.FOCUS_DOWN:
   2443             case View.FOCUS_UP:
   2444             case View.FOCUS_LEFT:
   2445             case View.FOCUS_RIGHT:
   2446             case View.FOCUS_FORWARD:
   2447             case View.FOCUS_BACKWARD:
   2448                 return;
   2449             default:
   2450                 throw new IllegalArgumentException("Unknown direction: " + direction);
   2451         }
   2452     }
   2453 
   2454     private void enforceValidFocusType(int focusType) {
   2455         switch (focusType) {
   2456             case FOCUS_INPUT:
   2457             case FOCUS_ACCESSIBILITY:
   2458                 return;
   2459             default:
   2460                 throw new IllegalArgumentException("Unknown focus type: " + focusType);
   2461         }
   2462     }
   2463 
   2464     /**
   2465      * Enforces that this instance is not sealed.
   2466      *
   2467      * @throws IllegalStateException If this instance is sealed.
   2468      *
   2469      * @hide
   2470      */
   2471     protected void enforceNotSealed() {
   2472         if (isSealed()) {
   2473             throw new IllegalStateException("Cannot perform this "
   2474                     + "action on a sealed instance.");
   2475         }
   2476     }
   2477 
   2478     /**
   2479      * Returns a cached instance if such is available otherwise a new one
   2480      * and sets the source.
   2481      *
   2482      * @param source The source view.
   2483      * @return An instance.
   2484      *
   2485      * @see #setSource(View)
   2486      */
   2487     public static AccessibilityNodeInfo obtain(View source) {
   2488         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   2489         info.setSource(source);
   2490         return info;
   2491     }
   2492 
   2493     /**
   2494      * Returns a cached instance if such is available otherwise a new one
   2495      * and sets the source.
   2496      *
   2497      * @param root The root of the virtual subtree.
   2498      * @param virtualDescendantId The id of the virtual descendant.
   2499      * @return An instance.
   2500      *
   2501      * @see #setSource(View, int)
   2502      */
   2503     public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
   2504         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   2505         info.setSource(root, virtualDescendantId);
   2506         return info;
   2507     }
   2508 
   2509     /**
   2510      * Returns a cached instance if such is available otherwise a new one.
   2511      *
   2512      * @return An instance.
   2513      */
   2514     public static AccessibilityNodeInfo obtain() {
   2515         AccessibilityNodeInfo info = sPool.acquire();
   2516         return (info != null) ? info : new AccessibilityNodeInfo();
   2517     }
   2518 
   2519     /**
   2520      * Returns a cached instance if such is available or a new one is
   2521      * create. The returned instance is initialized from the given
   2522      * <code>info</code>.
   2523      *
   2524      * @param info The other info.
   2525      * @return An instance.
   2526      */
   2527     public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
   2528         AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
   2529         infoClone.init(info);
   2530         return infoClone;
   2531     }
   2532 
   2533     /**
   2534      * Return an instance back to be reused.
   2535      * <p>
   2536      * <strong>Note:</strong> You must not touch the object after calling this function.
   2537      *
   2538      * @throws IllegalStateException If the info is already recycled.
   2539      */
   2540     public void recycle() {
   2541         clear();
   2542         sPool.release(this);
   2543     }
   2544 
   2545     /**
   2546      * {@inheritDoc}
   2547      * <p>
   2548      *   <strong>Note:</strong> After the instance is written to a parcel it
   2549      *      is recycled. You must not touch the object after calling this function.
   2550      * </p>
   2551      */
   2552     @Override
   2553     public void writeToParcel(Parcel parcel, int flags) {
   2554         parcel.writeInt(isSealed() ? 1 : 0);
   2555         parcel.writeLong(mSourceNodeId);
   2556         parcel.writeInt(mWindowId);
   2557         parcel.writeLong(mParentNodeId);
   2558         parcel.writeLong(mLabelForId);
   2559         parcel.writeLong(mLabeledById);
   2560         parcel.writeLong(mTraversalBefore);
   2561         parcel.writeLong(mTraversalAfter);
   2562 
   2563         parcel.writeInt(mConnectionId);
   2564 
   2565         final LongArray childIds = mChildNodeIds;
   2566         if (childIds == null) {
   2567             parcel.writeInt(0);
   2568         } else {
   2569             final int childIdsSize = childIds.size();
   2570             parcel.writeInt(childIdsSize);
   2571             for (int i = 0; i < childIdsSize; i++) {
   2572                 parcel.writeLong(childIds.get(i));
   2573             }
   2574         }
   2575 
   2576         parcel.writeInt(mBoundsInParent.top);
   2577         parcel.writeInt(mBoundsInParent.bottom);
   2578         parcel.writeInt(mBoundsInParent.left);
   2579         parcel.writeInt(mBoundsInParent.right);
   2580 
   2581         parcel.writeInt(mBoundsInScreen.top);
   2582         parcel.writeInt(mBoundsInScreen.bottom);
   2583         parcel.writeInt(mBoundsInScreen.left);
   2584         parcel.writeInt(mBoundsInScreen.right);
   2585 
   2586         if (mActions != null && !mActions.isEmpty()) {
   2587             final int actionCount = mActions.size();
   2588             parcel.writeInt(actionCount);
   2589 
   2590             int defaultLegacyStandardActions = 0;
   2591             for (int i = 0; i < actionCount; i++) {
   2592                 AccessibilityAction action = mActions.get(i);
   2593                 if (isDefaultLegacyStandardAction(action)) {
   2594                     defaultLegacyStandardActions |= action.getId();
   2595                 }
   2596             }
   2597             parcel.writeInt(defaultLegacyStandardActions);
   2598 
   2599             for (int i = 0; i < actionCount; i++) {
   2600                 AccessibilityAction action = mActions.get(i);
   2601                 if (!isDefaultLegacyStandardAction(action)) {
   2602                     parcel.writeInt(action.getId());
   2603                     parcel.writeCharSequence(action.getLabel());
   2604                 }
   2605             }
   2606         } else {
   2607             parcel.writeInt(0);
   2608         }
   2609 
   2610         parcel.writeInt(mMaxTextLength);
   2611         parcel.writeInt(mMovementGranularities);
   2612         parcel.writeInt(mBooleanProperties);
   2613 
   2614         parcel.writeCharSequence(mPackageName);
   2615         parcel.writeCharSequence(mClassName);
   2616         parcel.writeCharSequence(mText);
   2617         parcel.writeCharSequence(mError);
   2618         parcel.writeCharSequence(mContentDescription);
   2619         parcel.writeString(mViewIdResourceName);
   2620 
   2621         parcel.writeInt(mTextSelectionStart);
   2622         parcel.writeInt(mTextSelectionEnd);
   2623         parcel.writeInt(mInputType);
   2624         parcel.writeInt(mLiveRegion);
   2625 
   2626         if (mExtras != null) {
   2627             parcel.writeInt(1);
   2628             parcel.writeBundle(mExtras);
   2629         } else {
   2630             parcel.writeInt(0);
   2631         }
   2632 
   2633         if (mRangeInfo != null) {
   2634             parcel.writeInt(1);
   2635             parcel.writeInt(mRangeInfo.getType());
   2636             parcel.writeFloat(mRangeInfo.getMin());
   2637             parcel.writeFloat(mRangeInfo.getMax());
   2638             parcel.writeFloat(mRangeInfo.getCurrent());
   2639         } else {
   2640             parcel.writeInt(0);
   2641         }
   2642 
   2643         if (mCollectionInfo != null) {
   2644             parcel.writeInt(1);
   2645             parcel.writeInt(mCollectionInfo.getRowCount());
   2646             parcel.writeInt(mCollectionInfo.getColumnCount());
   2647             parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0);
   2648             parcel.writeInt(mCollectionInfo.getSelectionMode());
   2649         } else {
   2650             parcel.writeInt(0);
   2651         }
   2652 
   2653         if (mCollectionItemInfo != null) {
   2654             parcel.writeInt(1);
   2655             parcel.writeInt(mCollectionItemInfo.getColumnIndex());
   2656             parcel.writeInt(mCollectionItemInfo.getColumnSpan());
   2657             parcel.writeInt(mCollectionItemInfo.getRowIndex());
   2658             parcel.writeInt(mCollectionItemInfo.getRowSpan());
   2659             parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
   2660             parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0);
   2661         } else {
   2662             parcel.writeInt(0);
   2663         }
   2664 
   2665         // Since instances of this class are fetched via synchronous i.e. blocking
   2666         // calls in IPCs we always recycle as soon as the instance is marshaled.
   2667         recycle();
   2668     }
   2669 
   2670     /**
   2671      * Initializes this instance from another one.
   2672      *
   2673      * @param other The other instance.
   2674      */
   2675     private void init(AccessibilityNodeInfo other) {
   2676         mSealed = other.mSealed;
   2677         mSourceNodeId = other.mSourceNodeId;
   2678         mParentNodeId = other.mParentNodeId;
   2679         mLabelForId = other.mLabelForId;
   2680         mLabeledById = other.mLabeledById;
   2681         mTraversalBefore = other.mTraversalBefore;
   2682         mTraversalAfter = other.mTraversalAfter;
   2683         mWindowId = other.mWindowId;
   2684         mConnectionId = other.mConnectionId;
   2685         mBoundsInParent.set(other.mBoundsInParent);
   2686         mBoundsInScreen.set(other.mBoundsInScreen);
   2687         mPackageName = other.mPackageName;
   2688         mClassName = other.mClassName;
   2689         mText = other.mText;
   2690         mError = other.mError;
   2691         mContentDescription = other.mContentDescription;
   2692         mViewIdResourceName = other.mViewIdResourceName;
   2693 
   2694         final ArrayList<AccessibilityAction> otherActions = other.mActions;
   2695         if (otherActions != null && otherActions.size() > 0) {
   2696             if (mActions == null) {
   2697                 mActions = new ArrayList(otherActions);
   2698             } else {
   2699                 mActions.clear();
   2700                 mActions.addAll(other.mActions);
   2701             }
   2702         }
   2703 
   2704         mBooleanProperties = other.mBooleanProperties;
   2705         mMaxTextLength = other.mMaxTextLength;
   2706         mMovementGranularities = other.mMovementGranularities;
   2707 
   2708         final LongArray otherChildNodeIds = other.mChildNodeIds;
   2709         if (otherChildNodeIds != null && otherChildNodeIds.size() > 0) {
   2710             if (mChildNodeIds == null) {
   2711                 mChildNodeIds = otherChildNodeIds.clone();
   2712             } else {
   2713                 mChildNodeIds.clear();
   2714                 mChildNodeIds.addAll(otherChildNodeIds);
   2715             }
   2716         }
   2717 
   2718         mTextSelectionStart = other.mTextSelectionStart;
   2719         mTextSelectionEnd = other.mTextSelectionEnd;
   2720         mInputType = other.mInputType;
   2721         mLiveRegion = other.mLiveRegion;
   2722         if (other.mExtras != null && !other.mExtras.isEmpty()) {
   2723             getExtras().putAll(other.mExtras);
   2724         }
   2725         mRangeInfo = (other.mRangeInfo != null)
   2726                 ? RangeInfo.obtain(other.mRangeInfo) : null;
   2727         mCollectionInfo = (other.mCollectionInfo != null)
   2728                 ? CollectionInfo.obtain(other.mCollectionInfo) : null;
   2729         mCollectionItemInfo =  (other.mCollectionItemInfo != null)
   2730                 ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
   2731     }
   2732 
   2733     /**
   2734      * Creates a new instance from a {@link Parcel}.
   2735      *
   2736      * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
   2737      */
   2738     private void initFromParcel(Parcel parcel) {
   2739         mSealed = (parcel.readInt()  == 1);
   2740         mSourceNodeId = parcel.readLong();
   2741         mWindowId = parcel.readInt();
   2742         mParentNodeId = parcel.readLong();
   2743         mLabelForId = parcel.readLong();
   2744         mLabeledById = parcel.readLong();
   2745         mTraversalBefore = parcel.readLong();
   2746         mTraversalAfter = parcel.readLong();
   2747 
   2748         mConnectionId = parcel.readInt();
   2749 
   2750         final int childrenSize = parcel.readInt();
   2751         if (childrenSize <= 0) {
   2752             mChildNodeIds = null;
   2753         } else {
   2754             mChildNodeIds = new LongArray(childrenSize);
   2755             for (int i = 0; i < childrenSize; i++) {
   2756                 final long childId = parcel.readLong();
   2757                 mChildNodeIds.add(childId);
   2758             }
   2759         }
   2760 
   2761         mBoundsInParent.top = parcel.readInt();
   2762         mBoundsInParent.bottom = parcel.readInt();
   2763         mBoundsInParent.left = parcel.readInt();
   2764         mBoundsInParent.right = parcel.readInt();
   2765 
   2766         mBoundsInScreen.top = parcel.readInt();
   2767         mBoundsInScreen.bottom = parcel.readInt();
   2768         mBoundsInScreen.left = parcel.readInt();
   2769         mBoundsInScreen.right = parcel.readInt();
   2770 
   2771         final int actionCount = parcel.readInt();
   2772         if (actionCount > 0) {
   2773             final int legacyStandardActions = parcel.readInt();
   2774             addLegacyStandardActions(legacyStandardActions);
   2775             final int nonLegacyActionCount = actionCount - Integer.bitCount(legacyStandardActions);
   2776             for (int i = 0; i < nonLegacyActionCount; i++) {
   2777                 AccessibilityAction action = new AccessibilityAction(
   2778                         parcel.readInt(), parcel.readCharSequence());
   2779                 addAction(action);
   2780             }
   2781         }
   2782 
   2783         mMaxTextLength = parcel.readInt();
   2784         mMovementGranularities = parcel.readInt();
   2785         mBooleanProperties = parcel.readInt();
   2786 
   2787         mPackageName = parcel.readCharSequence();
   2788         mClassName = parcel.readCharSequence();
   2789         mText = parcel.readCharSequence();
   2790         mError = parcel.readCharSequence();
   2791         mContentDescription = parcel.readCharSequence();
   2792         mViewIdResourceName = parcel.readString();
   2793 
   2794         mTextSelectionStart = parcel.readInt();
   2795         mTextSelectionEnd = parcel.readInt();
   2796 
   2797         mInputType = parcel.readInt();
   2798         mLiveRegion = parcel.readInt();
   2799 
   2800         if (parcel.readInt() == 1) {
   2801             getExtras().putAll(parcel.readBundle());
   2802         }
   2803 
   2804         if (parcel.readInt() == 1) {
   2805             mRangeInfo = RangeInfo.obtain(
   2806                     parcel.readInt(),
   2807                     parcel.readFloat(),
   2808                     parcel.readFloat(),
   2809                     parcel.readFloat());
   2810         }
   2811 
   2812         if (parcel.readInt() == 1) {
   2813             mCollectionInfo = CollectionInfo.obtain(
   2814                     parcel.readInt(),
   2815                     parcel.readInt(),
   2816                     parcel.readInt() == 1,
   2817                     parcel.readInt());
   2818         }
   2819 
   2820         if (parcel.readInt() == 1) {
   2821             mCollectionItemInfo = CollectionItemInfo.obtain(
   2822                     parcel.readInt(),
   2823                     parcel.readInt(),
   2824                     parcel.readInt(),
   2825                     parcel.readInt(),
   2826                     parcel.readInt() == 1,
   2827                     parcel.readInt() == 1);
   2828         }
   2829     }
   2830 
   2831     /**
   2832      * Clears the state of this instance.
   2833      */
   2834     private void clear() {
   2835         mSealed = false;
   2836         mSourceNodeId = ROOT_NODE_ID;
   2837         mParentNodeId = ROOT_NODE_ID;
   2838         mLabelForId = ROOT_NODE_ID;
   2839         mLabeledById = ROOT_NODE_ID;
   2840         mTraversalBefore = ROOT_NODE_ID;
   2841         mTraversalAfter = ROOT_NODE_ID;
   2842         mWindowId = UNDEFINED_ITEM_ID;
   2843         mConnectionId = UNDEFINED_CONNECTION_ID;
   2844         mMaxTextLength = -1;
   2845         mMovementGranularities = 0;
   2846         if (mChildNodeIds != null) {
   2847             mChildNodeIds.clear();
   2848         }
   2849         mBoundsInParent.set(0, 0, 0, 0);
   2850         mBoundsInScreen.set(0, 0, 0, 0);
   2851         mBooleanProperties = 0;
   2852         mPackageName = null;
   2853         mClassName = null;
   2854         mText = null;
   2855         mError = null;
   2856         mContentDescription = null;
   2857         mViewIdResourceName = null;
   2858         if (mActions != null) {
   2859             mActions.clear();
   2860         }
   2861         mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
   2862         mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
   2863         mInputType = InputType.TYPE_NULL;
   2864         mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
   2865         if (mExtras != null) {
   2866             mExtras.clear();
   2867         }
   2868         if (mRangeInfo != null) {
   2869             mRangeInfo.recycle();
   2870             mRangeInfo = null;
   2871         }
   2872         if (mCollectionInfo != null) {
   2873             mCollectionInfo.recycle();
   2874             mCollectionInfo = null;
   2875         }
   2876         if (mCollectionItemInfo != null) {
   2877             mCollectionItemInfo.recycle();
   2878             mCollectionItemInfo = null;
   2879         }
   2880     }
   2881 
   2882     private static boolean isDefaultLegacyStandardAction(AccessibilityAction action) {
   2883         return (action.getId() <= LAST_LEGACY_STANDARD_ACTION
   2884                 && TextUtils.isEmpty(action.getLabel()));
   2885     }
   2886 
   2887     private static AccessibilityAction getActionSingleton(int actionId) {
   2888         final int actions = AccessibilityAction.sStandardActions.size();
   2889         for (int i = 0; i < actions; i++) {
   2890             AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i);
   2891             if (actionId == currentAction.getId()) {
   2892                 return currentAction;
   2893             }
   2894         }
   2895 
   2896         return null;
   2897     }
   2898 
   2899     private void addLegacyStandardActions(int actionMask) {
   2900         int remainingIds = actionMask;
   2901         while (remainingIds > 0) {
   2902             final int id = 1 << Integer.numberOfTrailingZeros(remainingIds);
   2903             remainingIds &= ~id;
   2904             AccessibilityAction action = getActionSingleton(id);
   2905             addAction(action);
   2906         }
   2907     }
   2908 
   2909     /**
   2910      * Gets the human readable action symbolic name.
   2911      *
   2912      * @param action The action.
   2913      * @return The symbolic name.
   2914      */
   2915     private static String getActionSymbolicName(int action) {
   2916         switch (action) {
   2917             case ACTION_FOCUS:
   2918                 return "ACTION_FOCUS";
   2919             case ACTION_CLEAR_FOCUS:
   2920                 return "ACTION_CLEAR_FOCUS";
   2921             case ACTION_SELECT:
   2922                 return "ACTION_SELECT";
   2923             case ACTION_CLEAR_SELECTION:
   2924                 return "ACTION_CLEAR_SELECTION";
   2925             case ACTION_CLICK:
   2926                 return "ACTION_CLICK";
   2927             case ACTION_LONG_CLICK:
   2928                 return "ACTION_LONG_CLICK";
   2929             case ACTION_ACCESSIBILITY_FOCUS:
   2930                 return "ACTION_ACCESSIBILITY_FOCUS";
   2931             case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
   2932                 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
   2933             case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
   2934                 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
   2935             case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
   2936                 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
   2937             case ACTION_NEXT_HTML_ELEMENT:
   2938                 return "ACTION_NEXT_HTML_ELEMENT";
   2939             case ACTION_PREVIOUS_HTML_ELEMENT:
   2940                 return "ACTION_PREVIOUS_HTML_ELEMENT";
   2941             case ACTION_SCROLL_FORWARD:
   2942                 return "ACTION_SCROLL_FORWARD";
   2943             case ACTION_SCROLL_BACKWARD:
   2944                 return "ACTION_SCROLL_BACKWARD";
   2945             case ACTION_CUT:
   2946                 return "ACTION_CUT";
   2947             case ACTION_COPY:
   2948                 return "ACTION_COPY";
   2949             case ACTION_PASTE:
   2950                 return "ACTION_PASTE";
   2951             case ACTION_SET_SELECTION:
   2952                 return "ACTION_SET_SELECTION";
   2953             default:
   2954                 return"ACTION_UNKNOWN";
   2955         }
   2956     }
   2957 
   2958     /**
   2959      * Gets the human readable movement granularity symbolic name.
   2960      *
   2961      * @param granularity The granularity.
   2962      * @return The symbolic name.
   2963      */
   2964     private static String getMovementGranularitySymbolicName(int granularity) {
   2965         switch (granularity) {
   2966             case MOVEMENT_GRANULARITY_CHARACTER:
   2967                 return "MOVEMENT_GRANULARITY_CHARACTER";
   2968             case MOVEMENT_GRANULARITY_WORD:
   2969                 return "MOVEMENT_GRANULARITY_WORD";
   2970             case MOVEMENT_GRANULARITY_LINE:
   2971                 return "MOVEMENT_GRANULARITY_LINE";
   2972             case MOVEMENT_GRANULARITY_PARAGRAPH:
   2973                 return "MOVEMENT_GRANULARITY_PARAGRAPH";
   2974             case MOVEMENT_GRANULARITY_PAGE:
   2975                 return "MOVEMENT_GRANULARITY_PAGE";
   2976             default:
   2977                 throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
   2978         }
   2979     }
   2980 
   2981     private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
   2982         return (mWindowId != UNDEFINED_ITEM_ID
   2983                 && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID
   2984                 && mConnectionId != UNDEFINED_CONNECTION_ID);
   2985     }
   2986 
   2987     @Override
   2988     public boolean equals(Object object) {
   2989         if (this == object) {
   2990             return true;
   2991         }
   2992         if (object == null) {
   2993             return false;
   2994         }
   2995         if (getClass() != object.getClass()) {
   2996             return false;
   2997         }
   2998         AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
   2999         if (mSourceNodeId != other.mSourceNodeId) {
   3000             return false;
   3001         }
   3002         if (mWindowId != other.mWindowId) {
   3003             return false;
   3004         }
   3005         return true;
   3006     }
   3007 
   3008     @Override
   3009     public int hashCode() {
   3010         final int prime = 31;
   3011         int result = 1;
   3012         result = prime * result + getAccessibilityViewId(mSourceNodeId);
   3013         result = prime * result + getVirtualDescendantId(mSourceNodeId);
   3014         result = prime * result + mWindowId;
   3015         return result;
   3016     }
   3017 
   3018     @Override
   3019     public String toString() {
   3020         StringBuilder builder = new StringBuilder();
   3021         builder.append(super.toString());
   3022 
   3023         if (DEBUG) {
   3024             builder.append("; sourceNodeId: " + mSourceNodeId);
   3025             builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
   3026             builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
   3027             builder.append("; mParentNodeId: " + mParentNodeId);
   3028             builder.append("; traversalBefore: ").append(mTraversalBefore);
   3029             builder.append("; traversalAfter: ").append(mTraversalAfter);
   3030 
   3031             int granularities = mMovementGranularities;
   3032             builder.append("; MovementGranularities: [");
   3033             while (granularities != 0) {
   3034                 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
   3035                 granularities &= ~granularity;
   3036                 builder.append(getMovementGranularitySymbolicName(granularity));
   3037                 if (granularities != 0) {
   3038                     builder.append(", ");
   3039                 }
   3040             }
   3041             builder.append("]");
   3042 
   3043             builder.append("; childAccessibilityIds: [");
   3044             final LongArray childIds = mChildNodeIds;
   3045             if (childIds != null) {
   3046                 for (int i = 0, count = childIds.size(); i < count; i++) {
   3047                     builder.append(childIds.get(i));
   3048                     if (i < count - 1) {
   3049                         builder.append(", ");
   3050                     }
   3051                 }
   3052             }
   3053             builder.append("]");
   3054         }
   3055 
   3056         builder.append("; boundsInParent: " + mBoundsInParent);
   3057         builder.append("; boundsInScreen: " + mBoundsInScreen);
   3058 
   3059         builder.append("; packageName: ").append(mPackageName);
   3060         builder.append("; className: ").append(mClassName);
   3061         builder.append("; text: ").append(mText);
   3062         builder.append("; error: ").append(mError);
   3063         builder.append("; maxTextLength: ").append(mMaxTextLength);
   3064         builder.append("; contentDescription: ").append(mContentDescription);
   3065         builder.append("; viewIdResName: ").append(mViewIdResourceName);
   3066 
   3067         builder.append("; checkable: ").append(isCheckable());
   3068         builder.append("; checked: ").append(isChecked());
   3069         builder.append("; focusable: ").append(isFocusable());
   3070         builder.append("; focused: ").append(isFocused());
   3071         builder.append("; selected: ").append(isSelected());
   3072         builder.append("; clickable: ").append(isClickable());
   3073         builder.append("; longClickable: ").append(isLongClickable());
   3074         builder.append("; enabled: ").append(isEnabled());
   3075         builder.append("; password: ").append(isPassword());
   3076         builder.append("; scrollable: ").append(isScrollable());
   3077         builder.append("; actions: ").append(mActions);
   3078 
   3079         return builder.toString();
   3080     }
   3081 
   3082     private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) {
   3083         if (!canPerformRequestOverConnection(accessibilityId)) {
   3084             return null;
   3085         }
   3086         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   3087         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
   3088                 mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS
   3089                         | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
   3090     }
   3091 
   3092     /**
   3093      * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
   3094      * Each action has a unique id that is mandatory and optional data.
   3095      * <p>
   3096      * There are three categories of actions:
   3097      * <ul>
   3098      * <li><strong>Standard actions</strong> - These are actions that are reported and
   3099      * handled by the standard UI widgets in the platform. For each standard action
   3100      * there is a static constant defined in this class, e.g. {@link #ACTION_FOCUS}.
   3101      * </li>
   3102      * <li><strong>Custom actions action</strong> - These are actions that are reported
   3103      * and handled by custom widgets. i.e. ones that are not part of the UI toolkit. For
   3104      * example, an application may define a custom action for clearing the user history.
   3105      * </li>
   3106      * <li><strong>Overriden standard actions</strong> - These are actions that override
   3107      * standard actions to customize them. For example, an app may add a label to the
   3108      * standard click action to announce that this action clears browsing history.
   3109      * </ul>
   3110      * </p>
   3111      */
   3112     public static final class AccessibilityAction {
   3113 
   3114         /**
   3115          * Action that gives input focus to the node.
   3116          */
   3117         public static final AccessibilityAction ACTION_FOCUS =
   3118                 new AccessibilityAction(
   3119                         AccessibilityNodeInfo.ACTION_FOCUS, null);
   3120 
   3121         /**
   3122          * Action that clears input focus of the node.
   3123          */
   3124         public static final AccessibilityAction ACTION_CLEAR_FOCUS =
   3125                 new AccessibilityAction(
   3126                         AccessibilityNodeInfo.ACTION_CLEAR_FOCUS, null);
   3127 
   3128         /**
   3129          *  Action that selects the node.
   3130          */
   3131         public static final AccessibilityAction ACTION_SELECT =
   3132                 new AccessibilityAction(
   3133                         AccessibilityNodeInfo.ACTION_SELECT, null);
   3134 
   3135         /**
   3136          * Action that deselects the node.
   3137          */
   3138         public static final AccessibilityAction ACTION_CLEAR_SELECTION =
   3139                 new AccessibilityAction(
   3140                         AccessibilityNodeInfo.ACTION_CLEAR_SELECTION, null);
   3141 
   3142         /**
   3143          * Action that clicks on the node info.
   3144          */
   3145         public static final AccessibilityAction ACTION_CLICK =
   3146                 new AccessibilityAction(
   3147                         AccessibilityNodeInfo.ACTION_CLICK, null);
   3148 
   3149         /**
   3150          * Action that long clicks on the node.
   3151          */
   3152         public static final AccessibilityAction ACTION_LONG_CLICK =
   3153                 new AccessibilityAction(
   3154                         AccessibilityNodeInfo.ACTION_LONG_CLICK, null);
   3155 
   3156         /**
   3157          * Action that gives accessibility focus to the node.
   3158          */
   3159         public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS =
   3160                 new AccessibilityAction(
   3161                         AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
   3162 
   3163         /**
   3164          * Action that clears accessibility focus of the node.
   3165          */
   3166         public static final AccessibilityAction ACTION_CLEAR_ACCESSIBILITY_FOCUS =
   3167                 new AccessibilityAction(
   3168                         AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
   3169 
   3170         /**
   3171          * Action that requests to go to the next entity in this node's text
   3172          * at a given movement granularity. For example, move to the next character,
   3173          * word, etc.
   3174          * <p>
   3175          * <strong>Arguments:</strong>
   3176          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   3177          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
   3178          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3179          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
   3180          * <strong>Example:</strong> Move to the previous character and do not extend selection.
   3181          * <code><pre><p>
   3182          *   Bundle arguments = new Bundle();
   3183          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
   3184          *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
   3185          *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
   3186          *           false);
   3187          *   info.performAction(AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
   3188          *           arguments);
   3189          * </code></pre></p>
   3190          * </p>
   3191          *
   3192          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   3193          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   3194          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3195          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3196          *
   3197          * @see AccessibilityNodeInfo#setMovementGranularities(int)
   3198          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3199          * @see AccessibilityNodeInfo#getMovementGranularities()
   3200          *  AccessibilityNodeInfo.getMovementGranularities()
   3201          *
   3202          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER
   3203          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
   3204          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD
   3205          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
   3206          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE
   3207          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
   3208          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH
   3209          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
   3210          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE
   3211          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE
   3212          */
   3213         public static final AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
   3214                 new AccessibilityAction(
   3215                         AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, null);
   3216 
   3217         /**
   3218          * Action that requests to go to the previous entity in this node's text
   3219          * at a given movement granularity. For example, move to the next character,
   3220          * word, etc.
   3221          * <p>
   3222          * <strong>Arguments:</strong>
   3223          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   3224          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
   3225          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3226          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
   3227          * <strong>Example:</strong> Move to the next character and do not extend selection.
   3228          * <code><pre><p>
   3229          *   Bundle arguments = new Bundle();
   3230          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
   3231          *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
   3232          *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
   3233          *           false);
   3234          *   info.performAction(AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
   3235          *           arguments);
   3236          * </code></pre></p>
   3237          * </p>
   3238          *
   3239          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   3240          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   3241          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3242          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3243          *
   3244          * @see AccessibilityNodeInfo#setMovementGranularities(int)
   3245          *   AccessibilityNodeInfo.setMovementGranularities(int)
   3246          * @see AccessibilityNodeInfo#getMovementGranularities()
   3247          *  AccessibilityNodeInfo.getMovementGranularities()
   3248          *
   3249          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER
   3250          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
   3251          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD
   3252          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
   3253          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE
   3254          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
   3255          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH
   3256          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
   3257          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE
   3258          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE
   3259          */
   3260         public static final AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
   3261                 new AccessibilityAction(
   3262                         AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, null);
   3263 
   3264         /**
   3265          * Action to move to the next HTML element of a given type. For example, move
   3266          * to the BUTTON, INPUT, TABLE, etc.
   3267          * <p>
   3268          * <strong>Arguments:</strong>
   3269          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
   3270          *  AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
   3271          * <strong>Example:</strong>
   3272          * <code><pre><p>
   3273          *   Bundle arguments = new Bundle();
   3274          *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
   3275          *   info.performAction(AccessibilityAction.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
   3276          * </code></pre></p>
   3277          * </p>
   3278          */
   3279         public static final AccessibilityAction ACTION_NEXT_HTML_ELEMENT =
   3280                 new AccessibilityAction(
   3281                         AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, null);
   3282 
   3283         /**
   3284          * Action to move to the previous HTML element of a given type. For example, move
   3285          * to the BUTTON, INPUT, TABLE, etc.
   3286          * <p>
   3287          * <strong>Arguments:</strong>
   3288          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
   3289          *  AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
   3290          * <strong>Example:</strong>
   3291          * <code><pre><p>
   3292          *   Bundle arguments = new Bundle();
   3293          *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
   3294          *   info.performAction(AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
   3295          * </code></pre></p>
   3296          * </p>
   3297          */
   3298         public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT =
   3299                 new AccessibilityAction(
   3300                         AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, null);
   3301 
   3302         /**
   3303          * Action to scroll the node content forward.
   3304          */
   3305         public static final AccessibilityAction ACTION_SCROLL_FORWARD =
   3306                 new AccessibilityAction(
   3307                         AccessibilityNodeInfo.ACTION_SCROLL_FORWARD, null);
   3308 
   3309         /**
   3310          * Action to scroll the node content backward.
   3311          */
   3312         public static final AccessibilityAction ACTION_SCROLL_BACKWARD =
   3313                 new AccessibilityAction(
   3314                         AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD, null);
   3315 
   3316         /**
   3317          * Action to copy the current selection to the clipboard.
   3318          */
   3319         public static final AccessibilityAction ACTION_COPY =
   3320                 new AccessibilityAction(
   3321                         AccessibilityNodeInfo.ACTION_COPY, null);
   3322 
   3323         /**
   3324          * Action to paste the current clipboard content.
   3325          */
   3326         public static final AccessibilityAction ACTION_PASTE =
   3327                 new AccessibilityAction(
   3328                         AccessibilityNodeInfo.ACTION_PASTE, null);
   3329 
   3330         /**
   3331          * Action to cut the current selection and place it to the clipboard.
   3332          */
   3333         public static final AccessibilityAction ACTION_CUT =
   3334                 new AccessibilityAction(
   3335                         AccessibilityNodeInfo.ACTION_CUT, null);
   3336 
   3337         /**
   3338          * Action to set the selection. Performing this action with no arguments
   3339          * clears the selection.
   3340          * <p>
   3341          * <strong>Arguments:</strong>
   3342          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT
   3343          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT},
   3344          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT
   3345          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT}<br>
   3346          * <strong>Example:</strong>
   3347          * <code><pre><p>
   3348          *   Bundle arguments = new Bundle();
   3349          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
   3350          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
   3351          *   info.performAction(AccessibilityAction.ACTION_SET_SELECTION.getId(), arguments);
   3352          * </code></pre></p>
   3353          * </p>
   3354          *
   3355          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT
   3356          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT
   3357          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT
   3358          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT
   3359          */
   3360         public static final AccessibilityAction ACTION_SET_SELECTION =
   3361                 new AccessibilityAction(
   3362                         AccessibilityNodeInfo.ACTION_SET_SELECTION, null);
   3363 
   3364         /**
   3365          * Action to expand an expandable node.
   3366          */
   3367         public static final AccessibilityAction ACTION_EXPAND =
   3368                 new AccessibilityAction(
   3369                         AccessibilityNodeInfo.ACTION_EXPAND, null);
   3370 
   3371         /**
   3372          * Action to collapse an expandable node.
   3373          */
   3374         public static final AccessibilityAction ACTION_COLLAPSE =
   3375                 new AccessibilityAction(
   3376                         AccessibilityNodeInfo.ACTION_COLLAPSE, null);
   3377 
   3378         /**
   3379          * Action to dismiss a dismissable node.
   3380          */
   3381         public static final AccessibilityAction ACTION_DISMISS =
   3382                 new AccessibilityAction(
   3383                         AccessibilityNodeInfo.ACTION_DISMISS, null);
   3384 
   3385         /**
   3386          * Action that sets the text of the node. Performing the action without argument,
   3387          * using <code> null</code> or empty {@link CharSequence} will clear the text. This
   3388          * action will also put the cursor at the end of text.
   3389          * <p>
   3390          * <strong>Arguments:</strong>
   3391          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
   3392          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
   3393          * <strong>Example:</strong>
   3394          * <code><pre><p>
   3395          *   Bundle arguments = new Bundle();
   3396          *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
   3397          *       "android");
   3398          *   info.performAction(AccessibilityAction.ACTION_SET_TEXT.getId(), arguments);
   3399          * </code></pre></p>
   3400          */
   3401         public static final AccessibilityAction ACTION_SET_TEXT =
   3402                 new AccessibilityAction(
   3403                         AccessibilityNodeInfo.ACTION_SET_TEXT, null);
   3404 
   3405         private static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<AccessibilityAction>();
   3406         static {
   3407             sStandardActions.add(ACTION_FOCUS);
   3408             sStandardActions.add(ACTION_CLEAR_FOCUS);
   3409             sStandardActions.add(ACTION_SELECT);
   3410             sStandardActions.add(ACTION_CLEAR_SELECTION);
   3411             sStandardActions.add(ACTION_CLICK);
   3412             sStandardActions.add(ACTION_LONG_CLICK);
   3413             sStandardActions.add(ACTION_ACCESSIBILITY_FOCUS);
   3414             sStandardActions.add(ACTION_CLEAR_ACCESSIBILITY_FOCUS);
   3415             sStandardActions.add(ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
   3416             sStandardActions.add(ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
   3417             sStandardActions.add(ACTION_NEXT_HTML_ELEMENT);
   3418             sStandardActions.add(ACTION_PREVIOUS_HTML_ELEMENT);
   3419             sStandardActions.add(ACTION_SCROLL_FORWARD);
   3420             sStandardActions.add(ACTION_SCROLL_BACKWARD);
   3421             sStandardActions.add(ACTION_COPY);
   3422             sStandardActions.add(ACTION_PASTE);
   3423             sStandardActions.add(ACTION_CUT);
   3424             sStandardActions.add(ACTION_SET_SELECTION);
   3425             sStandardActions.add(ACTION_EXPAND);
   3426             sStandardActions.add(ACTION_COLLAPSE);
   3427             sStandardActions.add(ACTION_DISMISS);
   3428             sStandardActions.add(ACTION_SET_TEXT);
   3429         }
   3430 
   3431         private final int mActionId;
   3432         private final CharSequence mLabel;
   3433 
   3434         /**
   3435          * Creates a new AccessibilityAction. For adding a standard action without a specific label,
   3436          * use the static constants.
   3437          *
   3438          * You can also override the description for one the standard actions. Below is an example
   3439          * how to override the standard click action by adding a custom label:
   3440          * <pre>
   3441          *   AccessibilityAction action = new AccessibilityAction(
   3442          *           AccessibilityAction.ACTION_ACTION_CLICK, getLocalizedLabel());
   3443          *   node.addAction(action);
   3444          * </pre>
   3445          *
   3446          * @param actionId The id for this action. This should either be one of the
   3447          *                 standard actions or a specific action for your app. In that case it is
   3448          *                 required to use a resource identifier.
   3449          * @param label The label for the new AccessibilityAction.
   3450          */
   3451         public AccessibilityAction(int actionId, @Nullable CharSequence label) {
   3452             if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) != 1) {
   3453                 throw new IllegalArgumentException("Invalid standard action id");
   3454             }
   3455 
   3456             mActionId = actionId;
   3457             mLabel = label;
   3458         }
   3459 
   3460         /**
   3461          * Gets the id for this action.
   3462          *
   3463          * @return The action id.
   3464          */
   3465         public int getId() {
   3466             return mActionId;
   3467         }
   3468 
   3469         /**
   3470          * Gets the label for this action. Its purpose is to describe the
   3471          * action to user.
   3472          *
   3473          * @return The label.
   3474          */
   3475         public CharSequence getLabel() {
   3476             return mLabel;
   3477         }
   3478 
   3479         @Override
   3480         public int hashCode() {
   3481             return mActionId;
   3482         }
   3483 
   3484         @Override
   3485         public boolean equals(Object other) {
   3486             if (other == null) {
   3487                 return false;
   3488             }
   3489 
   3490             if (other == this) {
   3491                 return true;
   3492             }
   3493 
   3494             if (getClass() != other.getClass()) {
   3495                 return false;
   3496             }
   3497 
   3498             return mActionId == ((AccessibilityAction)other).mActionId;
   3499         }
   3500 
   3501         @Override
   3502         public String toString() {
   3503             return "AccessibilityAction: " + getActionSymbolicName(mActionId) + " - " + mLabel;
   3504         }
   3505     }
   3506 
   3507     /**
   3508      * Class with information if a node is a range. Use
   3509      * {@link RangeInfo#obtain(int, float, float, float)} to get an instance.
   3510      */
   3511     public static final class RangeInfo {
   3512         private static final int MAX_POOL_SIZE = 10;
   3513 
   3514         /** Range type: integer. */
   3515         public static final int RANGE_TYPE_INT = 0;
   3516         /** Range type: float. */
   3517         public static final int RANGE_TYPE_FLOAT = 1;
   3518         /** Range type: percent with values from zero to one.*/
   3519         public static final int RANGE_TYPE_PERCENT = 2;
   3520 
   3521         private static final SynchronizedPool<RangeInfo> sPool =
   3522                 new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE);
   3523 
   3524         private int mType;
   3525         private float mMin;
   3526         private float mMax;
   3527         private float mCurrent;
   3528 
   3529         /**
   3530          * Obtains a pooled instance that is a clone of another one.
   3531          *
   3532          * @param other The instance to clone.
   3533          *
   3534          * @hide
   3535          */
   3536         public static RangeInfo obtain(RangeInfo other) {
   3537             return obtain(other.mType, other.mMin, other.mMax, other.mCurrent);
   3538         }
   3539 
   3540         /**
   3541          * Obtains a pooled instance.
   3542          *
   3543          * @param type The type of the range.
   3544          * @param min The min value.
   3545          * @param max The max value.
   3546          * @param current The current value.
   3547          */
   3548         public static RangeInfo obtain(int type, float min, float max, float current) {
   3549             RangeInfo info = sPool.acquire();
   3550             return (info != null) ? info : new RangeInfo(type, min, max, current);
   3551         }
   3552 
   3553         /**
   3554          * Creates a new range.
   3555          *
   3556          * @param type The type of the range.
   3557          * @param min The min value.
   3558          * @param max The max value.
   3559          * @param current The current value.
   3560          */
   3561         private RangeInfo(int type, float min, float max, float current) {
   3562             mType = type;
   3563             mMin = min;
   3564             mMax = max;
   3565             mCurrent = current;
   3566         }
   3567 
   3568         /**
   3569          * Gets the range type.
   3570          *
   3571          * @return The range type.
   3572          *
   3573          * @see #RANGE_TYPE_INT
   3574          * @see #RANGE_TYPE_FLOAT
   3575          * @see #RANGE_TYPE_PERCENT
   3576          */
   3577         public int getType() {
   3578             return mType;
   3579         }
   3580 
   3581         /**
   3582          * Gets the min value.
   3583          *
   3584          * @return The min value.
   3585          */
   3586         public float getMin() {
   3587             return mMin;
   3588         }
   3589 
   3590         /**
   3591          * Gets the max value.
   3592          *
   3593          * @return The max value.
   3594          */
   3595         public float getMax() {
   3596             return mMax;
   3597         }
   3598 
   3599         /**
   3600          * Gets the current value.
   3601          *
   3602          * @return The current value.
   3603          */
   3604         public float getCurrent() {
   3605             return mCurrent;
   3606         }
   3607 
   3608         /**
   3609          * Recycles this instance.
   3610          */
   3611         void recycle() {
   3612             clear();
   3613             sPool.release(this);
   3614         }
   3615 
   3616         private void clear() {
   3617             mType = 0;
   3618             mMin = 0;
   3619             mMax = 0;
   3620             mCurrent = 0;
   3621         }
   3622     }
   3623 
   3624     /**
   3625      * Class with information if a node is a collection. Use
   3626      * {@link CollectionInfo#obtain(int, int, boolean)} to get an instance.
   3627      * <p>
   3628      * A collection of items has rows and columns and may be hierarchical.
   3629      * For example, a horizontal list is a collection with one column, as
   3630      * many rows as the list items, and is not hierarchical; A table is a
   3631      * collection with several rows, several columns, and is not hierarchical;
   3632      * A vertical tree is a hierarchical collection with one column and
   3633      * as many rows as the first level children.
   3634      * </p>
   3635      */
   3636     public static final class CollectionInfo {
   3637         /** Selection mode where items are not selectable. */
   3638         public static final int SELECTION_MODE_NONE = 0;
   3639 
   3640         /** Selection mode where a single item may be selected. */
   3641         public static final int SELECTION_MODE_SINGLE = 1;
   3642 
   3643         /** Selection mode where multiple items may be selected. */
   3644         public static final int SELECTION_MODE_MULTIPLE = 2;
   3645 
   3646         private static final int MAX_POOL_SIZE = 20;
   3647 
   3648         private static final SynchronizedPool<CollectionInfo> sPool =
   3649                 new SynchronizedPool<CollectionInfo>(MAX_POOL_SIZE);
   3650 
   3651         private int mRowCount;
   3652         private int mColumnCount;
   3653         private boolean mHierarchical;
   3654         private int mSelectionMode;
   3655 
   3656         /**
   3657          * Obtains a pooled instance that is a clone of another one.
   3658          *
   3659          * @param other The instance to clone.
   3660          * @hide
   3661          */
   3662         public static CollectionInfo obtain(CollectionInfo other) {
   3663             return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical,
   3664                     other.mSelectionMode);
   3665         }
   3666 
   3667         /**
   3668          * Obtains a pooled instance.
   3669          *
   3670          * @param rowCount The number of rows.
   3671          * @param columnCount The number of columns.
   3672          * @param hierarchical Whether the collection is hierarchical.
   3673          */
   3674         public static CollectionInfo obtain(int rowCount, int columnCount,
   3675                 boolean hierarchical) {
   3676             return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
   3677         }
   3678 
   3679         /**
   3680          * Obtains a pooled instance.
   3681          *
   3682          * @param rowCount The number of rows.
   3683          * @param columnCount The number of columns.
   3684          * @param hierarchical Whether the collection is hierarchical.
   3685          * @param selectionMode The collection's selection mode, one of:
   3686          *            <ul>
   3687          *            <li>{@link #SELECTION_MODE_NONE}
   3688          *            <li>{@link #SELECTION_MODE_SINGLE}
   3689          *            <li>{@link #SELECTION_MODE_MULTIPLE}
   3690          *            </ul>
   3691          */
   3692         public static CollectionInfo obtain(int rowCount, int columnCount,
   3693                 boolean hierarchical, int selectionMode) {
   3694            final CollectionInfo info = sPool.acquire();
   3695             if (info == null) {
   3696                 return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
   3697             }
   3698 
   3699             info.mRowCount = rowCount;
   3700             info.mColumnCount = columnCount;
   3701             info.mHierarchical = hierarchical;
   3702             info.mSelectionMode = selectionMode;
   3703             return info;
   3704         }
   3705 
   3706         /**
   3707          * Creates a new instance.
   3708          *
   3709          * @param rowCount The number of rows.
   3710          * @param columnCount The number of columns.
   3711          * @param hierarchical Whether the collection is hierarchical.
   3712          * @param selectionMode The collection's selection mode.
   3713          */
   3714         private CollectionInfo(int rowCount, int columnCount, boolean hierarchical,
   3715                 int selectionMode) {
   3716             mRowCount = rowCount;
   3717             mColumnCount = columnCount;
   3718             mHierarchical = hierarchical;
   3719             mSelectionMode = selectionMode;
   3720         }
   3721 
   3722         /**
   3723          * Gets the number of rows.
   3724          *
   3725          * @return The row count.
   3726          */
   3727         public int getRowCount() {
   3728             return mRowCount;
   3729         }
   3730 
   3731         /**
   3732          * Gets the number of columns.
   3733          *
   3734          * @return The column count.
   3735          */
   3736         public int getColumnCount() {
   3737             return mColumnCount;
   3738         }
   3739 
   3740         /**
   3741          * Gets if the collection is a hierarchically ordered.
   3742          *
   3743          * @return Whether the collection is hierarchical.
   3744          */
   3745         public boolean isHierarchical() {
   3746             return mHierarchical;
   3747         }
   3748 
   3749         /**
   3750          * Gets the collection's selection mode.
   3751          *
   3752          * @return The collection's selection mode, one of:
   3753          *         <ul>
   3754          *         <li>{@link #SELECTION_MODE_NONE}
   3755          *         <li>{@link #SELECTION_MODE_SINGLE}
   3756          *         <li>{@link #SELECTION_MODE_MULTIPLE}
   3757          *         </ul>
   3758          */
   3759         public int getSelectionMode() {
   3760             return mSelectionMode;
   3761         }
   3762 
   3763         /**
   3764          * Recycles this instance.
   3765          */
   3766         void recycle() {
   3767             clear();
   3768             sPool.release(this);
   3769         }
   3770 
   3771         private void clear() {
   3772             mRowCount = 0;
   3773             mColumnCount = 0;
   3774             mHierarchical = false;
   3775             mSelectionMode = SELECTION_MODE_NONE;
   3776         }
   3777     }
   3778 
   3779     /**
   3780      * Class with information if a node is a collection item. Use
   3781      * {@link CollectionItemInfo#obtain(int, int, int, int, boolean)}
   3782      * to get an instance.
   3783      * <p>
   3784      * A collection item is contained in a collection, it starts at
   3785      * a given row and column in the collection, and spans one or
   3786      * more rows and columns. For example, a header of two related
   3787      * table columns starts at the first row and the first column,
   3788      * spans one row and two columns.
   3789      * </p>
   3790      */
   3791     public static final class CollectionItemInfo {
   3792         private static final int MAX_POOL_SIZE = 20;
   3793 
   3794         private static final SynchronizedPool<CollectionItemInfo> sPool =
   3795                 new SynchronizedPool<CollectionItemInfo>(MAX_POOL_SIZE);
   3796 
   3797         /**
   3798          * Obtains a pooled instance that is a clone of another one.
   3799          *
   3800          * @param other The instance to clone.
   3801          * @hide
   3802          */
   3803         public static CollectionItemInfo obtain(CollectionItemInfo other) {
   3804             return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex,
   3805                     other.mColumnSpan, other.mHeading, other.mSelected);
   3806         }
   3807 
   3808         /**
   3809          * Obtains a pooled instance.
   3810          *
   3811          * @param rowIndex The row index at which the item is located.
   3812          * @param rowSpan The number of rows the item spans.
   3813          * @param columnIndex The column index at which the item is located.
   3814          * @param columnSpan The number of columns the item spans.
   3815          * @param heading Whether the item is a heading.
   3816          */
   3817         public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
   3818                 int columnIndex, int columnSpan, boolean heading) {
   3819             return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false);
   3820         }
   3821 
   3822         /**
   3823          * Obtains a pooled instance.
   3824          *
   3825          * @param rowIndex The row index at which the item is located.
   3826          * @param rowSpan The number of rows the item spans.
   3827          * @param columnIndex The column index at which the item is located.
   3828          * @param columnSpan The number of columns the item spans.
   3829          * @param heading Whether the item is a heading.
   3830          * @param selected Whether the item is selected.
   3831          */
   3832         public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
   3833                 int columnIndex, int columnSpan, boolean heading, boolean selected) {
   3834             final CollectionItemInfo info = sPool.acquire();
   3835             if (info == null) {
   3836                 return new CollectionItemInfo(
   3837                         rowIndex, rowSpan, columnIndex, columnSpan, heading, selected);
   3838             }
   3839 
   3840             info.mRowIndex = rowIndex;
   3841             info.mRowSpan = rowSpan;
   3842             info.mColumnIndex = columnIndex;
   3843             info.mColumnSpan = columnSpan;
   3844             info.mHeading = heading;
   3845             info.mSelected = selected;
   3846             return info;
   3847         }
   3848 
   3849         private boolean mHeading;
   3850         private int mColumnIndex;
   3851         private int mRowIndex;
   3852         private int mColumnSpan;
   3853         private int mRowSpan;
   3854         private boolean mSelected;
   3855 
   3856         /**
   3857          * Creates a new instance.
   3858          *
   3859          * @param rowIndex The row index at which the item is located.
   3860          * @param rowSpan The number of rows the item spans.
   3861          * @param columnIndex The column index at which the item is located.
   3862          * @param columnSpan The number of columns the item spans.
   3863          * @param heading Whether the item is a heading.
   3864          */
   3865         private CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
   3866                 boolean heading, boolean selected) {
   3867             mRowIndex = rowIndex;
   3868             mRowSpan = rowSpan;
   3869             mColumnIndex = columnIndex;
   3870             mColumnSpan = columnSpan;
   3871             mHeading = heading;
   3872             mSelected = selected;
   3873         }
   3874 
   3875         /**
   3876          * Gets the column index at which the item is located.
   3877          *
   3878          * @return The column index.
   3879          */
   3880         public int getColumnIndex() {
   3881             return mColumnIndex;
   3882         }
   3883 
   3884         /**
   3885          * Gets the row index at which the item is located.
   3886          *
   3887          * @return The row index.
   3888          */
   3889         public int getRowIndex() {
   3890             return mRowIndex;
   3891         }
   3892 
   3893         /**
   3894          * Gets the number of columns the item spans.
   3895          *
   3896          * @return The column span.
   3897          */
   3898         public int getColumnSpan() {
   3899             return mColumnSpan;
   3900         }
   3901 
   3902         /**
   3903          * Gets the number of rows the item spans.
   3904          *
   3905          * @return The row span.
   3906          */
   3907         public int getRowSpan() {
   3908             return mRowSpan;
   3909         }
   3910 
   3911         /**
   3912          * Gets if the collection item is a heading. For example, section
   3913          * heading, table header, etc.
   3914          *
   3915          * @return If the item is a heading.
   3916          */
   3917         public boolean isHeading() {
   3918             return mHeading;
   3919         }
   3920 
   3921         /**
   3922          * Gets if the collection item is selected.
   3923          *
   3924          * @return If the item is selected.
   3925          */
   3926         public boolean isSelected() {
   3927             return mSelected;
   3928         }
   3929 
   3930         /**
   3931          * Recycles this instance.
   3932          */
   3933         void recycle() {
   3934             clear();
   3935             sPool.release(this);
   3936         }
   3937 
   3938         private void clear() {
   3939             mColumnIndex = 0;
   3940             mColumnSpan = 0;
   3941             mRowIndex = 0;
   3942             mRowSpan = 0;
   3943             mHeading = false;
   3944             mSelected = false;
   3945         }
   3946     }
   3947 
   3948     /**
   3949      * @see android.os.Parcelable.Creator
   3950      */
   3951     public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
   3952             new Parcelable.Creator<AccessibilityNodeInfo>() {
   3953         @Override
   3954         public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
   3955             AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   3956             info.initFromParcel(parcel);
   3957             return info;
   3958         }
   3959 
   3960         @Override
   3961         public AccessibilityNodeInfo[] newArray(int size) {
   3962             return new AccessibilityNodeInfo[size];
   3963         }
   3964     };
   3965 }
   3966