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