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