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