AccessibilityNodeInfo.java revision 33aef98fd28dcac0a2ad37e7329afd3e666f5e0a
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
17package android.view.accessibility;
18
19import android.graphics.Rect;
20import android.os.Bundle;
21import android.os.Parcel;
22import android.os.Parcelable;
23import android.util.SparseLongArray;
24import android.view.View;
25
26import java.util.Collections;
27import java.util.List;
28
29/**
30 * This class represents a node of the window content as well as actions that
31 * can be requested from its source. From the point of view of an
32 * {@link android.accessibilityservice.AccessibilityService} a window content is
33 * presented as tree of accessibility node info which may or may not map one-to-one
34 * to the view hierarchy. In other words, a custom view is free to report itself as
35 * a tree of accessibility node info.
36 * </p>
37 * <p>
38 * Once an accessibility node info is delivered to an accessibility service it is
39 * made immutable and calling a state mutation method generates an error.
40 * </p>
41 * <p>
42 * Please refer to {@link android.accessibilityservice.AccessibilityService} for
43 * details about how to obtain a handle to window content as a tree of accessibility
44 * node info as well as familiarizing with the security model.
45 * </p>
46 * <div class="special reference">
47 * <h3>Developer Guides</h3>
48 * <p>For more information about making applications accessible, read the
49 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
50 * developer guide.</p>
51 * </div>
52 *
53 * @see android.accessibilityservice.AccessibilityService
54 * @see AccessibilityEvent
55 * @see AccessibilityManager
56 */
57public class AccessibilityNodeInfo implements Parcelable {
58
59    private static final boolean DEBUG = false;
60
61    /** @hide */
62    public static final int UNDEFINED = -1;
63
64    /** @hide */
65    public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED);
66
67    /** @hide */
68    public static final int ACTIVE_WINDOW_ID = UNDEFINED;
69
70    /** @hide */
71    public static final int FLAG_PREFETCH_PREDECESSORS = 0x00000001;
72
73    /** @hide */
74    public static final int FLAG_PREFETCH_SIBLINGS = 0x00000002;
75
76    /** @hide */
77    public static final int FLAG_PREFETCH_DESCENDANTS = 0x00000004;
78
79    /** @hide */
80    public static final int INCLUDE_NOT_IMPORTANT_VIEWS = 0x00000008;
81
82    // Actions.
83
84    /**
85     * Action that gives input focus to the node.
86     */
87    public static final int ACTION_FOCUS =  0x00000001;
88
89    /**
90     * Action that clears input focus of the node.
91     */
92    public static final int ACTION_CLEAR_FOCUS = 0x00000002;
93
94    /**
95     * Action that selects the node.
96     */
97    public static final int ACTION_SELECT = 0x00000004;
98
99    /**
100     * Action that unselects the node.
101     */
102    public static final int ACTION_CLEAR_SELECTION = 0x00000008;
103
104    /**
105     * Action that clicks on the node info.
106     */
107    public static final int ACTION_CLICK = 0x00000010;
108
109    /**
110     * Action that long clicks on the node.
111     */
112    public static final int ACTION_LONG_CLICK = 0x00000020;
113
114    /**
115     * Action that gives accessibility focus to the node.
116     */
117    public static final int ACTION_ACCESSIBILITY_FOCUS = 0x00000040;
118
119    /**
120     * Action that clears accessibility focus of the node.
121     */
122    public static final int ACTION_CLEAR_ACCESSIBILITY_FOCUS = 0x00000080;
123
124    /**
125     * Action that requests to go to the next entity in this node's text
126     * at a given movement granularity. For example, move to the next character,
127     * word, etc.
128     * <p>
129     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
130     * <strong>Example:</strong>
131     * <code><pre><p>
132     *   Bundle arguments = new Bundle();
133     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
134     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
135     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, arguments);
136     * </code></pre></p>
137     * </p>
138     *
139     * @see #setMovementGranularities(int)
140     * @see #getMovementGranularities()
141     *
142     * @see #MOVEMENT_GRANULARITY_CHARACTER
143     * @see #MOVEMENT_GRANULARITY_WORD
144     * @see #MOVEMENT_GRANULARITY_LINE
145     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
146     * @see #MOVEMENT_GRANULARITY_PAGE
147     */
148    public static final int ACTION_NEXT_AT_MOVEMENT_GRANULARITY = 0x00000100;
149
150    /**
151     * Action that requests to go to the previous entity in this node's text
152     * at a given movement granularity. For example, move to the next character,
153     * word, etc.
154     * <p>
155     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT}<br>
156     * <strong>Example:</strong>
157     * <code><pre><p>
158     *   Bundle arguments = new Bundle();
159     *   arguments.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
160     *           AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
161     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
162     *           arguments);
163     * </code></pre></p>
164     * </p>
165     *
166     * @see #setMovementGranularities(int)
167     * @see #getMovementGranularities()
168     *
169     * @see #MOVEMENT_GRANULARITY_CHARACTER
170     * @see #MOVEMENT_GRANULARITY_WORD
171     * @see #MOVEMENT_GRANULARITY_LINE
172     * @see #MOVEMENT_GRANULARITY_PARAGRAPH
173     * @see #MOVEMENT_GRANULARITY_PAGE
174     */
175    public static final int ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY = 0x00000200;
176
177    /**
178     * Action to move to the next HTML element of a given type. For example, move
179     * to the BUTTON, INPUT, TABLE, etc.
180     * <p>
181     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
182     * <strong>Example:</strong>
183     * <code><pre><p>
184     *   Bundle arguments = new Bundle();
185     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
186     *   info.performAction(AccessibilityNodeInfo.ACTION_NEXT_HTML_ELEMENT, arguments);
187     * </code></pre></p>
188     * </p>
189     */
190    public static final int ACTION_NEXT_HTML_ELEMENT = 0x00000400;
191
192    /**
193     * Action to move to the previous HTML element of a given type. For example, move
194     * to the BUTTON, INPUT, TABLE, etc.
195     * <p>
196     * <strong>Arguments:</strong> {@link #ACTION_ARGUMENT_HTML_ELEMENT_STRING}<br>
197     * <strong>Example:</strong>
198     * <code><pre><p>
199     *   Bundle arguments = new Bundle();
200     *   arguments.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_HTML_ELEMENT_STRING, "BUTTON");
201     *   info.performAction(AccessibilityNodeInfo.ACTION_PREVIOUS_HTML_ELEMENT, arguments);
202     * </code></pre></p>
203     * </p>
204     */
205    public static final int ACTION_PREVIOUS_HTML_ELEMENT = 0x00000800;
206
207    /**
208     * Action to scroll the node content forward.
209     */
210    public static final int ACTION_SCROLL_FORWARD = 0x00001000;
211
212    /**
213     * Action to scroll the node content backward.
214     */
215    public static final int ACTION_SCROLL_BACKWARD = 0x00002000;
216
217    /**
218     * Argument for which movement granularity to be used when traversing the node text.
219     * <p>
220     * <strong>Type:</strong> int<br>
221     * <strong>Actions:</strong> {@link #ACTION_NEXT_AT_MOVEMENT_GRANULARITY},
222     * {@link #ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY}
223     * </p>
224     */
225    public static final String ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT =
226        "ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT";
227
228    /**
229     * Argument for which HTML element to get moving to the next/previous HTML element.
230     * <p>
231     * <strong>Type:</strong> String<br>
232     * <strong>Actions:</strong> {@link #ACTION_NEXT_HTML_ELEMENT},
233     *         {@link #ACTION_PREVIOUS_HTML_ELEMENT}
234     * </p>
235     */
236    public static final String ACTION_ARGUMENT_HTML_ELEMENT_STRING =
237        "ACTION_ARGUMENT_HTML_ELEMENT_STRING";
238
239    /**
240     * The input focus.
241     */
242    public static final int FOCUS_INPUT = 1;
243
244    /**
245     * The accessibility focus.
246     */
247    public static final int FOCUS_ACCESSIBILITY = 2;
248
249    // Movement granularities
250
251    /**
252     * Movement granularity bit for traversing the text of a node by character.
253     */
254    public static final int MOVEMENT_GRANULARITY_CHARACTER = 0x00000001;
255
256    /**
257     * Movement granularity bit for traversing the text of a node by word.
258     */
259    public static final int MOVEMENT_GRANULARITY_WORD = 0x00000002;
260
261    /**
262     * Movement granularity bit for traversing the text of a node by line.
263     */
264    public static final int MOVEMENT_GRANULARITY_LINE = 0x00000004;
265
266    /**
267     * Movement granularity bit for traversing the text of a node by paragraph.
268     */
269    public static final int MOVEMENT_GRANULARITY_PARAGRAPH = 0x00000008;
270
271    /**
272     * Movement granularity bit for traversing the text of a node by page.
273     */
274    public static final int MOVEMENT_GRANULARITY_PAGE = 0x00000010;
275
276    // Boolean attributes.
277
278    private static final int PROPERTY_CHECKABLE = 0x00000001;
279
280    private static final int PROPERTY_CHECKED = 0x00000002;
281
282    private static final int PROPERTY_FOCUSABLE = 0x00000004;
283
284    private static final int PROPERTY_FOCUSED = 0x00000008;
285
286    private static final int PROPERTY_SELECTED = 0x00000010;
287
288    private static final int PROPERTY_CLICKABLE = 0x00000020;
289
290    private static final int PROPERTY_LONG_CLICKABLE = 0x00000040;
291
292    private static final int PROPERTY_ENABLED = 0x00000080;
293
294    private static final int PROPERTY_PASSWORD = 0x00000100;
295
296    private static final int PROPERTY_SCROLLABLE = 0x00000200;
297
298    private static final int PROPERTY_ACCESSIBILITY_FOCUSED = 0x00000400;
299
300    private static final int PROPERTY_VISIBLE_TO_USER = 0x00000800;
301
302    /**
303     * Bits that provide the id of a virtual descendant of a view.
304     */
305    private static final long VIRTUAL_DESCENDANT_ID_MASK = 0xffffffff00000000L;
306
307    /**
308     * Bit shift of {@link #VIRTUAL_DESCENDANT_ID_MASK} to get to the id for a
309     * virtual descendant of a view. Such a descendant does not exist in the view
310     * hierarchy and is only reported via the accessibility APIs.
311     */
312    private static final int VIRTUAL_DESCENDANT_ID_SHIFT = 32;
313
314    /**
315     * Gets the accessibility view id which identifies a View in the view three.
316     *
317     * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
318     * @return The accessibility view id part of the node id.
319     *
320     * @hide
321     */
322    public static int getAccessibilityViewId(long accessibilityNodeId) {
323        return (int) accessibilityNodeId;
324    }
325
326    /**
327     * Gets the virtual descendant id which identifies an imaginary view in a
328     * containing View.
329     *
330     * @param accessibilityNodeId The id of an {@link AccessibilityNodeInfo}.
331     * @return The virtual view id part of the node id.
332     *
333     * @hide
334     */
335    public static int getVirtualDescendantId(long accessibilityNodeId) {
336        return (int) ((accessibilityNodeId & VIRTUAL_DESCENDANT_ID_MASK)
337                >> VIRTUAL_DESCENDANT_ID_SHIFT);
338    }
339
340    /**
341     * Makes a node id by shifting the <code>virtualDescendantId</code>
342     * by {@link #VIRTUAL_DESCENDANT_ID_SHIFT} and taking
343     * the bitwise or with the <code>accessibilityViewId</code>.
344     *
345     * @param accessibilityViewId A View accessibility id.
346     * @param virtualDescendantId A virtual descendant id.
347     * @return The node id.
348     *
349     * @hide
350     */
351    public static long makeNodeId(int accessibilityViewId, int virtualDescendantId) {
352        return (((long) virtualDescendantId) << VIRTUAL_DESCENDANT_ID_SHIFT) | accessibilityViewId;
353    }
354
355    // Housekeeping.
356    private static final int MAX_POOL_SIZE = 50;
357    private static final Object sPoolLock = new Object();
358    private static AccessibilityNodeInfo sPool;
359    private static int sPoolSize;
360    private AccessibilityNodeInfo mNext;
361    private boolean mIsInPool;
362    private boolean mSealed;
363
364    // Data.
365    private int mWindowId = UNDEFINED;
366    private long mSourceNodeId = ROOT_NODE_ID;
367    private long mParentNodeId = ROOT_NODE_ID;
368    private long mLabelForId = ROOT_NODE_ID;
369    private long mLabeledById = ROOT_NODE_ID;
370
371    private int mBooleanProperties;
372    private final Rect mBoundsInParent = new Rect();
373    private final Rect mBoundsInScreen = new Rect();
374
375    private CharSequence mPackageName;
376    private CharSequence mClassName;
377    private CharSequence mText;
378    private CharSequence mContentDescription;
379
380    private final SparseLongArray mChildNodeIds = new SparseLongArray();
381    private int mActions;
382
383    private int mMovementGranularities;
384
385    private int mConnectionId = UNDEFINED;
386
387    /**
388     * Hide constructor from clients.
389     */
390    private AccessibilityNodeInfo() {
391        /* do nothing */
392    }
393
394    /**
395     * Sets the source.
396     * <p>
397     *   <strong>Note:</strong> Cannot be called from an
398     *   {@link android.accessibilityservice.AccessibilityService}.
399     *   This class is made immutable before being delivered to an AccessibilityService.
400     * </p>
401     *
402     * @param source The info source.
403     */
404    public void setSource(View source) {
405        setSource(source, UNDEFINED);
406    }
407
408    /**
409     * Sets the source to be a virtual descendant of the given <code>root</code>.
410     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
411     * is set as the source.
412     * <p>
413     * A virtual descendant is an imaginary View that is reported as a part of the view
414     * hierarchy for accessibility purposes. This enables custom views that draw complex
415     * content to report themselves as a tree of virtual views, thus conveying their
416     * logical structure.
417     * </p>
418     * <p>
419     *   <strong>Note:</strong> Cannot be called from an
420     *   {@link android.accessibilityservice.AccessibilityService}.
421     *   This class is made immutable before being delivered to an AccessibilityService.
422     * </p>
423     *
424     * @param root The root of the virtual subtree.
425     * @param virtualDescendantId The id of the virtual descendant.
426     */
427    public void setSource(View root, int virtualDescendantId) {
428        enforceNotSealed();
429        mWindowId = (root != null) ? root.getAccessibilityWindowId() : UNDEFINED;
430        final int rootAccessibilityViewId =
431            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
432        mSourceNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
433    }
434
435    /**
436     * Find the view that has the specified focus type. The search starts from
437     * the view represented by this node info.
438     *
439     * @param focus The focus to find. One of {@link #FOCUS_INPUT} or
440     *         {@link #FOCUS_ACCESSIBILITY}.
441     * @return The node info of the focused view or null.
442     *
443     * @see #FOCUS_INPUT
444     * @see #FOCUS_ACCESSIBILITY
445     */
446    public AccessibilityNodeInfo findFocus(int focus) {
447        enforceSealed();
448        enforceValidFocusType(focus);
449        if (!canPerformRequestOverConnection(mSourceNodeId)) {
450            return null;
451        }
452        return AccessibilityInteractionClient.getInstance().findFocus(mConnectionId, mWindowId,
453                mSourceNodeId, focus);
454    }
455
456    /**
457     * Searches for the nearest view in the specified direction that can take
458     * the input focus.
459     *
460     * @param direction The direction. Can be one of:
461     *     {@link View#FOCUS_DOWN},
462     *     {@link View#FOCUS_UP},
463     *     {@link View#FOCUS_LEFT},
464     *     {@link View#FOCUS_RIGHT},
465     *     {@link View#FOCUS_FORWARD},
466     *     {@link View#FOCUS_BACKWARD}.
467     *
468     * @return The node info for the view that can take accessibility focus.
469     */
470    public AccessibilityNodeInfo focusSearch(int direction) {
471        enforceSealed();
472        enforceValidFocusDirection(direction);
473        if (!canPerformRequestOverConnection(mSourceNodeId)) {
474            return null;
475        }
476        return AccessibilityInteractionClient.getInstance().focusSearch(mConnectionId, mWindowId,
477                mSourceNodeId, direction);
478    }
479
480    /**
481     * Gets the id of the window from which the info comes from.
482     *
483     * @return The window id.
484     */
485    public int getWindowId() {
486        return mWindowId;
487    }
488
489    /**
490     * @return The ids of the children.
491     *
492     * @hide
493     */
494    public SparseLongArray getChildNodeIds() {
495        return mChildNodeIds;
496    }
497
498    /**
499     * Gets the number of children.
500     *
501     * @return The child count.
502     */
503    public int getChildCount() {
504        return mChildNodeIds.size();
505    }
506
507    /**
508     * Get the child at given index.
509     * <p>
510     *   <strong>Note:</strong> It is a client responsibility to recycle the
511     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
512     *     to avoid creating of multiple instances.
513     * </p>
514     *
515     * @param index The child index.
516     * @return The child node.
517     *
518     * @throws IllegalStateException If called outside of an AccessibilityService.
519     *
520     */
521    public AccessibilityNodeInfo getChild(int index) {
522        enforceSealed();
523        if (!canPerformRequestOverConnection(mSourceNodeId)) {
524            return null;
525        }
526        final long childId = mChildNodeIds.get(index);
527        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
528        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mWindowId,
529                childId, FLAG_PREFETCH_DESCENDANTS);
530    }
531
532    /**
533     * Adds a child.
534     * <p>
535     * <strong>Note:</strong> Cannot be called from an
536     * {@link android.accessibilityservice.AccessibilityService}.
537     * This class is made immutable before being delivered to an AccessibilityService.
538     * </p>
539     *
540     * @param child The child.
541     *
542     * @throws IllegalStateException If called from an AccessibilityService.
543     */
544    public void addChild(View child) {
545        addChild(child, UNDEFINED);
546    }
547
548    /**
549     * Adds a virtual child which is a descendant of the given <code>root</code>.
550     * If <code>virtualDescendantId</code> is {@link View#NO_ID} the root
551     * is added as a child.
552     * <p>
553     * A virtual descendant is an imaginary View that is reported as a part of the view
554     * hierarchy for accessibility purposes. This enables custom views that draw complex
555     * content to report them selves as a tree of virtual views, thus conveying their
556     * logical structure.
557     * </p>
558     *
559     * @param root The root of the virtual subtree.
560     * @param virtualDescendantId The id of the virtual child.
561     */
562    public void addChild(View root, int virtualDescendantId) {
563        enforceNotSealed();
564        final int index = mChildNodeIds.size();
565        final int rootAccessibilityViewId =
566            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
567        final long childNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
568        mChildNodeIds.put(index, childNodeId);
569    }
570
571    /**
572     * Gets the actions that can be performed on the node.
573     *
574     * @return The bit mask of with actions.
575     *
576     * @see AccessibilityNodeInfo#ACTION_FOCUS
577     * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS
578     * @see AccessibilityNodeInfo#ACTION_SELECT
579     * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION
580     * @see AccessibilityNodeInfo#ACTION_ACCESSIBILITY_FOCUS
581     * @see AccessibilityNodeInfo#ACTION_CLEAR_ACCESSIBILITY_FOCUS
582     * @see AccessibilityNodeInfo#ACTION_CLICK
583     * @see AccessibilityNodeInfo#ACTION_LONG_CLICK
584     * @see AccessibilityNodeInfo#ACTION_NEXT_AT_MOVEMENT_GRANULARITY
585     * @see AccessibilityNodeInfo#ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY
586     * @see AccessibilityNodeInfo#ACTION_NEXT_HTML_ELEMENT
587     * @see AccessibilityNodeInfo#ACTION_PREVIOUS_HTML_ELEMENT
588     * @see AccessibilityNodeInfo#ACTION_SCROLL_FORWARD
589     * @see AccessibilityNodeInfo#ACTION_SCROLL_BACKWARD
590     */
591    public int getActions() {
592        return mActions;
593    }
594
595    /**
596     * Adds an action that can be performed on the node.
597     * <p>
598     *   <strong>Note:</strong> Cannot be called from an
599     *   {@link android.accessibilityservice.AccessibilityService}.
600     *   This class is made immutable before being delivered to an AccessibilityService.
601     * </p>
602     *
603     * @param action The action.
604     *
605     * @throws IllegalStateException If called from an AccessibilityService.
606     */
607    public void addAction(int action) {
608        enforceNotSealed();
609        mActions |= action;
610    }
611
612    /**
613     * Sets the movement granularities for traversing the text of this node.
614     * <p>
615     *   <strong>Note:</strong> Cannot be called from an
616     *   {@link android.accessibilityservice.AccessibilityService}.
617     *   This class is made immutable before being delivered to an AccessibilityService.
618     * </p>
619     *
620     * @param granularities The bit mask with granularities.
621     *
622     * @throws IllegalStateException If called from an AccessibilityService.
623     */
624    public void setMovementGranularities(int granularities) {
625        enforceNotSealed();
626        mMovementGranularities = granularities;
627    }
628
629    /**
630     * Gets the movement granularities for traversing the text of this node.
631     *
632     * @return The bit mask with granularities.
633     */
634    public int getMovementGranularities() {
635        return mMovementGranularities;
636    }
637
638    /**
639     * Performs an action on the node.
640     * <p>
641     *   <strong>Note:</strong> An action can be performed only if the request is made
642     *   from an {@link android.accessibilityservice.AccessibilityService}.
643     * </p>
644     *
645     * @param action The action to perform.
646     * @return True if the action was performed.
647     *
648     * @throws IllegalStateException If called outside of an AccessibilityService.
649     */
650    public boolean performAction(int action) {
651        enforceSealed();
652        if (!canPerformRequestOverConnection(mSourceNodeId)) {
653            return false;
654        }
655        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
656        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
657                action, null);
658    }
659
660    /**
661     * Performs an action on the node.
662     * <p>
663     *   <strong>Note:</strong> An action can be performed only if the request is made
664     *   from an {@link android.accessibilityservice.AccessibilityService}.
665     * </p>
666     *
667     * @param action The action to perform.
668     * @param arguments A bundle with additional arguments.
669     * @return True if the action was performed.
670     *
671     * @throws IllegalStateException If called outside of an AccessibilityService.
672     */
673    public boolean performAction(int action, Bundle arguments) {
674        enforceSealed();
675        if (!canPerformRequestOverConnection(mSourceNodeId)) {
676            return false;
677        }
678        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
679        return client.performAccessibilityAction(mConnectionId, mWindowId, mSourceNodeId,
680                action, arguments);
681    }
682
683    /**
684     * Finds {@link AccessibilityNodeInfo}s by text. The match is case
685     * insensitive containment. The search is relative to this info i.e.
686     * this info is the root of the traversed tree.
687     *
688     * <p>
689     *   <strong>Note:</strong> It is a client responsibility to recycle the
690     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
691     *     to avoid creating of multiple instances.
692     * </p>
693     *
694     * @param text The searched text.
695     * @return A list of node info.
696     */
697    public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text) {
698        enforceSealed();
699        if (!canPerformRequestOverConnection(mSourceNodeId)) {
700            return Collections.emptyList();
701        }
702        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
703        return client.findAccessibilityNodeInfosByText(mConnectionId, mWindowId, mSourceNodeId,
704                text);
705    }
706
707    /**
708     * Gets the parent.
709     * <p>
710     *   <strong>Note:</strong> It is a client responsibility to recycle the
711     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
712     *     to avoid creating of multiple instances.
713     * </p>
714     *
715     * @return The parent.
716     */
717    public AccessibilityNodeInfo getParent() {
718        enforceSealed();
719        if (!canPerformRequestOverConnection(mParentNodeId)) {
720            return null;
721        }
722        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
723        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
724                mWindowId, mParentNodeId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
725    }
726
727    /**
728     * @return The parent node id.
729     *
730     * @hide
731     */
732    public long getParentNodeId() {
733        return mParentNodeId;
734    }
735
736    /**
737     * Sets the parent.
738     * <p>
739     *   <strong>Note:</strong> Cannot be called from an
740     *   {@link android.accessibilityservice.AccessibilityService}.
741     *   This class is made immutable before being delivered to an AccessibilityService.
742     * </p>
743     *
744     * @param parent The parent.
745     *
746     * @throws IllegalStateException If called from an AccessibilityService.
747     */
748    public void setParent(View parent) {
749        setParent(parent, UNDEFINED);
750    }
751
752    /**
753     * Sets the parent to be a virtual descendant of the given <code>root</code>.
754     * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
755     * is set as the parent.
756     * <p>
757     * A virtual descendant is an imaginary View that is reported as a part of the view
758     * hierarchy for accessibility purposes. This enables custom views that draw complex
759     * content to report them selves as a tree of virtual views, thus conveying their
760     * logical structure.
761     * </p>
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 root The root of the virtual subtree.
769     * @param virtualDescendantId The id of the virtual descendant.
770     */
771    public void setParent(View root, int virtualDescendantId) {
772        enforceNotSealed();
773        final int rootAccessibilityViewId =
774            (root != null) ? root.getAccessibilityViewId() : UNDEFINED;
775        mParentNodeId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
776    }
777
778    /**
779     * Gets the node bounds in parent coordinates.
780     *
781     * @param outBounds The output node bounds.
782     */
783    public void getBoundsInParent(Rect outBounds) {
784        outBounds.set(mBoundsInParent.left, mBoundsInParent.top,
785                mBoundsInParent.right, mBoundsInParent.bottom);
786    }
787
788    /**
789     * Sets the node bounds in parent coordinates.
790     * <p>
791     *   <strong>Note:</strong> Cannot be called from an
792     *   {@link android.accessibilityservice.AccessibilityService}.
793     *   This class is made immutable before being delivered to an AccessibilityService.
794     * </p>
795     *
796     * @param bounds The node bounds.
797     *
798     * @throws IllegalStateException If called from an AccessibilityService.
799     */
800    public void setBoundsInParent(Rect bounds) {
801        enforceNotSealed();
802        mBoundsInParent.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
803    }
804
805    /**
806     * Gets the node bounds in screen coordinates.
807     *
808     * @param outBounds The output node bounds.
809     */
810    public void getBoundsInScreen(Rect outBounds) {
811        outBounds.set(mBoundsInScreen.left, mBoundsInScreen.top,
812                mBoundsInScreen.right, mBoundsInScreen.bottom);
813    }
814
815    /**
816     * Sets the node bounds in screen coordinates.
817     * <p>
818     *   <strong>Note:</strong> Cannot be called from an
819     *   {@link android.accessibilityservice.AccessibilityService}.
820     *   This class is made immutable before being delivered to an AccessibilityService.
821     * </p>
822     *
823     * @param bounds The node bounds.
824     *
825     * @throws IllegalStateException If called from an AccessibilityService.
826     */
827    public void setBoundsInScreen(Rect bounds) {
828        enforceNotSealed();
829        mBoundsInScreen.set(bounds.left, bounds.top, bounds.right, bounds.bottom);
830    }
831
832    /**
833     * Gets whether this node is checkable.
834     *
835     * @return True if the node is checkable.
836     */
837    public boolean isCheckable() {
838        return getBooleanProperty(PROPERTY_CHECKABLE);
839    }
840
841    /**
842     * Sets whether this node is checkable.
843     * <p>
844     *   <strong>Note:</strong> Cannot be called from an
845     *   {@link android.accessibilityservice.AccessibilityService}.
846     *   This class is made immutable before being delivered to an AccessibilityService.
847     * </p>
848     *
849     * @param checkable True if the node is checkable.
850     *
851     * @throws IllegalStateException If called from an AccessibilityService.
852     */
853    public void setCheckable(boolean checkable) {
854        setBooleanProperty(PROPERTY_CHECKABLE, checkable);
855    }
856
857    /**
858     * Gets whether this node is checked.
859     *
860     * @return True if the node is checked.
861     */
862    public boolean isChecked() {
863        return getBooleanProperty(PROPERTY_CHECKED);
864    }
865
866    /**
867     * Sets whether this node is checked.
868     * <p>
869     *   <strong>Note:</strong> Cannot be called from an
870     *   {@link android.accessibilityservice.AccessibilityService}.
871     *   This class is made immutable before being delivered to an AccessibilityService.
872     * </p>
873     *
874     * @param checked True if the node is checked.
875     *
876     * @throws IllegalStateException If called from an AccessibilityService.
877     */
878    public void setChecked(boolean checked) {
879        setBooleanProperty(PROPERTY_CHECKED, checked);
880    }
881
882    /**
883     * Gets whether this node is focusable.
884     *
885     * @return True if the node is focusable.
886     */
887    public boolean isFocusable() {
888        return getBooleanProperty(PROPERTY_FOCUSABLE);
889    }
890
891    /**
892     * Sets whether this node is focusable.
893     * <p>
894     *   <strong>Note:</strong> Cannot be called from an
895     *   {@link android.accessibilityservice.AccessibilityService}.
896     *   This class is made immutable before being delivered to an AccessibilityService.
897     * </p>
898     *
899     * @param focusable True if the node is focusable.
900     *
901     * @throws IllegalStateException If called from an AccessibilityService.
902     */
903    public void setFocusable(boolean focusable) {
904        setBooleanProperty(PROPERTY_FOCUSABLE, focusable);
905    }
906
907    /**
908     * Gets whether this node is focused.
909     *
910     * @return True if the node is focused.
911     */
912    public boolean isFocused() {
913        return getBooleanProperty(PROPERTY_FOCUSED);
914    }
915
916    /**
917     * Sets whether this node is focused.
918     * <p>
919     *   <strong>Note:</strong> Cannot be called from an
920     *   {@link android.accessibilityservice.AccessibilityService}.
921     *   This class is made immutable before being delivered to an AccessibilityService.
922     * </p>
923     *
924     * @param focused True if the node is focused.
925     *
926     * @throws IllegalStateException If called from an AccessibilityService.
927     */
928    public void setFocused(boolean focused) {
929        setBooleanProperty(PROPERTY_FOCUSED, focused);
930    }
931
932    /**
933     * Sets whether this node is visible to the user.
934     *
935     * @return Whether the node is visible to the user.
936     */
937    public boolean isVisibleToUser() {
938        return getBooleanProperty(PROPERTY_VISIBLE_TO_USER);
939    }
940
941    /**
942     * Sets whether this node is visible to the user.
943     * <p>
944     *   <strong>Note:</strong> Cannot be called from an
945     *   {@link android.accessibilityservice.AccessibilityService}.
946     *   This class is made immutable before being delivered to an AccessibilityService.
947     * </p>
948     *
949     * @param visibleToUser Whether the node is visible to the user.
950     *
951     * @throws IllegalStateException If called from an AccessibilityService.
952     */
953    public void setVisibleToUser(boolean visibleToUser) {
954        setBooleanProperty(PROPERTY_VISIBLE_TO_USER, visibleToUser);
955    }
956
957    /**
958     * Gets whether this node is accessibility focused.
959     *
960     * @return True if the node is accessibility focused.
961     */
962    public boolean isAccessibilityFocused() {
963        return getBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED);
964    }
965
966    /**
967     * Sets whether this node is accessibility focused.
968     * <p>
969     *   <strong>Note:</strong> Cannot be called from an
970     *   {@link android.accessibilityservice.AccessibilityService}.
971     *   This class is made immutable before being delivered to an AccessibilityService.
972     * </p>
973     *
974     * @param focused True if the node is accessibility focused.
975     *
976     * @throws IllegalStateException If called from an AccessibilityService.
977     */
978    public void setAccessibilityFocused(boolean focused) {
979        setBooleanProperty(PROPERTY_ACCESSIBILITY_FOCUSED, focused);
980    }
981
982    /**
983     * Gets whether this node is selected.
984     *
985     * @return True if the node is selected.
986     */
987    public boolean isSelected() {
988        return getBooleanProperty(PROPERTY_SELECTED);
989    }
990
991    /**
992     * Sets whether this node is selected.
993     * <p>
994     *   <strong>Note:</strong> Cannot be called from an
995     *   {@link android.accessibilityservice.AccessibilityService}.
996     *   This class is made immutable before being delivered to an AccessibilityService.
997     * </p>
998     *
999     * @param selected True if the node is selected.
1000     *
1001     * @throws IllegalStateException If called from an AccessibilityService.
1002     */
1003    public void setSelected(boolean selected) {
1004        setBooleanProperty(PROPERTY_SELECTED, selected);
1005    }
1006
1007    /**
1008     * Gets whether this node is clickable.
1009     *
1010     * @return True if the node is clickable.
1011     */
1012    public boolean isClickable() {
1013        return getBooleanProperty(PROPERTY_CLICKABLE);
1014    }
1015
1016    /**
1017     * Sets whether this node is clickable.
1018     * <p>
1019     *   <strong>Note:</strong> Cannot be called from an
1020     *   {@link android.accessibilityservice.AccessibilityService}.
1021     *   This class is made immutable before being delivered to an AccessibilityService.
1022     * </p>
1023     *
1024     * @param clickable True if the node is clickable.
1025     *
1026     * @throws IllegalStateException If called from an AccessibilityService.
1027     */
1028    public void setClickable(boolean clickable) {
1029        setBooleanProperty(PROPERTY_CLICKABLE, clickable);
1030    }
1031
1032    /**
1033     * Gets whether this node is long clickable.
1034     *
1035     * @return True if the node is long clickable.
1036     */
1037    public boolean isLongClickable() {
1038        return getBooleanProperty(PROPERTY_LONG_CLICKABLE);
1039    }
1040
1041    /**
1042     * Sets whether this node is long clickable.
1043     * <p>
1044     *   <strong>Note:</strong> Cannot be called from an
1045     *   {@link android.accessibilityservice.AccessibilityService}.
1046     *   This class is made immutable before being delivered to an AccessibilityService.
1047     * </p>
1048     *
1049     * @param longClickable True if the node is long clickable.
1050     *
1051     * @throws IllegalStateException If called from an AccessibilityService.
1052     */
1053    public void setLongClickable(boolean longClickable) {
1054        setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable);
1055    }
1056
1057    /**
1058     * Gets whether this node is enabled.
1059     *
1060     * @return True if the node is enabled.
1061     */
1062    public boolean isEnabled() {
1063        return getBooleanProperty(PROPERTY_ENABLED);
1064    }
1065
1066    /**
1067     * Sets whether this node is enabled.
1068     * <p>
1069     *   <strong>Note:</strong> Cannot be called from an
1070     *   {@link android.accessibilityservice.AccessibilityService}.
1071     *   This class is made immutable before being delivered to an AccessibilityService.
1072     * </p>
1073     *
1074     * @param enabled True if the node is enabled.
1075     *
1076     * @throws IllegalStateException If called from an AccessibilityService.
1077     */
1078    public void setEnabled(boolean enabled) {
1079        setBooleanProperty(PROPERTY_ENABLED, enabled);
1080    }
1081
1082    /**
1083     * Gets whether this node is a password.
1084     *
1085     * @return True if the node is a password.
1086     */
1087    public boolean isPassword() {
1088        return getBooleanProperty(PROPERTY_PASSWORD);
1089    }
1090
1091    /**
1092     * Sets whether this node is a password.
1093     * <p>
1094     *   <strong>Note:</strong> Cannot be called from an
1095     *   {@link android.accessibilityservice.AccessibilityService}.
1096     *   This class is made immutable before being delivered to an AccessibilityService.
1097     * </p>
1098     *
1099     * @param password True if the node is a password.
1100     *
1101     * @throws IllegalStateException If called from an AccessibilityService.
1102     */
1103    public void setPassword(boolean password) {
1104        setBooleanProperty(PROPERTY_PASSWORD, password);
1105    }
1106
1107    /**
1108     * Gets if the node is scrollable.
1109     *
1110     * @return True if the node is scrollable, false otherwise.
1111     */
1112    public boolean isScrollable() {
1113        return getBooleanProperty(PROPERTY_SCROLLABLE);
1114    }
1115
1116    /**
1117     * Sets if the node is scrollable.
1118     * <p>
1119     *   <strong>Note:</strong> Cannot be called from an
1120     *   {@link android.accessibilityservice.AccessibilityService}.
1121     *   This class is made immutable before being delivered to an AccessibilityService.
1122     * </p>
1123     *
1124     * @param scrollable True if the node is scrollable, false otherwise.
1125     *
1126     * @throws IllegalStateException If called from an AccessibilityService.
1127     */
1128    public void setScrollable(boolean scrollable) {
1129        enforceNotSealed();
1130        setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
1131    }
1132
1133    /**
1134     * Gets the package this node comes from.
1135     *
1136     * @return The package name.
1137     */
1138    public CharSequence getPackageName() {
1139        return mPackageName;
1140    }
1141
1142    /**
1143     * Sets the package this node comes from.
1144     * <p>
1145     *   <strong>Note:</strong> Cannot be called from an
1146     *   {@link android.accessibilityservice.AccessibilityService}.
1147     *   This class is made immutable before being delivered to an AccessibilityService.
1148     * </p>
1149     *
1150     * @param packageName The package name.
1151     *
1152     * @throws IllegalStateException If called from an AccessibilityService.
1153     */
1154    public void setPackageName(CharSequence packageName) {
1155        enforceNotSealed();
1156        mPackageName = packageName;
1157    }
1158
1159    /**
1160     * Gets the class this node comes from.
1161     *
1162     * @return The class name.
1163     */
1164    public CharSequence getClassName() {
1165        return mClassName;
1166    }
1167
1168    /**
1169     * Sets the class this node comes from.
1170     * <p>
1171     *   <strong>Note:</strong> Cannot be called from an
1172     *   {@link android.accessibilityservice.AccessibilityService}.
1173     *   This class is made immutable before being delivered to an AccessibilityService.
1174     * </p>
1175     *
1176     * @param className The class name.
1177     *
1178     * @throws IllegalStateException If called from an AccessibilityService.
1179     */
1180    public void setClassName(CharSequence className) {
1181        enforceNotSealed();
1182        mClassName = className;
1183    }
1184
1185    /**
1186     * Gets the text of this node.
1187     *
1188     * @return The text.
1189     */
1190    public CharSequence getText() {
1191        return mText;
1192    }
1193
1194    /**
1195     * Sets the text of this node.
1196     * <p>
1197     *   <strong>Note:</strong> Cannot be called from an
1198     *   {@link android.accessibilityservice.AccessibilityService}.
1199     *   This class is made immutable before being delivered to an AccessibilityService.
1200     * </p>
1201     *
1202     * @param text The text.
1203     *
1204     * @throws IllegalStateException If called from an AccessibilityService.
1205     */
1206    public void setText(CharSequence text) {
1207        enforceNotSealed();
1208        mText = text;
1209    }
1210
1211    /**
1212     * Gets the content description of this node.
1213     *
1214     * @return The content description.
1215     */
1216    public CharSequence getContentDescription() {
1217        return mContentDescription;
1218    }
1219
1220    /**
1221     * Sets the content description of this node.
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 contentDescription The content description.
1229     *
1230     * @throws IllegalStateException If called from an AccessibilityService.
1231     */
1232    public void setContentDescription(CharSequence contentDescription) {
1233        enforceNotSealed();
1234        mContentDescription = contentDescription;
1235    }
1236
1237    /**
1238     * Sets the view for which the view represented by this info serves as a
1239     * label for accessibility purposes.
1240     *
1241     * @param labeled The view for which this info serves as a label.
1242     */
1243    public void setLabelFor(View labeled) {
1244        setLabelFor(labeled, UNDEFINED);
1245    }
1246
1247    /**
1248     * Sets the view for which the view represented by this info serves as a
1249     * label for accessibility purposes. If <code>virtualDescendantId</code>
1250     * is {@link View#NO_ID} the root is set as the labeled.
1251     * <p>
1252     * A virtual descendant is an imaginary View that is reported as a part of the view
1253     * hierarchy for accessibility purposes. This enables custom views that draw complex
1254     * content to report themselves as a tree of virtual views, thus conveying their
1255     * logical structure.
1256     * </p>
1257     * <p>
1258     *   <strong>Note:</strong> Cannot be called from an
1259     *   {@link android.accessibilityservice.AccessibilityService}.
1260     *   This class is made immutable before being delivered to an AccessibilityService.
1261     * </p>
1262     *
1263     * @param root The root whose virtual descendant serves as a label.
1264     * @param virtualDescendantId The id of the virtual descendant.
1265     */
1266    public void setLabelFor(View root, int virtualDescendantId) {
1267        enforceNotSealed();
1268        final int rootAccessibilityViewId = (root != null)
1269                ? root.getAccessibilityViewId() : UNDEFINED;
1270        mLabelForId = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1271    }
1272
1273    /**
1274     * Gets the node info for which the view represented by this info serves as
1275     * a label for accessibility purposes.
1276     * <p>
1277     *   <strong>Note:</strong> It is a client responsibility to recycle the
1278     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1279     *     to avoid creating of multiple instances.
1280     * </p>
1281     *
1282     * @return The labeled info.
1283     */
1284    public AccessibilityNodeInfo getLabelFor() {
1285        enforceSealed();
1286        if (!canPerformRequestOverConnection(mLabelForId)) {
1287            return null;
1288        }
1289        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1290        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
1291                mWindowId, mLabelForId, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
1292    }
1293
1294    /**
1295     * Sets the view which serves as the label of the view represented by
1296     * this info for accessibility purposes.
1297     *
1298     * @param label The view that labels this node's source.
1299     */
1300    public void setLabeledBy(View label) {
1301        setLabeledBy(label, UNDEFINED);
1302    }
1303
1304    /**
1305     * Sets the view which serves as the label of the view represented by
1306     * this info for accessibility purposes. If <code>virtualDescendantId</code>
1307     * is {@link View#NO_ID} the root is set as the label.
1308     * <p>
1309     * A virtual descendant is an imaginary View that is reported as a part of the view
1310     * hierarchy for accessibility purposes. This enables custom views that draw complex
1311     * content to report themselves as a tree of virtual views, thus conveying their
1312     * logical structure.
1313     * </p>
1314     * <p>
1315     *   <strong>Note:</strong> Cannot be called from an
1316     *   {@link android.accessibilityservice.AccessibilityService}.
1317     *   This class is made immutable before being delivered to an AccessibilityService.
1318     * </p>
1319     *
1320     * @param root The root whose virtual descendant labels this node's source.
1321     * @param virtualDescendantId The id of the virtual descendant.
1322     */
1323    public void setLabeledBy(View root, int virtualDescendantId) {
1324        enforceNotSealed();
1325        final int rootAccessibilityViewId = (root != null)
1326                ? root.getAccessibilityViewId() : UNDEFINED;
1327        mLabeledById = makeNodeId(rootAccessibilityViewId, virtualDescendantId);
1328    }
1329
1330    /**
1331     * Gets the node info which serves as the label of the view represented by
1332     * this info for accessibility purposes.
1333     * <p>
1334     *   <strong>Note:</strong> It is a client responsibility to recycle the
1335     *     received info by calling {@link AccessibilityNodeInfo#recycle()}
1336     *     to avoid creating of multiple instances.
1337     * </p>
1338     *
1339     * @return The label.
1340     */
1341    public AccessibilityNodeInfo getLabeledBy() {
1342        enforceSealed();
1343        if (!canPerformRequestOverConnection(mLabeledById)) {
1344            return null;
1345        }
1346        AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
1347        return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
1348                mWindowId, mLabeledById, FLAG_PREFETCH_DESCENDANTS | FLAG_PREFETCH_SIBLINGS);
1349    }
1350
1351    /**
1352     * Gets the value of a boolean property.
1353     *
1354     * @param property The property.
1355     * @return The value.
1356     */
1357    private boolean getBooleanProperty(int property) {
1358        return (mBooleanProperties & property) != 0;
1359    }
1360
1361    /**
1362     * Sets a boolean property.
1363     *
1364     * @param property The property.
1365     * @param value The value.
1366     *
1367     * @throws IllegalStateException If called from an AccessibilityService.
1368     */
1369    private void setBooleanProperty(int property, boolean value) {
1370        enforceNotSealed();
1371        if (value) {
1372            mBooleanProperties |= property;
1373        } else {
1374            mBooleanProperties &= ~property;
1375        }
1376    }
1377
1378    /**
1379     * Sets the unique id of the IAccessibilityServiceConnection over which
1380     * this instance can send requests to the system.
1381     *
1382     * @param connectionId The connection id.
1383     *
1384     * @hide
1385     */
1386    public void setConnectionId(int connectionId) {
1387        enforceNotSealed();
1388        mConnectionId = connectionId;
1389    }
1390
1391    /**
1392     * {@inheritDoc}
1393     */
1394    public int describeContents() {
1395        return 0;
1396    }
1397
1398    /**
1399     * Gets the id of the source node.
1400     *
1401     * @return The id.
1402     *
1403     * @hide
1404     */
1405    public long getSourceNodeId() {
1406        return mSourceNodeId;
1407    }
1408
1409    /**
1410     * Sets if this instance is sealed.
1411     *
1412     * @param sealed Whether is sealed.
1413     *
1414     * @hide
1415     */
1416    public void setSealed(boolean sealed) {
1417        mSealed = sealed;
1418    }
1419
1420    /**
1421     * Gets if this instance is sealed.
1422     *
1423     * @return Whether is sealed.
1424     *
1425     * @hide
1426     */
1427    public boolean isSealed() {
1428        return mSealed;
1429    }
1430
1431    /**
1432     * Enforces that this instance is sealed.
1433     *
1434     * @throws IllegalStateException If this instance is not sealed.
1435     *
1436     * @hide
1437     */
1438    protected void enforceSealed() {
1439        if (!isSealed()) {
1440            throw new IllegalStateException("Cannot perform this "
1441                    + "action on a not sealed instance.");
1442        }
1443    }
1444
1445    private void enforceValidFocusDirection(int direction) {
1446        switch (direction) {
1447            case View.FOCUS_DOWN:
1448            case View.FOCUS_UP:
1449            case View.FOCUS_LEFT:
1450            case View.FOCUS_RIGHT:
1451            case View.FOCUS_FORWARD:
1452            case View.FOCUS_BACKWARD:
1453                return;
1454            default:
1455                throw new IllegalArgumentException("Unknown direction: " + direction);
1456        }
1457    }
1458
1459    private void enforceValidFocusType(int focusType) {
1460        switch (focusType) {
1461            case FOCUS_INPUT:
1462            case FOCUS_ACCESSIBILITY:
1463                return;
1464            default:
1465                throw new IllegalArgumentException("Unknown focus type: " + focusType);
1466        }
1467    }
1468
1469    /**
1470     * Enforces that this instance is not sealed.
1471     *
1472     * @throws IllegalStateException If this instance is sealed.
1473     *
1474     * @hide
1475     */
1476    protected void enforceNotSealed() {
1477        if (isSealed()) {
1478            throw new IllegalStateException("Cannot perform this "
1479                    + "action on a sealed instance.");
1480        }
1481    }
1482
1483    /**
1484     * Returns a cached instance if such is available otherwise a new one
1485     * and sets the source.
1486     *
1487     * @param source The source view.
1488     * @return An instance.
1489     *
1490     * @see #setSource(View)
1491     */
1492    public static AccessibilityNodeInfo obtain(View source) {
1493        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1494        info.setSource(source);
1495        return info;
1496    }
1497
1498    /**
1499     * Returns a cached instance if such is available otherwise a new one
1500     * and sets the source.
1501     *
1502     * @param root The root of the virtual subtree.
1503     * @param virtualDescendantId The id of the virtual descendant.
1504     * @return An instance.
1505     *
1506     * @see #setSource(View, int)
1507     */
1508    public static AccessibilityNodeInfo obtain(View root, int virtualDescendantId) {
1509        AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1510        info.setSource(root, virtualDescendantId);
1511        return info;
1512    }
1513
1514    /**
1515     * Returns a cached instance if such is available otherwise a new one.
1516     *
1517     * @return An instance.
1518     */
1519    public static AccessibilityNodeInfo obtain() {
1520        synchronized (sPoolLock) {
1521            if (sPool != null) {
1522                AccessibilityNodeInfo info = sPool;
1523                sPool = sPool.mNext;
1524                sPoolSize--;
1525                info.mNext = null;
1526                info.mIsInPool = false;
1527                return info;
1528            }
1529            return new AccessibilityNodeInfo();
1530        }
1531    }
1532
1533    /**
1534     * Returns a cached instance if such is available or a new one is
1535     * create. The returned instance is initialized from the given
1536     * <code>info</code>.
1537     *
1538     * @param info The other info.
1539     * @return An instance.
1540     */
1541    public static AccessibilityNodeInfo obtain(AccessibilityNodeInfo info) {
1542        AccessibilityNodeInfo infoClone = AccessibilityNodeInfo.obtain();
1543        infoClone.init(info);
1544        return infoClone;
1545    }
1546
1547    /**
1548     * Return an instance back to be reused.
1549     * <p>
1550     * <strong>Note:</strong> You must not touch the object after calling this function.
1551     *
1552     * @throws IllegalStateException If the info is already recycled.
1553     */
1554    public void recycle() {
1555        if (mIsInPool) {
1556            throw new IllegalStateException("Info already recycled!");
1557        }
1558        clear();
1559        synchronized (sPoolLock) {
1560            if (sPoolSize <= MAX_POOL_SIZE) {
1561                mNext = sPool;
1562                sPool = this;
1563                mIsInPool = true;
1564                sPoolSize++;
1565            }
1566        }
1567    }
1568
1569    /**
1570     * {@inheritDoc}
1571     * <p>
1572     *   <strong>Note:</strong> After the instance is written to a parcel it
1573     *      is recycled. You must not touch the object after calling this function.
1574     * </p>
1575     */
1576    public void writeToParcel(Parcel parcel, int flags) {
1577        parcel.writeInt(isSealed() ? 1 : 0);
1578        parcel.writeLong(mSourceNodeId);
1579        parcel.writeInt(mWindowId);
1580        parcel.writeLong(mParentNodeId);
1581        parcel.writeLong(mLabelForId);
1582        parcel.writeLong(mLabeledById);
1583        parcel.writeInt(mConnectionId);
1584
1585        SparseLongArray childIds = mChildNodeIds;
1586        final int childIdsSize = childIds.size();
1587        parcel.writeInt(childIdsSize);
1588        for (int i = 0; i < childIdsSize; i++) {
1589            parcel.writeLong(childIds.valueAt(i));
1590        }
1591
1592        parcel.writeInt(mBoundsInParent.top);
1593        parcel.writeInt(mBoundsInParent.bottom);
1594        parcel.writeInt(mBoundsInParent.left);
1595        parcel.writeInt(mBoundsInParent.right);
1596
1597        parcel.writeInt(mBoundsInScreen.top);
1598        parcel.writeInt(mBoundsInScreen.bottom);
1599        parcel.writeInt(mBoundsInScreen.left);
1600        parcel.writeInt(mBoundsInScreen.right);
1601
1602        parcel.writeInt(mActions);
1603
1604        parcel.writeInt(mMovementGranularities);
1605
1606        parcel.writeInt(mBooleanProperties);
1607
1608        parcel.writeCharSequence(mPackageName);
1609        parcel.writeCharSequence(mClassName);
1610        parcel.writeCharSequence(mText);
1611        parcel.writeCharSequence(mContentDescription);
1612
1613        // Since instances of this class are fetched via synchronous i.e. blocking
1614        // calls in IPCs we always recycle as soon as the instance is marshaled.
1615        recycle();
1616    }
1617
1618    /**
1619     * Initializes this instance from another one.
1620     *
1621     * @param other The other instance.
1622     */
1623    @SuppressWarnings("unchecked")
1624    private void init(AccessibilityNodeInfo other) {
1625        mSealed = other.mSealed;
1626        mSourceNodeId = other.mSourceNodeId;
1627        mParentNodeId = other.mParentNodeId;
1628        mLabelForId = other.mLabelForId;
1629        mLabeledById = other.mLabeledById;
1630        mWindowId = other.mWindowId;
1631        mConnectionId = other.mConnectionId;
1632        mBoundsInParent.set(other.mBoundsInParent);
1633        mBoundsInScreen.set(other.mBoundsInScreen);
1634        mPackageName = other.mPackageName;
1635        mClassName = other.mClassName;
1636        mText = other.mText;
1637        mContentDescription = other.mContentDescription;
1638        mActions= other.mActions;
1639        mBooleanProperties = other.mBooleanProperties;
1640        mMovementGranularities = other.mMovementGranularities;
1641        final int otherChildIdCount = other.mChildNodeIds.size();
1642        for (int i = 0; i < otherChildIdCount; i++) {
1643            mChildNodeIds.put(i, other.mChildNodeIds.valueAt(i));
1644        }
1645    }
1646
1647    /**
1648     * Creates a new instance from a {@link Parcel}.
1649     *
1650     * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}.
1651     */
1652    private void initFromParcel(Parcel parcel) {
1653        mSealed = (parcel.readInt()  == 1);
1654        mSourceNodeId = parcel.readLong();
1655        mWindowId = parcel.readInt();
1656        mParentNodeId = parcel.readLong();
1657        mLabelForId = parcel.readLong();
1658        mLabeledById = parcel.readLong();
1659        mConnectionId = parcel.readInt();
1660
1661        SparseLongArray childIds = mChildNodeIds;
1662        final int childrenSize = parcel.readInt();
1663        for (int i = 0; i < childrenSize; i++) {
1664            final long childId = parcel.readLong();
1665            childIds.put(i, childId);
1666        }
1667
1668        mBoundsInParent.top = parcel.readInt();
1669        mBoundsInParent.bottom = parcel.readInt();
1670        mBoundsInParent.left = parcel.readInt();
1671        mBoundsInParent.right = parcel.readInt();
1672
1673        mBoundsInScreen.top = parcel.readInt();
1674        mBoundsInScreen.bottom = parcel.readInt();
1675        mBoundsInScreen.left = parcel.readInt();
1676        mBoundsInScreen.right = parcel.readInt();
1677
1678        mActions = parcel.readInt();
1679
1680        mMovementGranularities = parcel.readInt();
1681
1682        mBooleanProperties = parcel.readInt();
1683
1684        mPackageName = parcel.readCharSequence();
1685        mClassName = parcel.readCharSequence();
1686        mText = parcel.readCharSequence();
1687        mContentDescription = parcel.readCharSequence();
1688    }
1689
1690    /**
1691     * Clears the state of this instance.
1692     */
1693    private void clear() {
1694        mSealed = false;
1695        mSourceNodeId = ROOT_NODE_ID;
1696        mParentNodeId = ROOT_NODE_ID;
1697        mLabelForId = ROOT_NODE_ID;
1698        mLabeledById = ROOT_NODE_ID;
1699        mWindowId = UNDEFINED;
1700        mConnectionId = UNDEFINED;
1701        mMovementGranularities = 0;
1702        mChildNodeIds.clear();
1703        mBoundsInParent.set(0, 0, 0, 0);
1704        mBoundsInScreen.set(0, 0, 0, 0);
1705        mBooleanProperties = 0;
1706        mPackageName = null;
1707        mClassName = null;
1708        mText = null;
1709        mContentDescription = null;
1710        mActions = 0;
1711    }
1712
1713    /**
1714     * Gets the human readable action symbolic name.
1715     *
1716     * @param action The action.
1717     * @return The symbolic name.
1718     */
1719    private static String getActionSymbolicName(int action) {
1720        switch (action) {
1721            case ACTION_FOCUS:
1722                return "ACTION_FOCUS";
1723            case ACTION_CLEAR_FOCUS:
1724                return "ACTION_CLEAR_FOCUS";
1725            case ACTION_SELECT:
1726                return "ACTION_SELECT";
1727            case ACTION_CLEAR_SELECTION:
1728                return "ACTION_CLEAR_SELECTION";
1729            case ACTION_CLICK:
1730                return "ACTION_CLICK";
1731            case ACTION_LONG_CLICK:
1732                return "ACTION_LONG_CLICK";
1733            case ACTION_ACCESSIBILITY_FOCUS:
1734                return "ACTION_ACCESSIBILITY_FOCUS";
1735            case ACTION_CLEAR_ACCESSIBILITY_FOCUS:
1736                return "ACTION_CLEAR_ACCESSIBILITY_FOCUS";
1737            case ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
1738                return "ACTION_NEXT_AT_MOVEMENT_GRANULARITY";
1739            case ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
1740                return "ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY";
1741            case ACTION_NEXT_HTML_ELEMENT:
1742                return "ACTION_NEXT_HTML_ELEMENT";
1743            case ACTION_PREVIOUS_HTML_ELEMENT:
1744                return "ACTION_PREVIOUS_HTML_ELEMENT";
1745            case ACTION_SCROLL_FORWARD:
1746                return "ACTION_SCROLL_FORWARD";
1747            case ACTION_SCROLL_BACKWARD:
1748                return "ACTION_SCROLL_BACKWARD";
1749            default:
1750                throw new IllegalArgumentException("Unknown action: " + action);
1751        }
1752    }
1753
1754    /**
1755     * Gets the human readable movement granularity symbolic name.
1756     *
1757     * @param granularity The granularity.
1758     * @return The symbolic name.
1759     */
1760    private static String getMovementGranularitySymbolicName(int granularity) {
1761        switch (granularity) {
1762            case MOVEMENT_GRANULARITY_CHARACTER:
1763                return "MOVEMENT_GRANULARITY_CHARACTER";
1764            case MOVEMENT_GRANULARITY_WORD:
1765                return "MOVEMENT_GRANULARITY_WORD";
1766            case MOVEMENT_GRANULARITY_LINE:
1767                return "MOVEMENT_GRANULARITY_LINE";
1768            case MOVEMENT_GRANULARITY_PARAGRAPH:
1769                return "MOVEMENT_GRANULARITY_PARAGRAPH";
1770            case MOVEMENT_GRANULARITY_PAGE:
1771                return "MOVEMENT_GRANULARITY_PAGE";
1772            default:
1773                throw new IllegalArgumentException("Unknown movement granularity: " + granularity);
1774        }
1775    }
1776
1777    private boolean canPerformRequestOverConnection(long accessibilityNodeId) {
1778        return (mWindowId != UNDEFINED
1779                && getAccessibilityViewId(accessibilityNodeId) != UNDEFINED
1780                && mConnectionId != UNDEFINED);
1781    }
1782
1783    @Override
1784    public boolean equals(Object object) {
1785        if (this == object) {
1786            return true;
1787        }
1788        if (object == null) {
1789            return false;
1790        }
1791        if (getClass() != object.getClass()) {
1792            return false;
1793        }
1794        AccessibilityNodeInfo other = (AccessibilityNodeInfo) object;
1795        if (mSourceNodeId != other.mSourceNodeId) {
1796            return false;
1797        }
1798        if (mWindowId != other.mWindowId) {
1799            return false;
1800        }
1801        return true;
1802    }
1803
1804    @Override
1805    public int hashCode() {
1806        final int prime = 31;
1807        int result = 1;
1808        result = prime * result + getAccessibilityViewId(mSourceNodeId);
1809        result = prime * result + getVirtualDescendantId(mSourceNodeId);
1810        result = prime * result + mWindowId;
1811        return result;
1812    }
1813
1814    @Override
1815    public String toString() {
1816        StringBuilder builder = new StringBuilder();
1817        builder.append(super.toString());
1818
1819        if (DEBUG) {
1820            builder.append("; accessibilityViewId: " + getAccessibilityViewId(mSourceNodeId));
1821            builder.append("; virtualDescendantId: " + getVirtualDescendantId(mSourceNodeId));
1822            builder.append("; mParentNodeId: " + mParentNodeId);
1823
1824            int granularities = mMovementGranularities;
1825            builder.append("; MovementGranularities: [");
1826            while (granularities != 0) {
1827                final int granularity = 1 << Integer.numberOfTrailingZeros(granularities);
1828                granularities &= ~granularity;
1829                builder.append(getMovementGranularitySymbolicName(granularity));
1830                if (granularities != 0) {
1831                    builder.append(", ");
1832                }
1833            }
1834            builder.append("]");
1835
1836            SparseLongArray childIds = mChildNodeIds;
1837            builder.append("; childAccessibilityIds: [");
1838            for (int i = 0, count = childIds.size(); i < count; i++) {
1839                builder.append(childIds.valueAt(i));
1840                if (i < count - 1) {
1841                    builder.append(", ");
1842                }
1843            }
1844            builder.append("]");
1845        }
1846
1847        builder.append("; boundsInParent: " + mBoundsInParent);
1848        builder.append("; boundsInScreen: " + mBoundsInScreen);
1849
1850        builder.append("; packageName: ").append(mPackageName);
1851        builder.append("; className: ").append(mClassName);
1852        builder.append("; text: ").append(mText);
1853        builder.append("; contentDescription: ").append(mContentDescription);
1854
1855        builder.append("; checkable: ").append(isCheckable());
1856        builder.append("; checked: ").append(isChecked());
1857        builder.append("; focusable: ").append(isFocusable());
1858        builder.append("; focused: ").append(isFocused());
1859        builder.append("; selected: ").append(isSelected());
1860        builder.append("; clickable: ").append(isClickable());
1861        builder.append("; longClickable: ").append(isLongClickable());
1862        builder.append("; enabled: ").append(isEnabled());
1863        builder.append("; password: ").append(isPassword());
1864        builder.append("; scrollable: " + isScrollable());
1865
1866        builder.append("; [");
1867        for (int actionBits = mActions; actionBits != 0;) {
1868            final int action = 1 << Integer.numberOfTrailingZeros(actionBits);
1869            actionBits &= ~action;
1870            builder.append(getActionSymbolicName(action));
1871            if (actionBits != 0) {
1872                builder.append(", ");
1873            }
1874        }
1875        builder.append("]");
1876
1877        return builder.toString();
1878    }
1879
1880    /**
1881     * @see Parcelable.Creator
1882     */
1883    public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR =
1884            new Parcelable.Creator<AccessibilityNodeInfo>() {
1885        public AccessibilityNodeInfo createFromParcel(Parcel parcel) {
1886            AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain();
1887            info.initFromParcel(parcel);
1888            return info;
1889        }
1890
1891        public AccessibilityNodeInfo[] newArray(int size) {
1892            return new AccessibilityNodeInfo[size];
1893        }
1894    };
1895}
1896