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