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