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 */ AccessibilityRecord()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 */ setSource(View source)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 */ setSource(View root, int virtualDescendantId)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 */ getSource()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 */ setWindowId(int windowId)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 */ getWindowId()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 */ isChecked()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 */ setChecked(boolean isChecked)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 */ isEnabled()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 */ setEnabled(boolean isEnabled)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 */ isPassword()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 */ setPassword(boolean isPassword)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 */ isFullScreen()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 */ setFullScreen(boolean isFullScreen)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 */ isScrollable()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 */ setScrollable(boolean scrollable)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 */ isImportantForAccessibility()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 */ getItemCount()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 */ setItemCount(int itemCount)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 */ getCurrentItemIndex()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 */ setCurrentItemIndex(int currentItemIndex)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 */ getFromIndex()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 */ setFromIndex(int fromIndex)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 */ getToIndex()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 */ setToIndex(int toIndex)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 */ getScrollX()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 */ setScrollX(int scrollX)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 */ getScrollY()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 */ setScrollY(int scrollY)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 */ getMaxScrollX()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 */ setMaxScrollX(int maxScrollX)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 */ getMaxScrollY()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 */ setMaxScrollY(int maxScrollY)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 */ getAddedCount()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 */ setAddedCount(int addedCount)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 */ getRemovedCount()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 */ setRemovedCount(int removedCount)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 */ getClassName()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 */ setClassName(CharSequence className)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 */ getText()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 */ getBeforeText()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 */ setBeforeText(CharSequence beforeText)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 */ getContentDescription()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 */ setContentDescription(CharSequence contentDescription)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 */ getParcelableData()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 */ setParcelableData(Parcelable parcelableData)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 */ getSourceNodeId()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 */ setConnectionId(int connectionId)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 */ setSealed(boolean sealed)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 */ isSealed()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 */ enforceSealed()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 */ enforceNotSealed()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 */ getBooleanProperty(int property)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 */ setBooleanProperty(int property, boolean value)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 */ obtain(AccessibilityRecord record)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 */ obtain()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 */ recycle()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 */ init(AccessibilityRecord record)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 */ clear()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 clearSourceNode()829 private void clearSourceNode() { 830 if (mSourceNode != null) { 831 mSourceNode.recycle(); 832 mSourceNode = null; 833 } 834 } 835 836 @Override toString()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