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