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 = 1 /* << 0 */;
70     private static final int PROPERTY_ENABLED = 1 << 1;
71     private static final int PROPERTY_PASSWORD = 1 << 2;
72     private static final int PROPERTY_FULL_SCREEN = 1 << 7;
73     private static final int PROPERTY_SCROLLABLE = 1 << 8;
74     private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 1 << 9;
75     private static final int PROPERTY_ACCESSIBILITY_DATA_SENSITIVE = 1 << 10;
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      */
getSource( @ccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy)201     public @Nullable AccessibilityNodeInfo getSource(
202             @AccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy) {
203         enforceSealed();
204         if ((mConnectionId == UNDEFINED)
205                 || (mSourceWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
206                 || (AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId)
207                 == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)) {
208             return null;
209         }
210         AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
211         return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId,
212                 mSourceNodeId, false, prefetchingStrategy, null);
213     }
214 
215     /**
216      * Sets the display id.
217      *
218      * @param displayId The displayId id.
219      *
220      * @hide
221      */
222     @TestApi
setDisplayId(int displayId)223     public void setDisplayId(int displayId) {
224         mSourceDisplayId = displayId;
225     }
226 
227     /**
228      * Gets the id of the display from which the event comes from.
229      *
230      * @return The display id.
231      */
getDisplayId()232     public int getDisplayId() {
233         return mSourceDisplayId;
234     }
235 
236     /**
237      * Sets the window id.
238      *
239      * @param windowId The window id.
240      *
241      * @hide
242      */
setWindowId(int windowId)243     public void setWindowId(int windowId) {
244         mSourceWindowId = windowId;
245     }
246 
247     /**
248      * Gets the id of the window from which the event comes from.
249      *
250      * @return The window id.
251      */
getWindowId()252     public int getWindowId() {
253         return mSourceWindowId;
254     }
255 
256     /**
257      * Gets if the source is checked.
258      *
259      * @return True if the view is checked, false otherwise.
260      */
isChecked()261     public boolean isChecked() {
262         return getBooleanProperty(PROPERTY_CHECKED);
263     }
264 
265     /**
266      * Sets if the source is checked.
267      *
268      * @param isChecked True if the view is checked, false otherwise.
269      *
270      * @throws IllegalStateException If called from an AccessibilityService.
271      */
setChecked(boolean isChecked)272     public void setChecked(boolean isChecked) {
273         enforceNotSealed();
274         setBooleanProperty(PROPERTY_CHECKED, isChecked);
275     }
276 
277     /**
278      * Gets if the source is enabled.
279      *
280      * @return True if the view is enabled, false otherwise.
281      */
isEnabled()282     public boolean isEnabled() {
283         return getBooleanProperty(PROPERTY_ENABLED);
284     }
285 
286     /**
287      * Sets if the source is enabled.
288      *
289      * @param isEnabled True if the view is enabled, false otherwise.
290      *
291      * @throws IllegalStateException If called from an AccessibilityService.
292      */
setEnabled(boolean isEnabled)293     public void setEnabled(boolean isEnabled) {
294         enforceNotSealed();
295         setBooleanProperty(PROPERTY_ENABLED, isEnabled);
296     }
297 
298     /**
299      * Gets if the source is a password field.
300      *
301      * @return True if the view is a password field, false otherwise.
302      */
isPassword()303     public boolean isPassword() {
304         return getBooleanProperty(PROPERTY_PASSWORD);
305     }
306 
307     /**
308      * Sets if the source is a password field.
309      *
310      * @param isPassword True if the view is a password field, false otherwise.
311      *
312      * @throws IllegalStateException If called from an AccessibilityService.
313      */
setPassword(boolean isPassword)314     public void setPassword(boolean isPassword) {
315         enforceNotSealed();
316         setBooleanProperty(PROPERTY_PASSWORD, isPassword);
317     }
318 
319     /**
320      * Gets if the source is taking the entire screen.
321      *
322      * @return True if the source is full screen, false otherwise.
323      */
isFullScreen()324     public boolean isFullScreen() {
325         return getBooleanProperty(PROPERTY_FULL_SCREEN);
326     }
327 
328     /**
329      * Sets if the source is taking the entire screen.
330      *
331      * @param isFullScreen True if the source is full screen, false otherwise.
332      *
333      * @throws IllegalStateException If called from an AccessibilityService.
334      */
setFullScreen(boolean isFullScreen)335     public void setFullScreen(boolean isFullScreen) {
336         enforceNotSealed();
337         setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
338     }
339 
340     /**
341      * Gets if the source is scrollable.
342      *
343      * @return True if the source is scrollable, false otherwise.
344      */
isScrollable()345     public boolean isScrollable() {
346         return getBooleanProperty(PROPERTY_SCROLLABLE);
347     }
348 
349     /**
350      * Sets if the source is scrollable.
351      *
352      * @param scrollable True if the source is scrollable, false otherwise.
353      *
354      * @throws IllegalStateException If called from an AccessibilityService.
355      */
setScrollable(boolean scrollable)356     public void setScrollable(boolean scrollable) {
357         enforceNotSealed();
358         setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
359     }
360 
361     /**
362      * Gets if the source is important for accessibility.
363      *
364      * <strong>Note:</strong> Used only internally to determine whether
365      * to deliver the event to a given accessibility service since some
366      * services may want to regard all views for accessibility while others
367      * may want to regard only the important views for accessibility.
368      *
369      * @return True if the source is important for accessibility,
370      *        false otherwise.
371      *
372      * @hide
373      */
isImportantForAccessibility()374     public boolean isImportantForAccessibility() {
375         return getBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY);
376     }
377 
378     /**
379      * Sets if the source is important for accessibility.
380      *
381      * @param importantForAccessibility True if the source is important for accessibility,
382      *                                  false otherwise.
383      *
384      * @throws IllegalStateException If called from an AccessibilityService.
385      * @hide
386      */
setImportantForAccessibility(boolean importantForAccessibility)387     public void setImportantForAccessibility(boolean importantForAccessibility) {
388         enforceNotSealed();
389         setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, importantForAccessibility);
390     }
391 
392     /**
393      * @see AccessibilityEvent#isAccessibilityDataSensitive
394      * @hide
395      */
isAccessibilityDataSensitive()396     boolean isAccessibilityDataSensitive() {
397         return getBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_SENSITIVE);
398     }
399 
400     /**
401      * @see AccessibilityEvent#setAccessibilityDataSensitive
402      * @hide
403      */
setAccessibilityDataSensitive(boolean accessibilityDataSensitive)404     void setAccessibilityDataSensitive(boolean accessibilityDataSensitive) {
405         enforceNotSealed();
406         setBooleanProperty(PROPERTY_ACCESSIBILITY_DATA_SENSITIVE, accessibilityDataSensitive);
407     }
408 
409     /**
410      * Gets the number of items that can be visited.
411      *
412      * @return The number of items.
413      */
getItemCount()414     public int getItemCount() {
415         return mItemCount;
416     }
417 
418     /**
419      * Sets the number of items that can be visited.
420      *
421      * @param itemCount The number of items.
422      *
423      * @throws IllegalStateException If called from an AccessibilityService.
424      */
setItemCount(int itemCount)425     public void setItemCount(int itemCount) {
426         enforceNotSealed();
427         mItemCount = itemCount;
428     }
429 
430     /**
431      * Gets the index of the source in the list of items the can be visited.
432      *
433      * @return The current item index.
434      */
getCurrentItemIndex()435     public int getCurrentItemIndex() {
436         return mCurrentItemIndex;
437     }
438 
439     /**
440      * Sets the index of the source in the list of items that can be visited.
441      *
442      * @param currentItemIndex The current item index.
443      *
444      * @throws IllegalStateException If called from an AccessibilityService.
445      */
setCurrentItemIndex(int currentItemIndex)446     public void setCurrentItemIndex(int currentItemIndex) {
447         enforceNotSealed();
448         mCurrentItemIndex = currentItemIndex;
449     }
450 
451     /**
452      * Gets the index of the first character of the changed sequence,
453      * or the beginning of a text selection or the index of the first
454      * visible item when scrolling.
455      *
456      * @return The index of the first character or selection
457      *        start or the first visible item.
458      */
getFromIndex()459     public int getFromIndex() {
460         return mFromIndex;
461     }
462 
463     /**
464      * Sets the index of the first character of the changed sequence
465      * or the beginning of a text selection or the index of the first
466      * visible item when scrolling.
467      *
468      * @param fromIndex The index of the first character or selection
469      *        start or the first visible item.
470      *
471      * @throws IllegalStateException If called from an AccessibilityService.
472      */
setFromIndex(int fromIndex)473     public void setFromIndex(int fromIndex) {
474         enforceNotSealed();
475         mFromIndex = fromIndex;
476     }
477 
478     /**
479      * Gets the index of text selection end or the index of the last
480      * visible item when scrolling.
481      *
482      * @return The index of selection end or last item index.
483      */
getToIndex()484     public int getToIndex() {
485         return mToIndex;
486     }
487 
488     /**
489      * Sets the index of text selection end or the index of the last
490      * visible item when scrolling.
491      *
492      * @param toIndex The index of selection end or last item index.
493      */
setToIndex(int toIndex)494     public void setToIndex(int toIndex) {
495         enforceNotSealed();
496         mToIndex = toIndex;
497     }
498 
499     /**
500      * Gets the scroll offset of the source left edge in pixels.
501      *
502      * @return The scroll.
503      */
getScrollX()504     public int getScrollX() {
505         return mScrollX;
506     }
507 
508     /**
509      * Sets the scroll offset of the source left edge in pixels.
510      *
511      * @param scrollX The scroll.
512      */
setScrollX(int scrollX)513     public void setScrollX(int scrollX) {
514         enforceNotSealed();
515         mScrollX = scrollX;
516     }
517 
518     /**
519      * Gets the scroll offset of the source top edge in pixels.
520      *
521      * @return The scroll.
522      */
getScrollY()523     public int getScrollY() {
524         return mScrollY;
525     }
526 
527     /**
528      * Sets the scroll offset of the source top edge in pixels.
529      *
530      * @param scrollY The scroll.
531      */
setScrollY(int scrollY)532     public void setScrollY(int scrollY) {
533         enforceNotSealed();
534         mScrollY = scrollY;
535     }
536 
537     /**
538      * Gets the difference in pixels between the horizontal position before the scroll and the
539      * current horizontal position
540      *
541      * @return the scroll delta x
542      */
getScrollDeltaX()543     public int getScrollDeltaX() {
544         return mScrollDeltaX;
545     }
546 
547     /**
548      * Sets the difference in pixels between the horizontal position before the scroll and the
549      * current horizontal position
550      *
551      * @param scrollDeltaX the scroll delta x
552      */
setScrollDeltaX(int scrollDeltaX)553     public void setScrollDeltaX(int scrollDeltaX) {
554         enforceNotSealed();
555         mScrollDeltaX = scrollDeltaX;
556     }
557 
558     /**
559      * Gets the difference in pixels between the vertical position before the scroll and the
560      * current vertical position
561      *
562      * @return the scroll delta y
563      */
getScrollDeltaY()564     public int getScrollDeltaY() {
565         return mScrollDeltaY;
566     }
567 
568     /**
569      * Sets the difference in pixels between the vertical position before the scroll and the
570      * current vertical position
571      *
572      * @param scrollDeltaY the scroll delta y
573      */
setScrollDeltaY(int scrollDeltaY)574     public void setScrollDeltaY(int scrollDeltaY) {
575         enforceNotSealed();
576         mScrollDeltaY = scrollDeltaY;
577     }
578 
579     /**
580      * Gets the max scroll offset of the source left edge in pixels.
581      *
582      * @return The max scroll.
583      */
getMaxScrollX()584     public int getMaxScrollX() {
585         return mMaxScrollX;
586     }
587 
588     /**
589      * Sets the max scroll offset of the source left edge in pixels.
590      *
591      * @param maxScrollX The max scroll.
592      */
setMaxScrollX(int maxScrollX)593     public void setMaxScrollX(int maxScrollX) {
594         enforceNotSealed();
595         mMaxScrollX = maxScrollX;
596     }
597 
598     /**
599      * Gets the max scroll offset of the source top edge in pixels.
600      *
601      * @return The max scroll.
602      */
getMaxScrollY()603     public int getMaxScrollY() {
604         return mMaxScrollY;
605     }
606 
607     /**
608      * Sets the max scroll offset of the source top edge in pixels.
609      *
610      * @param maxScrollY The max scroll.
611      */
setMaxScrollY(int maxScrollY)612     public void setMaxScrollY(int maxScrollY) {
613         enforceNotSealed();
614         mMaxScrollY = maxScrollY;
615     }
616 
617     /**
618      * Gets the number of added characters.
619      *
620      * @return The number of added characters.
621      */
getAddedCount()622     public int getAddedCount() {
623         return mAddedCount;
624     }
625 
626     /**
627      * Sets the number of added characters.
628      *
629      * @param addedCount The number of added characters.
630      *
631      * @throws IllegalStateException If called from an AccessibilityService.
632      */
setAddedCount(int addedCount)633     public void setAddedCount(int addedCount) {
634         enforceNotSealed();
635         mAddedCount = addedCount;
636     }
637 
638     /**
639      * Gets the number of removed characters.
640      *
641      * @return The number of removed characters.
642      */
getRemovedCount()643     public int getRemovedCount() {
644         return mRemovedCount;
645     }
646 
647     /**
648      * Sets the number of removed characters.
649      *
650      * @param removedCount The number of removed characters.
651      *
652      * @throws IllegalStateException If called from an AccessibilityService.
653      */
setRemovedCount(int removedCount)654     public void setRemovedCount(int removedCount) {
655         enforceNotSealed();
656         mRemovedCount = removedCount;
657     }
658 
659     /**
660      * Gets the class name of the source.
661      *
662      * @return The class name.
663      */
getClassName()664     public @Nullable CharSequence getClassName() {
665         return mClassName;
666     }
667 
668     /**
669      * Sets the class name of the source.
670      *
671      * @param className The lass name.
672      *
673      * @throws IllegalStateException If called from an AccessibilityService.
674      */
setClassName(@ullable CharSequence className)675     public void setClassName(@Nullable CharSequence className) {
676         enforceNotSealed();
677         mClassName = className;
678     }
679 
680     /**
681      * Gets the text of the event. The index in the list represents the priority
682      * of the text. Specifically, the lower the index the higher the priority.
683      *
684      * @return The text.
685      */
getText()686     public @NonNull List<CharSequence> getText() {
687         return mText;
688     }
689 
690     /**
691      * Gets the text before a change.
692      *
693      * @return The text before the change.
694      */
getBeforeText()695     public @Nullable CharSequence getBeforeText() {
696         return mBeforeText;
697     }
698 
699     /**
700      * Sets the text before a change.
701      *
702      * @param beforeText The text before the change.
703      *
704      * @throws IllegalStateException If called from an AccessibilityService.
705      */
setBeforeText(@ullable CharSequence beforeText)706     public void setBeforeText(@Nullable CharSequence beforeText) {
707         enforceNotSealed();
708         mBeforeText = (beforeText == null) ? null
709                 : beforeText.subSequence(0, beforeText.length());
710     }
711 
712     /**
713      * Gets the description of the source.
714      *
715      * @return The description.
716      */
getContentDescription()717     public @Nullable CharSequence getContentDescription() {
718         return mContentDescription;
719     }
720 
721     /**
722      * Sets the description of the source.
723      *
724      * @param contentDescription The description.
725      *
726      * @throws IllegalStateException If called from an AccessibilityService.
727      */
setContentDescription(@ullable CharSequence contentDescription)728     public void setContentDescription(@Nullable CharSequence contentDescription) {
729         enforceNotSealed();
730         mContentDescription = (contentDescription == null) ? null
731                 : contentDescription.subSequence(0, contentDescription.length());
732     }
733 
734     /**
735      * Gets the {@link Parcelable} data.
736      *
737      * @return The parcelable data.
738      */
getParcelableData()739     public @Nullable Parcelable getParcelableData() {
740         return mParcelableData;
741     }
742 
743     /**
744      * Sets the {@link Parcelable} data of the event.
745      *
746      * @param parcelableData The parcelable data.
747      *
748      * @throws IllegalStateException If called from an AccessibilityService.
749      */
setParcelableData(@ullable Parcelable parcelableData)750     public void setParcelableData(@Nullable Parcelable parcelableData) {
751         enforceNotSealed();
752         mParcelableData = parcelableData;
753     }
754 
755     /**
756      * Gets the id of the source node.
757      *
758      * @return The id.
759      *
760      * @hide
761      */
762     @UnsupportedAppUsage
getSourceNodeId()763     public long getSourceNodeId() {
764         return mSourceNodeId;
765     }
766 
767     /**
768      * Sets the unique id of the IAccessibilityServiceConnection over which
769      * this instance can send requests to the system.
770      *
771      * @param connectionId The connection id.
772      *
773      * @hide
774      */
setConnectionId(int connectionId)775     public void setConnectionId(int connectionId) {
776         enforceNotSealed();
777         mConnectionId = connectionId;
778     }
779 
780     /**
781      * Sets if this instance is sealed.
782      *
783      * @param sealed Whether is sealed.
784      *
785      * @hide
786      */
setSealed(boolean sealed)787     public void setSealed(boolean sealed) {
788         mSealed = sealed;
789     }
790 
791     /**
792      * Gets if this instance is sealed.
793      *
794      * @return Whether is sealed.
795      */
isSealed()796     boolean isSealed() {
797         return mSealed;
798     }
799 
800     /**
801      * Enforces that this instance is sealed.
802      *
803      * @throws IllegalStateException If this instance is not sealed.
804      */
enforceSealed()805     void enforceSealed() {
806         if (!isSealed()) {
807             throw new IllegalStateException("Cannot perform this "
808                     + "action on a not sealed instance.");
809         }
810     }
811 
812     /**
813      * Enforces that this instance is not sealed.
814      *
815      * @throws IllegalStateException If this instance is sealed.
816      */
enforceNotSealed()817     void enforceNotSealed() {
818         if (isSealed()) {
819             throw new IllegalStateException("Cannot perform this "
820                     + "action on a sealed instance.");
821         }
822     }
823 
824     /**
825      * Gets the value of a boolean property.
826      *
827      * @param property The property.
828      * @return The value.
829      */
getBooleanProperty(int property)830     private boolean getBooleanProperty(int property) {
831         return (mBooleanProperties & property) == property;
832     }
833 
834     /**
835      * Sets a boolean property.
836      *
837      * @param property The property.
838      * @param value The value.
839      */
setBooleanProperty(int property, boolean value)840     private void setBooleanProperty(int property, boolean value) {
841         if (value) {
842             mBooleanProperties |= property;
843         } else {
844             mBooleanProperties &= ~property;
845         }
846     }
847 
848     /**
849      * Instantiates a new record initialized with data from the
850      * given record.
851      *
852      * @deprecated Object pooling has been discontinued. Create a new instance using the
853      * constructor {@link #AccessibilityRecord()} instead.
854      * @return An instance.
855      */
856     @Deprecated
obtain(@onNull AccessibilityRecord record)857     public static @NonNull AccessibilityRecord obtain(@NonNull AccessibilityRecord record) {
858        AccessibilityRecord clone = AccessibilityRecord.obtain();
859        clone.init(record);
860        return clone;
861     }
862 
863     /**
864      * Instantiates a new record.
865      *
866      * @deprecated Object pooling has been discontinued. Create a new instance using the
867      * constructor {@link #AccessibilityRecord()} instead.
868      * @return An instance.
869      */
870     @Deprecated
obtain()871     public static @NonNull AccessibilityRecord obtain() {
872         return new AccessibilityRecord();
873     }
874 
875     /**
876      * Would previously return an instance back to be reused.
877      *
878      * @deprecated Object pooling has been discontinued. Calling this function now will have
879      * no effect.
880      */
881     @Deprecated
recycle()882     public void recycle() { }
883 
884     /**
885      * Initialize this record from another one.
886      *
887      * @param record The to initialize from.
888      */
init(@onNull AccessibilityRecord record)889     void init(@NonNull AccessibilityRecord record) {
890         mSealed = record.mSealed;
891         mBooleanProperties = record.mBooleanProperties;
892         mCurrentItemIndex = record.mCurrentItemIndex;
893         mItemCount = record.mItemCount;
894         mFromIndex = record.mFromIndex;
895         mToIndex = record.mToIndex;
896         mScrollX = record.mScrollX;
897         mScrollY = record.mScrollY;
898         mMaxScrollX = record.mMaxScrollX;
899         mMaxScrollY = record.mMaxScrollY;
900         mScrollDeltaX = record.mScrollDeltaX;
901         mScrollDeltaY = record.mScrollDeltaY;
902         mAddedCount = record.mAddedCount;
903         mRemovedCount = record.mRemovedCount;
904         mClassName = record.mClassName;
905         mContentDescription = record.mContentDescription;
906         mBeforeText = record.mBeforeText;
907         mParcelableData = record.mParcelableData;
908         mText.addAll(record.mText);
909         mSourceWindowId = record.mSourceWindowId;
910         mSourceNodeId = record.mSourceNodeId;
911         mSourceDisplayId = record.mSourceDisplayId;
912         mConnectionId = record.mConnectionId;
913     }
914 
915     /**
916      * Clears the state of this instance.
917      */
clear()918     void clear() {
919         mSealed = false;
920         mBooleanProperties = 0;
921         mCurrentItemIndex = UNDEFINED;
922         mItemCount = UNDEFINED;
923         mFromIndex = UNDEFINED;
924         mToIndex = UNDEFINED;
925         mScrollX = 0;
926         mScrollY = 0;
927         mMaxScrollX = 0;
928         mMaxScrollY = 0;
929         mScrollDeltaX = UNDEFINED;
930         mScrollDeltaY = UNDEFINED;
931         mAddedCount = UNDEFINED;
932         mRemovedCount = UNDEFINED;
933         mClassName = null;
934         mContentDescription = null;
935         mBeforeText = null;
936         mParcelableData = null;
937         mText.clear();
938         mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
939         mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
940         mSourceDisplayId = Display.INVALID_DISPLAY;
941         mConnectionId = UNDEFINED;
942     }
943 
944     @Override
toString()945     public String toString() {
946         return appendTo(new StringBuilder()).toString();
947     }
948 
appendTo(StringBuilder builder)949     StringBuilder appendTo(StringBuilder builder) {
950         builder.append(" [ ClassName: ").append(mClassName);
951         if (!DEBUG_CONCISE_TOSTRING || !isEmpty(mText)) {
952             appendPropName(builder, "Text").append(mText);
953         }
954         append(builder, "ContentDescription", mContentDescription);
955         append(builder, "ItemCount", mItemCount);
956         append(builder, "CurrentItemIndex", mCurrentItemIndex);
957 
958         appendUnless(true, PROPERTY_ENABLED, builder);
959         appendUnless(false, PROPERTY_PASSWORD, builder);
960         appendUnless(false, PROPERTY_CHECKED, builder);
961         appendUnless(false, PROPERTY_FULL_SCREEN, builder);
962         appendUnless(false, PROPERTY_SCROLLABLE, builder);
963         appendUnless(false, PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, builder);
964         appendUnless(false, PROPERTY_ACCESSIBILITY_DATA_SENSITIVE, builder);
965 
966         append(builder, "BeforeText", mBeforeText);
967         append(builder, "FromIndex", mFromIndex);
968         append(builder, "ToIndex", mToIndex);
969         append(builder, "ScrollX", mScrollX);
970         append(builder, "ScrollY", mScrollY);
971         append(builder, "MaxScrollX", mMaxScrollX);
972         append(builder, "MaxScrollY", mMaxScrollY);
973         append(builder, "ScrollDeltaX", mScrollDeltaX);
974         append(builder, "ScrollDeltaY", mScrollDeltaY);
975         append(builder, "AddedCount", mAddedCount);
976         append(builder, "RemovedCount", mRemovedCount);
977         append(builder, "ParcelableData", mParcelableData);
978         append(builder, "DisplayId", mSourceDisplayId);
979         builder.append(" ]");
980         return builder;
981     }
982 
appendUnless(boolean defValue, int prop, StringBuilder builder)983     private void appendUnless(boolean defValue, int prop, StringBuilder builder) {
984         boolean value = getBooleanProperty(prop);
985         if (DEBUG_CONCISE_TOSTRING && value == defValue) return;
986         appendPropName(builder, singleBooleanPropertyToString(prop))
987                 .append(value);
988     }
989 
singleBooleanPropertyToString(int prop)990     private static String singleBooleanPropertyToString(int prop) {
991         switch (prop) {
992             case PROPERTY_CHECKED: return "Checked";
993             case PROPERTY_ENABLED: return "Enabled";
994             case PROPERTY_PASSWORD: return "Password";
995             case PROPERTY_FULL_SCREEN: return "FullScreen";
996             case PROPERTY_SCROLLABLE: return "Scrollable";
997             case PROPERTY_IMPORTANT_FOR_ACCESSIBILITY:
998                 return "ImportantForAccessibility";
999             case PROPERTY_ACCESSIBILITY_DATA_SENSITIVE:
1000                 return "AccessibilityDataSensitive";
1001             default: return Integer.toHexString(prop);
1002         }
1003     }
1004 
append(StringBuilder builder, String propName, int propValue)1005     private void append(StringBuilder builder, String propName, int propValue) {
1006         if (DEBUG_CONCISE_TOSTRING && propValue == UNDEFINED) return;
1007         appendPropName(builder, propName).append(propValue);
1008     }
1009 
append(StringBuilder builder, String propName, Object propValue)1010     private void append(StringBuilder builder, String propName, Object propValue) {
1011         if (DEBUG_CONCISE_TOSTRING && propValue == null) return;
1012         appendPropName(builder, propName).append(propValue);
1013     }
1014 
appendPropName(StringBuilder builder, String propName)1015     private StringBuilder appendPropName(StringBuilder builder, String propName) {
1016         return builder.append("; ").append(propName).append(": ");
1017     }
1018 }
1019