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