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