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     AccessibilityNodeInfo mSourceNode;
     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         boolean important = true;
    139         mSourceWindowId = UNDEFINED;
    140         clearSourceNode();
    141         if (root != null) {
    142             if (virtualDescendantId == UNDEFINED ||
    143                     virtualDescendantId == AccessibilityNodeInfo.UNDEFINED_ITEM_ID) {
    144                 important = root.isImportantForAccessibility();
    145                 mSourceNode = root.createAccessibilityNodeInfo();
    146             } else {
    147                 AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
    148                 if (provider != null) {
    149                     mSourceNode = provider.createAccessibilityNodeInfo(virtualDescendantId);
    150                 }
    151             }
    152 
    153             mSourceWindowId = root.getAccessibilityWindowId();
    154         }
    155         setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important);
    156     }
    157 
    158     /**
    159      * Gets the {@link AccessibilityNodeInfo} of the event source.
    160      * <p>
    161      *   <strong>Note:</strong> It is a client responsibility to recycle the received info
    162      *   by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()}
    163      *   to avoid creating of multiple instances.
    164      * </p>
    165      * @return The info of the source.
    166      */
    167     public AccessibilityNodeInfo getSource() {
    168         enforceSealed();
    169         if (mSourceNode != null) {
    170             return AccessibilityNodeInfo.obtain(mSourceNode);
    171         }
    172 
    173         return null;
    174     }
    175 
    176     /**
    177      * Sets the window id.
    178      *
    179      * @param windowId The window id.
    180      *
    181      * @hide
    182      */
    183     public void setWindowId(int windowId) {
    184         mSourceWindowId = windowId;
    185     }
    186 
    187     /**
    188      * Gets the id of the window from which the event comes from.
    189      *
    190      * @return The window id.
    191      */
    192     public int getWindowId() {
    193         return mSourceWindowId;
    194     }
    195 
    196     /**
    197      * Gets if the source is checked.
    198      *
    199      * @return True if the view is checked, false otherwise.
    200      */
    201     public boolean isChecked() {
    202         return getBooleanProperty(PROPERTY_CHECKED);
    203     }
    204 
    205     /**
    206      * Sets if the source is checked.
    207      *
    208      * @param isChecked True if the view is checked, false otherwise.
    209      *
    210      * @throws IllegalStateException If called from an AccessibilityService.
    211      */
    212     public void setChecked(boolean isChecked) {
    213         enforceNotSealed();
    214         setBooleanProperty(PROPERTY_CHECKED, isChecked);
    215     }
    216 
    217     /**
    218      * Gets if the source is enabled.
    219      *
    220      * @return True if the view is enabled, false otherwise.
    221      */
    222     public boolean isEnabled() {
    223         return getBooleanProperty(PROPERTY_ENABLED);
    224     }
    225 
    226     /**
    227      * Sets if the source is enabled.
    228      *
    229      * @param isEnabled True if the view is enabled, false otherwise.
    230      *
    231      * @throws IllegalStateException If called from an AccessibilityService.
    232      */
    233     public void setEnabled(boolean isEnabled) {
    234         enforceNotSealed();
    235         setBooleanProperty(PROPERTY_ENABLED, isEnabled);
    236     }
    237 
    238     /**
    239      * Gets if the source is a password field.
    240      *
    241      * @return True if the view is a password field, false otherwise.
    242      */
    243     public boolean isPassword() {
    244         return getBooleanProperty(PROPERTY_PASSWORD);
    245     }
    246 
    247     /**
    248      * Sets if the source is a password field.
    249      *
    250      * @param isPassword True if the view is a password field, false otherwise.
    251      *
    252      * @throws IllegalStateException If called from an AccessibilityService.
    253      */
    254     public void setPassword(boolean isPassword) {
    255         enforceNotSealed();
    256         setBooleanProperty(PROPERTY_PASSWORD, isPassword);
    257     }
    258 
    259     /**
    260      * Gets if the source is taking the entire screen.
    261      *
    262      * @return True if the source is full screen, false otherwise.
    263      */
    264     public boolean isFullScreen() {
    265         return getBooleanProperty(PROPERTY_FULL_SCREEN);
    266     }
    267 
    268     /**
    269      * Sets if the source is taking the entire screen.
    270      *
    271      * @param isFullScreen True if the source is full screen, false otherwise.
    272      *
    273      * @throws IllegalStateException If called from an AccessibilityService.
    274      */
    275     public void setFullScreen(boolean isFullScreen) {
    276         enforceNotSealed();
    277         setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
    278     }
    279 
    280     /**
    281      * Gets if the source is scrollable.
    282      *
    283      * @return True if the source is scrollable, false otherwise.
    284      */
    285     public boolean isScrollable() {
    286         return getBooleanProperty(PROPERTY_SCROLLABLE);
    287     }
    288 
    289     /**
    290      * Sets if the source is scrollable.
    291      *
    292      * @param scrollable True if the source is scrollable, false otherwise.
    293      *
    294      * @throws IllegalStateException If called from an AccessibilityService.
    295      */
    296     public void setScrollable(boolean scrollable) {
    297         enforceNotSealed();
    298         setBooleanProperty(PROPERTY_SCROLLABLE, scrollable);
    299     }
    300 
    301     /**
    302      * Gets if the source is important for accessibility.
    303      *
    304      * <strong>Note:</strong> Used only internally to determine whether
    305      * to deliver the event to a given accessibility service since some
    306      * services may want to regard all views for accessibility while others
    307      * may want to regard only the important views for accessibility.
    308      *
    309      * @return True if the source is important for accessibility,
    310      *        false otherwise.
    311      *
    312      * @hide
    313      */
    314     public boolean isImportantForAccessibility() {
    315         return getBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY);
    316     }
    317 
    318     /**
    319      * Gets the number of items that can be visited.
    320      *
    321      * @return The number of items.
    322      */
    323     public int getItemCount() {
    324         return mItemCount;
    325     }
    326 
    327     /**
    328      * Sets the number of items that can be visited.
    329      *
    330      * @param itemCount The number of items.
    331      *
    332      * @throws IllegalStateException If called from an AccessibilityService.
    333      */
    334     public void setItemCount(int itemCount) {
    335         enforceNotSealed();
    336         mItemCount = itemCount;
    337     }
    338 
    339     /**
    340      * Gets the index of the source in the list of items the can be visited.
    341      *
    342      * @return The current item index.
    343      */
    344     public int getCurrentItemIndex() {
    345         return mCurrentItemIndex;
    346     }
    347 
    348     /**
    349      * Sets the index of the source in the list of items that can be visited.
    350      *
    351      * @param currentItemIndex The current item index.
    352      *
    353      * @throws IllegalStateException If called from an AccessibilityService.
    354      */
    355     public void setCurrentItemIndex(int currentItemIndex) {
    356         enforceNotSealed();
    357         mCurrentItemIndex = currentItemIndex;
    358     }
    359 
    360     /**
    361      * Gets the index of the first character of the changed sequence,
    362      * or the beginning of a text selection or the index of the first
    363      * visible item when scrolling.
    364      *
    365      * @return The index of the first character or selection
    366      *        start or the first visible item.
    367      */
    368     public int getFromIndex() {
    369         return mFromIndex;
    370     }
    371 
    372     /**
    373      * Sets the index of the first character of the changed sequence
    374      * or the beginning of a text selection or the index of the first
    375      * visible item when scrolling.
    376      *
    377      * @param fromIndex The index of the first character or selection
    378      *        start or the first visible item.
    379      *
    380      * @throws IllegalStateException If called from an AccessibilityService.
    381      */
    382     public void setFromIndex(int fromIndex) {
    383         enforceNotSealed();
    384         mFromIndex = fromIndex;
    385     }
    386 
    387     /**
    388      * Gets the index of text selection end or the index of the last
    389      * visible item when scrolling.
    390      *
    391      * @return The index of selection end or last item index.
    392      */
    393     public int getToIndex() {
    394         return mToIndex;
    395     }
    396 
    397     /**
    398      * Sets the index of text selection end or the index of the last
    399      * visible item when scrolling.
    400      *
    401      * @param toIndex The index of selection end or last item index.
    402      */
    403     public void setToIndex(int toIndex) {
    404         enforceNotSealed();
    405         mToIndex = toIndex;
    406     }
    407 
    408     /**
    409      * Gets the scroll offset of the source left edge in pixels.
    410      *
    411      * @return The scroll.
    412      */
    413     public int getScrollX() {
    414         return mScrollX;
    415     }
    416 
    417     /**
    418      * Sets the scroll offset of the source left edge in pixels.
    419      *
    420      * @param scrollX The scroll.
    421      */
    422     public void setScrollX(int scrollX) {
    423         enforceNotSealed();
    424         mScrollX = scrollX;
    425     }
    426 
    427     /**
    428      * Gets the scroll offset of the source top edge in pixels.
    429      *
    430      * @return The scroll.
    431      */
    432     public int getScrollY() {
    433         return mScrollY;
    434     }
    435 
    436     /**
    437      * Sets the scroll offset of the source top edge in pixels.
    438      *
    439      * @param scrollY The scroll.
    440      */
    441     public void setScrollY(int scrollY) {
    442         enforceNotSealed();
    443         mScrollY = scrollY;
    444     }
    445 
    446     /**
    447      * Gets the max scroll offset of the source left edge in pixels.
    448      *
    449      * @return The max scroll.
    450      */
    451     public int getMaxScrollX() {
    452         return mMaxScrollX;
    453     }
    454 
    455     /**
    456      * Sets the max scroll offset of the source left edge in pixels.
    457      *
    458      * @param maxScrollX The max scroll.
    459      */
    460     public void setMaxScrollX(int maxScrollX) {
    461         enforceNotSealed();
    462         mMaxScrollX = maxScrollX;
    463     }
    464 
    465     /**
    466      * Gets the max scroll offset of the source top edge in pixels.
    467      *
    468      * @return The max scroll.
    469      */
    470     public int getMaxScrollY() {
    471         return mMaxScrollY;
    472     }
    473 
    474     /**
    475      * Sets the max scroll offset of the source top edge in pixels.
    476      *
    477      * @param maxScrollY The max scroll.
    478      */
    479     public void setMaxScrollY(int maxScrollY) {
    480         enforceNotSealed();
    481         mMaxScrollY = maxScrollY;
    482     }
    483 
    484     /**
    485      * Gets the number of added characters.
    486      *
    487      * @return The number of added characters.
    488      */
    489     public int getAddedCount() {
    490         return mAddedCount;
    491     }
    492 
    493     /**
    494      * Sets the number of added characters.
    495      *
    496      * @param addedCount The number of added characters.
    497      *
    498      * @throws IllegalStateException If called from an AccessibilityService.
    499      */
    500     public void setAddedCount(int addedCount) {
    501         enforceNotSealed();
    502         mAddedCount = addedCount;
    503     }
    504 
    505     /**
    506      * Gets the number of removed characters.
    507      *
    508      * @return The number of removed characters.
    509      */
    510     public int getRemovedCount() {
    511         return mRemovedCount;
    512     }
    513 
    514     /**
    515      * Sets the number of removed characters.
    516      *
    517      * @param removedCount The number of removed characters.
    518      *
    519      * @throws IllegalStateException If called from an AccessibilityService.
    520      */
    521     public void setRemovedCount(int removedCount) {
    522         enforceNotSealed();
    523         mRemovedCount = removedCount;
    524     }
    525 
    526     /**
    527      * Gets the class name of the source.
    528      *
    529      * @return The class name.
    530      */
    531     public CharSequence getClassName() {
    532         return mClassName;
    533     }
    534 
    535     /**
    536      * Sets the class name of the source.
    537      *
    538      * @param className The lass name.
    539      *
    540      * @throws IllegalStateException If called from an AccessibilityService.
    541      */
    542     public void setClassName(CharSequence className) {
    543         enforceNotSealed();
    544         mClassName = className;
    545     }
    546 
    547     /**
    548      * Gets the text of the event. The index in the list represents the priority
    549      * of the text. Specifically, the lower the index the higher the priority.
    550      *
    551      * @return The text.
    552      */
    553     public List<CharSequence> getText() {
    554         return mText;
    555     }
    556 
    557     /**
    558      * Sets the text before a change.
    559      *
    560      * @return The text before the change.
    561      */
    562     public CharSequence getBeforeText() {
    563         return mBeforeText;
    564     }
    565 
    566     /**
    567      * Sets the text before a change.
    568      *
    569      * @param beforeText The text before the change.
    570      *
    571      * @throws IllegalStateException If called from an AccessibilityService.
    572      */
    573     public void setBeforeText(CharSequence beforeText) {
    574         enforceNotSealed();
    575         mBeforeText = beforeText;
    576     }
    577 
    578     /**
    579      * Gets the description of the source.
    580      *
    581      * @return The description.
    582      */
    583     public CharSequence getContentDescription() {
    584         return mContentDescription;
    585     }
    586 
    587     /**
    588      * Sets the description of the source.
    589      *
    590      * @param contentDescription The description.
    591      *
    592      * @throws IllegalStateException If called from an AccessibilityService.
    593      */
    594     public void setContentDescription(CharSequence contentDescription) {
    595         enforceNotSealed();
    596         mContentDescription = contentDescription;
    597     }
    598 
    599     /**
    600      * Gets the {@link Parcelable} data.
    601      *
    602      * @return The parcelable data.
    603      */
    604     public Parcelable getParcelableData() {
    605         return mParcelableData;
    606     }
    607 
    608     /**
    609      * Sets the {@link Parcelable} data of the event.
    610      *
    611      * @param parcelableData The parcelable data.
    612      *
    613      * @throws IllegalStateException If called from an AccessibilityService.
    614      */
    615     public void setParcelableData(Parcelable parcelableData) {
    616         enforceNotSealed();
    617         mParcelableData = parcelableData;
    618     }
    619 
    620     /**
    621      * Gets the id of the source node.
    622      *
    623      * @return The id.
    624      *
    625      * @hide
    626      */
    627     public long getSourceNodeId() {
    628         return mSourceNode != null ? mSourceNode.getSourceNodeId() : UNDEFINED;
    629     }
    630 
    631     /**
    632      * Sets the unique id of the IAccessibilityServiceConnection over which
    633      * this instance can send requests to the system.
    634      *
    635      * @param connectionId The connection id.
    636      *
    637      * @hide
    638      */
    639     public void setConnectionId(int connectionId) {
    640         enforceNotSealed();
    641         mConnectionId = connectionId;
    642         if (mSourceNode != null) {
    643             mSourceNode.setConnectionId(mConnectionId);
    644         }
    645     }
    646 
    647     /**
    648      * Sets if this instance is sealed.
    649      *
    650      * @param sealed Whether is sealed.
    651      *
    652      * @hide
    653      */
    654     public void setSealed(boolean sealed) {
    655         mSealed = sealed;
    656         if (mSourceNode != null) {
    657             mSourceNode.setSealed(sealed);
    658         }
    659     }
    660 
    661     /**
    662      * Gets if this instance is sealed.
    663      *
    664      * @return Whether is sealed.
    665      */
    666     boolean isSealed() {
    667         return mSealed;
    668     }
    669 
    670     /**
    671      * Enforces that this instance is sealed.
    672      *
    673      * @throws IllegalStateException If this instance is not sealed.
    674      */
    675     void enforceSealed() {
    676         if (!isSealed()) {
    677             throw new IllegalStateException("Cannot perform this "
    678                     + "action on a not sealed instance.");
    679         }
    680     }
    681 
    682     /**
    683      * Enforces that this instance is not sealed.
    684      *
    685      * @throws IllegalStateException If this instance is sealed.
    686      */
    687     void enforceNotSealed() {
    688         if (isSealed()) {
    689             throw new IllegalStateException("Cannot perform this "
    690                     + "action on a sealed instance.");
    691         }
    692     }
    693 
    694     /**
    695      * Gets the value of a boolean property.
    696      *
    697      * @param property The property.
    698      * @return The value.
    699      */
    700     private boolean getBooleanProperty(int property) {
    701         return (mBooleanProperties & property) == property;
    702     }
    703 
    704     /**
    705      * Sets a boolean property.
    706      *
    707      * @param property The property.
    708      * @param value The value.
    709      */
    710     private void setBooleanProperty(int property, boolean value) {
    711         if (value) {
    712             mBooleanProperties |= property;
    713         } else {
    714             mBooleanProperties &= ~property;
    715         }
    716     }
    717 
    718     /**
    719      * Returns a cached instance if such is available or a new one is
    720      * instantiated. The instance is initialized with data from the
    721      * given record.
    722      *
    723      * @return An instance.
    724      */
    725     public static AccessibilityRecord obtain(AccessibilityRecord record) {
    726        AccessibilityRecord clone = AccessibilityRecord.obtain();
    727        clone.init(record);
    728        return clone;
    729     }
    730 
    731     /**
    732      * Returns a cached instance if such is available or a new one is
    733      * instantiated.
    734      *
    735      * @return An instance.
    736      */
    737     public static AccessibilityRecord obtain() {
    738         synchronized (sPoolLock) {
    739             if (sPool != null) {
    740                 AccessibilityRecord record = sPool;
    741                 sPool = sPool.mNext;
    742                 sPoolSize--;
    743                 record.mNext = null;
    744                 record.mIsInPool = false;
    745                 return record;
    746             }
    747             return new AccessibilityRecord();
    748         }
    749     }
    750 
    751     /**
    752      * Return an instance back to be reused.
    753      * <p>
    754      * <strong>Note:</strong> You must not touch the object after calling this function.
    755      *
    756      * @throws IllegalStateException If the record is already recycled.
    757      */
    758     public void recycle() {
    759         if (mIsInPool) {
    760             throw new IllegalStateException("Record already recycled!");
    761         }
    762         clear();
    763         synchronized (sPoolLock) {
    764             if (sPoolSize <= MAX_POOL_SIZE) {
    765                 mNext = sPool;
    766                 sPool = this;
    767                 mIsInPool = true;
    768                 sPoolSize++;
    769             }
    770         }
    771     }
    772 
    773     /**
    774      * Initialize this record from another one.
    775      *
    776      * @param record The to initialize from.
    777      */
    778     void init(AccessibilityRecord record) {
    779         mSealed = record.mSealed;
    780         mBooleanProperties = record.mBooleanProperties;
    781         mCurrentItemIndex = record.mCurrentItemIndex;
    782         mItemCount = record.mItemCount;
    783         mFromIndex = record.mFromIndex;
    784         mToIndex = record.mToIndex;
    785         mScrollX = record.mScrollX;
    786         mScrollY = record.mScrollY;
    787         mMaxScrollX = record.mMaxScrollX;
    788         mMaxScrollY = record.mMaxScrollY;
    789         mAddedCount = record.mAddedCount;
    790         mRemovedCount = record.mRemovedCount;
    791         mClassName = record.mClassName;
    792         mContentDescription = record.mContentDescription;
    793         mBeforeText = record.mBeforeText;
    794         mParcelableData = record.mParcelableData;
    795         mText.addAll(record.mText);
    796         mSourceWindowId = record.mSourceWindowId;
    797         if (record.mSourceNode != null) {
    798             mSourceNode = AccessibilityNodeInfo.obtain(record.mSourceNode);
    799         }
    800         mConnectionId = record.mConnectionId;
    801     }
    802 
    803     /**
    804      * Clears the state of this instance.
    805      */
    806     void clear() {
    807         mSealed = false;
    808         mBooleanProperties = 0;
    809         mCurrentItemIndex = UNDEFINED;
    810         mItemCount = UNDEFINED;
    811         mFromIndex = UNDEFINED;
    812         mToIndex = UNDEFINED;
    813         mScrollX = UNDEFINED;
    814         mScrollY = UNDEFINED;
    815         mMaxScrollX = UNDEFINED;
    816         mMaxScrollY = UNDEFINED;
    817         mAddedCount = UNDEFINED;
    818         mRemovedCount = UNDEFINED;
    819         mClassName = null;
    820         mContentDescription = null;
    821         mBeforeText = null;
    822         mParcelableData = null;
    823         mText.clear();
    824         clearSourceNode();
    825         mSourceWindowId = UNDEFINED;
    826         mConnectionId = UNDEFINED;
    827     }
    828 
    829     private void clearSourceNode() {
    830         if (mSourceNode != null) {
    831             mSourceNode.recycle();
    832             mSourceNode = null;
    833         }
    834     }
    835 
    836     @Override
    837     public String toString() {
    838         StringBuilder builder = new StringBuilder();
    839         builder.append(" [ ClassName: " + mClassName);
    840         builder.append("; Text: " + mText);
    841         builder.append("; ContentDescription: " + mContentDescription);
    842         builder.append("; ItemCount: " + mItemCount);
    843         builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
    844         builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED));
    845         builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD));
    846         builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED));
    847         builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN));
    848         builder.append("; Scrollable: " + getBooleanProperty(PROPERTY_SCROLLABLE));
    849         builder.append("; BeforeText: " + mBeforeText);
    850         builder.append("; FromIndex: " + mFromIndex);
    851         builder.append("; ToIndex: " + mToIndex);
    852         builder.append("; ScrollX: " + mScrollX);
    853         builder.append("; ScrollY: " + mScrollY);
    854         builder.append("; MaxScrollX: " + mMaxScrollX);
    855         builder.append("; MaxScrollY: " + mMaxScrollY);
    856         builder.append("; AddedCount: " + mAddedCount);
    857         builder.append("; RemovedCount: " + mRemovedCount);
    858         builder.append("; ParcelableData: " + mParcelableData);
    859         builder.append(" ]");
    860         return builder.toString();
    861     }
    862 }
    863