• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view.accessibility;
18 
19 import android.os.Parcelable;
20 import android.view.View;
21 
22 import java.util.ArrayList;
23 import java.util.List;
24 
25 /**
26  * Represents a record in an {@link AccessibilityEvent} and contains information
27  * about state change of its source {@link android.view.View}. When a view fires
28  * an accessibility event it requests from its parent to dispatch the
29  * constructed event. The parent may optionally append a record for itself
30  * for providing more context to
31  * {@link android.accessibilityservice.AccessibilityService}s. Hence,
32  * accessibility services can facilitate additional accessibility records
33  * to enhance feedback.
34  * </p>
35  * <p>
36  * Once the accessibility event containing a record is dispatched the record is
37  * made immutable and calling a state mutation method generates an error.
38  * </p>
39  * <p>
40  * <strong>Note:</strong> Not all properties are applicable to all accessibility
41  * event types. For detailed information please refer to {@link AccessibilityEvent}.
42  * </p>
43  *
44  * <div class="special reference">
45  * <h3>Developer Guides</h3>
46  * <p>For more information about creating and processing AccessibilityRecords, read the
47  * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a>
48  * developer guide.</p>
49  * </div>
50  *
51  * @see AccessibilityEvent
52  * @see AccessibilityManager
53  * @see android.accessibilityservice.AccessibilityService
54  * @see AccessibilityNodeInfo
55  */
56 public class AccessibilityRecord {
57 
58     private static final int UNDEFINED = -1;
59 
60     private static final int PROPERTY_CHECKED = 0x00000001;
61     private static final int PROPERTY_ENABLED = 0x00000002;
62     private static final int PROPERTY_PASSWORD = 0x00000004;
63     private static final int PROPERTY_FULL_SCREEN = 0x00000080;
64     private static final int PROPERTY_SCROLLABLE = 0x00000100;
65     private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200;
66 
67     private static final int GET_SOURCE_PREFETCH_FLAGS =
68         AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS
69         | AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS
70         | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS;
71 
72     // Housekeeping
73     private static final int MAX_POOL_SIZE = 10;
74     private static final Object sPoolLock = new Object();
75     private static AccessibilityRecord sPool;
76     private static int sPoolSize;
77     private AccessibilityRecord mNext;
78     private boolean mIsInPool;
79 
80     boolean mSealed;
81     int mBooleanProperties = 0;
82     int mCurrentItemIndex = UNDEFINED;
83     int mItemCount = UNDEFINED;
84     int mFromIndex = UNDEFINED;
85     int mToIndex = UNDEFINED;
86     int mScrollX = UNDEFINED;
87     int mScrollY = UNDEFINED;
88     int mMaxScrollX = UNDEFINED;
89     int mMaxScrollY = UNDEFINED;
90 
91     int mAddedCount= UNDEFINED;
92     int mRemovedCount = UNDEFINED;
93     AccessibilityNodeInfo mSourceNode;
94     int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
95 
96     CharSequence mClassName;
97     CharSequence mContentDescription;
98     CharSequence mBeforeText;
99     Parcelable mParcelableData;
100 
101     final List<CharSequence> mText = new ArrayList<CharSequence>();
102 
103     int mConnectionId = UNDEFINED;
104 
105     /*
106      * Hide constructor.
107      */
AccessibilityRecord()108     AccessibilityRecord() {
109     }
110 
111     /**
112      * Sets the event source.
113      *
114      * @param source The source.
115      *
116      * @throws IllegalStateException If called from an AccessibilityService.
117      */
setSource(View source)118     public void setSource(View source) {
119         setSource(source, UNDEFINED);
120     }
121 
122     /**
123      * Sets the source to be a virtual descendant of the given <code>root</code>.
124      * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root
125      * is set as the source.
126      * <p>
127      * A virtual descendant is an imaginary View that is reported as a part of the view
128      * hierarchy for accessibility purposes. This enables custom views that draw complex
129      * content to report them selves as a tree of virtual views, thus conveying their
130      * logical structure.
131      * </p>
132      *
133      * @param root The root of the virtual subtree.
134      * @param virtualDescendantId The id of the virtual descendant.
135      */
setSource(View root, int virtualDescendantId)136     public void setSource(View root, int virtualDescendantId) {
137         enforceNotSealed();
138         boolean important = true;
139         mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
140         clearSourceNode();
141         if (root != null) {
142             if (virtualDescendantId == View.NO_ID
143                     || virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID
144                     || virtualDescendantId == AccessibilityNodeProvider.HOST_VIEW_ID) {
145                 important = root.isImportantForAccessibility();
146                 mSourceNode = root.createAccessibilityNodeInfo();
147             } else {
148                 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
149                 if (provider != null) {
150                     mSourceNode = provider.createAccessibilityNodeInfo(virtualDescendantId);
151                 }
152             }
153 
154             mSourceWindowId = root.getAccessibilityWindowId();
155         }
156         setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
157     }
158 
159     /**
160      * Set the source directly to an AccessibilityNodeInfo rather than indirectly via a View
161      *
162      * @param info The source
163      *
164      * @hide
165      */
setSource(AccessibilityNodeInfo info)166     public void setSource(AccessibilityNodeInfo info) {
167         enforceNotSealed();
168         clearSourceNode();
169         mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
170         if (info != null) {
171             mSourceNode = AccessibilityNodeInfo.obtain(info);
172             setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY,
173                     mSourceNode.isImportantForAccessibility());
174             mSourceWindowId = info.getWindowId();
175         }
176     }
177 
178     /**
179      * Gets the {@link AccessibilityNodeInfo} of the event source.
180      * <p>
181      *   <strong>Note:</strong> It is a client responsibility to recycle the received info
182      *   by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()}
183      *   to avoid creating of multiple instances.
184      * </p>
185      * @return The info of the source.
186      */
getSource()187     public AccessibilityNodeInfo getSource() {
188         enforceSealed();
189         if (mSourceNode != null) {
190             return AccessibilityNodeInfo.obtain(mSourceNode);
191         }
192 
193         return null;
194     }
195 
196     /**
197      * Sets the window id.
198      *
199      * @param windowId The window id.
200      *
201      * @hide
202      */
setWindowId(int windowId)203     public void setWindowId(int windowId) {
204         mSourceWindowId = windowId;
205     }
206 
207     /**
208      * Gets the id of the window from which the event comes from.
209      *
210      * @return The window id.
211      */
getWindowId()212     public int getWindowId() {
213         return mSourceWindowId;
214     }
215 
216     /**
217      * Gets if the source is checked.
218      *
219      * @return True if the view is checked, false otherwise.
220      */
isChecked()221     public boolean isChecked() {
222         return getBooleanProperty(PROPERTY_CHECKED);
223     }
224 
225     /**
226      * Sets if the source is checked.
227      *
228      * @param isChecked True if the view is checked, false otherwise.
229      *
230      * @throws IllegalStateException If called from an AccessibilityService.
231      */
setChecked(boolean isChecked)232     public void setChecked(boolean isChecked) {
233         enforceNotSealed();
234         setBooleanProperty(PROPERTY_CHECKED, isChecked);
235     }
236 
237     /**
238      * Gets if the source is enabled.
239      *
240      * @return True if the view is enabled, false otherwise.
241      */
isEnabled()242     public boolean isEnabled() {
243         return getBooleanProperty(PROPERTY_ENABLED);
244     }
245 
246     /**
247      * Sets if the source is enabled.
248      *
249      * @param isEnabled True if the view is enabled, false otherwise.
250      *
251      * @throws IllegalStateException If called from an AccessibilityService.
252      */
setEnabled(boolean isEnabled)253     public void setEnabled(boolean isEnabled) {
254         enforceNotSealed();
255         setBooleanProperty(PROPERTY_ENABLED, isEnabled);
256     }
257 
258     /**
259      * Gets if the source is a password field.
260      *
261      * @return True if the view is a password field, false otherwise.
262      */
isPassword()263     public boolean isPassword() {
264         return getBooleanProperty(PROPERTY_PASSWORD);
265     }
266 
267     /**
268      * Sets if the source is a password field.
269      *
270      * @param isPassword True if the view is a password field, false otherwise.
271      *
272      * @throws IllegalStateException If called from an AccessibilityService.
273      */
setPassword(boolean isPassword)274     public void setPassword(boolean isPassword) {
275         enforceNotSealed();
276         setBooleanProperty(PROPERTY_PASSWORD, isPassword);
277     }
278 
279     /**
280      * Gets if the source is taking the entire screen.
281      *
282      * @return True if the source is full screen, false otherwise.
283      */
isFullScreen()284     public boolean isFullScreen() {
285         return getBooleanProperty(PROPERTY_FULL_SCREEN);
286     }
287 
288     /**
289      * Sets if the source is taking the entire screen.
290      *
291      * @param isFullScreen True if the source is full screen, false otherwise.
292      *
293      * @throws IllegalStateException If called from an AccessibilityService.
294      */
setFullScreen(boolean isFullScreen)295     public void setFullScreen(boolean isFullScreen) {
296         enforceNotSealed();
297         setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
298     }
299 
300     /**
301      * Gets if the source is scrollable.
302      *
303      * @return True if the source is scrollable, false otherwise.
304      */
isScrollable()305     public boolean isScrollable() {
306         return getBooleanProperty(PROPERTY_SCROLLABLE);
307     }
308 
309     /**
310      * Sets if the source is scrollable.
311      *
312      * @param scrollable True if the source is scrollable, false otherwise.
313      *
314      * @throws IllegalStateException If called from an AccessibilityService.
315      */
setScrollable(boolean scrollable)316     public void setScrollable(boolean scrollable) {
317         enforceNotSealed();
318         setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
319     }
320 
321     /**
322      * Gets if the source is important for accessibility.
323      *
324      * <strong>Note:</strong> Used only internally to determine whether
325      * to deliver the event to a given accessibility service since some
326      * services may want to regard all views for accessibility while others
327      * may want to regard only the important views for accessibility.
328      *
329      * @return True if the source is important for accessibility,
330      *        false otherwise.
331      *
332      * @hide
333      */
isImportantForAccessibility()334     public boolean isImportantForAccessibility() {
335         return getBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY);
336     }
337 
338     /**
339      * Gets the number of items that can be visited.
340      *
341      * @return The number of items.
342      */
getItemCount()343     public int getItemCount() {
344         return mItemCount;
345     }
346 
347     /**
348      * Sets the number of items that can be visited.
349      *
350      * @param itemCount The number of items.
351      *
352      * @throws IllegalStateException If called from an AccessibilityService.
353      */
setItemCount(int itemCount)354     public void setItemCount(int itemCount) {
355         enforceNotSealed();
356         mItemCount = itemCount;
357     }
358 
359     /**
360      * Gets the index of the source in the list of items the can be visited.
361      *
362      * @return The current item index.
363      */
getCurrentItemIndex()364     public int getCurrentItemIndex() {
365         return mCurrentItemIndex;
366     }
367 
368     /**
369      * Sets the index of the source in the list of items that can be visited.
370      *
371      * @param currentItemIndex The current item index.
372      *
373      * @throws IllegalStateException If called from an AccessibilityService.
374      */
setCurrentItemIndex(int currentItemIndex)375     public void setCurrentItemIndex(int currentItemIndex) {
376         enforceNotSealed();
377         mCurrentItemIndex = currentItemIndex;
378     }
379 
380     /**
381      * Gets the index of the first character of the changed sequence,
382      * or the beginning of a text selection or the index of the first
383      * visible item when scrolling.
384      *
385      * @return The index of the first character or selection
386      *        start or the first visible item.
387      */
getFromIndex()388     public int getFromIndex() {
389         return mFromIndex;
390     }
391 
392     /**
393      * Sets the index of the first character of the changed sequence
394      * or the beginning of a text selection or the index of the first
395      * visible item when scrolling.
396      *
397      * @param fromIndex The index of the first character or selection
398      *        start or the first visible item.
399      *
400      * @throws IllegalStateException If called from an AccessibilityService.
401      */
setFromIndex(int fromIndex)402     public void setFromIndex(int fromIndex) {
403         enforceNotSealed();
404         mFromIndex = fromIndex;
405     }
406 
407     /**
408      * Gets the index of text selection end or the index of the last
409      * visible item when scrolling.
410      *
411      * @return The index of selection end or last item index.
412      */
getToIndex()413     public int getToIndex() {
414         return mToIndex;
415     }
416 
417     /**
418      * Sets the index of text selection end or the index of the last
419      * visible item when scrolling.
420      *
421      * @param toIndex The index of selection end or last item index.
422      */
setToIndex(int toIndex)423     public void setToIndex(int toIndex) {
424         enforceNotSealed();
425         mToIndex = toIndex;
426     }
427 
428     /**
429      * Gets the scroll offset of the source left edge in pixels.
430      *
431      * @return The scroll.
432      */
getScrollX()433     public int getScrollX() {
434         return mScrollX;
435     }
436 
437     /**
438      * Sets the scroll offset of the source left edge in pixels.
439      *
440      * @param scrollX The scroll.
441      */
setScrollX(int scrollX)442     public void setScrollX(int scrollX) {
443         enforceNotSealed();
444         mScrollX = scrollX;
445     }
446 
447     /**
448      * Gets the scroll offset of the source top edge in pixels.
449      *
450      * @return The scroll.
451      */
getScrollY()452     public int getScrollY() {
453         return mScrollY;
454     }
455 
456     /**
457      * Sets the scroll offset of the source top edge in pixels.
458      *
459      * @param scrollY The scroll.
460      */
setScrollY(int scrollY)461     public void setScrollY(int scrollY) {
462         enforceNotSealed();
463         mScrollY = scrollY;
464     }
465 
466     /**
467      * Gets the max scroll offset of the source left edge in pixels.
468      *
469      * @return The max scroll.
470      */
getMaxScrollX()471     public int getMaxScrollX() {
472         return mMaxScrollX;
473     }
474 
475     /**
476      * Sets the max scroll offset of the source left edge in pixels.
477      *
478      * @param maxScrollX The max scroll.
479      */
setMaxScrollX(int maxScrollX)480     public void setMaxScrollX(int maxScrollX) {
481         enforceNotSealed();
482         mMaxScrollX = maxScrollX;
483     }
484 
485     /**
486      * Gets the max scroll offset of the source top edge in pixels.
487      *
488      * @return The max scroll.
489      */
getMaxScrollY()490     public int getMaxScrollY() {
491         return mMaxScrollY;
492     }
493 
494     /**
495      * Sets the max scroll offset of the source top edge in pixels.
496      *
497      * @param maxScrollY The max scroll.
498      */
setMaxScrollY(int maxScrollY)499     public void setMaxScrollY(int maxScrollY) {
500         enforceNotSealed();
501         mMaxScrollY = maxScrollY;
502     }
503 
504     /**
505      * Gets the number of added characters.
506      *
507      * @return The number of added characters.
508      */
getAddedCount()509     public int getAddedCount() {
510         return mAddedCount;
511     }
512 
513     /**
514      * Sets the number of added characters.
515      *
516      * @param addedCount The number of added characters.
517      *
518      * @throws IllegalStateException If called from an AccessibilityService.
519      */
setAddedCount(int addedCount)520     public void setAddedCount(int addedCount) {
521         enforceNotSealed();
522         mAddedCount = addedCount;
523     }
524 
525     /**
526      * Gets the number of removed characters.
527      *
528      * @return The number of removed characters.
529      */
getRemovedCount()530     public int getRemovedCount() {
531         return mRemovedCount;
532     }
533 
534     /**
535      * Sets the number of removed characters.
536      *
537      * @param removedCount The number of removed characters.
538      *
539      * @throws IllegalStateException If called from an AccessibilityService.
540      */
setRemovedCount(int removedCount)541     public void setRemovedCount(int removedCount) {
542         enforceNotSealed();
543         mRemovedCount = removedCount;
544     }
545 
546     /**
547      * Gets the class name of the source.
548      *
549      * @return The class name.
550      */
getClassName()551     public CharSequence getClassName() {
552         return mClassName;
553     }
554 
555     /**
556      * Sets the class name of the source.
557      *
558      * @param className The lass name.
559      *
560      * @throws IllegalStateException If called from an AccessibilityService.
561      */
setClassName(CharSequence className)562     public void setClassName(CharSequence className) {
563         enforceNotSealed();
564         mClassName = className;
565     }
566 
567     /**
568      * Gets the text of the event. The index in the list represents the priority
569      * of the text. Specifically, the lower the index the higher the priority.
570      *
571      * @return The text.
572      */
getText()573     public List<CharSequence> getText() {
574         return mText;
575     }
576 
577     /**
578      * Sets the text before a change.
579      *
580      * @return The text before the change.
581      */
getBeforeText()582     public CharSequence getBeforeText() {
583         return mBeforeText;
584     }
585 
586     /**
587      * Sets the text before a change.
588      *
589      * @param beforeText The text before the change.
590      *
591      * @throws IllegalStateException If called from an AccessibilityService.
592      */
setBeforeText(CharSequence beforeText)593     public void setBeforeText(CharSequence beforeText) {
594         enforceNotSealed();
595         mBeforeText = (beforeText == null) ? null
596                 : beforeText.subSequence(0, beforeText.length());
597     }
598 
599     /**
600      * Gets the description of the source.
601      *
602      * @return The description.
603      */
getContentDescription()604     public CharSequence getContentDescription() {
605         return mContentDescription;
606     }
607 
608     /**
609      * Sets the description of the source.
610      *
611      * @param contentDescription The description.
612      *
613      * @throws IllegalStateException If called from an AccessibilityService.
614      */
setContentDescription(CharSequence contentDescription)615     public void setContentDescription(CharSequence contentDescription) {
616         enforceNotSealed();
617         mContentDescription = (contentDescription == null) ? null
618                 : contentDescription.subSequence(0, contentDescription.length());
619     }
620 
621     /**
622      * Gets the {@link Parcelable} data.
623      *
624      * @return The parcelable data.
625      */
getParcelableData()626     public Parcelable getParcelableData() {
627         return mParcelableData;
628     }
629 
630     /**
631      * Sets the {@link Parcelable} data of the event.
632      *
633      * @param parcelableData The parcelable data.
634      *
635      * @throws IllegalStateException If called from an AccessibilityService.
636      */
setParcelableData(Parcelable parcelableData)637     public void setParcelableData(Parcelable parcelableData) {
638         enforceNotSealed();
639         mParcelableData = parcelableData;
640     }
641 
642     /**
643      * Gets the id of the source node.
644      *
645      * @return The id.
646      *
647      * @hide
648      */
getSourceNodeId()649     public long getSourceNodeId() {
650         return mSourceNode != null ? mSourceNode.getSourceNodeId() : UNDEFINED;
651     }
652 
653     /**
654      * Sets the unique id of the IAccessibilityServiceConnection over which
655      * this instance can send requests to the system.
656      *
657      * @param connectionId The connection id.
658      *
659      * @hide
660      */
setConnectionId(int connectionId)661     public void setConnectionId(int connectionId) {
662         enforceNotSealed();
663         mConnectionId = connectionId;
664         if (mSourceNode != null) {
665             mSourceNode.setConnectionId(mConnectionId);
666         }
667     }
668 
669     /**
670      * Sets if this instance is sealed.
671      *
672      * @param sealed Whether is sealed.
673      *
674      * @hide
675      */
setSealed(boolean sealed)676     public void setSealed(boolean sealed) {
677         mSealed = sealed;
678         if (mSourceNode != null) {
679             mSourceNode.setSealed(sealed);
680         }
681     }
682 
683     /**
684      * Gets if this instance is sealed.
685      *
686      * @return Whether is sealed.
687      */
isSealed()688     boolean isSealed() {
689         return mSealed;
690     }
691 
692     /**
693      * Enforces that this instance is sealed.
694      *
695      * @throws IllegalStateException If this instance is not sealed.
696      */
enforceSealed()697     void enforceSealed() {
698         if (!isSealed()) {
699             throw new IllegalStateException("Cannot perform this "
700                     + "action on a not sealed instance.");
701         }
702     }
703 
704     /**
705      * Enforces that this instance is not sealed.
706      *
707      * @throws IllegalStateException If this instance is sealed.
708      */
enforceNotSealed()709     void enforceNotSealed() {
710         if (isSealed()) {
711             throw new IllegalStateException("Cannot perform this "
712                     + "action on a sealed instance.");
713         }
714     }
715 
716     /**
717      * Gets the value of a boolean property.
718      *
719      * @param property The property.
720      * @return The value.
721      */
getBooleanProperty(int property)722     private boolean getBooleanProperty(int property) {
723         return (mBooleanProperties & property) == property;
724     }
725 
726     /**
727      * Sets a boolean property.
728      *
729      * @param property The property.
730      * @param value The value.
731      */
setBooleanProperty(int property, boolean value)732     private void setBooleanProperty(int property, boolean value) {
733         if (value) {
734             mBooleanProperties |= property;
735         } else {
736             mBooleanProperties &= ~property;
737         }
738     }
739 
740     /**
741      * Returns a cached instance if such is available or a new one is
742      * instantiated. The instance is initialized with data from the
743      * given record.
744      *
745      * @return An instance.
746      */
obtain(AccessibilityRecord record)747     public static AccessibilityRecord obtain(AccessibilityRecord record) {
748        AccessibilityRecord clone = AccessibilityRecord.obtain();
749        clone.init(record);
750        return clone;
751     }
752 
753     /**
754      * Returns a cached instance if such is available or a new one is
755      * instantiated.
756      *
757      * @return An instance.
758      */
obtain()759     public static AccessibilityRecord obtain() {
760         synchronized (sPoolLock) {
761             if (sPool != null) {
762                 AccessibilityRecord record = sPool;
763                 sPool = sPool.mNext;
764                 sPoolSize--;
765                 record.mNext = null;
766                 record.mIsInPool = false;
767                 return record;
768             }
769             return new AccessibilityRecord();
770         }
771     }
772 
773     /**
774      * Return an instance back to be reused.
775      * <p>
776      * <strong>Note:</strong> You must not touch the object after calling this function.
777      *
778      * @throws IllegalStateException If the record is already recycled.
779      */
recycle()780     public void recycle() {
781         if (mIsInPool) {
782             throw new IllegalStateException("Record already recycled!");
783         }
784         clear();
785         synchronized (sPoolLock) {
786             if (sPoolSize <= MAX_POOL_SIZE) {
787                 mNext = sPool;
788                 sPool = this;
789                 mIsInPool = true;
790                 sPoolSize++;
791             }
792         }
793     }
794 
795     /**
796      * Initialize this record from another one.
797      *
798      * @param record The to initialize from.
799      */
init(AccessibilityRecord record)800     void init(AccessibilityRecord record) {
801         mSealed = record.mSealed;
802         mBooleanProperties = record.mBooleanProperties;
803         mCurrentItemIndex = record.mCurrentItemIndex;
804         mItemCount = record.mItemCount;
805         mFromIndex = record.mFromIndex;
806         mToIndex = record.mToIndex;
807         mScrollX = record.mScrollX;
808         mScrollY = record.mScrollY;
809         mMaxScrollX = record.mMaxScrollX;
810         mMaxScrollY = record.mMaxScrollY;
811         mAddedCount = record.mAddedCount;
812         mRemovedCount = record.mRemovedCount;
813         mClassName = record.mClassName;
814         mContentDescription = record.mContentDescription;
815         mBeforeText = record.mBeforeText;
816         mParcelableData = record.mParcelableData;
817         mText.addAll(record.mText);
818         mSourceWindowId = record.mSourceWindowId;
819         if (record.mSourceNode != null) {
820             mSourceNode = AccessibilityNodeInfo.obtain(record.mSourceNode);
821         }
822         mConnectionId = record.mConnectionId;
823     }
824 
825     /**
826      * Clears the state of this instance.
827      */
clear()828     void clear() {
829         mSealed = false;
830         mBooleanProperties = 0;
831         mCurrentItemIndex = UNDEFINED;
832         mItemCount = UNDEFINED;
833         mFromIndex = UNDEFINED;
834         mToIndex = UNDEFINED;
835         mScrollX = UNDEFINED;
836         mScrollY = UNDEFINED;
837         mMaxScrollX = UNDEFINED;
838         mMaxScrollY = UNDEFINED;
839         mAddedCount = UNDEFINED;
840         mRemovedCount = UNDEFINED;
841         mClassName = null;
842         mContentDescription = null;
843         mBeforeText = null;
844         mParcelableData = null;
845         mText.clear();
846         clearSourceNode();
847         mSourceWindowId = UNDEFINED;
848         mConnectionId = UNDEFINED;
849     }
850 
clearSourceNode()851     private void clearSourceNode() {
852         if (mSourceNode != null) {
853             mSourceNode.recycle();
854             mSourceNode = null;
855         }
856 
857     }
858 
859     @Override
toString()860     public String toString() {
861         StringBuilder builder = new StringBuilder();
862         builder.append(" [ ClassName: " + mClassName);
863         builder.append("; Text: " + mText);
864         builder.append("; ContentDescription: " + mContentDescription);
865         builder.append("; ItemCount: " + mItemCount);
866         builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
867         builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED));
868         builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD));
869         builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED));
870         builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN));
871         builder.append("; Scrollable: " + getBooleanProperty(PROPERTY_SCROLLABLE));
872         builder.append("; BeforeText: " + mBeforeText);
873         builder.append("; FromIndex: " + mFromIndex);
874         builder.append("; ToIndex: " + mToIndex);
875         builder.append("; ScrollX: " + mScrollX);
876         builder.append("; ScrollY: " + mScrollY);
877         builder.append("; MaxScrollX: " + mMaxScrollX);
878         builder.append("; MaxScrollY: " + mMaxScrollY);
879         builder.append("; AddedCount: " + mAddedCount);
880         builder.append("; RemovedCount: " + mRemovedCount);
881         builder.append("; ParcelableData: " + mParcelableData);
882         builder.append(" ]");
883         return builder.toString();
884     }
885 }
886