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 static com.android.internal.util.BitUtils.bitAt;
     20 import static com.android.internal.util.BitUtils.isBitSet;
     21 
     22 import static java.util.Collections.EMPTY_LIST;
     23 
     24 import android.accessibilityservice.AccessibilityService;
     25 import android.accessibilityservice.AccessibilityServiceInfo;
     26 import android.annotation.Nullable;
     27 import android.annotation.TestApi;
     28 import android.graphics.Rect;
     29 import android.os.Bundle;
     30 import android.os.Parcel;
     31 import android.os.Parcelable;
     32 import android.text.InputType;
     33 import android.text.Spannable;
     34 import android.text.SpannableStringBuilder;
     35 import android.text.Spanned;
     36 import android.text.TextUtils;
     37 import android.text.style.AccessibilityClickableSpan;
     38 import android.text.style.AccessibilityURLSpan;
     39 import android.text.style.ClickableSpan;
     40 import android.text.style.URLSpan;
     41 import android.util.ArraySet;
     42 import android.util.LongArray;
     43 import android.util.Pools.SynchronizedPool;
     44 import android.view.View;
     45 
     46 import com.android.internal.R;
     47 import com.android.internal.util.CollectionUtils;
     48 
     49 import java.util.ArrayList;
     50 import java.util.Collections;
     51 import java.util.List;
     52 import java.util.Objects;
     53 import java.util.concurrent.atomic.AtomicInteger;
     54 
     55 /**
     56  * This class represents a node of the window content as well as actions that
     57  * can be requested from its source. From the point of view of an
     58  * {@link android.accessibilityservice.AccessibilityService} a window's content is
     59  * presented as a tree of accessibility node infos, which may or may not map one-to-one
     60  * to the view hierarchy. In other words, a custom view is free to report itself as
     61  * a tree of accessibility node info.
     62  * </p>
     63  * <p>
     64  * Once an accessibility node info is delivered to an accessibility service it is
     65  * made immutable and calling a state mutation method generates an error.
     66  * </p>
     67  * <p>
     68  * Please refer to {@link android.accessibilityservice.AccessibilityService} for
     69  * details about how to obtain a handle to window content as a tree of accessibility
     70  * node info as well as details about the security model.
     71  * </p>
     72  * <div class="special reference">
     73  * <h3>Developer Guides</h3>
     74  * <p>For more information about making applications accessible, read the
     75  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
     76  * developer guide.</p>
     77  * </div>
     78  *
     79  * @see android.accessibilityservice.AccessibilityService
     80  * @see AccessibilityEvent
     81  * @see AccessibilityManager
     82  */
     83 public class AccessibilityNodeInfo implements Parcelable {
     84 
     85     private static final boolean DEBUG = false;
     86 
     87     /** @hide */
     88     public static final int UNDEFINED_CONNECTION_ID = -1;
     89 
     90     /** @hide */
     91     public static final int UNDEFINED_SELECTION_INDEX = -1;
     92 
     93     /** @hide */
     94     public static final int UNDEFINED_ITEM_ID = Integer.MAX_VALUE;
     95 
     96     /** @hide */
     97     public static final int ROOT_ITEM_ID = Integer.MAX_VALUE - 1;
     98 
     99     /** @hide */
    100     public static final long UNDEFINED_NODE_ID = makeNodeId(UNDEFINED_ITEM_ID, UNDEFINED_ITEM_ID);
    101 
    102     /** @hide */
    103     public static final long ROOT_NODE_ID = makeNodeId(ROOT_ITEM_ID,
    104             AccessibilityNodeProvider.HOST_VIEW_ID);
    105 
    106     /** @hide */
    107     public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
    108 
    109     /** @hide */
    110     public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
    111 
    112     /** @hide */
    113     public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
    114 
    115     /** @hide */
    116     public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
    117 
    118     /** @hide */
    119     public static final int FLAG_REPORT_VIEW_IDS = 0x00000010;
    120 
    121     // Actions.
    122 
    123     /**
    124      * Action that gives input focus to the node.
    125      */
    126     public static final int ACTION_FOCUS =  0x00000001;
    127 
    128     /**
    129      * Action that clears input focus of the node.
    130      */
    131     public static final int ACTION_CLEAR_FOCUS = 0x00000002;
    132 
    133     /**
    134      * Action that selects the node.
    135      */
    136     public static final int ACTION_SELECT = 0x00000004;
    137 
    138     /**
    139      * Action that deselects the node.
    140      */
    141     public static final int ACTION_CLEAR_SELECTION = 0x00000008;
    142 
    143     /**
    144      * Action that clicks on the node info.
    145      *
    146      * See {@link AccessibilityAction#ACTION_CLICK}
    147      */
    148     public static final int ACTION_CLICK = 0x00000010;
    149 
    150     /**
    151      * Action that long clicks on the node.
    152      */
    153     public static final int ACTION_LONG_CLICK = 0x00000020;
    154 
    155     /**
    156      * Action that gives accessibility focus to the node.
    157      */
    158     public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
    159 
    160     /**
    161      * Action that clears accessibility focus of the node.
    162      */
    163     public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
    164 
    165     /**
    166      * Action that requests to go to the next entity in this node's text
    167      * at a given movement granularity. For example, move to the next character,
    168      * word, etc.
    169      * <p>
    170      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
    171      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
    172      * <strong>Example:</strong> Move to the previous character and do not extend selection.
    173      * <code><pre><p>
    174      *   Bundle arguments = new Bundle();
    175      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
    176      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
    177      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
    178      *           false);
    179      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
    180      * </code></pre></p>
    181      * </p>
    182      *
    183      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
    184      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
    185      *
    186      * @see #setMovementGranularities(int)
    187      * @see #getMovementGranularities()
    188      *
    189      * @see #MOVEMENT_GRANULARITY_CHARACTER
    190      * @see #MOVEMENT_GRANULARITY_WORD
    191      * @see #MOVEMENT_GRANULARITY_LINE
    192      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
    193      * @see #MOVEMENT_GRANULARITY_PAGE
    194      */
    195     public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
    196 
    197     /**
    198      * Action that requests to go to the previous entity in this node's text
    199      * at a given movement granularity. For example, move to the next character,
    200      * word, etc.
    201      * <p>
    202      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<,
    203      * {@link #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
    204      * <strong>Example:</strong> Move to the next character and do not extend selection.
    205      * <code><pre><p>
    206      *   Bundle arguments = new Bundle();
    207      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
    208      *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
    209      *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
    210      *           false);
    211      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
    212      *           arguments);
    213      * </code></pre></p>
    214      * </p>
    215      *
    216      * @see #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
    217      * @see #ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
    218      *
    219      * @see #setMovementGranularities(int)
    220      * @see #getMovementGranularities()
    221      *
    222      * @see #MOVEMENT_GRANULARITY_CHARACTER
    223      * @see #MOVEMENT_GRANULARITY_WORD
    224      * @see #MOVEMENT_GRANULARITY_LINE
    225      * @see #MOVEMENT_GRANULARITY_PARAGRAPH
    226      * @see #MOVEMENT_GRANULARITY_PAGE
    227      */
    228     public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
    229 
    230     /**
    231      * Action to move to the next HTML element of a given type. For example, move
    232      * to the BUTTON, INPUT, TABLE, etc.
    233      * <p>
    234      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
    235      * <strong>Example:</strong>
    236      * <code><pre><p>
    237      *   Bundle arguments = new Bundle();
    238      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
    239      *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
    240      * </code></pre></p>
    241      * </p>
    242      */
    243     public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
    244 
    245     /**
    246      * Action to move to the previous HTML element of a given type. For example, move
    247      * to the BUTTON, INPUT, TABLE, etc.
    248      * <p>
    249      * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
    250      * <strong>Example:</strong>
    251      * <code><pre><p>
    252      *   Bundle arguments = new Bundle();
    253      *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
    254      *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
    255      * </code></pre></p>
    256      * </p>
    257      */
    258     public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
    259 
    260     /**
    261      * Action to scroll the node content forward.
    262      */
    263     public static final int ACTION_SCROLL_FORWARD = 0x00001000;
    264 
    265     /**
    266      * Action to scroll the node content backward.
    267      */
    268     public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
    269 
    270     /**
    271      * Action to copy the current selection to the clipboard.
    272      */
    273     public static final int ACTION_COPY = 0x00004000;
    274 
    275     /**
    276      * Action to paste the current clipboard content.
    277      */
    278     public static final int ACTION_PASTE = 0x00008000;
    279 
    280     /**
    281      * Action to cut the current selection and place it to the clipboard.
    282      */
    283     public static final int ACTION_CUT = 0x00010000;
    284 
    285     /**
    286      * Action to set the selection. Performing this action with no arguments
    287      * clears the selection.
    288      * <p>
    289      * <strong>Arguments:</strong>
    290      * {@link #ACTION_ARGUMENT_SELECTION_START_INT},
    291      * {@link #ACTION_ARGUMENT_SELECTION_END_INT}<br>
    292      * <strong>Example:</strong>
    293      * <code><pre><p>
    294      *   Bundle arguments = new Bundle();
    295      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
    296      *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
    297      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_SELECTION, arguments);
    298      * </code></pre></p>
    299      * </p>
    300      *
    301      * @see #ACTION_ARGUMENT_SELECTION_START_INT
    302      * @see #ACTION_ARGUMENT_SELECTION_END_INT
    303      */
    304     public static final int ACTION_SET_SELECTION = 0x00020000;
    305 
    306     /**
    307      * Action to expand an expandable node.
    308      */
    309     public static final int ACTION_EXPAND = 0x00040000;
    310 
    311     /**
    312      * Action to collapse an expandable node.
    313      */
    314     public static final int ACTION_COLLAPSE = 0x00080000;
    315 
    316     /**
    317      * Action to dismiss a dismissable node.
    318      */
    319     public static final int ACTION_DISMISS = 0x00100000;
    320 
    321     /**
    322      * Action that sets the text of the node. Performing the action without argument, using <code>
    323      * null</code> or empty {@link CharSequence} will clear the text. This action will also put the
    324      * cursor at the end of text.
    325      * <p>
    326      * <strong>Arguments:</strong>
    327      * {@link #ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
    328      * <strong>Example:</strong>
    329      * <code><pre><p>
    330      *   Bundle arguments = new Bundle();
    331      *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
    332      *       "android");
    333      *   info.performAction(AccessibilityNodeInfo.ACTION_SET_TEXT, arguments);
    334      * </code></pre></p>
    335      */
    336     public static final int ACTION_SET_TEXT = 0x00200000;
    337 
    338     /** @hide */
    339     public static final int LAST_LEGACY_STANDARD_ACTION = ACTION_SET_TEXT;
    340 
    341     /**
    342      * Mask to see if the value is larger than the largest ACTION_ constant
    343      */
    344     private static final int ACTION_TYPE_MASK = 0xFF000000;
    345 
    346     // Action arguments
    347 
    348     /**
    349      * Argument for which movement granularity to be used when traversing the node text.
    350      * <p>
    351      * <strong>Type:</strong> int<br>
    352      * <strong>Actions:</strong>
    353      * <ul>
    354      *     <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li>
    355      *     <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li>
    356      * </ul>
    357      * </p>
    358      *
    359      * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
    360      * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
    361      */
    362     public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
    363             "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
    364 
    365     /**
    366      * Argument for which HTML element to get moving to the next/previous HTML element.
    367      * <p>
    368      * <strong>Type:</strong> String<br>
    369      * <strong>Actions:</strong>
    370      * <ul>
    371      *     <li>{@link AccessibilityAction#ACTION_NEXT_HTML_ELEMENT}</li>
    372      *     <li>{@link AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT}</li>
    373      * </ul>
    374      * </p>
    375      *
    376      * @see AccessibilityAction#ACTION_NEXT_HTML_ELEMENT
    377      * @see AccessibilityAction#ACTION_PREVIOUS_HTML_ELEMENT
    378      */
    379     public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
    380             "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
    381 
    382     /**
    383      * Argument for whether when moving at granularity to extend the selection
    384      * or to move it otherwise.
    385      * <p>
    386      * <strong>Type:</strong> boolean<br>
    387      * <strong>Actions:</strong>
    388      * <ul>
    389      *     <li>{@link AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY}</li>
    390      *     <li>{@link AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}</li>
    391      * </ul>
    392      *
    393      * @see AccessibilityAction#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
    394      * @see AccessibilityAction#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
    395      */
    396     public static final String ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN =
    397             "ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN";
    398 
    399     /**
    400      * Argument for specifying the selection start.
    401      * <p>
    402      * <strong>Type:</strong> int<br>
    403      * <strong>Actions:</strong>
    404      * <ul>
    405      *     <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li>
    406      * </ul>
    407      *
    408      * @see AccessibilityAction#ACTION_SET_SELECTION
    409      */
    410     public static final String ACTION_ARGUMENT_SELECTION_START_INT =
    411             "ACTION_ARGUMENT_SELECTION_START_INT";
    412 
    413     /**
    414      * Argument for specifying the selection end.
    415      * <p>
    416      * <strong>Type:</strong> int<br>
    417      * <strong>Actions:</strong>
    418      * <ul>
    419      *     <li>{@link AccessibilityAction#ACTION_SET_SELECTION}</li>
    420      * </ul>
    421      *
    422      * @see AccessibilityAction#ACTION_SET_SELECTION
    423      */
    424     public static final String ACTION_ARGUMENT_SELECTION_END_INT =
    425             "ACTION_ARGUMENT_SELECTION_END_INT";
    426 
    427     /**
    428      * Argument for specifying the text content to set.
    429      * <p>
    430      * <strong>Type:</strong> CharSequence<br>
    431      * <strong>Actions:</strong>
    432      * <ul>
    433      *     <li>{@link AccessibilityAction#ACTION_SET_TEXT}</li>
    434      * </ul>
    435      *
    436      * @see AccessibilityAction#ACTION_SET_TEXT
    437      */
    438     public static final String ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE =
    439             "ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE";
    440 
    441     /**
    442      * Argument for specifying the collection row to make visible on screen.
    443      * <p>
    444      * <strong>Type:</strong> int<br>
    445      * <strong>Actions:</strong>
    446      * <ul>
    447      *     <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li>
    448      * </ul>
    449      *
    450      * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION
    451      */
    452     public static final String ACTION_ARGUMENT_ROW_INT =
    453             "android.view.accessibility.action.ARGUMENT_ROW_INT";
    454 
    455     /**
    456      * Argument for specifying the collection column to make visible on screen.
    457      * <p>
    458      * <strong>Type:</strong> int<br>
    459      * <strong>Actions:</strong>
    460      * <ul>
    461      *     <li>{@link AccessibilityAction#ACTION_SCROLL_TO_POSITION}</li>
    462      * </ul>
    463      *
    464      * @see AccessibilityAction#ACTION_SCROLL_TO_POSITION
    465      */
    466     public static final String ACTION_ARGUMENT_COLUMN_INT =
    467             "android.view.accessibility.action.ARGUMENT_COLUMN_INT";
    468 
    469     /**
    470      * Argument for specifying the progress value to set.
    471      * <p>
    472      * <strong>Type:</strong> float<br>
    473      * <strong>Actions:</strong>
    474      * <ul>
    475      *     <li>{@link AccessibilityAction#ACTION_SET_PROGRESS}</li>
    476      * </ul>
    477      *
    478      * @see AccessibilityAction#ACTION_SET_PROGRESS
    479      */
    480     public static final String ACTION_ARGUMENT_PROGRESS_VALUE =
    481             "android.view.accessibility.action.ARGUMENT_PROGRESS_VALUE";
    482 
    483     /**
    484      * Argument for specifying the x coordinate to which to move a window.
    485      * <p>
    486      * <strong>Type:</strong> int<br>
    487      * <strong>Actions:</strong>
    488      * <ul>
    489      *     <li>{@link AccessibilityAction#ACTION_MOVE_WINDOW}</li>
    490      * </ul>
    491      *
    492      * @see AccessibilityAction#ACTION_MOVE_WINDOW
    493      */
    494     public static final String ACTION_ARGUMENT_MOVE_WINDOW_X =
    495             "ACTION_ARGUMENT_MOVE_WINDOW_X";
    496 
    497     /**
    498      * Argument for specifying the y coordinate to which to move a window.
    499      * <p>
    500      * <strong>Type:</strong> int<br>
    501      * <strong>Actions:</strong>
    502      * <ul>
    503      *     <li>{@link AccessibilityAction#ACTION_MOVE_WINDOW}</li>
    504      * </ul>
    505      *
    506      * @see AccessibilityAction#ACTION_MOVE_WINDOW
    507      */
    508     public static final String ACTION_ARGUMENT_MOVE_WINDOW_Y =
    509             "ACTION_ARGUMENT_MOVE_WINDOW_Y";
    510 
    511     /**
    512      * Argument to pass the {@link AccessibilityClickableSpan}.
    513      * For use with R.id.accessibilityActionClickOnClickableSpan
    514      * @hide
    515      */
    516     public static final String ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN =
    517             "android.view.accessibility.action.ACTION_ARGUMENT_ACCESSIBLE_CLICKABLE_SPAN";
    518 
    519     // Focus types
    520 
    521     /**
    522      * The input focus.
    523      */
    524     public static final int FOCUS_INPUT = 1;
    525 
    526     /**
    527      * The accessibility focus.
    528      */
    529     public static final int FOCUS_ACCESSIBILITY = 2;
    530 
    531     // Movement granularities
    532 
    533     /**
    534      * Movement granularity bit for traversing the text of a node by character.
    535      */
    536     public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
    537 
    538     /**
    539      * Movement granularity bit for traversing the text of a node by word.
    540      */
    541     public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
    542 
    543     /**
    544      * Movement granularity bit for traversing the text of a node by line.
    545      */
    546     public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
    547 
    548     /**
    549      * Movement granularity bit for traversing the text of a node by paragraph.
    550      */
    551     public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
    552 
    553     /**
    554      * Movement granularity bit for traversing the text of a node by page.
    555      */
    556     public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
    557 
    558     /**
    559      * Key used to request and locate extra data for text character location. This key requests that
    560      * an array of {@link android.graphics.RectF}s be added to the extras. This request is made with
    561      * {@link #refreshWithExtraData(String, Bundle)}. The arguments taken by this request are two
    562      * integers: {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX} and
    563      * {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH}. The starting index must be valid
    564      * inside the CharSequence returned by {@link #getText()}, and the length must be positive.
    565      * <p>
    566      * The data can be retrieved from the {@code Bundle} returned by {@link #getExtras()} using this
    567      * string as a key for {@link Bundle#getParcelableArray(String)}. The
    568      * {@link android.graphics.RectF} will be null for characters that either do not exist or are
    569      * off the screen.
    570      *
    571      * {@see #refreshWithExtraData(String, Bundle)}
    572      */
    573     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY =
    574             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_KEY";
    575 
    576     /**
    577      * Integer argument specifying the start index of the requested text location data. Must be
    578      * valid inside the CharSequence returned by {@link #getText()}.
    579      *
    580      * {@see EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY}
    581      */
    582     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX =
    583             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX";
    584 
    585     /**
    586      * Integer argument specifying the end index of the requested text location data. Must be
    587      * positive.
    588      *
    589      * {@see EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY}
    590      */
    591     public static final String EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH =
    592             "android.view.accessibility.extra.DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH";
    593 
    594     /** @hide */
    595     public static final String EXTRA_DATA_REQUESTED_KEY =
    596             "android.view.accessibility.AccessibilityNodeInfo.extra_data_requested";
    597 
    598     // Boolean attributes.
    599 
    600     private static final int BOOLEAN_PROPERTY_CHECKABLE = 0x00000001;
    601 
    602     private static final int BOOLEAN_PROPERTY_CHECKED = 0x00000002;
    603 
    604     private static final int BOOLEAN_PROPERTY_FOCUSABLE = 0x00000004;
    605 
    606     private static final int BOOLEAN_PROPERTY_FOCUSED = 0x00000008;
    607 
    608     private static final int BOOLEAN_PROPERTY_SELECTED = 0x00000010;
    609 
    610     private static final int BOOLEAN_PROPERTY_CLICKABLE = 0x00000020;
    611 
    612     private static final int BOOLEAN_PROPERTY_LONG_CLICKABLE = 0x00000040;
    613 
    614     private static final int BOOLEAN_PROPERTY_ENABLED = 0x00000080;
    615 
    616     private static final int BOOLEAN_PROPERTY_PASSWORD = 0x00000100;
    617 
    618     private static final int BOOLEAN_PROPERTY_SCROLLABLE = 0x00000200;
    619 
    620     private static final int BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
    621 
    622     private static final int BOOLEAN_PROPERTY_VISIBLE_TO_USER = 0x00000800;
    623 
    624     private static final int BOOLEAN_PROPERTY_EDITABLE = 0x00001000;
    625 
    626     private static final int BOOLEAN_PROPERTY_OPENS_POPUP = 0x00002000;
    627 
    628     private static final int BOOLEAN_PROPERTY_DISMISSABLE = 0x00004000;
    629 
    630     private static final int BOOLEAN_PROPERTY_MULTI_LINE = 0x00008000;
    631 
    632     private static final int BOOLEAN_PROPERTY_CONTENT_INVALID = 0x00010000;
    633 
    634     private static final int BOOLEAN_PROPERTY_CONTEXT_CLICKABLE = 0x00020000;
    635 
    636     private static final int BOOLEAN_PROPERTY_IMPORTANCE = 0x0040000;
    637 
    638     private static final int BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE = 0x0080000;
    639 
    640     private static final int BOOLEAN_PROPERTY_IS_SHOWING_HINT = 0x0100000;
    641 
    642     private static final int BOOLEAN_PROPERTY_IS_HEADING = 0x0200000;
    643 
    644     /**
    645      * Bits that provide the id of a virtual descendant of a view.
    646      */
    647     private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
    648     /**
    649      * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
    650      * virtual descendant of a view. Such a descendant does not exist in the view
    651      * hierarchy and is only reported via the accessibility APIs.
    652      */
    653     private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
    654 
    655     private static AtomicInteger sNumInstancesInUse;
    656 
    657     /**
    658      * Gets the accessibility view id which identifies a View in the view three.
    659      *
    660      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
    661      * @return The accessibility view id part of the node id.
    662      *
    663      * @hide
    664      */
    665     public static int getAccessibilityViewId(long accessibilityNodeId) {
    666         return (int) accessibilityNodeId;
    667     }
    668 
    669     /**
    670      * Gets the virtual descendant id which identifies an imaginary view in a
    671      * containing View.
    672      *
    673      * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
    674      * @return The virtual view id part of the node id.
    675      *
    676      * @hide
    677      */
    678     public static int getVirtualDescendantId(long accessibilityNodeId) {
    679         return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK)
    680                 >> VIRTUAL_DESCENDANT_ID_SHIFT);
    681     }
    682 
    683     /**
    684      * Makes a node id by shifting the <code>virtualDescendantId</code>
    685      * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
    686      * the bitwise or with the <code>accessibilityViewId</code>.
    687      *
    688      * @param accessibilityViewId A View accessibility id.
    689      * @param virtualDescendantId A virtual descendant id.
    690      * @return The node id.
    691      *
    692      * @hide
    693      */
    694     public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
    695         return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
    696     }
    697 
    698     // Housekeeping.
    699     private static final int MAX_POOL_SIZE = 50;
    700     private static final SynchronizedPool<AccessibilityNodeInfo> sPool =
    701             new SynchronizedPool<>(MAX_POOL_SIZE);
    702 
    703     private static final AccessibilityNodeInfo DEFAULT = new AccessibilityNodeInfo();
    704 
    705     private boolean mSealed;
    706 
    707     // Data.
    708     private int mWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
    709     private long mSourceNodeId = UNDEFINED_NODE_ID;
    710     private long mParentNodeId = UNDEFINED_NODE_ID;
    711     private long mLabelForId = UNDEFINED_NODE_ID;
    712     private long mLabeledById = UNDEFINED_NODE_ID;
    713     private long mTraversalBefore = UNDEFINED_NODE_ID;
    714     private long mTraversalAfter = UNDEFINED_NODE_ID;
    715 
    716     private int mBooleanProperties;
    717     private final Rect mBoundsInParent = new Rect();
    718     private final Rect mBoundsInScreen = new Rect();
    719     private int mDrawingOrderInParent;
    720 
    721     private CharSequence mPackageName;
    722     private CharSequence mClassName;
    723     // Hidden, unparceled value used to hold the original value passed to setText
    724     private CharSequence mOriginalText;
    725     private CharSequence mText;
    726     private CharSequence mHintText;
    727     private CharSequence mError;
    728     private CharSequence mPaneTitle;
    729     private CharSequence mContentDescription;
    730     private CharSequence mTooltipText;
    731     private String mViewIdResourceName;
    732     private ArrayList<String> mExtraDataKeys;
    733 
    734     private LongArray mChildNodeIds;
    735     private ArrayList<AccessibilityAction> mActions;
    736 
    737     private int mMaxTextLength = -1;
    738     private int mMovementGranularities;
    739 
    740     private int mTextSelectionStart = UNDEFINED_SELECTION_INDEX;
    741     private int mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
    742     private int mInputType = InputType.TYPE_NULL;
    743     private int mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
    744 
    745     private Bundle mExtras;
    746 
    747     private int mConnectionId = UNDEFINED_CONNECTION_ID;
    748 
    749     private RangeInfo mRangeInfo;
    750     private CollectionInfo mCollectionInfo;
    751     private CollectionItemInfo mCollectionItemInfo;
    752 
    753     /**
    754      * Hide constructor from clients.
    755      */
    756     private AccessibilityNodeInfo() {
    757         /* do nothing */
    758     }
    759 
    760     /**
    761      * Sets the source.
    762      * <p>
    763      *   <strong>Note:</strong> Cannot be called from an
    764      *   {@link android.accessibilityservice.AccessibilityService}.
    765      *   This class is made immutable before being delivered to an AccessibilityService.
    766      * </p>
    767      *
    768      * @param source The info source.
    769      */
    770     public void setSource(View source) {
    771         setSource(source, AccessibilityNodeProvider.HOST_VIEW_ID);
    772     }
    773 
    774     /**
    775      * Sets the source to be a virtual descendant of the given <code>root</code>.
    776      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
    777      * is set as the source.
    778      * <p>
    779      * A virtual descendant is an imaginary View that is reported as a part of the view
    780      * hierarchy for accessibility purposes. This enables custom views that draw complex
    781      * content to report themselves as a tree of virtual views, thus conveying their
    782      * logical structure.
    783      * </p>
    784      * <p>
    785      *   <strong>Note:</strong> Cannot be called from an
    786      *   {@link android.accessibilityservice.AccessibilityService}.
    787      *   This class is made immutable before being delivered to an AccessibilityService.
    788      * </p>
    789      *
    790      * @param root The root of the virtual subtree.
    791      * @param virtualDescendantId The id of the virtual descendant.
    792      */
    793     public void setSource(View root, int virtualDescendantId) {
    794         enforceNotSealed();
    795         mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED_ITEM_ID;
    796         final int rootAccessibilityViewId =
    797             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
    798         mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
    799     }
    800 
    801     /**
    802      * Find the view that has the specified focus type. The search starts from
    803      * the view represented by this node info.
    804      *
    805      * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
    806      *         {@link #FOCUS_ACCESSIBILITY}.
    807      * @return The node info of the focused view or null.
    808      *
    809      * @see #FOCUS_INPUT
    810      * @see #FOCUS_ACCESSIBILITY
    811      */
    812     public AccessibilityNodeInfo findFocus(int focus) {
    813         enforceSealed();
    814         enforceValidFocusType(focus);
    815         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    816             return null;
    817         }
    818         return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
    819                 mSourceNodeId, focus);
    820     }
    821 
    822     /**
    823      * Searches for the nearest view in the specified direction that can take
    824      * the input focus.
    825      *
    826      * @param direction The direction. Can be one of:
    827      *     {@link View#FOCUS_DOWN},
    828      *     {@link View#FOCUS_UP},
    829      *     {@link View#FOCUS_LEFT},
    830      *     {@link View#FOCUS_RIGHT},
    831      *     {@link View#FOCUS_FORWARD},
    832      *     {@link View#FOCUS_BACKWARD}.
    833      *
    834      * @return The node info for the view that can take accessibility focus.
    835      */
    836     public AccessibilityNodeInfo focusSearch(int direction) {
    837         enforceSealed();
    838         enforceValidFocusDirection(direction);
    839         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    840             return null;
    841         }
    842         return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
    843                 mSourceNodeId, direction);
    844     }
    845 
    846     /**
    847      * Gets the id of the window from which the info comes from.
    848      *
    849      * @return The window id.
    850      */
    851     public int getWindowId() {
    852         return mWindowId;
    853     }
    854 
    855     /**
    856      * Refreshes this info with the latest state of the view it represents.
    857      * <p>
    858      * <strong>Note:</strong> If this method returns false this info is obsolete
    859      * since it represents a view that is no longer in the view tree and should
    860      * be recycled.
    861      * </p>
    862      *
    863      * @param bypassCache Whether to bypass the cache.
    864      * @return Whether the refresh succeeded.
    865      *
    866      * @hide
    867      */
    868     public boolean refresh(Bundle arguments, boolean bypassCache) {
    869         enforceSealed();
    870         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    871             return false;
    872         }
    873         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    874         AccessibilityNodeInfo refreshedInfo = client.findAccessibilityNodeInfoByAccessibilityId(
    875                 mConnectionId, mWindowId, mSourceNodeId, bypassCache, 0, arguments);
    876         if (refreshedInfo == null) {
    877             return false;
    878         }
    879         // Hard-to-reproduce bugs seem to be due to some tools recycling a node on another
    880         // thread. If that happens, the init will re-seal the node, which then is in a bad state
    881         // when it is obtained. Enforce sealing again before we init to fail when a node has been
    882         // recycled during a refresh to catch such errors earlier.
    883         enforceSealed();
    884         init(refreshedInfo);
    885         refreshedInfo.recycle();
    886         return true;
    887     }
    888 
    889     /**
    890      * Refreshes this info with the latest state of the view it represents.
    891      *
    892      * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented
    893      * by this node is no longer in the view tree (and thus this node is obsolete and should be
    894      * recycled).
    895      */
    896     public boolean refresh() {
    897         return refresh(null, true);
    898     }
    899 
    900     /**
    901      * Refreshes this info with the latest state of the view it represents, and request new
    902      * data be added by the View.
    903      *
    904      * @param extraDataKey The extra data requested. Data that must be requested
    905      *                     with this mechanism is generally expensive to retrieve, so should only be
    906      *                     requested when needed. See
    907      *                     {@link #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY} and
    908      *                     {@link #getAvailableExtraData()}.
    909      * @param args A bundle of arguments for the request. These depend on the particular request.
    910      *
    911      * @return {@code true} if the refresh succeeded. {@code false} if the {@link View} represented
    912      * by this node is no longer in the view tree (and thus this node is obsolete and should be
    913      * recycled).
    914      */
    915     public boolean refreshWithExtraData(String extraDataKey, Bundle args) {
    916         args.putString(EXTRA_DATA_REQUESTED_KEY, extraDataKey);
    917         return refresh(args, true);
    918     }
    919 
    920     /**
    921      * Returns the array containing the IDs of this node's children.
    922      *
    923      * @hide
    924      */
    925     public LongArray getChildNodeIds() {
    926         return mChildNodeIds;
    927     }
    928 
    929     /**
    930      * Returns the id of the child at the specified index.
    931      *
    932      * @throws IndexOutOfBoundsException when index &lt; 0 || index &gt;=
    933      *             getChildCount()
    934      * @hide
    935      */
    936     public long getChildId(int index) {
    937         if (mChildNodeIds == null) {
    938             throw new IndexOutOfBoundsException();
    939         }
    940         return mChildNodeIds.get(index);
    941     }
    942 
    943     /**
    944      * Gets the number of children.
    945      *
    946      * @return The child count.
    947      */
    948     public int getChildCount() {
    949         return mChildNodeIds == null ? 0 : mChildNodeIds.size();
    950     }
    951 
    952     /**
    953      * Get the child at given index.
    954      * <p>
    955      *   <strong>Note:</strong> It is a client responsibility to recycle the
    956      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
    957      *     to avoid creating of multiple instances.
    958      * </p>
    959      *
    960      * @param index The child index.
    961      * @return The child node.
    962      *
    963      * @throws IllegalStateException If called outside of an AccessibilityService.
    964      *
    965      */
    966     public AccessibilityNodeInfo getChild(int index) {
    967         enforceSealed();
    968         if (mChildNodeIds == null) {
    969             return null;
    970         }
    971         if (!canPerformRequestOverConnection(mSourceNodeId)) {
    972             return null;
    973         }
    974         final long childId = mChildNodeIds.get(index);
    975         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
    976         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
    977                 childId, false, FLAG_PREFETCH_DESCENDANTS, null);
    978     }
    979 
    980     /**
    981      * Adds a child.
    982      * <p>
    983      * <strong>Note:</strong> Cannot be called from an
    984      * {@link android.accessibilityservice.AccessibilityService}.
    985      * This class is made immutable before being delivered to an AccessibilityService.
    986      * </p>
    987      *
    988      * @param child The child.
    989      *
    990      * @throws IllegalStateException If called from an AccessibilityService.
    991      */
    992     public void addChild(View child) {
    993         addChildInternal(child, AccessibilityNodeProvider.HOST_VIEW_ID, true);
    994     }
    995 
    996     /**
    997      * Unchecked version of {@link #addChild(View)} that does not verify
    998      * uniqueness. For framework use only.
    999      *
   1000      * @hide
   1001      */
   1002     public void addChildUnchecked(View child) {
   1003         addChildInternal(child, AccessibilityNodeProvider.HOST_VIEW_ID, false);
   1004     }
   1005 
   1006     /**
   1007      * Removes a child. If the child was not previously added to the node,
   1008      * calling this method has no effect.
   1009      * <p>
   1010      * <strong>Note:</strong> Cannot be called from an
   1011      * {@link android.accessibilityservice.AccessibilityService}.
   1012      * This class is made immutable before being delivered to an AccessibilityService.
   1013      * </p>
   1014      *
   1015      * @param child The child.
   1016      * @return true if the child was present
   1017      *
   1018      * @throws IllegalStateException If called from an AccessibilityService.
   1019      */
   1020     public boolean removeChild(View child) {
   1021         return removeChild(child, AccessibilityNodeProvider.HOST_VIEW_ID);
   1022     }
   1023 
   1024     /**
   1025      * Adds a virtual child which is a descendant of the given <code>root</code>.
   1026      * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
   1027      * is added as a child.
   1028      * <p>
   1029      * A virtual descendant is an imaginary View that is reported as a part of the view
   1030      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1031      * content to report them selves as a tree of virtual views, thus conveying their
   1032      * logical structure.
   1033      * </p>
   1034      *
   1035      * @param root The root of the virtual subtree.
   1036      * @param virtualDescendantId The id of the virtual child.
   1037      */
   1038     public void addChild(View root, int virtualDescendantId) {
   1039         addChildInternal(root, virtualDescendantId, true);
   1040     }
   1041 
   1042     private void addChildInternal(View root, int virtualDescendantId, boolean checked) {
   1043         enforceNotSealed();
   1044         if (mChildNodeIds == null) {
   1045             mChildNodeIds = new LongArray();
   1046         }
   1047         final int rootAccessibilityViewId =
   1048             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1049         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1050         // If we're checking uniqueness and the ID already exists, abort.
   1051         if (checked && mChildNodeIds.indexOf(childNodeId) >= 0) {
   1052             return;
   1053         }
   1054         mChildNodeIds.add(childNodeId);
   1055     }
   1056 
   1057     /**
   1058      * Removes a virtual child which is a descendant of the given
   1059      * <code>root</code>. If the child was not previously added to the node,
   1060      * calling this method has no effect.
   1061      *
   1062      * @param root The root of the virtual subtree.
   1063      * @param virtualDescendantId The id of the virtual child.
   1064      * @return true if the child was present
   1065      * @see #addChild(View, int)
   1066      */
   1067     public boolean removeChild(View root, int virtualDescendantId) {
   1068         enforceNotSealed();
   1069         final LongArray childIds = mChildNodeIds;
   1070         if (childIds == null) {
   1071             return false;
   1072         }
   1073         final int rootAccessibilityViewId =
   1074                 (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1075         final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1076         final int index = childIds.indexOf(childNodeId);
   1077         if (index < 0) {
   1078             return false;
   1079         }
   1080         childIds.remove(index);
   1081         return true;
   1082     }
   1083 
   1084     /**
   1085      * Gets the actions that can be performed on the node.
   1086      */
   1087     public List<AccessibilityAction> getActionList() {
   1088         return CollectionUtils.emptyIfNull(mActions);
   1089     }
   1090 
   1091     /**
   1092      * Gets the actions that can be performed on the node.
   1093      *
   1094      * @return The bit mask of with actions.
   1095      *
   1096      * @see AccessibilityNodeInfo#ACTION_FOCUS
   1097      * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
   1098      * @see AccessibilityNodeInfo#ACTION_SELECT
   1099      * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
   1100      * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
   1101      * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
   1102      * @see AccessibilityNodeInfo#ACTION_CLICK
   1103      * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
   1104      * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
   1105      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
   1106      * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
   1107      * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
   1108      * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
   1109      * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
   1110      *
   1111      * @deprecated Use {@link #getActionList()}.
   1112      */
   1113     @Deprecated
   1114     public int getActions() {
   1115         int returnValue = 0;
   1116 
   1117         if (mActions == null) {
   1118             return returnValue;
   1119         }
   1120 
   1121         final int actionSize = mActions.size();
   1122         for (int i = 0; i < actionSize; i++) {
   1123             int actionId = mActions.get(i).getId();
   1124             if (actionId <= LAST_LEGACY_STANDARD_ACTION) {
   1125                 returnValue |= actionId;
   1126             }
   1127         }
   1128 
   1129         return returnValue;
   1130     }
   1131 
   1132     /**
   1133      * Adds an action that can be performed on the node.
   1134      * <p>
   1135      * To add a standard action use the static constants on {@link AccessibilityAction}.
   1136      * To add a custom action create a new {@link AccessibilityAction} by passing in a
   1137      * resource id from your application as the action id and an optional label that
   1138      * describes the action. To override one of the standard actions use as the action
   1139      * id of a standard action id such as {@link #ACTION_CLICK} and an optional label that
   1140      * describes the action.
   1141      * </p>
   1142      * <p>
   1143      *   <strong>Note:</strong> Cannot be called from an
   1144      *   {@link android.accessibilityservice.AccessibilityService}.
   1145      *   This class is made immutable before being delivered to an AccessibilityService.
   1146      * </p>
   1147      *
   1148      * @param action The action.
   1149      *
   1150      * @throws IllegalStateException If called from an AccessibilityService.
   1151      */
   1152     public void addAction(AccessibilityAction action) {
   1153         enforceNotSealed();
   1154 
   1155         addActionUnchecked(action);
   1156     }
   1157 
   1158     private void addActionUnchecked(AccessibilityAction action) {
   1159         if (action == null) {
   1160             return;
   1161         }
   1162 
   1163         if (mActions == null) {
   1164             mActions = new ArrayList<>();
   1165         }
   1166 
   1167         mActions.remove(action);
   1168         mActions.add(action);
   1169     }
   1170 
   1171     /**
   1172      * Adds an action that can be performed on the node.
   1173      * <p>
   1174      *   <strong>Note:</strong> Cannot be called from an
   1175      *   {@link android.accessibilityservice.AccessibilityService}.
   1176      *   This class is made immutable before being delivered to an AccessibilityService.
   1177      * </p>
   1178      *
   1179      * @param action The action.
   1180      *
   1181      * @throws IllegalStateException If called from an AccessibilityService.
   1182      * @throws IllegalArgumentException If the argument is not one of the standard actions.
   1183      *
   1184      * @deprecated This has been deprecated for {@link #addAction(AccessibilityAction)}
   1185      */
   1186     @Deprecated
   1187     public void addAction(int action) {
   1188         enforceNotSealed();
   1189 
   1190         if ((action & ACTION_TYPE_MASK) != 0) {
   1191             throw new IllegalArgumentException("Action is not a combination of the standard " +
   1192                     "actions: " + action);
   1193         }
   1194 
   1195         addStandardActions(action);
   1196     }
   1197 
   1198     /**
   1199      * Removes an action that can be performed on the node. If the action was
   1200      * not already added to the node, calling this method has no effect.
   1201      * <p>
   1202      *   <strong>Note:</strong> Cannot be called from an
   1203      *   {@link android.accessibilityservice.AccessibilityService}.
   1204      *   This class is made immutable before being delivered to an AccessibilityService.
   1205      * </p>
   1206      *
   1207      * @param action The action to be removed.
   1208      *
   1209      * @throws IllegalStateException If called from an AccessibilityService.
   1210      * @deprecated Use {@link #removeAction(AccessibilityAction)}
   1211      */
   1212     @Deprecated
   1213     public void removeAction(int action) {
   1214         enforceNotSealed();
   1215 
   1216         removeAction(getActionSingleton(action));
   1217     }
   1218 
   1219     /**
   1220      * Removes an action that can be performed on the node. If the action was
   1221      * not already added to the node, calling this method has no effect.
   1222      * <p>
   1223      *   <strong>Note:</strong> Cannot be called from an
   1224      *   {@link android.accessibilityservice.AccessibilityService}.
   1225      *   This class is made immutable before being delivered to an AccessibilityService.
   1226      * </p>
   1227      *
   1228      * @param action The action to be removed.
   1229      * @return The action removed from the list of actions.
   1230      *
   1231      * @throws IllegalStateException If called from an AccessibilityService.
   1232      */
   1233     public boolean removeAction(AccessibilityAction action) {
   1234         enforceNotSealed();
   1235 
   1236         if (mActions == null || action == null) {
   1237             return false;
   1238         }
   1239 
   1240         return mActions.remove(action);
   1241     }
   1242 
   1243     /**
   1244      * Removes all actions.
   1245      *
   1246      * @hide
   1247      */
   1248     public void removeAllActions() {
   1249         if (mActions != null) {
   1250             mActions.clear();
   1251         }
   1252     }
   1253 
   1254     /**
   1255      * Gets the node before which this one is visited during traversal. A screen-reader
   1256      * must visit the content of this node before the content of the one it precedes.
   1257      *
   1258      * @return The succeeding node if such or <code>null</code>.
   1259      *
   1260      * @see #setTraversalBefore(android.view.View)
   1261      * @see #setTraversalBefore(android.view.View, int)
   1262      */
   1263     public AccessibilityNodeInfo getTraversalBefore() {
   1264         enforceSealed();
   1265         return getNodeForAccessibilityId(mTraversalBefore);
   1266     }
   1267 
   1268     /**
   1269      * Sets the view before whose node this one should be visited during traversal. A
   1270      * screen-reader must visit the content of this node before the content of the one
   1271      * it precedes.
   1272      * <p>
   1273      *   <strong>Note:</strong> Cannot be called from an
   1274      *   {@link android.accessibilityservice.AccessibilityService}.
   1275      *   This class is made immutable before being delivered to an AccessibilityService.
   1276      * </p>
   1277      *
   1278      * @param view The view providing the preceding node.
   1279      *
   1280      * @see #getTraversalBefore()
   1281      */
   1282     public void setTraversalBefore(View view) {
   1283         setTraversalBefore(view, AccessibilityNodeProvider.HOST_VIEW_ID);
   1284     }
   1285 
   1286     /**
   1287      * Sets the node before which this one is visited during traversal. A screen-reader
   1288      * must visit the content of this node before the content of the one it precedes.
   1289      * The successor is a virtual descendant of the given <code>root</code>. If
   1290      * <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root is set
   1291      * as the successor.
   1292      * <p>
   1293      * A virtual descendant is an imaginary View that is reported as a part of the view
   1294      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1295      * content to report them selves as a tree of virtual views, thus conveying their
   1296      * logical structure.
   1297      * </p>
   1298      * <p>
   1299      *   <strong>Note:</strong> Cannot be called from an
   1300      *   {@link android.accessibilityservice.AccessibilityService}.
   1301      *   This class is made immutable before being delivered to an AccessibilityService.
   1302      * </p>
   1303      *
   1304      * @param root The root of the virtual subtree.
   1305      * @param virtualDescendantId The id of the virtual descendant.
   1306      */
   1307     public void setTraversalBefore(View root, int virtualDescendantId) {
   1308         enforceNotSealed();
   1309         final int rootAccessibilityViewId = (root != null)
   1310                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1311         mTraversalBefore = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1312     }
   1313 
   1314     /**
   1315      * Gets the node after which this one is visited in accessibility traversal.
   1316      * A screen-reader must visit the content of the other node before the content
   1317      * of this one.
   1318      *
   1319      * @return The succeeding node if such or <code>null</code>.
   1320      *
   1321      * @see #setTraversalAfter(android.view.View)
   1322      * @see #setTraversalAfter(android.view.View, int)
   1323      */
   1324     public AccessibilityNodeInfo getTraversalAfter() {
   1325         enforceSealed();
   1326         return getNodeForAccessibilityId(mTraversalAfter);
   1327     }
   1328 
   1329     /**
   1330      * Sets the view whose node is visited after this one in accessibility traversal.
   1331      * A screen-reader must visit the content of the other node before the content
   1332      * of this one.
   1333      * <p>
   1334      *   <strong>Note:</strong> Cannot be called from an
   1335      *   {@link android.accessibilityservice.AccessibilityService}.
   1336      *   This class is made immutable before being delivered to an AccessibilityService.
   1337      * </p>
   1338      *
   1339      * @param view The previous view.
   1340      *
   1341      * @see #getTraversalAfter()
   1342      */
   1343     public void setTraversalAfter(View view) {
   1344         setTraversalAfter(view, AccessibilityNodeProvider.HOST_VIEW_ID);
   1345     }
   1346 
   1347     /**
   1348      * Sets the node after which this one is visited in accessibility traversal.
   1349      * A screen-reader must visit the content of the other node before the content
   1350      * of this one. If <code>virtualDescendantId</code> equals to {@link View#NO_ID}
   1351      * the root is set as the predecessor.
   1352      * <p>
   1353      * A virtual descendant is an imaginary View that is reported as a part of the view
   1354      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1355      * content to report them selves as a tree of virtual views, thus conveying their
   1356      * logical structure.
   1357      * </p>
   1358      * <p>
   1359      *   <strong>Note:</strong> Cannot be called from an
   1360      *   {@link android.accessibilityservice.AccessibilityService}.
   1361      *   This class is made immutable before being delivered to an AccessibilityService.
   1362      * </p>
   1363      *
   1364      * @param root The root of the virtual subtree.
   1365      * @param virtualDescendantId The id of the virtual descendant.
   1366      */
   1367     public void setTraversalAfter(View root, int virtualDescendantId) {
   1368         enforceNotSealed();
   1369         final int rootAccessibilityViewId = (root != null)
   1370                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1371         mTraversalAfter = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1372     }
   1373 
   1374     /**
   1375      * Get the extra data available for this node.
   1376      * <p>
   1377      * Some data that is useful for some accessibility services is expensive to compute, and would
   1378      * place undue overhead on apps to compute all the time. That data can be requested with
   1379      * {@link #refreshWithExtraData(String, Bundle)}.
   1380      *
   1381      * @return An unmodifiable list of keys corresponding to extra data that can be requested.
   1382      * @see #EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
   1383      */
   1384     public List<String> getAvailableExtraData() {
   1385         if (mExtraDataKeys != null) {
   1386             return Collections.unmodifiableList(mExtraDataKeys);
   1387         } else {
   1388             return EMPTY_LIST;
   1389         }
   1390     }
   1391 
   1392     /**
   1393      * Set the extra data available for this node.
   1394      * <p>
   1395      * <strong>Note:</strong> When a {@code View} passes in a non-empty list, it promises that
   1396      * it will populate the node's extras with corresponding pieces of information in
   1397      * {@link View#addExtraDataToAccessibilityNodeInfo(AccessibilityNodeInfo, String, Bundle)}.
   1398      * <p>
   1399      * <strong>Note:</strong> Cannot be called from an
   1400      * {@link android.accessibilityservice.AccessibilityService}.
   1401      * This class is made immutable before being delivered to an AccessibilityService.
   1402      *
   1403      * @param extraDataKeys A list of types of extra data that are available.
   1404      * @see #getAvailableExtraData()
   1405      *
   1406      * @throws IllegalStateException If called from an AccessibilityService.
   1407      */
   1408     public void setAvailableExtraData(List<String> extraDataKeys) {
   1409         enforceNotSealed();
   1410         mExtraDataKeys = new ArrayList<>(extraDataKeys);
   1411     }
   1412 
   1413     /**
   1414      * Sets the maximum text length, or -1 for no limit.
   1415      * <p>
   1416      * Typically used to indicate that an editable text field has a limit on
   1417      * the number of characters entered.
   1418      * <p>
   1419      * <strong>Note:</strong> Cannot be called from an
   1420      * {@link android.accessibilityservice.AccessibilityService}.
   1421      * This class is made immutable before being delivered to an AccessibilityService.
   1422      *
   1423      * @param max The maximum text length.
   1424      * @see #getMaxTextLength()
   1425      *
   1426      * @throws IllegalStateException If called from an AccessibilityService.
   1427      */
   1428     public void setMaxTextLength(int max) {
   1429         enforceNotSealed();
   1430         mMaxTextLength = max;
   1431     }
   1432 
   1433     /**
   1434      * Returns the maximum text length for this node.
   1435      *
   1436      * @return The maximum text length, or -1 for no limit.
   1437      * @see #setMaxTextLength(int)
   1438      */
   1439     public int getMaxTextLength() {
   1440         return mMaxTextLength;
   1441     }
   1442 
   1443     /**
   1444      * Sets the movement granularities for traversing the text of this node.
   1445      * <p>
   1446      *   <strong>Note:</strong> Cannot be called from an
   1447      *   {@link android.accessibilityservice.AccessibilityService}.
   1448      *   This class is made immutable before being delivered to an AccessibilityService.
   1449      * </p>
   1450      *
   1451      * @param granularities The bit mask with granularities.
   1452      *
   1453      * @throws IllegalStateException If called from an AccessibilityService.
   1454      */
   1455     public void setMovementGranularities(int granularities) {
   1456         enforceNotSealed();
   1457         mMovementGranularities = granularities;
   1458     }
   1459 
   1460     /**
   1461      * Gets the movement granularities for traversing the text of this node.
   1462      *
   1463      * @return The bit mask with granularities.
   1464      */
   1465     public int getMovementGranularities() {
   1466         return mMovementGranularities;
   1467     }
   1468 
   1469     /**
   1470      * Performs an action on the node.
   1471      * <p>
   1472      *   <strong>Note:</strong> An action can be performed only if the request is made
   1473      *   from an {@link android.accessibilityservice.AccessibilityService}.
   1474      * </p>
   1475      *
   1476      * @param action The action to perform.
   1477      * @return True if the action was performed.
   1478      *
   1479      * @throws IllegalStateException If called outside of an AccessibilityService.
   1480      */
   1481     public boolean performAction(int action) {
   1482         enforceSealed();
   1483         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1484             return false;
   1485         }
   1486         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1487         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
   1488                 action, null);
   1489     }
   1490 
   1491     /**
   1492      * Performs an action on the node.
   1493      * <p>
   1494      *   <strong>Note:</strong> An action can be performed only if the request is made
   1495      *   from an {@link android.accessibilityservice.AccessibilityService}.
   1496      * </p>
   1497      *
   1498      * @param action The action to perform.
   1499      * @param arguments A bundle with additional arguments.
   1500      * @return True if the action was performed.
   1501      *
   1502      * @throws IllegalStateException If called outside of an AccessibilityService.
   1503      */
   1504     public boolean performAction(int action, Bundle arguments) {
   1505         enforceSealed();
   1506         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1507             return false;
   1508         }
   1509         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1510         return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
   1511                 action, arguments);
   1512     }
   1513 
   1514     /**
   1515      * Finds {@link AccessibilityNodeInfo}s by text. The match is case
   1516      * insensitive containment. The search is relative to this info i.e.
   1517      * this info is the root of the traversed tree.
   1518      *
   1519      * <p>
   1520      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1521      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1522      *     to avoid creating of multiple instances.
   1523      * </p>
   1524      *
   1525      * @param text The searched text.
   1526      * @return A list of node info.
   1527      */
   1528     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
   1529         enforceSealed();
   1530         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1531             return Collections.emptyList();
   1532         }
   1533         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1534         return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
   1535                 text);
   1536     }
   1537 
   1538     /**
   1539      * Finds {@link AccessibilityNodeInfo}s by the fully qualified view id's resource
   1540      * name where a fully qualified id is of the from "package:id/id_resource_name".
   1541      * For example, if the target application's package is "foo.bar" and the id
   1542      * resource name is "baz", the fully qualified resource id is "foo.bar:id/baz".
   1543      *
   1544      * <p>
   1545      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1546      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1547      *     to avoid creating of multiple instances.
   1548      * </p>
   1549      * <p>
   1550      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
   1551      *   and in order to report the fully qualified view id if an {@link AccessibilityNodeInfo}
   1552      *   the client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
   1553      *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
   1554      * </p>
   1555      *
   1556      * @param viewId The fully qualified resource name of the view id to find.
   1557      * @return A list of node info.
   1558      */
   1559     public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByViewId(String viewId) {
   1560         enforceSealed();
   1561         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1562             return Collections.emptyList();
   1563         }
   1564         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1565         return client.findAccessibilityNodeInfosByViewId(mConnectionId, mWindowId, mSourceNodeId,
   1566                 viewId);
   1567     }
   1568 
   1569     /**
   1570      * Gets the window to which this node belongs.
   1571      *
   1572      * @return The window.
   1573      *
   1574      * @see android.accessibilityservice.AccessibilityService#getWindows()
   1575      */
   1576     public AccessibilityWindowInfo getWindow() {
   1577         enforceSealed();
   1578         if (!canPerformRequestOverConnection(mSourceNodeId)) {
   1579             return null;
   1580         }
   1581         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   1582         return client.getWindow(mConnectionId, mWindowId);
   1583     }
   1584 
   1585     /**
   1586      * Gets the parent.
   1587      * <p>
   1588      *   <strong>Note:</strong> It is a client responsibility to recycle the
   1589      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   1590      *     to avoid creating of multiple instances.
   1591      * </p>
   1592      *
   1593      * @return The parent.
   1594      */
   1595     public AccessibilityNodeInfo getParent() {
   1596         enforceSealed();
   1597         return getNodeForAccessibilityId(mParentNodeId);
   1598     }
   1599 
   1600     /**
   1601      * @return The parent node id.
   1602      *
   1603      * @hide
   1604      */
   1605     public long getParentNodeId() {
   1606         return mParentNodeId;
   1607     }
   1608 
   1609     /**
   1610      * Sets the parent.
   1611      * <p>
   1612      *   <strong>Note:</strong> Cannot be called from an
   1613      *   {@link android.accessibilityservice.AccessibilityService}.
   1614      *   This class is made immutable before being delivered to an AccessibilityService.
   1615      * </p>
   1616      *
   1617      * @param parent The parent.
   1618      *
   1619      * @throws IllegalStateException If called from an AccessibilityService.
   1620      */
   1621     public void setParent(View parent) {
   1622         setParent(parent, AccessibilityNodeProvider.HOST_VIEW_ID);
   1623     }
   1624 
   1625     /**
   1626      * Sets the parent to be a virtual descendant of the given <code>root</code>.
   1627      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
   1628      * is set as the parent.
   1629      * <p>
   1630      * A virtual descendant is an imaginary View that is reported as a part of the view
   1631      * hierarchy for accessibility purposes. This enables custom views that draw complex
   1632      * content to report them selves as a tree of virtual views, thus conveying their
   1633      * logical structure.
   1634      * </p>
   1635      * <p>
   1636      *   <strong>Note:</strong> Cannot be called from an
   1637      *   {@link android.accessibilityservice.AccessibilityService}.
   1638      *   This class is made immutable before being delivered to an AccessibilityService.
   1639      * </p>
   1640      *
   1641      * @param root The root of the virtual subtree.
   1642      * @param virtualDescendantId The id of the virtual descendant.
   1643      */
   1644     public void setParent(View root, int virtualDescendantId) {
   1645         enforceNotSealed();
   1646         final int rootAccessibilityViewId =
   1647             (root != null) ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   1648         mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   1649     }
   1650 
   1651     /**
   1652      * Gets the node bounds in parent coordinates.
   1653      *
   1654      * @param outBounds The output node bounds.
   1655      */
   1656     public void getBoundsInParent(Rect outBounds) {
   1657         outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
   1658                 mBoundsInParent.right, mBoundsInParent.bottom);
   1659     }
   1660 
   1661     /**
   1662      * Sets the node bounds in parent coordinates.
   1663      * <p>
   1664      *   <strong>Note:</strong> Cannot be called from an
   1665      *   {@link android.accessibilityservice.AccessibilityService}.
   1666      *   This class is made immutable before being delivered to an AccessibilityService.
   1667      * </p>
   1668      *
   1669      * @param bounds The node bounds.
   1670      *
   1671      * @throws IllegalStateException If called from an AccessibilityService.
   1672      */
   1673     public void setBoundsInParent(Rect bounds) {
   1674         enforceNotSealed();
   1675         mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
   1676     }
   1677 
   1678     /**
   1679      * Gets the node bounds in screen coordinates.
   1680      *
   1681      * @param outBounds The output node bounds.
   1682      */
   1683     public void getBoundsInScreen(Rect outBounds) {
   1684         outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
   1685                 mBoundsInScreen.right, mBoundsInScreen.bottom);
   1686     }
   1687 
   1688     /**
   1689      * Returns the actual rect containing the node bounds in screen coordinates.
   1690      *
   1691      * @hide Not safe to expose outside the framework.
   1692      */
   1693     public Rect getBoundsInScreen() {
   1694         return mBoundsInScreen;
   1695     }
   1696 
   1697     /**
   1698      * Sets the node bounds in screen coordinates.
   1699      * <p>
   1700      *   <strong>Note:</strong> Cannot be called from an
   1701      *   {@link android.accessibilityservice.AccessibilityService}.
   1702      *   This class is made immutable before being delivered to an AccessibilityService.
   1703      * </p>
   1704      *
   1705      * @param bounds The node bounds.
   1706      *
   1707      * @throws IllegalStateException If called from an AccessibilityService.
   1708      */
   1709     public void setBoundsInScreen(Rect bounds) {
   1710         enforceNotSealed();
   1711         mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
   1712     }
   1713 
   1714     /**
   1715      * Gets whether this node is checkable.
   1716      *
   1717      * @return True if the node is checkable.
   1718      */
   1719     public boolean isCheckable() {
   1720         return getBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE);
   1721     }
   1722 
   1723     /**
   1724      * Sets whether this node is checkable.
   1725      * <p>
   1726      *   <strong>Note:</strong> Cannot be called from an
   1727      *   {@link android.accessibilityservice.AccessibilityService}.
   1728      *   This class is made immutable before being delivered to an AccessibilityService.
   1729      * </p>
   1730      *
   1731      * @param checkable True if the node is checkable.
   1732      *
   1733      * @throws IllegalStateException If called from an AccessibilityService.
   1734      */
   1735     public void setCheckable(boolean checkable) {
   1736         setBooleanProperty(BOOLEAN_PROPERTY_CHECKABLE, checkable);
   1737     }
   1738 
   1739     /**
   1740      * Gets whether this node is checked.
   1741      *
   1742      * @return True if the node is checked.
   1743      */
   1744     public boolean isChecked() {
   1745         return getBooleanProperty(BOOLEAN_PROPERTY_CHECKED);
   1746     }
   1747 
   1748     /**
   1749      * Sets whether this node is checked.
   1750      * <p>
   1751      *   <strong>Note:</strong> Cannot be called from an
   1752      *   {@link android.accessibilityservice.AccessibilityService}.
   1753      *   This class is made immutable before being delivered to an AccessibilityService.
   1754      * </p>
   1755      *
   1756      * @param checked True if the node is checked.
   1757      *
   1758      * @throws IllegalStateException If called from an AccessibilityService.
   1759      */
   1760     public void setChecked(boolean checked) {
   1761         setBooleanProperty(BOOLEAN_PROPERTY_CHECKED, checked);
   1762     }
   1763 
   1764     /**
   1765      * Gets whether this node is focusable.
   1766      *
   1767      * @return True if the node is focusable.
   1768      */
   1769     public boolean isFocusable() {
   1770         return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE);
   1771     }
   1772 
   1773     /**
   1774      * Sets whether this node is focusable.
   1775      * <p>
   1776      *   <strong>Note:</strong> Cannot be called from an
   1777      *   {@link android.accessibilityservice.AccessibilityService}.
   1778      *   This class is made immutable before being delivered to an AccessibilityService.
   1779      * </p>
   1780      *
   1781      * @param focusable True if the node is focusable.
   1782      *
   1783      * @throws IllegalStateException If called from an AccessibilityService.
   1784      */
   1785     public void setFocusable(boolean focusable) {
   1786         setBooleanProperty(BOOLEAN_PROPERTY_FOCUSABLE, focusable);
   1787     }
   1788 
   1789     /**
   1790      * Gets whether this node is focused.
   1791      *
   1792      * @return True if the node is focused.
   1793      */
   1794     public boolean isFocused() {
   1795         return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED);
   1796     }
   1797 
   1798     /**
   1799      * Sets whether this node is focused.
   1800      * <p>
   1801      *   <strong>Note:</strong> Cannot be called from an
   1802      *   {@link android.accessibilityservice.AccessibilityService}.
   1803      *   This class is made immutable before being delivered to an AccessibilityService.
   1804      * </p>
   1805      *
   1806      * @param focused True if the node is focused.
   1807      *
   1808      * @throws IllegalStateException If called from an AccessibilityService.
   1809      */
   1810     public void setFocused(boolean focused) {
   1811         setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
   1812     }
   1813 
   1814     /**
   1815      * Gets whether this node is visible to the user.
   1816      *
   1817      * @return Whether the node is visible to the user.
   1818      */
   1819     public boolean isVisibleToUser() {
   1820         return getBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER);
   1821     }
   1822 
   1823     /**
   1824      * Sets whether this node is visible to the user.
   1825      * <p>
   1826      *   <strong>Note:</strong> Cannot be called from an
   1827      *   {@link android.accessibilityservice.AccessibilityService}.
   1828      *   This class is made immutable before being delivered to an AccessibilityService.
   1829      * </p>
   1830      *
   1831      * @param visibleToUser Whether the node is visible to the user.
   1832      *
   1833      * @throws IllegalStateException If called from an AccessibilityService.
   1834      */
   1835     public void setVisibleToUser(boolean visibleToUser) {
   1836         setBooleanProperty(BOOLEAN_PROPERTY_VISIBLE_TO_USER, visibleToUser);
   1837     }
   1838 
   1839     /**
   1840      * Gets whether this node is accessibility focused.
   1841      *
   1842      * @return True if the node is accessibility focused.
   1843      */
   1844     public boolean isAccessibilityFocused() {
   1845         return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
   1846     }
   1847 
   1848     /**
   1849      * Sets whether this node is accessibility focused.
   1850      * <p>
   1851      *   <strong>Note:</strong> Cannot be called from an
   1852      *   {@link android.accessibilityservice.AccessibilityService}.
   1853      *   This class is made immutable before being delivered to an AccessibilityService.
   1854      * </p>
   1855      *
   1856      * @param focused True if the node is accessibility focused.
   1857      *
   1858      * @throws IllegalStateException If called from an AccessibilityService.
   1859      */
   1860     public void setAccessibilityFocused(boolean focused) {
   1861         setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED, focused);
   1862     }
   1863 
   1864     /**
   1865      * Gets whether this node is selected.
   1866      *
   1867      * @return True if the node is selected.
   1868      */
   1869     public boolean isSelected() {
   1870         return getBooleanProperty(BOOLEAN_PROPERTY_SELECTED);
   1871     }
   1872 
   1873     /**
   1874      * Sets whether this node is selected.
   1875      * <p>
   1876      *   <strong>Note:</strong> Cannot be called from an
   1877      *   {@link android.accessibilityservice.AccessibilityService}.
   1878      *   This class is made immutable before being delivered to an AccessibilityService.
   1879      * </p>
   1880      *
   1881      * @param selected True if the node is selected.
   1882      *
   1883      * @throws IllegalStateException If called from an AccessibilityService.
   1884      */
   1885     public void setSelected(boolean selected) {
   1886         setBooleanProperty(BOOLEAN_PROPERTY_SELECTED, selected);
   1887     }
   1888 
   1889     /**
   1890      * Gets whether this node is clickable.
   1891      *
   1892      * @return True if the node is clickable.
   1893      */
   1894     public boolean isClickable() {
   1895         return getBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE);
   1896     }
   1897 
   1898     /**
   1899      * Sets whether this node is clickable.
   1900      * <p>
   1901      *   <strong>Note:</strong> Cannot be called from an
   1902      *   {@link android.accessibilityservice.AccessibilityService}.
   1903      *   This class is made immutable before being delivered to an AccessibilityService.
   1904      * </p>
   1905      *
   1906      * @param clickable True if the node is clickable.
   1907      *
   1908      * @throws IllegalStateException If called from an AccessibilityService.
   1909      */
   1910     public void setClickable(boolean clickable) {
   1911         setBooleanProperty(BOOLEAN_PROPERTY_CLICKABLE, clickable);
   1912     }
   1913 
   1914     /**
   1915      * Gets whether this node is long clickable.
   1916      *
   1917      * @return True if the node is long clickable.
   1918      */
   1919     public boolean isLongClickable() {
   1920         return getBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE);
   1921     }
   1922 
   1923     /**
   1924      * Sets whether this node is long clickable.
   1925      * <p>
   1926      *   <strong>Note:</strong> Cannot be called from an
   1927      *   {@link android.accessibilityservice.AccessibilityService}.
   1928      *   This class is made immutable before being delivered to an AccessibilityService.
   1929      * </p>
   1930      *
   1931      * @param longClickable True if the node is long clickable.
   1932      *
   1933      * @throws IllegalStateException If called from an AccessibilityService.
   1934      */
   1935     public void setLongClickable(boolean longClickable) {
   1936         setBooleanProperty(BOOLEAN_PROPERTY_LONG_CLICKABLE, longClickable);
   1937     }
   1938 
   1939     /**
   1940      * Gets whether this node is enabled.
   1941      *
   1942      * @return True if the node is enabled.
   1943      */
   1944     public boolean isEnabled() {
   1945         return getBooleanProperty(BOOLEAN_PROPERTY_ENABLED);
   1946     }
   1947 
   1948     /**
   1949      * Sets whether this node is enabled.
   1950      * <p>
   1951      *   <strong>Note:</strong> Cannot be called from an
   1952      *   {@link android.accessibilityservice.AccessibilityService}.
   1953      *   This class is made immutable before being delivered to an AccessibilityService.
   1954      * </p>
   1955      *
   1956      * @param enabled True if the node is enabled.
   1957      *
   1958      * @throws IllegalStateException If called from an AccessibilityService.
   1959      */
   1960     public void setEnabled(boolean enabled) {
   1961         setBooleanProperty(BOOLEAN_PROPERTY_ENABLED, enabled);
   1962     }
   1963 
   1964     /**
   1965      * Gets whether this node is a password.
   1966      *
   1967      * @return True if the node is a password.
   1968      */
   1969     public boolean isPassword() {
   1970         return getBooleanProperty(BOOLEAN_PROPERTY_PASSWORD);
   1971     }
   1972 
   1973     /**
   1974      * Sets whether this node is a password.
   1975      * <p>
   1976      *   <strong>Note:</strong> Cannot be called from an
   1977      *   {@link android.accessibilityservice.AccessibilityService}.
   1978      *   This class is made immutable before being delivered to an AccessibilityService.
   1979      * </p>
   1980      *
   1981      * @param password True if the node is a password.
   1982      *
   1983      * @throws IllegalStateException If called from an AccessibilityService.
   1984      */
   1985     public void setPassword(boolean password) {
   1986         setBooleanProperty(BOOLEAN_PROPERTY_PASSWORD, password);
   1987     }
   1988 
   1989     /**
   1990      * Gets if the node is scrollable.
   1991      *
   1992      * @return True if the node is scrollable, false otherwise.
   1993      */
   1994     public boolean isScrollable() {
   1995         return getBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE);
   1996     }
   1997 
   1998     /**
   1999      * Sets if the node is scrollable.
   2000      * <p>
   2001      *   <strong>Note:</strong> Cannot be called from an
   2002      *   {@link android.accessibilityservice.AccessibilityService}.
   2003      *   This class is made immutable before being delivered to an AccessibilityService.
   2004      * </p>
   2005      *
   2006      * @param scrollable True if the node is scrollable, false otherwise.
   2007      *
   2008      * @throws IllegalStateException If called from an AccessibilityService.
   2009      */
   2010     public void setScrollable(boolean scrollable) {
   2011         setBooleanProperty(BOOLEAN_PROPERTY_SCROLLABLE, scrollable);
   2012     }
   2013 
   2014     /**
   2015      * Gets if the node is editable.
   2016      *
   2017      * @return True if the node is editable, false otherwise.
   2018      */
   2019     public boolean isEditable() {
   2020         return getBooleanProperty(BOOLEAN_PROPERTY_EDITABLE);
   2021     }
   2022 
   2023     /**
   2024      * Sets whether this node is editable.
   2025      * <p>
   2026      *   <strong>Note:</strong> Cannot be called from an
   2027      *   {@link android.accessibilityservice.AccessibilityService}.
   2028      *   This class is made immutable before being delivered to an AccessibilityService.
   2029      * </p>
   2030      *
   2031      * @param editable True if the node is editable.
   2032      *
   2033      * @throws IllegalStateException If called from an AccessibilityService.
   2034      */
   2035     public void setEditable(boolean editable) {
   2036         setBooleanProperty(BOOLEAN_PROPERTY_EDITABLE, editable);
   2037     }
   2038 
   2039     /**
   2040      * If this node represents a visually distinct region of the screen that may update separately
   2041      * from the rest of the window, it is considered a pane. Set the pane title to indicate that
   2042      * the node is a pane, and to provide a title for it.
   2043      * <p>
   2044      *   <strong>Note:</strong> Cannot be called from an
   2045      *   {@link android.accessibilityservice.AccessibilityService}.
   2046      *   This class is made immutable before being delivered to an AccessibilityService.
   2047      * </p>
   2048      * @param paneTitle The title of the pane represented by this node.
   2049      */
   2050     public void setPaneTitle(@Nullable CharSequence paneTitle) {
   2051         enforceNotSealed();
   2052         mPaneTitle = (paneTitle == null)
   2053                 ? null : paneTitle.subSequence(0, paneTitle.length());
   2054     }
   2055 
   2056     /**
   2057      * Get the title of the pane represented by this node.
   2058      *
   2059      * @return The title of the pane represented by this node, or {@code null} if this node does
   2060      *         not represent a pane.
   2061      */
   2062     public @Nullable CharSequence getPaneTitle() {
   2063         return mPaneTitle;
   2064     }
   2065 
   2066     /**
   2067      * Get the drawing order of the view corresponding it this node.
   2068      * <p>
   2069      * Drawing order is determined only within the node's parent, so this index is only relative
   2070      * to its siblings.
   2071      * <p>
   2072      * In some cases, the drawing order is essentially simultaneous, so it is possible for two
   2073      * siblings to return the same value. It is also possible that values will be skipped.
   2074      *
   2075      * @return The drawing position of the view corresponding to this node relative to its siblings.
   2076      */
   2077     public int getDrawingOrder() {
   2078         return mDrawingOrderInParent;
   2079     }
   2080 
   2081     /**
   2082      * Set the drawing order of the view corresponding it this node.
   2083      *
   2084      * <p>
   2085      *   <strong>Note:</strong> Cannot be called from an
   2086      *   {@link android.accessibilityservice.AccessibilityService}.
   2087      *   This class is made immutable before being delivered to an AccessibilityService.
   2088      * </p>
   2089      * @param drawingOrderInParent
   2090      * @throws IllegalStateException If called from an AccessibilityService.
   2091      */
   2092     public void setDrawingOrder(int drawingOrderInParent) {
   2093         enforceNotSealed();
   2094         mDrawingOrderInParent = drawingOrderInParent;
   2095     }
   2096 
   2097     /**
   2098      * Gets the collection info if the node is a collection. A collection
   2099      * child is always a collection item.
   2100      *
   2101      * @return The collection info.
   2102      */
   2103     public CollectionInfo getCollectionInfo() {
   2104         return mCollectionInfo;
   2105     }
   2106 
   2107     /**
   2108      * Sets the collection info if the node is a collection. A collection
   2109      * child is always a collection item.
   2110      * <p>
   2111      *   <strong>Note:</strong> Cannot be called from an
   2112      *   {@link android.accessibilityservice.AccessibilityService}.
   2113      *   This class is made immutable before being delivered to an AccessibilityService.
   2114      * </p>
   2115      *
   2116      * @param collectionInfo The collection info.
   2117      */
   2118     public void setCollectionInfo(CollectionInfo collectionInfo) {
   2119         enforceNotSealed();
   2120         mCollectionInfo = collectionInfo;
   2121     }
   2122 
   2123     /**
   2124      * Gets the collection item info if the node is a collection item. A collection
   2125      * item is always a child of a collection.
   2126      *
   2127      * @return The collection item info.
   2128      */
   2129     public CollectionItemInfo getCollectionItemInfo() {
   2130         return mCollectionItemInfo;
   2131     }
   2132 
   2133     /**
   2134      * Sets the collection item info if the node is a collection item. A collection
   2135      * item is always a child of a collection.
   2136      * <p>
   2137      *   <strong>Note:</strong> Cannot be called from an
   2138      *   {@link android.accessibilityservice.AccessibilityService}.
   2139      *   This class is made immutable before being delivered to an AccessibilityService.
   2140      * </p>
   2141      */
   2142     public void setCollectionItemInfo(CollectionItemInfo collectionItemInfo) {
   2143         enforceNotSealed();
   2144         mCollectionItemInfo = collectionItemInfo;
   2145     }
   2146 
   2147     /**
   2148      * Gets the range info if this node is a range.
   2149      *
   2150      * @return The range.
   2151      */
   2152     public RangeInfo getRangeInfo() {
   2153         return mRangeInfo;
   2154     }
   2155 
   2156     /**
   2157      * Sets the range info if this node is a range.
   2158      * <p>
   2159      *   <strong>Note:</strong> Cannot be called from an
   2160      *   {@link android.accessibilityservice.AccessibilityService}.
   2161      *   This class is made immutable before being delivered to an AccessibilityService.
   2162      * </p>
   2163      *
   2164      * @param rangeInfo The range info.
   2165      */
   2166     public void setRangeInfo(RangeInfo rangeInfo) {
   2167         enforceNotSealed();
   2168         mRangeInfo = rangeInfo;
   2169     }
   2170 
   2171     /**
   2172      * Gets if the content of this node is invalid. For example,
   2173      * a date is not well-formed.
   2174      *
   2175      * @return If the node content is invalid.
   2176      */
   2177     public boolean isContentInvalid() {
   2178         return getBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID);
   2179     }
   2180 
   2181     /**
   2182      * Sets if the content of this node is invalid. For example,
   2183      * a date is not well-formed.
   2184      * <p>
   2185      *   <strong>Note:</strong> Cannot be called from an
   2186      *   {@link android.accessibilityservice.AccessibilityService}.
   2187      *   This class is made immutable before being delivered to an AccessibilityService.
   2188      * </p>
   2189      *
   2190      * @param contentInvalid If the node content is invalid.
   2191      */
   2192     public void setContentInvalid(boolean contentInvalid) {
   2193         setBooleanProperty(BOOLEAN_PROPERTY_CONTENT_INVALID, contentInvalid);
   2194     }
   2195 
   2196     /**
   2197      * Gets whether this node is context clickable.
   2198      *
   2199      * @return True if the node is context clickable.
   2200      */
   2201     public boolean isContextClickable() {
   2202         return getBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE);
   2203     }
   2204 
   2205     /**
   2206      * Sets whether this node is context clickable.
   2207      * <p>
   2208      * <strong>Note:</strong> Cannot be called from an
   2209      * {@link android.accessibilityservice.AccessibilityService}. This class is made immutable
   2210      * before being delivered to an AccessibilityService.
   2211      * </p>
   2212      *
   2213      * @param contextClickable True if the node is context clickable.
   2214      * @throws IllegalStateException If called from an AccessibilityService.
   2215      */
   2216     public void setContextClickable(boolean contextClickable) {
   2217         setBooleanProperty(BOOLEAN_PROPERTY_CONTEXT_CLICKABLE, contextClickable);
   2218     }
   2219 
   2220     /**
   2221      * Gets the node's live region mode.
   2222      * <p>
   2223      * A live region is a node that contains information that is important for
   2224      * the user and when it changes the user should be notified. For example,
   2225      * in a login screen with a TextView that displays an "incorrect password"
   2226      * notification, that view should be marked as a live region with mode
   2227      * {@link View#ACCESSIBILITY_LIVE_REGION_POLITE}.
   2228      * <p>
   2229      * It is the responsibility of the accessibility service to monitor
   2230      * {@link AccessibilityEvent#TYPE_WINDOW_CONTENT_CHANGED} events indicating
   2231      * changes to live region nodes and their children.
   2232      *
   2233      * @return The live region mode, or
   2234      *         {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
   2235      *         live region.
   2236      * @see android.view.View#getAccessibilityLiveRegion()
   2237      */
   2238     public int getLiveRegion() {
   2239         return mLiveRegion;
   2240     }
   2241 
   2242     /**
   2243      * Sets the node's live region mode.
   2244      * <p>
   2245      * <strong>Note:</strong> Cannot be called from an
   2246      * {@link android.accessibilityservice.AccessibilityService}. This class is
   2247      * made immutable before being delivered to an AccessibilityService.
   2248      *
   2249      * @param mode The live region mode, or
   2250      *        {@link View#ACCESSIBILITY_LIVE_REGION_NONE} if the view is not a
   2251      *        live region.
   2252      * @see android.view.View#setAccessibilityLiveRegion(int)
   2253      */
   2254     public void setLiveRegion(int mode) {
   2255         enforceNotSealed();
   2256         mLiveRegion = mode;
   2257     }
   2258 
   2259     /**
   2260      * Gets if the node is a multi line editable text.
   2261      *
   2262      * @return True if the node is multi line.
   2263      */
   2264     public boolean isMultiLine() {
   2265         return getBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE);
   2266     }
   2267 
   2268     /**
   2269      * Sets if the node is a multi line editable text.
   2270      * <p>
   2271      *   <strong>Note:</strong> Cannot be called from an
   2272      *   {@link android.accessibilityservice.AccessibilityService}.
   2273      *   This class is made immutable before being delivered to an AccessibilityService.
   2274      * </p>
   2275      *
   2276      * @param multiLine True if the node is multi line.
   2277      */
   2278     public void setMultiLine(boolean multiLine) {
   2279         setBooleanProperty(BOOLEAN_PROPERTY_MULTI_LINE, multiLine);
   2280     }
   2281 
   2282     /**
   2283      * Gets if this node opens a popup or a dialog.
   2284      *
   2285      * @return If the the node opens a popup.
   2286      */
   2287     public boolean canOpenPopup() {
   2288         return getBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP);
   2289     }
   2290 
   2291     /**
   2292      * Sets if this node opens a popup or a dialog.
   2293      * <p>
   2294      *   <strong>Note:</strong> Cannot be called from an
   2295      *   {@link android.accessibilityservice.AccessibilityService}.
   2296      *   This class is made immutable before being delivered to an AccessibilityService.
   2297      * </p>
   2298      *
   2299      * @param opensPopup If the the node opens a popup.
   2300      */
   2301     public void setCanOpenPopup(boolean opensPopup) {
   2302         enforceNotSealed();
   2303         setBooleanProperty(BOOLEAN_PROPERTY_OPENS_POPUP, opensPopup);
   2304     }
   2305 
   2306     /**
   2307      * Gets if the node can be dismissed.
   2308      *
   2309      * @return If the node can be dismissed.
   2310      */
   2311     public boolean isDismissable() {
   2312         return getBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE);
   2313     }
   2314 
   2315     /**
   2316      * Sets if the node can be dismissed.
   2317      * <p>
   2318      *   <strong>Note:</strong> Cannot be called from an
   2319      *   {@link android.accessibilityservice.AccessibilityService}.
   2320      *   This class is made immutable before being delivered to an AccessibilityService.
   2321      * </p>
   2322      *
   2323      * @param dismissable If the node can be dismissed.
   2324      */
   2325     public void setDismissable(boolean dismissable) {
   2326         setBooleanProperty(BOOLEAN_PROPERTY_DISMISSABLE, dismissable);
   2327     }
   2328 
   2329     /**
   2330      * Returns whether the node originates from a view considered important for accessibility.
   2331      *
   2332      * @return {@code true} if the node originates from a view considered important for
   2333      *         accessibility, {@code false} otherwise
   2334      *
   2335      * @see View#isImportantForAccessibility()
   2336      */
   2337     public boolean isImportantForAccessibility() {
   2338         return getBooleanProperty(BOOLEAN_PROPERTY_IMPORTANCE);
   2339     }
   2340 
   2341     /**
   2342      * Sets whether the node is considered important for accessibility.
   2343      * <p>
   2344      *   <strong>Note:</strong> Cannot be called from an
   2345      *   {@link android.accessibilityservice.AccessibilityService}.
   2346      *   This class is made immutable before being delivered to an AccessibilityService.
   2347      * </p>
   2348      *
   2349      * @param important {@code true} if the node is considered important for accessibility,
   2350      *                  {@code false} otherwise
   2351      */
   2352     public void setImportantForAccessibility(boolean important) {
   2353         setBooleanProperty(BOOLEAN_PROPERTY_IMPORTANCE, important);
   2354     }
   2355 
   2356     /**
   2357      * Returns whether the node is explicitly marked as a focusable unit by a screen reader. Note
   2358      * that {@code false} indicates that it is not explicitly marked, not that the node is not
   2359      * a focusable unit. Screen readers should generally use other signals, such as
   2360      * {@link #isFocusable()}, or the presence of text in a node, to determine what should receive
   2361      * focus.
   2362      *
   2363      * @return {@code true} if the node is specifically marked as a focusable unit for screen
   2364      *         readers, {@code false} otherwise.
   2365      *
   2366      * @see View#isScreenReaderFocusable()
   2367      */
   2368     public boolean isScreenReaderFocusable() {
   2369         return getBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE);
   2370     }
   2371 
   2372     /**
   2373      * Sets whether the node should be considered a focusable unit by a screen reader.
   2374      * <p>
   2375      *   <strong>Note:</strong> Cannot be called from an
   2376      *   {@link android.accessibilityservice.AccessibilityService}.
   2377      *   This class is made immutable before being delivered to an AccessibilityService.
   2378      * </p>
   2379      *
   2380      * @param screenReaderFocusable {@code true} if the node is a focusable unit for screen readers,
   2381      *                              {@code false} otherwise.
   2382      */
   2383     public void setScreenReaderFocusable(boolean screenReaderFocusable) {
   2384         setBooleanProperty(BOOLEAN_PROPERTY_SCREEN_READER_FOCUSABLE, screenReaderFocusable);
   2385     }
   2386 
   2387     /**
   2388      * Returns whether the node's text represents a hint for the user to enter text. It should only
   2389      * be {@code true} if the node has editable text.
   2390      *
   2391      * @return {@code true} if the text in the node represents a hint to the user, {@code false}
   2392      * otherwise.
   2393      */
   2394     public boolean isShowingHintText() {
   2395         return getBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT);
   2396     }
   2397 
   2398     /**
   2399      * Sets whether the node's text represents a hint for the user to enter text. It should only
   2400      * be {@code true} if the node has editable text.
   2401      * <p>
   2402      *   <strong>Note:</strong> Cannot be called from an
   2403      *   {@link android.accessibilityservice.AccessibilityService}.
   2404      *   This class is made immutable before being delivered to an AccessibilityService.
   2405      * </p>
   2406      *
   2407      * @param showingHintText {@code true} if the text in the node represents a hint to the user,
   2408      * {@code false} otherwise.
   2409      */
   2410     public void setShowingHintText(boolean showingHintText) {
   2411         setBooleanProperty(BOOLEAN_PROPERTY_IS_SHOWING_HINT, showingHintText);
   2412     }
   2413 
   2414     /**
   2415      * Returns whether node represents a heading.
   2416      * <p><strong>Note:</strong> Returns {@code true} if either {@link #setHeading(boolean)}
   2417      * marks this node as a heading or if the node has a {@link CollectionItemInfo} that marks
   2418      * it as such, to accomodate apps that use the now-deprecated API.</p>
   2419      *
   2420      * @return {@code true} if the node is a heading, {@code false} otherwise.
   2421      */
   2422     public boolean isHeading() {
   2423         if (getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING)) return true;
   2424         CollectionItemInfo itemInfo = getCollectionItemInfo();
   2425         return ((itemInfo != null) && itemInfo.mHeading);
   2426     }
   2427 
   2428     /**
   2429      * Sets whether the node represents a heading.
   2430      *
   2431      * <p>
   2432      *   <strong>Note:</strong> Cannot be called from an
   2433      *   {@link android.accessibilityservice.AccessibilityService}.
   2434      *   This class is made immutable before being delivered to an AccessibilityService.
   2435      * </p>
   2436      *
   2437      * @param isHeading {@code true} if the node is a heading, {@code false} otherwise.
   2438      */
   2439     public void setHeading(boolean isHeading) {
   2440         setBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING, isHeading);
   2441     }
   2442 
   2443     /**
   2444      * Gets the package this node comes from.
   2445      *
   2446      * @return The package name.
   2447      */
   2448     public CharSequence getPackageName() {
   2449         return mPackageName;
   2450     }
   2451 
   2452     /**
   2453      * Sets the package this node comes from.
   2454      * <p>
   2455      *   <strong>Note:</strong> Cannot be called from an
   2456      *   {@link android.accessibilityservice.AccessibilityService}.
   2457      *   This class is made immutable before being delivered to an AccessibilityService.
   2458      * </p>
   2459      *
   2460      * @param packageName The package name.
   2461      *
   2462      * @throws IllegalStateException If called from an AccessibilityService.
   2463      */
   2464     public void setPackageName(CharSequence packageName) {
   2465         enforceNotSealed();
   2466         mPackageName = packageName;
   2467     }
   2468 
   2469     /**
   2470      * Gets the class this node comes from.
   2471      *
   2472      * @return The class name.
   2473      */
   2474     public CharSequence getClassName() {
   2475         return mClassName;
   2476     }
   2477 
   2478     /**
   2479      * Sets the class this node comes from.
   2480      * <p>
   2481      *   <strong>Note:</strong> Cannot be called from an
   2482      *   {@link android.accessibilityservice.AccessibilityService}.
   2483      *   This class is made immutable before being delivered to an AccessibilityService.
   2484      * </p>
   2485      *
   2486      * @param className The class name.
   2487      *
   2488      * @throws IllegalStateException If called from an AccessibilityService.
   2489      */
   2490     public void setClassName(CharSequence className) {
   2491         enforceNotSealed();
   2492         mClassName = className;
   2493     }
   2494 
   2495     /**
   2496      * Gets the text of this node.
   2497      * <p>
   2498      *   <strong>Note:</strong> If the text contains {@link ClickableSpan}s or {@link URLSpan}s,
   2499      *   these spans will have been replaced with ones whose {@link ClickableSpan#onClick(View)}
   2500      *   can be called from an {@link AccessibilityService}. When called from a service, the
   2501      *   {@link View} argument is ignored and the corresponding span will be found on the view that
   2502      *   this {@code AccessibilityNodeInfo} represents and called with that view as its argument.
   2503      *   <p>
   2504      *   This treatment of {@link ClickableSpan}s means that the text returned from this method may
   2505      *   different slightly one passed to {@link #setText(CharSequence)}, although they will be
   2506      *   equivalent according to {@link TextUtils#equals(CharSequence, CharSequence)}. The
   2507      *   {@link ClickableSpan#onClick(View)} of any spans, however, will generally not work outside
   2508      *   of an accessibility service.
   2509      * </p>
   2510      *
   2511      * @return The text.
   2512      */
   2513     public CharSequence getText() {
   2514         // Attach this node to any spans that need it
   2515         if (mText instanceof Spanned) {
   2516             Spanned spanned = (Spanned) mText;
   2517             AccessibilityClickableSpan[] clickableSpans =
   2518                     spanned.getSpans(0, mText.length(), AccessibilityClickableSpan.class);
   2519             for (int i = 0; i < clickableSpans.length; i++) {
   2520                 clickableSpans[i].copyConnectionDataFrom(this);
   2521             }
   2522             AccessibilityURLSpan[] urlSpans =
   2523                     spanned.getSpans(0, mText.length(), AccessibilityURLSpan.class);
   2524             for (int i = 0; i < urlSpans.length; i++) {
   2525                 urlSpans[i].copyConnectionDataFrom(this);
   2526             }
   2527         }
   2528         return mText;
   2529     }
   2530 
   2531     /**
   2532      * Get the text passed to setText before any changes to the spans.
   2533      * @hide
   2534      */
   2535     public CharSequence getOriginalText() {
   2536         return mOriginalText;
   2537     }
   2538 
   2539     /**
   2540      * Sets the text of this node.
   2541      * <p>
   2542      *   <strong>Note:</strong> Cannot be called from an
   2543      *   {@link android.accessibilityservice.AccessibilityService}.
   2544      *   This class is made immutable before being delivered to an AccessibilityService.
   2545      * </p>
   2546      *
   2547      * @param text The text.
   2548      *
   2549      * @throws IllegalStateException If called from an AccessibilityService.
   2550      */
   2551     public void setText(CharSequence text) {
   2552         enforceNotSealed();
   2553         mOriginalText = text;
   2554         // Replace any ClickableSpans in mText with placeholders
   2555         if (text instanceof Spanned) {
   2556             ClickableSpan[] spans =
   2557                     ((Spanned) text).getSpans(0, text.length(), ClickableSpan.class);
   2558             if (spans.length > 0) {
   2559                 Spannable spannable = new SpannableStringBuilder(text);
   2560                 for (int i = 0; i < spans.length; i++) {
   2561                     ClickableSpan span = spans[i];
   2562                     if ((span instanceof AccessibilityClickableSpan)
   2563                             || (span instanceof AccessibilityURLSpan)) {
   2564                         // We've already done enough
   2565                         break;
   2566                     }
   2567                     int spanToReplaceStart = spannable.getSpanStart(span);
   2568                     int spanToReplaceEnd = spannable.getSpanEnd(span);
   2569                     int spanToReplaceFlags = spannable.getSpanFlags(span);
   2570                     spannable.removeSpan(span);
   2571                     ClickableSpan replacementSpan = (span instanceof URLSpan)
   2572                             ? new AccessibilityURLSpan((URLSpan) span)
   2573                             : new AccessibilityClickableSpan(span.getId());
   2574                     spannable.setSpan(replacementSpan, spanToReplaceStart, spanToReplaceEnd,
   2575                             spanToReplaceFlags);
   2576                 }
   2577                 mText = spannable;
   2578                 return;
   2579             }
   2580         }
   2581         mText = (text == null) ? null : text.subSequence(0, text.length());
   2582     }
   2583 
   2584     /**
   2585      * Gets the hint text of this node. Only applies to nodes where text can be entered.
   2586      *
   2587      * @return The hint text.
   2588      */
   2589     public CharSequence getHintText() {
   2590         return mHintText;
   2591     }
   2592 
   2593     /**
   2594      * Sets the hint text of this node. Only applies to nodes where text can be entered.
   2595      * <p>
   2596      *   <strong>Note:</strong> Cannot be called from an
   2597      *   {@link android.accessibilityservice.AccessibilityService}.
   2598      *   This class is made immutable before being delivered to an AccessibilityService.
   2599      * </p>
   2600      *
   2601      * @param hintText The hint text for this mode.
   2602      *
   2603      * @throws IllegalStateException If called from an AccessibilityService.
   2604      */
   2605     public void setHintText(CharSequence hintText) {
   2606         enforceNotSealed();
   2607         mHintText = (hintText == null) ? null : hintText.subSequence(0, hintText.length());
   2608     }
   2609 
   2610     /**
   2611      * Sets the error text of this node.
   2612      * <p>
   2613      *   <strong>Note:</strong> Cannot be called from an
   2614      *   {@link android.accessibilityservice.AccessibilityService}.
   2615      *   This class is made immutable before being delivered to an AccessibilityService.
   2616      * </p>
   2617      *
   2618      * @param error The error text.
   2619      *
   2620      * @throws IllegalStateException If called from an AccessibilityService.
   2621      */
   2622     public void setError(CharSequence error) {
   2623         enforceNotSealed();
   2624         mError = (error == null) ? null : error.subSequence(0, error.length());
   2625     }
   2626 
   2627     /**
   2628      * Gets the error text of this node.
   2629      *
   2630      * @return The error text.
   2631      */
   2632     public CharSequence getError() {
   2633         return mError;
   2634     }
   2635 
   2636     /**
   2637      * Gets the content description of this node.
   2638      *
   2639      * @return The content description.
   2640      */
   2641     public CharSequence getContentDescription() {
   2642         return mContentDescription;
   2643     }
   2644 
   2645     /**
   2646      * Sets the content description of this node.
   2647      * <p>
   2648      *   <strong>Note:</strong> Cannot be called from an
   2649      *   {@link android.accessibilityservice.AccessibilityService}.
   2650      *   This class is made immutable before being delivered to an AccessibilityService.
   2651      * </p>
   2652      *
   2653      * @param contentDescription The content description.
   2654      *
   2655      * @throws IllegalStateException If called from an AccessibilityService.
   2656      */
   2657     public void setContentDescription(CharSequence contentDescription) {
   2658         enforceNotSealed();
   2659         mContentDescription = (contentDescription == null) ? null
   2660                 : contentDescription.subSequence(0, contentDescription.length());
   2661     }
   2662 
   2663     /**
   2664      * Gets the tooltip text of this node.
   2665      *
   2666      * @return The tooltip text.
   2667      */
   2668     @Nullable
   2669     public CharSequence getTooltipText() {
   2670         return mTooltipText;
   2671     }
   2672 
   2673     /**
   2674      * Sets the tooltip text of this node.
   2675      * <p>
   2676      *   <strong>Note:</strong> Cannot be called from an
   2677      *   {@link android.accessibilityservice.AccessibilityService}.
   2678      *   This class is made immutable before being delivered to an AccessibilityService.
   2679      * </p>
   2680      *
   2681      * @param tooltipText The tooltip text.
   2682      *
   2683      * @throws IllegalStateException If called from an AccessibilityService.
   2684      */
   2685     public void setTooltipText(@Nullable CharSequence tooltipText) {
   2686         enforceNotSealed();
   2687         mTooltipText = (tooltipText == null) ? null
   2688                 : tooltipText.subSequence(0, tooltipText.length());
   2689     }
   2690 
   2691     /**
   2692      * Sets the view for which the view represented by this info serves as a
   2693      * label for accessibility purposes.
   2694      *
   2695      * @param labeled The view for which this info serves as a label.
   2696      */
   2697     public void setLabelFor(View labeled) {
   2698         setLabelFor(labeled, AccessibilityNodeProvider.HOST_VIEW_ID);
   2699     }
   2700 
   2701     /**
   2702      * Sets the view for which the view represented by this info serves as a
   2703      * label for accessibility purposes. If <code>virtualDescendantId</code>
   2704      * is {@link View#NO_ID} the root is set as the labeled.
   2705      * <p>
   2706      * A virtual descendant is an imaginary View that is reported as a part of the view
   2707      * hierarchy for accessibility purposes. This enables custom views that draw complex
   2708      * content to report themselves as a tree of virtual views, thus conveying their
   2709      * logical structure.
   2710      * </p>
   2711      * <p>
   2712      *   <strong>Note:</strong> Cannot be called from an
   2713      *   {@link android.accessibilityservice.AccessibilityService}.
   2714      *   This class is made immutable before being delivered to an AccessibilityService.
   2715      * </p>
   2716      *
   2717      * @param root The root whose virtual descendant serves as a label.
   2718      * @param virtualDescendantId The id of the virtual descendant.
   2719      */
   2720     public void setLabelFor(View root, int virtualDescendantId) {
   2721         enforceNotSealed();
   2722         final int rootAccessibilityViewId = (root != null)
   2723                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   2724         mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   2725     }
   2726 
   2727     /**
   2728      * Gets the node info for which the view represented by this info serves as
   2729      * a label for accessibility purposes.
   2730      * <p>
   2731      *   <strong>Note:</strong> It is a client responsibility to recycle the
   2732      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   2733      *     to avoid creating of multiple instances.
   2734      * </p>
   2735      *
   2736      * @return The labeled info.
   2737      */
   2738     public AccessibilityNodeInfo getLabelFor() {
   2739         enforceSealed();
   2740         return getNodeForAccessibilityId(mLabelForId);
   2741     }
   2742 
   2743     /**
   2744      * Sets the view which serves as the label of the view represented by
   2745      * this info for accessibility purposes.
   2746      *
   2747      * @param label The view that labels this node's source.
   2748      */
   2749     public void setLabeledBy(View label) {
   2750         setLabeledBy(label, AccessibilityNodeProvider.HOST_VIEW_ID);
   2751     }
   2752 
   2753     /**
   2754      * Sets the view which serves as the label of the view represented by
   2755      * this info for accessibility purposes. If <code>virtualDescendantId</code>
   2756      * is {@link View#NO_ID} the root is set as the label.
   2757      * <p>
   2758      * A virtual descendant is an imaginary View that is reported as a part of the view
   2759      * hierarchy for accessibility purposes. This enables custom views that draw complex
   2760      * content to report themselves as a tree of virtual views, thus conveying their
   2761      * logical structure.
   2762      * </p>
   2763      * <p>
   2764      *   <strong>Note:</strong> Cannot be called from an
   2765      *   {@link android.accessibilityservice.AccessibilityService}.
   2766      *   This class is made immutable before being delivered to an AccessibilityService.
   2767      * </p>
   2768      *
   2769      * @param root The root whose virtual descendant labels this node's source.
   2770      * @param virtualDescendantId The id of the virtual descendant.
   2771      */
   2772     public void setLabeledBy(View root, int virtualDescendantId) {
   2773         enforceNotSealed();
   2774         final int rootAccessibilityViewId = (root != null)
   2775                 ? root.getAccessibilityViewId() : UNDEFINED_ITEM_ID;
   2776         mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
   2777     }
   2778 
   2779     /**
   2780      * Gets the node info which serves as the label of the view represented by
   2781      * this info for accessibility purposes.
   2782      * <p>
   2783      *   <strong>Note:</strong> It is a client responsibility to recycle the
   2784      *     received info by calling {@link AccessibilityNodeInfo#recycle()}
   2785      *     to avoid creating of multiple instances.
   2786      * </p>
   2787      *
   2788      * @return The label.
   2789      */
   2790     public AccessibilityNodeInfo getLabeledBy() {
   2791         enforceSealed();
   2792         return getNodeForAccessibilityId(mLabeledById);
   2793     }
   2794 
   2795     /**
   2796      * Sets the fully qualified resource name of the source view's id.
   2797      *
   2798      * <p>
   2799      *   <strong>Note:</strong> Cannot be called from an
   2800      *   {@link android.accessibilityservice.AccessibilityService}.
   2801      *   This class is made immutable before being delivered to an AccessibilityService.
   2802      * </p>
   2803      *
   2804      * @param viewIdResName The id resource name.
   2805      */
   2806     public void setViewIdResourceName(String viewIdResName) {
   2807         enforceNotSealed();
   2808         mViewIdResourceName = viewIdResName;
   2809     }
   2810 
   2811     /**
   2812      * Gets the fully qualified resource name of the source view's id.
   2813      *
   2814      * <p>
   2815      *   <strong>Note:</strong> The primary usage of this API is for UI test automation
   2816      *   and in order to report the source view id of an {@link AccessibilityNodeInfo} the
   2817      *   client has to set the {@link AccessibilityServiceInfo#FLAG_REPORT_VIEW_IDS}
   2818      *   flag when configuring his {@link android.accessibilityservice.AccessibilityService}.
   2819      * </p>
   2820 
   2821      * @return The id resource name.
   2822      */
   2823     public String getViewIdResourceName() {
   2824         return mViewIdResourceName;
   2825     }
   2826 
   2827     /**
   2828      * Gets the text selection start or the cursor position.
   2829      * <p>
   2830      * If no text is selected, both this method and
   2831      * {@link AccessibilityNodeInfo#getTextSelectionEnd()} return the same value:
   2832      * the current location of the cursor.
   2833      * </p>
   2834      *
   2835      * @return The text selection start, the cursor location if there is no selection, or -1 if
   2836      *         there is no text selection and no cursor.
   2837      */
   2838     public int getTextSelectionStart() {
   2839         return mTextSelectionStart;
   2840     }
   2841 
   2842     /**
   2843      * Gets the text selection end if text is selected.
   2844      * <p>
   2845      * If no text is selected, both this method and
   2846      * {@link AccessibilityNodeInfo#getTextSelectionStart()} return the same value:
   2847      * the current location of the cursor.
   2848      * </p>
   2849      *
   2850      * @return The text selection end, the cursor location if there is no selection, or -1 if
   2851      *         there is no text selection and no cursor.
   2852      */
   2853     public int getTextSelectionEnd() {
   2854         return mTextSelectionEnd;
   2855     }
   2856 
   2857     /**
   2858      * Sets the text selection start and end.
   2859      * <p>
   2860      *   <strong>Note:</strong> Cannot be called from an
   2861      *   {@link android.accessibilityservice.AccessibilityService}.
   2862      *   This class is made immutable before being delivered to an AccessibilityService.
   2863      * </p>
   2864      *
   2865      * @param start The text selection start.
   2866      * @param end The text selection end.
   2867      *
   2868      * @throws IllegalStateException If called from an AccessibilityService.
   2869      */
   2870     public void setTextSelection(int start, int end) {
   2871         enforceNotSealed();
   2872         mTextSelectionStart = start;
   2873         mTextSelectionEnd = end;
   2874     }
   2875 
   2876     /**
   2877      * Gets the input type of the source as defined by {@link InputType}.
   2878      *
   2879      * @return The input type.
   2880      */
   2881     public int getInputType() {
   2882         return mInputType;
   2883     }
   2884 
   2885     /**
   2886      * Sets the input type of the source as defined by {@link InputType}.
   2887      * <p>
   2888      *   <strong>Note:</strong> Cannot be called from an
   2889      *   {@link android.accessibilityservice.AccessibilityService}.
   2890      *   This class is made immutable before being delivered to an
   2891      *   AccessibilityService.
   2892      * </p>
   2893      *
   2894      * @param inputType The input type.
   2895      *
   2896      * @throws IllegalStateException If called from an AccessibilityService.
   2897      */
   2898     public void setInputType(int inputType) {
   2899         enforceNotSealed();
   2900         mInputType = inputType;
   2901     }
   2902 
   2903     /**
   2904      * Gets an optional bundle with extra data. The bundle
   2905      * is lazily created and never <code>null</code>.
   2906      * <p>
   2907      * <strong>Note:</strong> It is recommended to use the package
   2908      * name of your application as a prefix for the keys to avoid
   2909      * collisions which may confuse an accessibility service if the
   2910      * same key has different meaning when emitted from different
   2911      * applications.
   2912      * </p>
   2913      *
   2914      * @return The bundle.
   2915      */
   2916     public Bundle getExtras() {
   2917         if (mExtras == null) {
   2918             mExtras = new Bundle();
   2919         }
   2920         return mExtras;
   2921     }
   2922 
   2923     /**
   2924      * Check if a node has an extras bundle
   2925      * @hide
   2926      */
   2927     public boolean hasExtras() {
   2928         return mExtras != null;
   2929     }
   2930 
   2931     /**
   2932      * Gets the value of a boolean property.
   2933      *
   2934      * @param property The property.
   2935      * @return The value.
   2936      */
   2937     private boolean getBooleanProperty(int property) {
   2938         return (mBooleanProperties & property) != 0;
   2939     }
   2940 
   2941     /**
   2942      * Sets a boolean property.
   2943      *
   2944      * @param property The property.
   2945      * @param value The value.
   2946      *
   2947      * @throws IllegalStateException If called from an AccessibilityService.
   2948      */
   2949     private void setBooleanProperty(int property, boolean value) {
   2950         enforceNotSealed();
   2951         if (value) {
   2952             mBooleanProperties |= property;
   2953         } else {
   2954             mBooleanProperties &= ~property;
   2955         }
   2956     }
   2957 
   2958     /**
   2959      * Sets the unique id of the IAccessibilityServiceConnection over which
   2960      * this instance can send requests to the system.
   2961      *
   2962      * @param connectionId The connection id.
   2963      *
   2964      * @hide
   2965      */
   2966     public void setConnectionId(int connectionId) {
   2967         enforceNotSealed();
   2968         mConnectionId = connectionId;
   2969     }
   2970 
   2971     /**
   2972      * Get the connection ID.
   2973      *
   2974      * @return The connection id
   2975      *
   2976      * @hide
   2977      */
   2978     public int getConnectionId() {
   2979         return mConnectionId;
   2980     }
   2981 
   2982     /**
   2983      * {@inheritDoc}
   2984      */
   2985     @Override
   2986     public int describeContents() {
   2987         return 0;
   2988     }
   2989 
   2990     /**
   2991      * Sets the id of the source node.
   2992      *
   2993      * @param sourceId The id.
   2994      * @param windowId The window id.
   2995      *
   2996      * @hide
   2997      */
   2998     public void setSourceNodeId(long sourceId, int windowId) {
   2999         enforceNotSealed();
   3000         mSourceNodeId = sourceId;
   3001         mWindowId = windowId;
   3002     }
   3003 
   3004     /**
   3005      * Gets the id of the source node.
   3006      *
   3007      * @return The id.
   3008      *
   3009      * @hide
   3010      */
   3011     public long getSourceNodeId() {
   3012         return mSourceNodeId;
   3013     }
   3014 
   3015     /**
   3016      * Sets if this instance is sealed.
   3017      *
   3018      * @param sealed Whether is sealed.
   3019      *
   3020      * @hide
   3021      */
   3022     public void setSealed(boolean sealed) {
   3023         mSealed = sealed;
   3024     }
   3025 
   3026     /**
   3027      * Gets if this instance is sealed.
   3028      *
   3029      * @return Whether is sealed.
   3030      *
   3031      * @hide
   3032      */
   3033     public boolean isSealed() {
   3034         return mSealed;
   3035     }
   3036 
   3037     /**
   3038      * Enforces that this instance is sealed.
   3039      *
   3040      * @throws IllegalStateException If this instance is not sealed.
   3041      *
   3042      * @hide
   3043      */
   3044     protected void enforceSealed() {
   3045         if (!isSealed()) {
   3046             throw new IllegalStateException("Cannot perform this "
   3047                     + "action on a not sealed instance.");
   3048         }
   3049     }
   3050 
   3051     private void enforceValidFocusDirection(int direction) {
   3052         switch (direction) {
   3053             case View.FOCUS_DOWN:
   3054             case View.FOCUS_UP:
   3055             case View.FOCUS_LEFT:
   3056             case View.FOCUS_RIGHT:
   3057             case View.FOCUS_FORWARD:
   3058             case View.FOCUS_BACKWARD:
   3059                 return;
   3060             default:
   3061                 throw new IllegalArgumentException("Unknown direction: " + direction);
   3062         }
   3063     }
   3064 
   3065     private void enforceValidFocusType(int focusType) {
   3066         switch (focusType) {
   3067             case FOCUS_INPUT:
   3068             case FOCUS_ACCESSIBILITY:
   3069                 return;
   3070             default:
   3071                 throw new IllegalArgumentException("Unknown focus type: " + focusType);
   3072         }
   3073     }
   3074 
   3075     /**
   3076      * Enforces that this instance is not sealed.
   3077      *
   3078      * @throws IllegalStateException If this instance is sealed.
   3079      *
   3080      * @hide
   3081      */
   3082     protected void enforceNotSealed() {
   3083         if (isSealed()) {
   3084             throw new IllegalStateException("Cannot perform this "
   3085                     + "action on a sealed instance.");
   3086         }
   3087     }
   3088 
   3089     /**
   3090      * Returns a cached instance if such is available otherwise a new one
   3091      * and sets the source.
   3092      *
   3093      * @param source The source view.
   3094      * @return An instance.
   3095      *
   3096      * @see #setSource(View)
   3097      */
   3098     public static AccessibilityNodeInfo obtain(View source) {
   3099         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   3100         info.setSource(source);
   3101         return info;
   3102     }
   3103 
   3104     /**
   3105      * Returns a cached instance if such is available otherwise a new one
   3106      * and sets the source.
   3107      *
   3108      * @param root The root of the virtual subtree.
   3109      * @param virtualDescendantId The id of the virtual descendant.
   3110      * @return An instance.
   3111      *
   3112      * @see #setSource(View, int)
   3113      */
   3114     public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
   3115         AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   3116         info.setSource(root, virtualDescendantId);
   3117         return info;
   3118     }
   3119 
   3120     /**
   3121      * Returns a cached instance if such is available otherwise a new one.
   3122      *
   3123      * @return An instance.
   3124      */
   3125     public static AccessibilityNodeInfo obtain() {
   3126         AccessibilityNodeInfo info = sPool.acquire();
   3127         if (sNumInstancesInUse != null) {
   3128             sNumInstancesInUse.incrementAndGet();
   3129         }
   3130         return (info != null) ? info : new AccessibilityNodeInfo();
   3131     }
   3132 
   3133     /**
   3134      * Returns a cached instance if such is available or a new one is
   3135      * create. The returned instance is initialized from the given
   3136      * <code>info</code>.
   3137      *
   3138      * @param info The other info.
   3139      * @return An instance.
   3140      */
   3141     public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
   3142         AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
   3143         infoClone.init(info);
   3144         return infoClone;
   3145     }
   3146 
   3147     /**
   3148      * Return an instance back to be reused.
   3149      * <p>
   3150      * <strong>Note:</strong> You must not touch the object after calling this function.
   3151      *
   3152      * @throws IllegalStateException If the info is already recycled.
   3153      */
   3154     public void recycle() {
   3155         clear();
   3156         sPool.release(this);
   3157         if (sNumInstancesInUse != null) {
   3158             sNumInstancesInUse.decrementAndGet();
   3159         }
   3160     }
   3161 
   3162     /**
   3163      * Specify a counter that will be incremented on obtain() and decremented on recycle()
   3164      *
   3165      * @hide
   3166      */
   3167     @TestApi
   3168     public static void setNumInstancesInUseCounter(AtomicInteger counter) {
   3169         sNumInstancesInUse = counter;
   3170     }
   3171 
   3172     /**
   3173      * {@inheritDoc}
   3174      * <p>
   3175      *   <strong>Note:</strong> After the instance is written to a parcel it
   3176      *      is recycled. You must not touch the object after calling this function.
   3177      * </p>
   3178      */
   3179     @Override
   3180     public void writeToParcel(Parcel parcel, int flags) {
   3181         writeToParcelNoRecycle(parcel, flags);
   3182         // Since instances of this class are fetched via synchronous i.e. blocking
   3183         // calls in IPCs we always recycle as soon as the instance is marshaled.
   3184         recycle();
   3185     }
   3186 
   3187     /** @hide */
   3188     @TestApi
   3189     public void writeToParcelNoRecycle(Parcel parcel, int flags) {
   3190         // Write bit set of indices of fields with values differing from default
   3191         long nonDefaultFields = 0;
   3192         int fieldIndex = 0; // index of the current field
   3193         if (isSealed() != DEFAULT.isSealed()) nonDefaultFields |= bitAt(fieldIndex);
   3194         fieldIndex++;
   3195         if (mSourceNodeId != DEFAULT.mSourceNodeId) nonDefaultFields |= bitAt(fieldIndex);
   3196         fieldIndex++;
   3197         if (mWindowId != DEFAULT.mWindowId) nonDefaultFields |= bitAt(fieldIndex);
   3198         fieldIndex++;
   3199         if (mParentNodeId != DEFAULT.mParentNodeId) nonDefaultFields |= bitAt(fieldIndex);
   3200         fieldIndex++;
   3201         if (mLabelForId != DEFAULT.mLabelForId) nonDefaultFields |= bitAt(fieldIndex);
   3202         fieldIndex++;
   3203         if (mLabeledById != DEFAULT.mLabeledById) nonDefaultFields |= bitAt(fieldIndex);
   3204         fieldIndex++;
   3205         if (mTraversalBefore != DEFAULT.mTraversalBefore) nonDefaultFields |= bitAt(fieldIndex);
   3206         fieldIndex++;
   3207         if (mTraversalAfter != DEFAULT.mTraversalAfter) nonDefaultFields |= bitAt(fieldIndex);
   3208         fieldIndex++;
   3209         if (mConnectionId != DEFAULT.mConnectionId) nonDefaultFields |= bitAt(fieldIndex);
   3210         fieldIndex++;
   3211         if (!LongArray.elementsEqual(mChildNodeIds, DEFAULT.mChildNodeIds)) {
   3212             nonDefaultFields |= bitAt(fieldIndex);
   3213         }
   3214         fieldIndex++;
   3215         if (!Objects.equals(mBoundsInParent, DEFAULT.mBoundsInParent)) {
   3216             nonDefaultFields |= bitAt(fieldIndex);
   3217         }
   3218         fieldIndex++;
   3219         if (!Objects.equals(mBoundsInScreen, DEFAULT.mBoundsInScreen)) {
   3220             nonDefaultFields |= bitAt(fieldIndex);
   3221         }
   3222         fieldIndex++;
   3223         if (!Objects.equals(mActions, DEFAULT.mActions)) nonDefaultFields |= bitAt(fieldIndex);
   3224         fieldIndex++;
   3225         if (mMaxTextLength != DEFAULT.mMaxTextLength) nonDefaultFields |= bitAt(fieldIndex);
   3226         fieldIndex++;
   3227         if (mMovementGranularities != DEFAULT.mMovementGranularities) {
   3228             nonDefaultFields |= bitAt(fieldIndex);
   3229         }
   3230         fieldIndex++;
   3231         if (mBooleanProperties != DEFAULT.mBooleanProperties) nonDefaultFields |= bitAt(fieldIndex);
   3232         fieldIndex++;
   3233         if (!Objects.equals(mPackageName, DEFAULT.mPackageName)) {
   3234             nonDefaultFields |= bitAt(fieldIndex);
   3235         }
   3236         fieldIndex++;
   3237         if (!Objects.equals(mClassName, DEFAULT.mClassName)) nonDefaultFields |= bitAt(fieldIndex);
   3238         fieldIndex++;
   3239         if (!Objects.equals(mText, DEFAULT.mText)) nonDefaultFields |= bitAt(fieldIndex);
   3240         fieldIndex++;
   3241         if (!Objects.equals(mHintText, DEFAULT.mHintText)) {
   3242             nonDefaultFields |= bitAt(fieldIndex);
   3243         }
   3244         fieldIndex++;
   3245         if (!Objects.equals(mError, DEFAULT.mError)) nonDefaultFields |= bitAt(fieldIndex);
   3246         fieldIndex++;
   3247         if (!Objects.equals(mContentDescription, DEFAULT.mContentDescription)) {
   3248             nonDefaultFields |= bitAt(fieldIndex);
   3249         }
   3250         fieldIndex++;
   3251         if (!Objects.equals(mPaneTitle, DEFAULT.mPaneTitle)) {
   3252             nonDefaultFields |= bitAt(fieldIndex);
   3253         }
   3254         fieldIndex++;
   3255         if (!Objects.equals(mTooltipText, DEFAULT.mTooltipText)) {
   3256             nonDefaultFields |= bitAt(fieldIndex);
   3257         }
   3258         fieldIndex++;
   3259         if (!Objects.equals(mViewIdResourceName, DEFAULT.mViewIdResourceName)) {
   3260             nonDefaultFields |= bitAt(fieldIndex);
   3261         }
   3262         fieldIndex++;
   3263         if (mTextSelectionStart != DEFAULT.mTextSelectionStart) {
   3264             nonDefaultFields |= bitAt(fieldIndex);
   3265         }
   3266         fieldIndex++;
   3267         if (mTextSelectionEnd != DEFAULT.mTextSelectionEnd) {
   3268             nonDefaultFields |= bitAt(fieldIndex);
   3269         }
   3270         fieldIndex++;
   3271         if (mInputType != DEFAULT.mInputType) nonDefaultFields |= bitAt(fieldIndex);
   3272         fieldIndex++;
   3273         if (mLiveRegion != DEFAULT.mLiveRegion) nonDefaultFields |= bitAt(fieldIndex);
   3274         fieldIndex++;
   3275         if (mDrawingOrderInParent != DEFAULT.mDrawingOrderInParent) {
   3276             nonDefaultFields |= bitAt(fieldIndex);
   3277         }
   3278         fieldIndex++;
   3279         if (!Objects.equals(mExtraDataKeys, DEFAULT.mExtraDataKeys)) {
   3280             nonDefaultFields |= bitAt(fieldIndex);
   3281         }
   3282         fieldIndex++;
   3283         if (!Objects.equals(mExtras, DEFAULT.mExtras)) nonDefaultFields |= bitAt(fieldIndex);
   3284         fieldIndex++;
   3285         if (!Objects.equals(mRangeInfo, DEFAULT.mRangeInfo)) nonDefaultFields |= bitAt(fieldIndex);
   3286         fieldIndex++;
   3287         if (!Objects.equals(mCollectionInfo, DEFAULT.mCollectionInfo)) {
   3288             nonDefaultFields |= bitAt(fieldIndex);
   3289         }
   3290         fieldIndex++;
   3291         if (!Objects.equals(mCollectionItemInfo, DEFAULT.mCollectionItemInfo)) {
   3292             nonDefaultFields |= bitAt(fieldIndex);
   3293         }
   3294         int totalFields = fieldIndex;
   3295         parcel.writeLong(nonDefaultFields);
   3296 
   3297         fieldIndex = 0;
   3298         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(isSealed() ? 1 : 0);
   3299         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mSourceNodeId);
   3300         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mWindowId);
   3301         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mParentNodeId);
   3302         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabelForId);
   3303         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mLabeledById);
   3304         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalBefore);
   3305         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeLong(mTraversalAfter);
   3306 
   3307         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mConnectionId);
   3308 
   3309         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3310             final LongArray childIds = mChildNodeIds;
   3311             if (childIds == null) {
   3312                 parcel.writeInt(0);
   3313             } else {
   3314                 final int childIdsSize = childIds.size();
   3315                 parcel.writeInt(childIdsSize);
   3316                 for (int i = 0; i < childIdsSize; i++) {
   3317                     parcel.writeLong(childIds.get(i));
   3318                 }
   3319             }
   3320         }
   3321 
   3322         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3323             parcel.writeInt(mBoundsInParent.top);
   3324             parcel.writeInt(mBoundsInParent.bottom);
   3325             parcel.writeInt(mBoundsInParent.left);
   3326             parcel.writeInt(mBoundsInParent.right);
   3327         }
   3328 
   3329         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3330             parcel.writeInt(mBoundsInScreen.top);
   3331             parcel.writeInt(mBoundsInScreen.bottom);
   3332             parcel.writeInt(mBoundsInScreen.left);
   3333             parcel.writeInt(mBoundsInScreen.right);
   3334         }
   3335 
   3336         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3337             if (mActions != null && !mActions.isEmpty()) {
   3338                 final int actionCount = mActions.size();
   3339 
   3340                 int nonStandardActionCount = 0;
   3341                 long defaultStandardActions = 0;
   3342                 for (int i = 0; i < actionCount; i++) {
   3343                     AccessibilityAction action = mActions.get(i);
   3344                     if (isDefaultStandardAction(action)) {
   3345                         defaultStandardActions |= action.mSerializationFlag;
   3346                     } else {
   3347                         nonStandardActionCount++;
   3348                     }
   3349                 }
   3350                 parcel.writeLong(defaultStandardActions);
   3351 
   3352                 parcel.writeInt(nonStandardActionCount);
   3353                 for (int i = 0; i < actionCount; i++) {
   3354                     AccessibilityAction action = mActions.get(i);
   3355                     if (!isDefaultStandardAction(action)) {
   3356                         parcel.writeInt(action.getId());
   3357                         parcel.writeCharSequence(action.getLabel());
   3358                     }
   3359                 }
   3360             } else {
   3361                 parcel.writeLong(0);
   3362                 parcel.writeInt(0);
   3363             }
   3364         }
   3365 
   3366         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mMaxTextLength);
   3367         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mMovementGranularities);
   3368         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mBooleanProperties);
   3369 
   3370         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPackageName);
   3371         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mClassName);
   3372         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mText);
   3373         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mHintText);
   3374         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mError);
   3375         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3376             parcel.writeCharSequence(mContentDescription);
   3377         }
   3378         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mPaneTitle);
   3379         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeCharSequence(mTooltipText);
   3380 
   3381         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeString(mViewIdResourceName);
   3382 
   3383         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionStart);
   3384         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mTextSelectionEnd);
   3385         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mInputType);
   3386         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mLiveRegion);
   3387         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeInt(mDrawingOrderInParent);
   3388 
   3389         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeStringList(mExtraDataKeys);
   3390 
   3391         if (isBitSet(nonDefaultFields, fieldIndex++)) parcel.writeBundle(mExtras);
   3392 
   3393         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3394             parcel.writeInt(mRangeInfo.getType());
   3395             parcel.writeFloat(mRangeInfo.getMin());
   3396             parcel.writeFloat(mRangeInfo.getMax());
   3397             parcel.writeFloat(mRangeInfo.getCurrent());
   3398         }
   3399 
   3400         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3401             parcel.writeInt(mCollectionInfo.getRowCount());
   3402             parcel.writeInt(mCollectionInfo.getColumnCount());
   3403             parcel.writeInt(mCollectionInfo.isHierarchical() ? 1 : 0);
   3404             parcel.writeInt(mCollectionInfo.getSelectionMode());
   3405         }
   3406 
   3407         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3408             parcel.writeInt(mCollectionItemInfo.getRowIndex());
   3409             parcel.writeInt(mCollectionItemInfo.getRowSpan());
   3410             parcel.writeInt(mCollectionItemInfo.getColumnIndex());
   3411             parcel.writeInt(mCollectionItemInfo.getColumnSpan());
   3412             parcel.writeInt(mCollectionItemInfo.isHeading() ? 1 : 0);
   3413             parcel.writeInt(mCollectionItemInfo.isSelected() ? 1 : 0);
   3414         }
   3415 
   3416         if (DEBUG) {
   3417             fieldIndex--;
   3418             if (totalFields != fieldIndex) {
   3419                 throw new IllegalStateException("Number of fields mismatch: " + totalFields
   3420                         + " vs " + fieldIndex);
   3421             }
   3422         }
   3423     }
   3424 
   3425     /**
   3426      * Initializes this instance from another one.
   3427      *
   3428      * @param other The other instance.
   3429      */
   3430     private void init(AccessibilityNodeInfo other) {
   3431         mSealed = other.mSealed;
   3432         mSourceNodeId = other.mSourceNodeId;
   3433         mParentNodeId = other.mParentNodeId;
   3434         mLabelForId = other.mLabelForId;
   3435         mLabeledById = other.mLabeledById;
   3436         mTraversalBefore = other.mTraversalBefore;
   3437         mTraversalAfter = other.mTraversalAfter;
   3438         mWindowId = other.mWindowId;
   3439         mConnectionId = other.mConnectionId;
   3440         mBoundsInParent.set(other.mBoundsInParent);
   3441         mBoundsInScreen.set(other.mBoundsInScreen);
   3442         mPackageName = other.mPackageName;
   3443         mClassName = other.mClassName;
   3444         mText = other.mText;
   3445         mOriginalText = other.mOriginalText;
   3446         mHintText = other.mHintText;
   3447         mError = other.mError;
   3448         mContentDescription = other.mContentDescription;
   3449         mPaneTitle = other.mPaneTitle;
   3450         mTooltipText = other.mTooltipText;
   3451         mViewIdResourceName = other.mViewIdResourceName;
   3452 
   3453         if (mActions != null) mActions.clear();
   3454         final ArrayList<AccessibilityAction> otherActions = other.mActions;
   3455         if (otherActions != null && otherActions.size() > 0) {
   3456             if (mActions == null) {
   3457                 mActions = new ArrayList(otherActions);
   3458             } else {
   3459                 mActions.addAll(other.mActions);
   3460             }
   3461         }
   3462 
   3463         mBooleanProperties = other.mBooleanProperties;
   3464         mMaxTextLength = other.mMaxTextLength;
   3465         mMovementGranularities = other.mMovementGranularities;
   3466 
   3467 
   3468         if (mChildNodeIds != null) mChildNodeIds.clear();
   3469         final LongArray otherChildNodeIds = other.mChildNodeIds;
   3470         if (otherChildNodeIds != null && otherChildNodeIds.size() > 0) {
   3471             if (mChildNodeIds == null) {
   3472                 mChildNodeIds = otherChildNodeIds.clone();
   3473             } else {
   3474                 mChildNodeIds.addAll(otherChildNodeIds);
   3475             }
   3476         }
   3477 
   3478         mTextSelectionStart = other.mTextSelectionStart;
   3479         mTextSelectionEnd = other.mTextSelectionEnd;
   3480         mInputType = other.mInputType;
   3481         mLiveRegion = other.mLiveRegion;
   3482         mDrawingOrderInParent = other.mDrawingOrderInParent;
   3483 
   3484         mExtraDataKeys = other.mExtraDataKeys;
   3485 
   3486         mExtras = other.mExtras != null ? new Bundle(other.mExtras) : null;
   3487 
   3488         if (mRangeInfo != null) mRangeInfo.recycle();
   3489         mRangeInfo = (other.mRangeInfo != null)
   3490                 ? RangeInfo.obtain(other.mRangeInfo) : null;
   3491         if (mCollectionInfo != null) mCollectionInfo.recycle();
   3492         mCollectionInfo = (other.mCollectionInfo != null)
   3493                 ? CollectionInfo.obtain(other.mCollectionInfo) : null;
   3494         if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
   3495         mCollectionItemInfo =  (other.mCollectionItemInfo != null)
   3496                 ? CollectionItemInfo.obtain(other.mCollectionItemInfo) : null;
   3497     }
   3498 
   3499     /**
   3500      * Creates a new instance from a {@link Parcel}.
   3501      *
   3502      * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
   3503      */
   3504     private void initFromParcel(Parcel parcel) {
   3505         // Bit mask of non-default-valued field indices
   3506         long nonDefaultFields = parcel.readLong();
   3507         int fieldIndex = 0;
   3508         final boolean sealed = isBitSet(nonDefaultFields, fieldIndex++)
   3509                 ? (parcel.readInt() == 1)
   3510                 : DEFAULT.mSealed;
   3511         if (isBitSet(nonDefaultFields, fieldIndex++)) mSourceNodeId = parcel.readLong();
   3512         if (isBitSet(nonDefaultFields, fieldIndex++)) mWindowId = parcel.readInt();
   3513         if (isBitSet(nonDefaultFields, fieldIndex++)) mParentNodeId = parcel.readLong();
   3514         if (isBitSet(nonDefaultFields, fieldIndex++)) mLabelForId = parcel.readLong();
   3515         if (isBitSet(nonDefaultFields, fieldIndex++)) mLabeledById = parcel.readLong();
   3516         if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalBefore = parcel.readLong();
   3517         if (isBitSet(nonDefaultFields, fieldIndex++)) mTraversalAfter = parcel.readLong();
   3518 
   3519         if (isBitSet(nonDefaultFields, fieldIndex++)) mConnectionId = parcel.readInt();
   3520 
   3521         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3522             final int childrenSize = parcel.readInt();
   3523             if (childrenSize <= 0) {
   3524                 mChildNodeIds = null;
   3525             } else {
   3526                 mChildNodeIds = new LongArray(childrenSize);
   3527                 for (int i = 0; i < childrenSize; i++) {
   3528                     final long childId = parcel.readLong();
   3529                     mChildNodeIds.add(childId);
   3530                 }
   3531             }
   3532         }
   3533 
   3534         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3535             mBoundsInParent.top = parcel.readInt();
   3536             mBoundsInParent.bottom = parcel.readInt();
   3537             mBoundsInParent.left = parcel.readInt();
   3538             mBoundsInParent.right = parcel.readInt();
   3539         }
   3540 
   3541         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3542             mBoundsInScreen.top = parcel.readInt();
   3543             mBoundsInScreen.bottom = parcel.readInt();
   3544             mBoundsInScreen.left = parcel.readInt();
   3545             mBoundsInScreen.right = parcel.readInt();
   3546         }
   3547 
   3548         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3549             final long standardActions = parcel.readLong();
   3550             addStandardActions(standardActions);
   3551             final int nonStandardActionCount = parcel.readInt();
   3552             for (int i = 0; i < nonStandardActionCount; i++) {
   3553                 final AccessibilityAction action = new AccessibilityAction(
   3554                         parcel.readInt(), parcel.readCharSequence());
   3555                 addActionUnchecked(action);
   3556             }
   3557         }
   3558 
   3559         if (isBitSet(nonDefaultFields, fieldIndex++)) mMaxTextLength = parcel.readInt();
   3560         if (isBitSet(nonDefaultFields, fieldIndex++)) mMovementGranularities = parcel.readInt();
   3561         if (isBitSet(nonDefaultFields, fieldIndex++)) mBooleanProperties = parcel.readInt();
   3562 
   3563         if (isBitSet(nonDefaultFields, fieldIndex++)) mPackageName = parcel.readCharSequence();
   3564         if (isBitSet(nonDefaultFields, fieldIndex++)) mClassName = parcel.readCharSequence();
   3565         if (isBitSet(nonDefaultFields, fieldIndex++)) mText = parcel.readCharSequence();
   3566         if (isBitSet(nonDefaultFields, fieldIndex++)) mHintText = parcel.readCharSequence();
   3567         if (isBitSet(nonDefaultFields, fieldIndex++)) mError = parcel.readCharSequence();
   3568         if (isBitSet(nonDefaultFields, fieldIndex++)) {
   3569             mContentDescription = parcel.readCharSequence();
   3570         }
   3571         if (isBitSet(nonDefaultFields, fieldIndex++)) mPaneTitle = parcel.readCharSequence();
   3572         if (isBitSet(nonDefaultFields, fieldIndex++)) mTooltipText = parcel.readCharSequence();
   3573         if (isBitSet(nonDefaultFields, fieldIndex++)) mViewIdResourceName = parcel.readString();
   3574 
   3575         if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionStart = parcel.readInt();
   3576         if (isBitSet(nonDefaultFields, fieldIndex++)) mTextSelectionEnd = parcel.readInt();
   3577 
   3578         if (isBitSet(nonDefaultFields, fieldIndex++)) mInputType = parcel.readInt();
   3579         if (isBitSet(nonDefaultFields, fieldIndex++)) mLiveRegion = parcel.readInt();
   3580         if (isBitSet(nonDefaultFields, fieldIndex++)) mDrawingOrderInParent = parcel.readInt();
   3581 
   3582         mExtraDataKeys = isBitSet(nonDefaultFields, fieldIndex++)
   3583                 ? parcel.createStringArrayList()
   3584                 : null;
   3585 
   3586         mExtras = isBitSet(nonDefaultFields, fieldIndex++)
   3587                 ? parcel.readBundle()
   3588                 : null;
   3589 
   3590         if (mRangeInfo != null) mRangeInfo.recycle();
   3591         mRangeInfo = isBitSet(nonDefaultFields, fieldIndex++)
   3592                 ? RangeInfo.obtain(
   3593                         parcel.readInt(),
   3594                         parcel.readFloat(),
   3595                         parcel.readFloat(),
   3596                         parcel.readFloat())
   3597                 : null;
   3598 
   3599         if (mCollectionInfo != null) mCollectionInfo.recycle();
   3600         mCollectionInfo = isBitSet(nonDefaultFields, fieldIndex++)
   3601                 ? CollectionInfo.obtain(
   3602                         parcel.readInt(),
   3603                         parcel.readInt(),
   3604                         parcel.readInt() == 1,
   3605                         parcel.readInt())
   3606                 : null;
   3607 
   3608         if (mCollectionItemInfo != null) mCollectionItemInfo.recycle();
   3609         mCollectionItemInfo = isBitSet(nonDefaultFields, fieldIndex++)
   3610                 ? CollectionItemInfo.obtain(
   3611                         parcel.readInt(),
   3612                         parcel.readInt(),
   3613                         parcel.readInt(),
   3614                         parcel.readInt(),
   3615                         parcel.readInt() == 1,
   3616                         parcel.readInt() == 1)
   3617                 : null;
   3618 
   3619         mSealed = sealed;
   3620     }
   3621 
   3622     /**
   3623      * Clears the state of this instance.
   3624      */
   3625     private void clear() {
   3626         init(DEFAULT);
   3627     }
   3628 
   3629     private static boolean isDefaultStandardAction(AccessibilityAction action) {
   3630         return (action.mSerializationFlag != -1L) && TextUtils.isEmpty(action.getLabel());
   3631     }
   3632 
   3633     private static AccessibilityAction getActionSingleton(int actionId) {
   3634         final int actions = AccessibilityAction.sStandardActions.size();
   3635         for (int i = 0; i < actions; i++) {
   3636             AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i);
   3637             if (actionId == currentAction.getId()) {
   3638                 return currentAction;
   3639             }
   3640         }
   3641 
   3642         return null;
   3643     }
   3644 
   3645     private static AccessibilityAction getActionSingletonBySerializationFlag(long flag) {
   3646         final int actions = AccessibilityAction.sStandardActions.size();
   3647         for (int i = 0; i < actions; i++) {
   3648             AccessibilityAction currentAction = AccessibilityAction.sStandardActions.valueAt(i);
   3649             if (flag == currentAction.mSerializationFlag) {
   3650                 return currentAction;
   3651             }
   3652         }
   3653 
   3654         return null;
   3655     }
   3656 
   3657     private void addStandardActions(long serializationIdMask) {
   3658         long remainingIds = serializationIdMask;
   3659         while (remainingIds > 0) {
   3660             final long id = 1L << Long.numberOfTrailingZeros(remainingIds);
   3661             remainingIds &= ~id;
   3662             AccessibilityAction action = getActionSingletonBySerializationFlag(id);
   3663             addAction(action);
   3664         }
   3665     }
   3666 
   3667     /**
   3668      * Gets the human readable action symbolic name.
   3669      *
   3670      * @param action The action.
   3671      * @return The symbolic name.
   3672      */
   3673     private static String getActionSymbolicName(int action) {
   3674         switch (action) {
   3675             case ACTION_FOCUS:
   3676                 return "ACTION_FOCUS";
   3677             case ACTION_CLEAR_FOCUS:
   3678                 return "ACTION_CLEAR_FOCUS";
   3679             case ACTION_SELECT:
   3680                 return "ACTION_SELECT";
   3681             case ACTION_CLEAR_SELECTION:
   3682                 return "ACTION_CLEAR_SELECTION";
   3683             case ACTION_CLICK:
   3684                 return "ACTION_CLICK";
   3685             case ACTION_LONG_CLICK:
   3686                 return "ACTION_LONG_CLICK";
   3687             case ACTION_ACCESSIBILITY_FOCUS:
   3688                 return "ACTION_ACCESSIBILITY_FOCUS";
   3689             case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
   3690                 return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
   3691             case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
   3692                 return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
   3693             case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
   3694                 return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
   3695             case ACTION_NEXT_HTML_ELEMENT:
   3696                 return "ACTION_NEXT_HTML_ELEMENT";
   3697             case ACTION_PREVIOUS_HTML_ELEMENT:
   3698                 return "ACTION_PREVIOUS_HTML_ELEMENT";
   3699             case ACTION_SCROLL_FORWARD:
   3700                 return "ACTION_SCROLL_FORWARD";
   3701             case ACTION_SCROLL_BACKWARD:
   3702                 return "ACTION_SCROLL_BACKWARD";
   3703             case ACTION_CUT:
   3704                 return "ACTION_CUT";
   3705             case ACTION_COPY:
   3706                 return "ACTION_COPY";
   3707             case ACTION_PASTE:
   3708                 return "ACTION_PASTE";
   3709             case ACTION_SET_SELECTION:
   3710                 return "ACTION_SET_SELECTION";
   3711             case ACTION_EXPAND:
   3712                 return "ACTION_EXPAND";
   3713             case ACTION_COLLAPSE:
   3714                 return "ACTION_COLLAPSE";
   3715             case ACTION_DISMISS:
   3716                 return "ACTION_DISMISS";
   3717             case ACTION_SET_TEXT:
   3718                 return "ACTION_SET_TEXT";
   3719             case R.id.accessibilityActionShowOnScreen:
   3720                 return "ACTION_SHOW_ON_SCREEN";
   3721             case R.id.accessibilityActionScrollToPosition:
   3722                 return "ACTION_SCROLL_TO_POSITION";
   3723             case R.id.accessibilityActionScrollUp:
   3724                 return "ACTION_SCROLL_UP";
   3725             case R.id.accessibilityActionScrollLeft:
   3726                 return "ACTION_SCROLL_LEFT";
   3727             case R.id.accessibilityActionScrollDown:
   3728                 return "ACTION_SCROLL_DOWN";
   3729             case R.id.accessibilityActionScrollRight:
   3730                 return "ACTION_SCROLL_RIGHT";
   3731             case R.id.accessibilityActionSetProgress:
   3732                 return "ACTION_SET_PROGRESS";
   3733             case R.id.accessibilityActionContextClick:
   3734                 return "ACTION_CONTEXT_CLICK";
   3735             case R.id.accessibilityActionShowTooltip:
   3736                 return "ACTION_SHOW_TOOLTIP";
   3737             case R.id.accessibilityActionHideTooltip:
   3738                 return "ACTION_HIDE_TOOLTIP";
   3739             default:
   3740                 return "ACTION_UNKNOWN";
   3741         }
   3742     }
   3743 
   3744     /**
   3745      * Gets the human readable movement granularity symbolic name.
   3746      *
   3747      * @param granularity The granularity.
   3748      * @return The symbolic name.
   3749      */
   3750     private static String getMovementGranularitySymbolicName(int granularity) {
   3751         switch (granularity) {
   3752             case MOVEMENT_GRANULARITY_CHARACTER:
   3753                 return "MOVEMENT_GRANULARITY_CHARACTER";
   3754             case MOVEMENT_GRANULARITY_WORD:
   3755                 return "MOVEMENT_GRANULARITY_WORD";
   3756             case MOVEMENT_GRANULARITY_LINE:
   3757                 return "MOVEMENT_GRANULARITY_LINE";
   3758             case MOVEMENT_GRANULARITY_PARAGRAPH:
   3759                 return "MOVEMENT_GRANULARITY_PARAGRAPH";
   3760             case MOVEMENT_GRANULARITY_PAGE:
   3761                 return "MOVEMENT_GRANULARITY_PAGE";
   3762             default:
   3763                 throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
   3764         }
   3765     }
   3766 
   3767     private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
   3768         return ((mWindowId != AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
   3769                 && (getAccessibilityViewId(accessibilityNodeId) != UNDEFINED_ITEM_ID)
   3770                 && (mConnectionId != UNDEFINED_CONNECTION_ID));
   3771     }
   3772 
   3773     @Override
   3774     public boolean equals(Object object) {
   3775         if (this == object) {
   3776             return true;
   3777         }
   3778         if (object == null) {
   3779             return false;
   3780         }
   3781         if (getClass() != object.getClass()) {
   3782             return false;
   3783         }
   3784         AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
   3785         if (mSourceNodeId != other.mSourceNodeId) {
   3786             return false;
   3787         }
   3788         if (mWindowId != other.mWindowId) {
   3789             return false;
   3790         }
   3791         return true;
   3792     }
   3793 
   3794     @Override
   3795     public int hashCode() {
   3796         final int prime = 31;
   3797         int result = 1;
   3798         result = prime * result + getAccessibilityViewId(mSourceNodeId);
   3799         result = prime * result + getVirtualDescendantId(mSourceNodeId);
   3800         result = prime * result + mWindowId;
   3801         return result;
   3802     }
   3803 
   3804     @Override
   3805     public String toString() {
   3806         StringBuilder builder = new StringBuilder();
   3807         builder.append(super.toString());
   3808 
   3809         if (DEBUG) {
   3810             builder.append("; sourceNodeId: " + mSourceNodeId);
   3811             builder.append("; windowId: " + mWindowId);
   3812             builder.append("; accessibilityViewId: ").append(getAccessibilityViewId(mSourceNodeId));
   3813             builder.append("; virtualDescendantId: ").append(getVirtualDescendantId(mSourceNodeId));
   3814             builder.append("; mParentNodeId: " + mParentNodeId);
   3815             builder.append("; traversalBefore: ").append(mTraversalBefore);
   3816             builder.append("; traversalAfter: ").append(mTraversalAfter);
   3817 
   3818             int granularities = mMovementGranularities;
   3819             builder.append("; MovementGranularities: [");
   3820             while (granularities != 0) {
   3821                 final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
   3822                 granularities &= ~granularity;
   3823                 builder.append(getMovementGranularitySymbolicName(granularity));
   3824                 if (granularities != 0) {
   3825                     builder.append(", ");
   3826                 }
   3827             }
   3828             builder.append("]");
   3829 
   3830             builder.append("; childAccessibilityIds: [");
   3831             final LongArray childIds = mChildNodeIds;
   3832             if (childIds != null) {
   3833                 for (int i = 0, count = childIds.size(); i < count; i++) {
   3834                     builder.append(childIds.get(i));
   3835                     if (i < count - 1) {
   3836                         builder.append(", ");
   3837                     }
   3838                 }
   3839             }
   3840             builder.append("]");
   3841         }
   3842 
   3843         builder.append("; boundsInParent: ").append(mBoundsInParent);
   3844         builder.append("; boundsInScreen: ").append(mBoundsInScreen);
   3845 
   3846         builder.append("; packageName: ").append(mPackageName);
   3847         builder.append("; className: ").append(mClassName);
   3848         builder.append("; text: ").append(mText);
   3849         builder.append("; error: ").append(mError);
   3850         builder.append("; maxTextLength: ").append(mMaxTextLength);
   3851         builder.append("; contentDescription: ").append(mContentDescription);
   3852         builder.append("; tooltipText: ").append(mTooltipText);
   3853         builder.append("; viewIdResName: ").append(mViewIdResourceName);
   3854 
   3855         builder.append("; checkable: ").append(isCheckable());
   3856         builder.append("; checked: ").append(isChecked());
   3857         builder.append("; focusable: ").append(isFocusable());
   3858         builder.append("; focused: ").append(isFocused());
   3859         builder.append("; selected: ").append(isSelected());
   3860         builder.append("; clickable: ").append(isClickable());
   3861         builder.append("; longClickable: ").append(isLongClickable());
   3862         builder.append("; contextClickable: ").append(isContextClickable());
   3863         builder.append("; enabled: ").append(isEnabled());
   3864         builder.append("; password: ").append(isPassword());
   3865         builder.append("; scrollable: ").append(isScrollable());
   3866         builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
   3867         builder.append("; visible: ").append(isVisibleToUser());
   3868         builder.append("; actions: ").append(mActions);
   3869 
   3870         return builder.toString();
   3871     }
   3872 
   3873     private AccessibilityNodeInfo getNodeForAccessibilityId(long accessibilityId) {
   3874         if (!canPerformRequestOverConnection(accessibilityId)) {
   3875             return null;
   3876         }
   3877         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
   3878         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
   3879                 mWindowId, accessibilityId, false, FLAG_PREFETCH_PREDECESSORS
   3880                         | FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS, null);
   3881     }
   3882 
   3883     /** @hide */
   3884     public static String idToString(long accessibilityId) {
   3885         int accessibilityViewId = getAccessibilityViewId(accessibilityId);
   3886         int virtualDescendantId = getVirtualDescendantId(accessibilityId);
   3887         return virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID
   3888                 ? idItemToString(accessibilityViewId)
   3889                 : idItemToString(accessibilityViewId) + ":" + idItemToString(virtualDescendantId);
   3890     }
   3891 
   3892     private static String idItemToString(int item) {
   3893         switch (item) {
   3894             case ROOT_ITEM_ID: return "ROOT";
   3895             case UNDEFINED_ITEM_ID: return "UNDEFINED";
   3896             case AccessibilityNodeProvider.HOST_VIEW_ID: return "HOST";
   3897             default: return "" + item;
   3898         }
   3899     }
   3900 
   3901     /**
   3902      * A class defining an action that can be performed on an {@link AccessibilityNodeInfo}.
   3903      * Each action has a unique id that is mandatory and optional data.
   3904      * <p>
   3905      * There are three categories of actions:
   3906      * <ul>
   3907      * <li><strong>Standard actions</strong> - These are actions that are reported and
   3908      * handled by the standard UI widgets in the platform. For each standard action
   3909      * there is a static constant defined in this class, e.g. {@link #ACTION_FOCUS}.
   3910      * These actions will have {@code null} labels.
   3911      * </li>
   3912      * <li><strong>Custom actions action</strong> - These are actions that are reported
   3913      * and handled by custom widgets. i.e. ones that are not part of the UI toolkit. For
   3914      * example, an application may define a custom action for clearing the user history.
   3915      * </li>
   3916      * <li><strong>Overriden standard actions</strong> - These are actions that override
   3917      * standard actions to customize them. For example, an app may add a label to the
   3918      * standard {@link #ACTION_CLICK} action to announce that this action clears browsing history.
   3919      * </ul>
   3920      * </p>
   3921      * <p>
   3922      * Actions are typically added to an {@link AccessibilityNodeInfo} by using
   3923      * {@link AccessibilityNodeInfo#addAction(AccessibilityAction)} within
   3924      * {@link View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)} and are performed
   3925      * within {@link View#performAccessibilityAction(int, Bundle)}.
   3926      * </p>
   3927      * <p class="note">
   3928      * <strong>Note:</strong> Views which support these actions should invoke
   3929      * {@link View#setImportantForAccessibility(int)} with
   3930      * {@link View#IMPORTANT_FOR_ACCESSIBILITY_YES} to ensure an {@link AccessibilityService}
   3931      * can discover the set of supported actions.
   3932      * </p>
   3933      */
   3934     public static final class AccessibilityAction {
   3935 
   3936         /** @hide */
   3937         public static final ArraySet<AccessibilityAction> sStandardActions = new ArraySet<>();
   3938 
   3939         /**
   3940          * Action that gives input focus to the node.
   3941          */
   3942         public static final AccessibilityAction ACTION_FOCUS =
   3943                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_FOCUS);
   3944 
   3945         /**
   3946          * Action that clears input focus of the node.
   3947          */
   3948         public static final AccessibilityAction ACTION_CLEAR_FOCUS =
   3949                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_FOCUS);
   3950 
   3951         /**
   3952          *  Action that selects the node.
   3953          */
   3954         public static final AccessibilityAction ACTION_SELECT =
   3955                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_SELECT);
   3956 
   3957         /**
   3958          * Action that deselects the node.
   3959          */
   3960         public static final AccessibilityAction ACTION_CLEAR_SELECTION =
   3961                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_SELECTION);
   3962 
   3963         /**
   3964          * Action that clicks on the node info.
   3965          */
   3966         public static final AccessibilityAction ACTION_CLICK =
   3967                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLICK);
   3968 
   3969         /**
   3970          * Action that long clicks on the node.
   3971          */
   3972         public static final AccessibilityAction ACTION_LONG_CLICK =
   3973                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_LONG_CLICK);
   3974 
   3975         /**
   3976          * Action that gives accessibility focus to the node.
   3977          */
   3978         public static final AccessibilityAction ACTION_ACCESSIBILITY_FOCUS =
   3979                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
   3980 
   3981         /**
   3982          * Action that clears accessibility focus of the node.
   3983          */
   3984         public static final AccessibilityAction ACTION_CLEAR_ACCESSIBILITY_FOCUS =
   3985                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
   3986 
   3987         /**
   3988          * Action that requests to go to the next entity in this node's text
   3989          * at a given movement granularity. For example, move to the next character,
   3990          * word, etc.
   3991          * <p>
   3992          * <strong>Arguments:</strong>
   3993          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   3994          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
   3995          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   3996          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
   3997          * <strong>Example:</strong> Move to the previous character and do not extend selection.
   3998          * <code><pre><p>
   3999          *   Bundle arguments = new Bundle();
   4000          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
   4001          *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
   4002          *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
   4003          *           false);
   4004          *   info.performAction(AccessibilityAction.ACTION_NEXT_AT_MOVEMENT_GRANULARITY.getId(),
   4005          *           arguments);
   4006          * </code></pre></p>
   4007          * </p>
   4008          *
   4009          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   4010          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   4011          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   4012          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   4013          *
   4014          * @see AccessibilityNodeInfo#setMovementGranularities(int)
   4015          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   4016          * @see AccessibilityNodeInfo#getMovementGranularities()
   4017          *  AccessibilityNodeInfo.getMovementGranularities()
   4018          *
   4019          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER
   4020          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
   4021          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD
   4022          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
   4023          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE
   4024          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
   4025          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH
   4026          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
   4027          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE
   4028          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE
   4029          */
   4030         public static final AccessibilityAction ACTION_NEXT_AT_MOVEMENT_GRANULARITY =
   4031                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY);
   4032 
   4033         /**
   4034          * Action that requests to go to the previous entity in this node's text
   4035          * at a given movement granularity. For example, move to the next character,
   4036          * word, etc.
   4037          * <p>
   4038          * <strong>Arguments:</strong>
   4039          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   4040          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT},
   4041          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   4042          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN}<br>
   4043          * <strong>Example:</strong> Move to the next character and do not extend selection.
   4044          * <code><pre><p>
   4045          *   Bundle arguments = new Bundle();
   4046          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
   4047          *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
   4048          *   arguments.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN,
   4049          *           false);
   4050          *   info.performAction(AccessibilityAction.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY.getId(),
   4051          *           arguments);
   4052          * </code></pre></p>
   4053          * </p>
   4054          *
   4055          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   4056          *  AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT
   4057          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   4058          *  AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN
   4059          *
   4060          * @see AccessibilityNodeInfo#setMovementGranularities(int)
   4061          *   AccessibilityNodeInfo.setMovementGranularities(int)
   4062          * @see AccessibilityNodeInfo#getMovementGranularities()
   4063          *  AccessibilityNodeInfo.getMovementGranularities()
   4064          *
   4065          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_CHARACTER
   4066          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER
   4067          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_WORD
   4068          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD
   4069          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_LINE
   4070          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE
   4071          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PARAGRAPH
   4072          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PARAGRAPH
   4073          * @see AccessibilityNodeInfo#MOVEMENT_GRANULARITY_PAGE
   4074          *  AccessibilityNodeInfo.MOVEMENT_GRANULARITY_PAGE
   4075          */
   4076         public static final AccessibilityAction ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY =
   4077                 new AccessibilityAction(
   4078                         AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY);
   4079 
   4080         /**
   4081          * Action to move to the next HTML element of a given type. For example, move
   4082          * to the BUTTON, INPUT, TABLE, etc.
   4083          * <p>
   4084          * <strong>Arguments:</strong>
   4085          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
   4086          *  AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
   4087          * <strong>Example:</strong>
   4088          * <code><pre><p>
   4089          *   Bundle arguments = new Bundle();
   4090          *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
   4091          *   info.performAction(AccessibilityAction.ACTION_NEXT_HTML_ELEMENT.getId(), arguments);
   4092          * </code></pre></p>
   4093          * </p>
   4094          */
   4095         public static final AccessibilityAction ACTION_NEXT_HTML_ELEMENT =
   4096                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT);
   4097 
   4098         /**
   4099          * Action to move to the previous HTML element of a given type. For example, move
   4100          * to the BUTTON, INPUT, TABLE, etc.
   4101          * <p>
   4102          * <strong>Arguments:</strong>
   4103          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_HTML_ELEMENT_STRING
   4104          *  AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
   4105          * <strong>Example:</strong>
   4106          * <code><pre><p>
   4107          *   Bundle arguments = new Bundle();
   4108          *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
   4109          *   info.performAction(AccessibilityAction.ACTION_PREVIOUS_HTML_ELEMENT.getId(), arguments);
   4110          * </code></pre></p>
   4111          * </p>
   4112          */
   4113         public static final AccessibilityAction ACTION_PREVIOUS_HTML_ELEMENT =
   4114                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT);
   4115 
   4116         /**
   4117          * Action to scroll the node content forward.
   4118          */
   4119         public static final AccessibilityAction ACTION_SCROLL_FORWARD =
   4120                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
   4121 
   4122         /**
   4123          * Action to scroll the node content backward.
   4124          */
   4125         public static final AccessibilityAction ACTION_SCROLL_BACKWARD =
   4126                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
   4127 
   4128         /**
   4129          * Action to copy the current selection to the clipboard.
   4130          */
   4131         public static final AccessibilityAction ACTION_COPY =
   4132                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_COPY);
   4133 
   4134         /**
   4135          * Action to paste the current clipboard content.
   4136          */
   4137         public static final AccessibilityAction ACTION_PASTE =
   4138                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_PASTE);
   4139 
   4140         /**
   4141          * Action to cut the current selection and place it to the clipboard.
   4142          */
   4143         public static final AccessibilityAction ACTION_CUT =
   4144                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_CUT);
   4145 
   4146         /**
   4147          * Action to set the selection. Performing this action with no arguments
   4148          * clears the selection.
   4149          * <p>
   4150          * <strong>Arguments:</strong>
   4151          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT
   4152          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT},
   4153          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT
   4154          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT}<br>
   4155          * <strong>Example:</strong>
   4156          * <code><pre><p>
   4157          *   Bundle arguments = new Bundle();
   4158          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, 1);
   4159          *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, 2);
   4160          *   info.performAction(AccessibilityAction.ACTION_SET_SELECTION.getId(), arguments);
   4161          * </code></pre></p>
   4162          * </p>
   4163          *
   4164          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_START_INT
   4165          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT
   4166          * @see AccessibilityNodeInfo#ACTION_ARGUMENT_SELECTION_END_INT
   4167          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT
   4168          */
   4169         public static final AccessibilityAction ACTION_SET_SELECTION =
   4170                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_SET_SELECTION);
   4171 
   4172         /**
   4173          * Action to expand an expandable node.
   4174          */
   4175         public static final AccessibilityAction ACTION_EXPAND =
   4176                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_EXPAND);
   4177 
   4178         /**
   4179          * Action to collapse an expandable node.
   4180          */
   4181         public static final AccessibilityAction ACTION_COLLAPSE =
   4182                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_COLLAPSE);
   4183 
   4184         /**
   4185          * Action to dismiss a dismissable node.
   4186          */
   4187         public static final AccessibilityAction ACTION_DISMISS =
   4188                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_DISMISS);
   4189 
   4190         /**
   4191          * Action that sets the text of the node. Performing the action without argument,
   4192          * using <code> null</code> or empty {@link CharSequence} will clear the text. This
   4193          * action will also put the cursor at the end of text.
   4194          * <p>
   4195          * <strong>Arguments:</strong>
   4196          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE
   4197          *  AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE}<br>
   4198          * <strong>Example:</strong>
   4199          * <code><pre><p>
   4200          *   Bundle arguments = new Bundle();
   4201          *   arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE,
   4202          *       "android");
   4203          *   info.performAction(AccessibilityAction.ACTION_SET_TEXT.getId(), arguments);
   4204          * </code></pre></p>
   4205          */
   4206         public static final AccessibilityAction ACTION_SET_TEXT =
   4207                 new AccessibilityAction(AccessibilityNodeInfo.ACTION_SET_TEXT);
   4208 
   4209         /**
   4210          * Action that requests the node make its bounding rectangle visible
   4211          * on the screen, scrolling if necessary just enough.
   4212          *
   4213          * @see View#requestRectangleOnScreen(Rect)
   4214          */
   4215         public static final AccessibilityAction ACTION_SHOW_ON_SCREEN =
   4216                 new AccessibilityAction(R.id.accessibilityActionShowOnScreen);
   4217 
   4218         /**
   4219          * Action that scrolls the node to make the specified collection
   4220          * position visible on screen.
   4221          * <p>
   4222          * <strong>Arguments:</strong>
   4223          * <ul>
   4224          *     <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_ROW_INT}</li>
   4225          *     <li>{@link AccessibilityNodeInfo#ACTION_ARGUMENT_COLUMN_INT}</li>
   4226          * <ul>
   4227          *
   4228          * @see AccessibilityNodeInfo#getCollectionInfo()
   4229          */
   4230         public static final AccessibilityAction ACTION_SCROLL_TO_POSITION =
   4231                 new AccessibilityAction(R.id.accessibilityActionScrollToPosition);
   4232 
   4233         /**
   4234          * Action to scroll the node content up.
   4235          */
   4236         public static final AccessibilityAction ACTION_SCROLL_UP =
   4237                 new AccessibilityAction(R.id.accessibilityActionScrollUp);
   4238 
   4239         /**
   4240          * Action to scroll the node content left.
   4241          */
   4242         public static final AccessibilityAction ACTION_SCROLL_LEFT =
   4243                 new AccessibilityAction(R.id.accessibilityActionScrollLeft);
   4244 
   4245         /**
   4246          * Action to scroll the node content down.
   4247          */
   4248         public static final AccessibilityAction ACTION_SCROLL_DOWN =
   4249                 new AccessibilityAction(R.id.accessibilityActionScrollDown);
   4250 
   4251         /**
   4252          * Action to scroll the node content right.
   4253          */
   4254         public static final AccessibilityAction ACTION_SCROLL_RIGHT =
   4255                 new AccessibilityAction(R.id.accessibilityActionScrollRight);
   4256 
   4257         /**
   4258          * Action that context clicks the node.
   4259          */
   4260         public static final AccessibilityAction ACTION_CONTEXT_CLICK =
   4261                 new AccessibilityAction(R.id.accessibilityActionContextClick);
   4262 
   4263         /**
   4264          * Action that sets progress between {@link  RangeInfo#getMin() RangeInfo.getMin()} and
   4265          * {@link  RangeInfo#getMax() RangeInfo.getMax()}. It should use the same value type as
   4266          * {@link RangeInfo#getType() RangeInfo.getType()}
   4267          * <p>
   4268          * <strong>Arguments:</strong>
   4269          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_PROGRESS_VALUE}
   4270          *
   4271          * @see RangeInfo
   4272          */
   4273         public static final AccessibilityAction ACTION_SET_PROGRESS =
   4274                 new AccessibilityAction(R.id.accessibilityActionSetProgress);
   4275 
   4276         /**
   4277          * Action to move a window to a new location.
   4278          * <p>
   4279          * <strong>Arguments:</strong>
   4280          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVE_WINDOW_X}
   4281          * {@link AccessibilityNodeInfo#ACTION_ARGUMENT_MOVE_WINDOW_Y}
   4282          */
   4283         public static final AccessibilityAction ACTION_MOVE_WINDOW =
   4284                 new AccessibilityAction(R.id.accessibilityActionMoveWindow);
   4285 
   4286         /**
   4287          * Action to show a tooltip. A node should expose this action only for views with tooltip
   4288          * text that but are not currently showing a tooltip.
   4289          */
   4290         public static final AccessibilityAction ACTION_SHOW_TOOLTIP =
   4291                 new AccessibilityAction(R.id.accessibilityActionShowTooltip);
   4292 
   4293         /**
   4294          * Action to hide a tooltip. A node should expose this action only for views that are
   4295          * currently showing a tooltip.
   4296          */
   4297         public static final AccessibilityAction ACTION_HIDE_TOOLTIP =
   4298                 new AccessibilityAction(R.id.accessibilityActionHideTooltip);
   4299 
   4300         private final int mActionId;
   4301         private final CharSequence mLabel;
   4302 
   4303         /** @hide */
   4304         public long mSerializationFlag = -1L;
   4305 
   4306         /**
   4307          * Creates a new AccessibilityAction. For adding a standard action without a specific label,
   4308          * use the static constants.
   4309          *
   4310          * You can also override the description for one the standard actions. Below is an example
   4311          * how to override the standard click action by adding a custom label:
   4312          * <pre>
   4313          *   AccessibilityAction action = new AccessibilityAction(
   4314          *           AccessibilityAction.ACTION_CLICK.getId(), getLocalizedLabel());
   4315          *   node.addAction(action);
   4316          * </pre>
   4317          *
   4318          * @param actionId The id for this action. This should either be one of the
   4319          *                 standard actions or a specific action for your app. In that case it is
   4320          *                 required to use a resource identifier.
   4321          * @param label The label for the new AccessibilityAction.
   4322          */
   4323         public AccessibilityAction(int actionId, @Nullable CharSequence label) {
   4324             if ((actionId & ACTION_TYPE_MASK) == 0 && Integer.bitCount(actionId) != 1) {
   4325                 throw new IllegalArgumentException("Invalid standard action id");
   4326             }
   4327 
   4328             mActionId = actionId;
   4329             mLabel = label;
   4330         }
   4331 
   4332         /**
   4333          * Constructor for a {@link #sStandardActions standard} action
   4334          */
   4335         private AccessibilityAction(int standardActionId) {
   4336             this(standardActionId, null);
   4337 
   4338             mSerializationFlag = bitAt(sStandardActions.size());
   4339             sStandardActions.add(this);
   4340         }
   4341 
   4342         /**
   4343          * Gets the id for this action.
   4344          *
   4345          * @return The action id.
   4346          */
   4347         public int getId() {
   4348             return mActionId;
   4349         }
   4350 
   4351         /**
   4352          * Gets the label for this action. Its purpose is to describe the
   4353          * action to user.
   4354          *
   4355          * @return The label.
   4356          */
   4357         public CharSequence getLabel() {
   4358             return mLabel;
   4359         }
   4360 
   4361         @Override
   4362         public int hashCode() {
   4363             return mActionId;
   4364         }
   4365 
   4366         @Override
   4367         public boolean equals(Object other) {
   4368             if (other == null) {
   4369                 return false;
   4370             }
   4371 
   4372             if (other == this) {
   4373                 return true;
   4374             }
   4375 
   4376             if (getClass() != other.getClass()) {
   4377                 return false;
   4378             }
   4379 
   4380             return mActionId == ((AccessibilityAction)other).mActionId;
   4381         }
   4382 
   4383         @Override
   4384         public String toString() {
   4385             return "AccessibilityAction: " + getActionSymbolicName(mActionId) + " - " + mLabel;
   4386         }
   4387     }
   4388 
   4389     /**
   4390      * Class with information if a node is a range. Use
   4391      * {@link RangeInfo#obtain(int, float, float, float)} to get an instance. Recycling is
   4392      * handled by the {@link AccessibilityNodeInfo} to which this object is attached.
   4393      */
   4394     public static final class RangeInfo {
   4395         private static final int MAX_POOL_SIZE = 10;
   4396 
   4397         /** Range type: integer. */
   4398         public static final int RANGE_TYPE_INT = 0;
   4399         /** Range type: float. */
   4400         public static final int RANGE_TYPE_FLOAT = 1;
   4401         /** Range type: percent with values from zero to one.*/
   4402         public static final int RANGE_TYPE_PERCENT = 2;
   4403 
   4404         private static final SynchronizedPool<RangeInfo> sPool =
   4405                 new SynchronizedPool<AccessibilityNodeInfo.RangeInfo>(MAX_POOL_SIZE);
   4406 
   4407         private int mType;
   4408         private float mMin;
   4409         private float mMax;
   4410         private float mCurrent;
   4411 
   4412         /**
   4413          * Obtains a pooled instance that is a clone of another one.
   4414          *
   4415          * @param other The instance to clone.
   4416          *
   4417          * @hide
   4418          */
   4419         public static RangeInfo obtain(RangeInfo other) {
   4420             return obtain(other.mType, other.mMin, other.mMax, other.mCurrent);
   4421         }
   4422 
   4423         /**
   4424          * Obtains a pooled instance.
   4425          *
   4426          * @param type The type of the range.
   4427          * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
   4428          *            minimum.
   4429          * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
   4430          *            maximum.
   4431          * @param current The current value.
   4432          */
   4433         public static RangeInfo obtain(int type, float min, float max, float current) {
   4434             RangeInfo info = sPool.acquire();
   4435             if (info == null) {
   4436                 return new RangeInfo(type, min, max, current);
   4437             }
   4438 
   4439             info.mType = type;
   4440             info.mMin = min;
   4441             info.mMax = max;
   4442             info.mCurrent = current;
   4443             return info;
   4444         }
   4445 
   4446         /**
   4447          * Creates a new range.
   4448          *
   4449          * @param type The type of the range.
   4450          * @param min The minimum value. Use {@code Float.NEGATIVE_INFINITY} if the range has no
   4451          *            minimum.
   4452          * @param max The maximum value. Use {@code Float.POSITIVE_INFINITY} if the range has no
   4453          *            maximum.
   4454          * @param current The current value.
   4455          */
   4456         private RangeInfo(int type, float min, float max, float current) {
   4457             mType = type;
   4458             mMin = min;
   4459             mMax = max;
   4460             mCurrent = current;
   4461         }
   4462 
   4463         /**
   4464          * Gets the range type.
   4465          *
   4466          * @return The range type.
   4467          *
   4468          * @see #RANGE_TYPE_INT
   4469          * @see #RANGE_TYPE_FLOAT
   4470          * @see #RANGE_TYPE_PERCENT
   4471          */
   4472         public int getType() {
   4473             return mType;
   4474         }
   4475 
   4476         /**
   4477          * Gets the minimum value.
   4478          *
   4479          * @return The minimum value, or {@code Float.NEGATIVE_INFINITY} if no minimum exists.
   4480          */
   4481         public float getMin() {
   4482             return mMin;
   4483         }
   4484 
   4485         /**
   4486          * Gets the maximum value.
   4487          *
   4488          * @return The maximum value, or {@code Float.POSITIVE_INFINITY} if no maximum exists.
   4489          */
   4490         public float getMax() {
   4491             return mMax;
   4492         }
   4493 
   4494         /**
   4495          * Gets the current value.
   4496          *
   4497          * @return The current value.
   4498          */
   4499         public float getCurrent() {
   4500             return mCurrent;
   4501         }
   4502 
   4503         /**
   4504          * Recycles this instance.
   4505          */
   4506         void recycle() {
   4507             clear();
   4508             sPool.release(this);
   4509         }
   4510 
   4511         private void clear() {
   4512             mType = 0;
   4513             mMin = 0;
   4514             mMax = 0;
   4515             mCurrent = 0;
   4516         }
   4517     }
   4518 
   4519     /**
   4520      * Class with information if a node is a collection. Use
   4521      * {@link CollectionInfo#obtain(int, int, boolean)} to get an instance. Recycling is
   4522      * handled by the {@link AccessibilityNodeInfo} to which this object is attached.
   4523      * <p>
   4524      * A collection of items has rows and columns and may be hierarchical.
   4525      * For example, a horizontal list is a collection with one column, as
   4526      * many rows as the list items, and is not hierarchical; A table is a
   4527      * collection with several rows, several columns, and is not hierarchical;
   4528      * A vertical tree is a hierarchical collection with one column and
   4529      * as many rows as the first level children.
   4530      * </p>
   4531      */
   4532     public static final class CollectionInfo {
   4533         /** Selection mode where items are not selectable. */
   4534         public static final int SELECTION_MODE_NONE = 0;
   4535 
   4536         /** Selection mode where a single item may be selected. */
   4537         public static final int SELECTION_MODE_SINGLE = 1;
   4538 
   4539         /** Selection mode where multiple items may be selected. */
   4540         public static final int SELECTION_MODE_MULTIPLE = 2;
   4541 
   4542         private static final int MAX_POOL_SIZE = 20;
   4543 
   4544         private static final SynchronizedPool<CollectionInfo> sPool =
   4545                 new SynchronizedPool<>(MAX_POOL_SIZE);
   4546 
   4547         private int mRowCount;
   4548         private int mColumnCount;
   4549         private boolean mHierarchical;
   4550         private int mSelectionMode;
   4551 
   4552         /**
   4553          * Obtains a pooled instance that is a clone of another one.
   4554          *
   4555          * @param other The instance to clone.
   4556          * @hide
   4557          */
   4558         public static CollectionInfo obtain(CollectionInfo other) {
   4559             return CollectionInfo.obtain(other.mRowCount, other.mColumnCount, other.mHierarchical,
   4560                     other.mSelectionMode);
   4561         }
   4562 
   4563         /**
   4564          * Obtains a pooled instance.
   4565          *
   4566          * @param rowCount The number of rows.
   4567          * @param columnCount The number of columns.
   4568          * @param hierarchical Whether the collection is hierarchical.
   4569          */
   4570         public static CollectionInfo obtain(int rowCount, int columnCount,
   4571                 boolean hierarchical) {
   4572             return obtain(rowCount, columnCount, hierarchical, SELECTION_MODE_NONE);
   4573         }
   4574 
   4575         /**
   4576          * Obtains a pooled instance.
   4577          *
   4578          * @param rowCount The number of rows.
   4579          * @param columnCount The number of columns.
   4580          * @param hierarchical Whether the collection is hierarchical.
   4581          * @param selectionMode The collection's selection mode, one of:
   4582          *            <ul>
   4583          *            <li>{@link #SELECTION_MODE_NONE}
   4584          *            <li>{@link #SELECTION_MODE_SINGLE}
   4585          *            <li>{@link #SELECTION_MODE_MULTIPLE}
   4586          *            </ul>
   4587          */
   4588         public static CollectionInfo obtain(int rowCount, int columnCount,
   4589                 boolean hierarchical, int selectionMode) {
   4590            final CollectionInfo info = sPool.acquire();
   4591             if (info == null) {
   4592                 return new CollectionInfo(rowCount, columnCount, hierarchical, selectionMode);
   4593             }
   4594 
   4595             info.mRowCount = rowCount;
   4596             info.mColumnCount = columnCount;
   4597             info.mHierarchical = hierarchical;
   4598             info.mSelectionMode = selectionMode;
   4599             return info;
   4600         }
   4601 
   4602         /**
   4603          * Creates a new instance.
   4604          *
   4605          * @param rowCount The number of rows.
   4606          * @param columnCount The number of columns.
   4607          * @param hierarchical Whether the collection is hierarchical.
   4608          * @param selectionMode The collection's selection mode.
   4609          */
   4610         private CollectionInfo(int rowCount, int columnCount, boolean hierarchical,
   4611                 int selectionMode) {
   4612             mRowCount = rowCount;
   4613             mColumnCount = columnCount;
   4614             mHierarchical = hierarchical;
   4615             mSelectionMode = selectionMode;
   4616         }
   4617 
   4618         /**
   4619          * Gets the number of rows.
   4620          *
   4621          * @return The row count.
   4622          */
   4623         public int getRowCount() {
   4624             return mRowCount;
   4625         }
   4626 
   4627         /**
   4628          * Gets the number of columns.
   4629          *
   4630          * @return The column count.
   4631          */
   4632         public int getColumnCount() {
   4633             return mColumnCount;
   4634         }
   4635 
   4636         /**
   4637          * Gets if the collection is a hierarchically ordered.
   4638          *
   4639          * @return Whether the collection is hierarchical.
   4640          */
   4641         public boolean isHierarchical() {
   4642             return mHierarchical;
   4643         }
   4644 
   4645         /**
   4646          * Gets the collection's selection mode.
   4647          *
   4648          * @return The collection's selection mode, one of:
   4649          *         <ul>
   4650          *         <li>{@link #SELECTION_MODE_NONE}
   4651          *         <li>{@link #SELECTION_MODE_SINGLE}
   4652          *         <li>{@link #SELECTION_MODE_MULTIPLE}
   4653          *         </ul>
   4654          */
   4655         public int getSelectionMode() {
   4656             return mSelectionMode;
   4657         }
   4658 
   4659         /**
   4660          * Recycles this instance.
   4661          */
   4662         void recycle() {
   4663             clear();
   4664             sPool.release(this);
   4665         }
   4666 
   4667         private void clear() {
   4668             mRowCount = 0;
   4669             mColumnCount = 0;
   4670             mHierarchical = false;
   4671             mSelectionMode = SELECTION_MODE_NONE;
   4672         }
   4673     }
   4674 
   4675     /**
   4676      * Class with information if a node is a collection item. Use
   4677      * {@link CollectionItemInfo#obtain(int, int, int, int, boolean)}
   4678      * to get an instance. Recycling is handled by the {@link AccessibilityNodeInfo} to which this
   4679      * object is attached.
   4680      * <p>
   4681      * A collection item is contained in a collection, it starts at
   4682      * a given row and column in the collection, and spans one or
   4683      * more rows and columns. For example, a header of two related
   4684      * table columns starts at the first row and the first column,
   4685      * spans one row and two columns.
   4686      * </p>
   4687      */
   4688     public static final class CollectionItemInfo {
   4689         private static final int MAX_POOL_SIZE = 20;
   4690 
   4691         private static final SynchronizedPool<CollectionItemInfo> sPool =
   4692                 new SynchronizedPool<>(MAX_POOL_SIZE);
   4693 
   4694         /**
   4695          * Obtains a pooled instance that is a clone of another one.
   4696          *
   4697          * @param other The instance to clone.
   4698          * @hide
   4699          */
   4700         public static CollectionItemInfo obtain(CollectionItemInfo other) {
   4701             return CollectionItemInfo.obtain(other.mRowIndex, other.mRowSpan, other.mColumnIndex,
   4702                     other.mColumnSpan, other.mHeading, other.mSelected);
   4703         }
   4704 
   4705         /**
   4706          * Obtains a pooled instance.
   4707          *
   4708          * @param rowIndex The row index at which the item is located.
   4709          * @param rowSpan The number of rows the item spans.
   4710          * @param columnIndex The column index at which the item is located.
   4711          * @param columnSpan The number of columns the item spans.
   4712          * @param heading Whether the item is a heading. (Prefer
   4713          *                {@link AccessibilityNodeInfo#setHeading(boolean)}).
   4714          */
   4715         public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
   4716                 int columnIndex, int columnSpan, boolean heading) {
   4717             return obtain(rowIndex, rowSpan, columnIndex, columnSpan, heading, false);
   4718         }
   4719 
   4720         /**
   4721          * Obtains a pooled instance.
   4722          *
   4723          * @param rowIndex The row index at which the item is located.
   4724          * @param rowSpan The number of rows the item spans.
   4725          * @param columnIndex The column index at which the item is located.
   4726          * @param columnSpan The number of columns the item spans.
   4727          * @param heading Whether the item is a heading. (Prefer
   4728          *                {@link AccessibilityNodeInfo#setHeading(boolean)})
   4729          * @param selected Whether the item is selected.
   4730          */
   4731         public static CollectionItemInfo obtain(int rowIndex, int rowSpan,
   4732                 int columnIndex, int columnSpan, boolean heading, boolean selected) {
   4733             final CollectionItemInfo info = sPool.acquire();
   4734             if (info == null) {
   4735                 return new CollectionItemInfo(
   4736                         rowIndex, rowSpan, columnIndex, columnSpan, heading, selected);
   4737             }
   4738 
   4739             info.mRowIndex = rowIndex;
   4740             info.mRowSpan = rowSpan;
   4741             info.mColumnIndex = columnIndex;
   4742             info.mColumnSpan = columnSpan;
   4743             info.mHeading = heading;
   4744             info.mSelected = selected;
   4745             return info;
   4746         }
   4747 
   4748         private boolean mHeading;
   4749         private int mColumnIndex;
   4750         private int mRowIndex;
   4751         private int mColumnSpan;
   4752         private int mRowSpan;
   4753         private boolean mSelected;
   4754 
   4755         /**
   4756          * Creates a new instance.
   4757          *
   4758          * @param rowIndex The row index at which the item is located.
   4759          * @param rowSpan The number of rows the item spans.
   4760          * @param columnIndex The column index at which the item is located.
   4761          * @param columnSpan The number of columns the item spans.
   4762          * @param heading Whether the item is a heading.
   4763          */
   4764         private CollectionItemInfo(int rowIndex, int rowSpan, int columnIndex, int columnSpan,
   4765                 boolean heading, boolean selected) {
   4766             mRowIndex = rowIndex;
   4767             mRowSpan = rowSpan;
   4768             mColumnIndex = columnIndex;
   4769             mColumnSpan = columnSpan;
   4770             mHeading = heading;
   4771             mSelected = selected;
   4772         }
   4773 
   4774         /**
   4775          * Gets the column index at which the item is located.
   4776          *
   4777          * @return The column index.
   4778          */
   4779         public int getColumnIndex() {
   4780             return mColumnIndex;
   4781         }
   4782 
   4783         /**
   4784          * Gets the row index at which the item is located.
   4785          *
   4786          * @return The row index.
   4787          */
   4788         public int getRowIndex() {
   4789             return mRowIndex;
   4790         }
   4791 
   4792         /**
   4793          * Gets the number of columns the item spans.
   4794          *
   4795          * @return The column span.
   4796          */
   4797         public int getColumnSpan() {
   4798             return mColumnSpan;
   4799         }
   4800 
   4801         /**
   4802          * Gets the number of rows the item spans.
   4803          *
   4804          * @return The row span.
   4805          */
   4806         public int getRowSpan() {
   4807             return mRowSpan;
   4808         }
   4809 
   4810         /**
   4811          * Gets if the collection item is a heading. For example, section
   4812          * heading, table header, etc.
   4813          *
   4814          * @return If the item is a heading.
   4815          * @deprecated Use {@link AccessibilityNodeInfo#isHeading()}
   4816          */
   4817         public boolean isHeading() {
   4818             return mHeading;
   4819         }
   4820 
   4821         /**
   4822          * Gets if the collection item is selected.
   4823          *
   4824          * @return If the item is selected.
   4825          */
   4826         public boolean isSelected() {
   4827             return mSelected;
   4828         }
   4829 
   4830         /**
   4831          * Recycles this instance.
   4832          */
   4833         void recycle() {
   4834             clear();
   4835             sPool.release(this);
   4836         }
   4837 
   4838         private void clear() {
   4839             mColumnIndex = 0;
   4840             mColumnSpan = 0;
   4841             mRowIndex = 0;
   4842             mRowSpan = 0;
   4843             mHeading = false;
   4844             mSelected = false;
   4845         }
   4846     }
   4847 
   4848     /**
   4849      * @see android.os.Parcelable.Creator
   4850      */
   4851     public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
   4852             new Parcelable.Creator<AccessibilityNodeInfo>() {
   4853         @Override
   4854         public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
   4855             AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
   4856             info.initFromParcel(parcel);
   4857             return info;
   4858         }
   4859 
   4860         @Override
   4861         public AccessibilityNodeInfo[] newArray(int size) {
   4862             return new AccessibilityNodeInfo[size];
   4863         }
   4864     };
   4865 }
   4866