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 static com.android.internal.util.CollectionUtils.isEmpty; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.annotation.TestApi; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.os.Parcelable; 26 import android.view.Display; 27 import android.view.View; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 32 /** 33 * Represents a record in an {@link AccessibilityEvent} and contains information 34 * about state change of its source {@link android.view.View}. When a view fires 35 * an accessibility event it requests from its parent to dispatch the 36 * constructed event. The parent may optionally append a record for itself 37 * for providing more context to 38 * {@link android.accessibilityservice.AccessibilityService}s. Hence, 39 * accessibility services can facilitate additional accessibility records 40 * to enhance feedback. 41 * </p> 42 * <p> 43 * Once the accessibility event containing a record is dispatched the record is 44 * made immutable and calling a state mutation method generates an error. 45 * </p> 46 * <p> 47 * <strong>Note:</strong> Not all properties are applicable to all accessibility 48 * event types. For detailed information please refer to {@link AccessibilityEvent}. 49 * </p> 50 * 51 * <div class="special reference"> 52 * <h3>Developer Guides</h3> 53 * <p>For more information about creating and processing AccessibilityRecords, read the 54 * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> 55 * developer guide.</p> 56 * </div> 57 * 58 * @see AccessibilityEvent 59 * @see AccessibilityManager 60 * @see android.accessibilityservice.AccessibilityService 61 * @see AccessibilityNodeInfo 62 */ 63 public class AccessibilityRecord { 64 /** @hide */ 65 protected static final boolean DEBUG_CONCISE_TOSTRING = false; 66 67 private static final int UNDEFINED = -1; 68 69 private static final int PROPERTY_CHECKED = 0x00000001; 70 private static final int PROPERTY_ENABLED = 0x00000002; 71 private static final int PROPERTY_PASSWORD = 0x00000004; 72 private static final int PROPERTY_FULL_SCREEN = 0x00000080; 73 private static final int PROPERTY_SCROLLABLE = 0x00000100; 74 private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200; 75 76 private static final int GET_SOURCE_PREFETCH_FLAGS = 77 AccessibilityNodeInfo.FLAG_PREFETCH_ANCESTORS 78 | AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS 79 | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS_HYBRID; 80 81 @UnsupportedAppUsage 82 boolean mSealed; 83 int mBooleanProperties = 0; 84 int mCurrentItemIndex = UNDEFINED; 85 int mItemCount = UNDEFINED; 86 int mFromIndex = UNDEFINED; 87 int mToIndex = UNDEFINED; 88 int mScrollX = 0; 89 int mScrollY = 0; 90 91 int mScrollDeltaX = UNDEFINED; 92 int mScrollDeltaY = UNDEFINED; 93 int mMaxScrollX = 0; 94 int mMaxScrollY = 0; 95 96 int mAddedCount= UNDEFINED; 97 int mRemovedCount = UNDEFINED; 98 @UnsupportedAppUsage 99 long mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; 100 int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 101 int mSourceDisplayId = Display.INVALID_DISPLAY; 102 103 CharSequence mClassName; 104 CharSequence mContentDescription; 105 CharSequence mBeforeText; 106 Parcelable mParcelableData; 107 108 final List<CharSequence> mText = new ArrayList<CharSequence>(); 109 110 int mConnectionId = UNDEFINED; 111 112 /** 113 * Creates a new {@link AccessibilityRecord}. 114 */ AccessibilityRecord()115 public AccessibilityRecord() { 116 } 117 118 /** 119 * Copy constructor. Creates a new {@link AccessibilityRecord}, and this instance is initialized 120 * with data from the given <code>record</code>. 121 * 122 * @param record The other record. 123 */ AccessibilityRecord(@onNull AccessibilityRecord record)124 public AccessibilityRecord(@NonNull AccessibilityRecord record) { 125 init(record); 126 } 127 128 /** 129 * Sets the event source. 130 * 131 * @param source The source. 132 * 133 * @throws IllegalStateException If called from an AccessibilityService. 134 */ setSource(@ullable View source)135 public void setSource(@Nullable View source) { 136 setSource(source, AccessibilityNodeProvider.HOST_VIEW_ID); 137 } 138 139 /** 140 * Sets the source to be a virtual descendant of the given <code>root</code>. 141 * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root 142 * is set as the source. 143 * <p> 144 * A virtual descendant is an imaginary View that is reported as a part of the view 145 * hierarchy for accessibility purposes. This enables custom views that draw complex 146 * content to report them selves as a tree of virtual views, thus conveying their 147 * logical structure. 148 * </p> 149 * 150 * @param root The root of the virtual subtree. 151 * @param virtualDescendantId The id of the virtual descendant. 152 */ setSource(@ullable View root, int virtualDescendantId)153 public void setSource(@Nullable View root, int virtualDescendantId) { 154 enforceNotSealed(); 155 boolean important = true; 156 int rootViewId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; 157 mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 158 if (root != null) { 159 important = root.isImportantForAccessibility(); 160 rootViewId = root.getAccessibilityViewId(); 161 mSourceWindowId = root.getAccessibilityWindowId(); 162 } 163 setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important); 164 mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId); 165 } 166 167 /** 168 * Set the source node ID directly 169 * 170 * @param sourceNodeId The source node Id 171 * @hide 172 */ setSourceNodeId(long sourceNodeId)173 public void setSourceNodeId(long sourceNodeId) { 174 mSourceNodeId = sourceNodeId; 175 } 176 177 /** 178 * Gets the {@link AccessibilityNodeInfo} of the event source. 179 * <p> 180 * <strong>Note:</strong> It is a client responsibility to recycle the received info 181 * by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()} 182 * to avoid creating of multiple instances. 183 * </p> 184 * @return The info of the source. 185 */ getSource()186 public @Nullable AccessibilityNodeInfo getSource() { 187 return getSource(GET_SOURCE_PREFETCH_FLAGS); 188 } 189 190 /** 191 * Gets the {@link AccessibilityNodeInfo} of the event source. 192 * 193 * @param prefetchingStrategy the prefetching strategy. 194 * @return The info of the source. 195 * 196 * @see AccessibilityNodeInfo#getParent(int) for a description of prefetching. 197 */ 198 @Nullable getSource( @ccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy)199 public AccessibilityNodeInfo getSource( 200 @AccessibilityNodeInfo.PrefetchingStrategy int prefetchingStrategy) { 201 enforceSealed(); 202 if ((mConnectionId == UNDEFINED) 203 || (mSourceWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) 204 || (AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId) 205 == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)) { 206 return null; 207 } 208 AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); 209 return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId, 210 mSourceNodeId, false, prefetchingStrategy, null); 211 } 212 213 /** 214 * Sets the display id. 215 * 216 * @param displayId The displayId id. 217 * 218 * @hide 219 */ 220 @TestApi setDisplayId(int displayId)221 public void setDisplayId(int displayId) { 222 mSourceDisplayId = displayId; 223 } 224 225 /** 226 * Gets the id of the display from which the event comes from. 227 * 228 * @return The display id. 229 */ getDisplayId()230 public int getDisplayId() { 231 return mSourceDisplayId; 232 } 233 234 /** 235 * Sets the window id. 236 * 237 * @param windowId The window id. 238 * 239 * @hide 240 */ setWindowId(int windowId)241 public void setWindowId(int windowId) { 242 mSourceWindowId = windowId; 243 } 244 245 /** 246 * Gets the id of the window from which the event comes from. 247 * 248 * @return The window id. 249 */ getWindowId()250 public int getWindowId() { 251 return mSourceWindowId; 252 } 253 254 /** 255 * Gets if the source is checked. 256 * 257 * @return True if the view is checked, false otherwise. 258 */ isChecked()259 public boolean isChecked() { 260 return getBooleanProperty(PROPERTY_CHECKED); 261 } 262 263 /** 264 * Sets if the source is checked. 265 * 266 * @param isChecked True if the view is checked, false otherwise. 267 * 268 * @throws IllegalStateException If called from an AccessibilityService. 269 */ setChecked(boolean isChecked)270 public void setChecked(boolean isChecked) { 271 enforceNotSealed(); 272 setBooleanProperty(PROPERTY_CHECKED, isChecked); 273 } 274 275 /** 276 * Gets if the source is enabled. 277 * 278 * @return True if the view is enabled, false otherwise. 279 */ isEnabled()280 public boolean isEnabled() { 281 return getBooleanProperty(PROPERTY_ENABLED); 282 } 283 284 /** 285 * Sets if the source is enabled. 286 * 287 * @param isEnabled True if the view is enabled, false otherwise. 288 * 289 * @throws IllegalStateException If called from an AccessibilityService. 290 */ setEnabled(boolean isEnabled)291 public void setEnabled(boolean isEnabled) { 292 enforceNotSealed(); 293 setBooleanProperty(PROPERTY_ENABLED, isEnabled); 294 } 295 296 /** 297 * Gets if the source is a password field. 298 * 299 * @return True if the view is a password field, false otherwise. 300 */ isPassword()301 public boolean isPassword() { 302 return getBooleanProperty(PROPERTY_PASSWORD); 303 } 304 305 /** 306 * Sets if the source is a password field. 307 * 308 * @param isPassword True if the view is a password field, false otherwise. 309 * 310 * @throws IllegalStateException If called from an AccessibilityService. 311 */ setPassword(boolean isPassword)312 public void setPassword(boolean isPassword) { 313 enforceNotSealed(); 314 setBooleanProperty(PROPERTY_PASSWORD, isPassword); 315 } 316 317 /** 318 * Gets if the source is taking the entire screen. 319 * 320 * @return True if the source is full screen, false otherwise. 321 */ isFullScreen()322 public boolean isFullScreen() { 323 return getBooleanProperty(PROPERTY_FULL_SCREEN); 324 } 325 326 /** 327 * Sets if the source is taking the entire screen. 328 * 329 * @param isFullScreen True if the source is full screen, false otherwise. 330 * 331 * @throws IllegalStateException If called from an AccessibilityService. 332 */ setFullScreen(boolean isFullScreen)333 public void setFullScreen(boolean isFullScreen) { 334 enforceNotSealed(); 335 setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); 336 } 337 338 /** 339 * Gets if the source is scrollable. 340 * 341 * @return True if the source is scrollable, false otherwise. 342 */ isScrollable()343 public boolean isScrollable() { 344 return getBooleanProperty(PROPERTY_SCROLLABLE); 345 } 346 347 /** 348 * Sets if the source is scrollable. 349 * 350 * @param scrollable True if the source is scrollable, false otherwise. 351 * 352 * @throws IllegalStateException If called from an AccessibilityService. 353 */ setScrollable(boolean scrollable)354 public void setScrollable(boolean scrollable) { 355 enforceNotSealed(); 356 setBooleanProperty(PROPERTY_SCROLLABLE, scrollable); 357 } 358 359 /** 360 * Gets if the source is important for accessibility. 361 * 362 * <strong>Note:</strong> Used only internally to determine whether 363 * to deliver the event to a given accessibility service since some 364 * services may want to regard all views for accessibility while others 365 * may want to regard only the important views for accessibility. 366 * 367 * @return True if the source is important for accessibility, 368 * false otherwise. 369 * 370 * @hide 371 */ isImportantForAccessibility()372 public boolean isImportantForAccessibility() { 373 return getBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY); 374 } 375 376 /** 377 * Sets if the source is important for accessibility. 378 * 379 * @param importantForAccessibility True if the source is important for accessibility, 380 * false otherwise. 381 * 382 * @throws IllegalStateException If called from an AccessibilityService. 383 * @hide 384 */ setImportantForAccessibility(boolean importantForAccessibility)385 public void setImportantForAccessibility(boolean importantForAccessibility) { 386 enforceNotSealed(); 387 setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, importantForAccessibility); 388 } 389 390 /** 391 * Gets the number of items that can be visited. 392 * 393 * @return The number of items. 394 */ getItemCount()395 public int getItemCount() { 396 return mItemCount; 397 } 398 399 /** 400 * Sets the number of items that can be visited. 401 * 402 * @param itemCount The number of items. 403 * 404 * @throws IllegalStateException If called from an AccessibilityService. 405 */ setItemCount(int itemCount)406 public void setItemCount(int itemCount) { 407 enforceNotSealed(); 408 mItemCount = itemCount; 409 } 410 411 /** 412 * Gets the index of the source in the list of items the can be visited. 413 * 414 * @return The current item index. 415 */ getCurrentItemIndex()416 public int getCurrentItemIndex() { 417 return mCurrentItemIndex; 418 } 419 420 /** 421 * Sets the index of the source in the list of items that can be visited. 422 * 423 * @param currentItemIndex The current item index. 424 * 425 * @throws IllegalStateException If called from an AccessibilityService. 426 */ setCurrentItemIndex(int currentItemIndex)427 public void setCurrentItemIndex(int currentItemIndex) { 428 enforceNotSealed(); 429 mCurrentItemIndex = currentItemIndex; 430 } 431 432 /** 433 * Gets the index of the first character of the changed sequence, 434 * or the beginning of a text selection or the index of the first 435 * visible item when scrolling. 436 * 437 * @return The index of the first character or selection 438 * start or the first visible item. 439 */ getFromIndex()440 public int getFromIndex() { 441 return mFromIndex; 442 } 443 444 /** 445 * Sets the index of the first character of the changed sequence 446 * or the beginning of a text selection or the index of the first 447 * visible item when scrolling. 448 * 449 * @param fromIndex The index of the first character or selection 450 * start or the first visible item. 451 * 452 * @throws IllegalStateException If called from an AccessibilityService. 453 */ setFromIndex(int fromIndex)454 public void setFromIndex(int fromIndex) { 455 enforceNotSealed(); 456 mFromIndex = fromIndex; 457 } 458 459 /** 460 * Gets the index of text selection end or the index of the last 461 * visible item when scrolling. 462 * 463 * @return The index of selection end or last item index. 464 */ getToIndex()465 public int getToIndex() { 466 return mToIndex; 467 } 468 469 /** 470 * Sets the index of text selection end or the index of the last 471 * visible item when scrolling. 472 * 473 * @param toIndex The index of selection end or last item index. 474 */ setToIndex(int toIndex)475 public void setToIndex(int toIndex) { 476 enforceNotSealed(); 477 mToIndex = toIndex; 478 } 479 480 /** 481 * Gets the scroll offset of the source left edge in pixels. 482 * 483 * @return The scroll. 484 */ getScrollX()485 public int getScrollX() { 486 return mScrollX; 487 } 488 489 /** 490 * Sets the scroll offset of the source left edge in pixels. 491 * 492 * @param scrollX The scroll. 493 */ setScrollX(int scrollX)494 public void setScrollX(int scrollX) { 495 enforceNotSealed(); 496 mScrollX = scrollX; 497 } 498 499 /** 500 * Gets the scroll offset of the source top edge in pixels. 501 * 502 * @return The scroll. 503 */ getScrollY()504 public int getScrollY() { 505 return mScrollY; 506 } 507 508 /** 509 * Sets the scroll offset of the source top edge in pixels. 510 * 511 * @param scrollY The scroll. 512 */ setScrollY(int scrollY)513 public void setScrollY(int scrollY) { 514 enforceNotSealed(); 515 mScrollY = scrollY; 516 } 517 518 /** 519 * Gets the difference in pixels between the horizontal position before the scroll and the 520 * current horizontal position 521 * 522 * @return the scroll delta x 523 */ getScrollDeltaX()524 public int getScrollDeltaX() { 525 return mScrollDeltaX; 526 } 527 528 /** 529 * Sets the difference in pixels between the horizontal position before the scroll and the 530 * current horizontal position 531 * 532 * @param scrollDeltaX the scroll delta x 533 */ setScrollDeltaX(int scrollDeltaX)534 public void setScrollDeltaX(int scrollDeltaX) { 535 enforceNotSealed(); 536 mScrollDeltaX = scrollDeltaX; 537 } 538 539 /** 540 * Gets the difference in pixels between the vertical position before the scroll and the 541 * current vertical position 542 * 543 * @return the scroll delta y 544 */ getScrollDeltaY()545 public int getScrollDeltaY() { 546 return mScrollDeltaY; 547 } 548 549 /** 550 * Sets the difference in pixels between the vertical position before the scroll and the 551 * current vertical position 552 * 553 * @param scrollDeltaY the scroll delta y 554 */ setScrollDeltaY(int scrollDeltaY)555 public void setScrollDeltaY(int scrollDeltaY) { 556 enforceNotSealed(); 557 mScrollDeltaY = scrollDeltaY; 558 } 559 560 /** 561 * Gets the max scroll offset of the source left edge in pixels. 562 * 563 * @return The max scroll. 564 */ getMaxScrollX()565 public int getMaxScrollX() { 566 return mMaxScrollX; 567 } 568 569 /** 570 * Sets the max scroll offset of the source left edge in pixels. 571 * 572 * @param maxScrollX The max scroll. 573 */ setMaxScrollX(int maxScrollX)574 public void setMaxScrollX(int maxScrollX) { 575 enforceNotSealed(); 576 mMaxScrollX = maxScrollX; 577 } 578 579 /** 580 * Gets the max scroll offset of the source top edge in pixels. 581 * 582 * @return The max scroll. 583 */ getMaxScrollY()584 public int getMaxScrollY() { 585 return mMaxScrollY; 586 } 587 588 /** 589 * Sets the max scroll offset of the source top edge in pixels. 590 * 591 * @param maxScrollY The max scroll. 592 */ setMaxScrollY(int maxScrollY)593 public void setMaxScrollY(int maxScrollY) { 594 enforceNotSealed(); 595 mMaxScrollY = maxScrollY; 596 } 597 598 /** 599 * Gets the number of added characters. 600 * 601 * @return The number of added characters. 602 */ getAddedCount()603 public int getAddedCount() { 604 return mAddedCount; 605 } 606 607 /** 608 * Sets the number of added characters. 609 * 610 * @param addedCount The number of added characters. 611 * 612 * @throws IllegalStateException If called from an AccessibilityService. 613 */ setAddedCount(int addedCount)614 public void setAddedCount(int addedCount) { 615 enforceNotSealed(); 616 mAddedCount = addedCount; 617 } 618 619 /** 620 * Gets the number of removed characters. 621 * 622 * @return The number of removed characters. 623 */ getRemovedCount()624 public int getRemovedCount() { 625 return mRemovedCount; 626 } 627 628 /** 629 * Sets the number of removed characters. 630 * 631 * @param removedCount The number of removed characters. 632 * 633 * @throws IllegalStateException If called from an AccessibilityService. 634 */ setRemovedCount(int removedCount)635 public void setRemovedCount(int removedCount) { 636 enforceNotSealed(); 637 mRemovedCount = removedCount; 638 } 639 640 /** 641 * Gets the class name of the source. 642 * 643 * @return The class name. 644 */ getClassName()645 public @Nullable CharSequence getClassName() { 646 return mClassName; 647 } 648 649 /** 650 * Sets the class name of the source. 651 * 652 * @param className The lass name. 653 * 654 * @throws IllegalStateException If called from an AccessibilityService. 655 */ setClassName(@ullable CharSequence className)656 public void setClassName(@Nullable CharSequence className) { 657 enforceNotSealed(); 658 mClassName = className; 659 } 660 661 /** 662 * Gets the text of the event. The index in the list represents the priority 663 * of the text. Specifically, the lower the index the higher the priority. 664 * 665 * @return The text. 666 */ getText()667 public @NonNull List<CharSequence> getText() { 668 return mText; 669 } 670 671 /** 672 * Gets the text before a change. 673 * 674 * @return The text before the change. 675 */ getBeforeText()676 public @Nullable CharSequence getBeforeText() { 677 return mBeforeText; 678 } 679 680 /** 681 * Sets the text before a change. 682 * 683 * @param beforeText The text before the change. 684 * 685 * @throws IllegalStateException If called from an AccessibilityService. 686 */ setBeforeText(@ullable CharSequence beforeText)687 public void setBeforeText(@Nullable CharSequence beforeText) { 688 enforceNotSealed(); 689 mBeforeText = (beforeText == null) ? null 690 : beforeText.subSequence(0, beforeText.length()); 691 } 692 693 /** 694 * Gets the description of the source. 695 * 696 * @return The description. 697 */ getContentDescription()698 public @Nullable CharSequence getContentDescription() { 699 return mContentDescription; 700 } 701 702 /** 703 * Sets the description of the source. 704 * 705 * @param contentDescription The description. 706 * 707 * @throws IllegalStateException If called from an AccessibilityService. 708 */ setContentDescription(@ullable CharSequence contentDescription)709 public void setContentDescription(@Nullable CharSequence contentDescription) { 710 enforceNotSealed(); 711 mContentDescription = (contentDescription == null) ? null 712 : contentDescription.subSequence(0, contentDescription.length()); 713 } 714 715 /** 716 * Gets the {@link Parcelable} data. 717 * 718 * @return The parcelable data. 719 */ getParcelableData()720 public @Nullable Parcelable getParcelableData() { 721 return mParcelableData; 722 } 723 724 /** 725 * Sets the {@link Parcelable} data of the event. 726 * 727 * @param parcelableData The parcelable data. 728 * 729 * @throws IllegalStateException If called from an AccessibilityService. 730 */ setParcelableData(@ullable Parcelable parcelableData)731 public void setParcelableData(@Nullable Parcelable parcelableData) { 732 enforceNotSealed(); 733 mParcelableData = parcelableData; 734 } 735 736 /** 737 * Gets the id of the source node. 738 * 739 * @return The id. 740 * 741 * @hide 742 */ 743 @UnsupportedAppUsage getSourceNodeId()744 public long getSourceNodeId() { 745 return mSourceNodeId; 746 } 747 748 /** 749 * Sets the unique id of the IAccessibilityServiceConnection over which 750 * this instance can send requests to the system. 751 * 752 * @param connectionId The connection id. 753 * 754 * @hide 755 */ setConnectionId(int connectionId)756 public void setConnectionId(int connectionId) { 757 enforceNotSealed(); 758 mConnectionId = connectionId; 759 } 760 761 /** 762 * Sets if this instance is sealed. 763 * 764 * @param sealed Whether is sealed. 765 * 766 * @hide 767 */ setSealed(boolean sealed)768 public void setSealed(boolean sealed) { 769 mSealed = sealed; 770 } 771 772 /** 773 * Gets if this instance is sealed. 774 * 775 * @return Whether is sealed. 776 */ isSealed()777 boolean isSealed() { 778 return mSealed; 779 } 780 781 /** 782 * Enforces that this instance is sealed. 783 * 784 * @throws IllegalStateException If this instance is not sealed. 785 */ enforceSealed()786 void enforceSealed() { 787 if (!isSealed()) { 788 throw new IllegalStateException("Cannot perform this " 789 + "action on a not sealed instance."); 790 } 791 } 792 793 /** 794 * Enforces that this instance is not sealed. 795 * 796 * @throws IllegalStateException If this instance is sealed. 797 */ enforceNotSealed()798 void enforceNotSealed() { 799 if (isSealed()) { 800 throw new IllegalStateException("Cannot perform this " 801 + "action on a sealed instance."); 802 } 803 } 804 805 /** 806 * Gets the value of a boolean property. 807 * 808 * @param property The property. 809 * @return The value. 810 */ getBooleanProperty(int property)811 private boolean getBooleanProperty(int property) { 812 return (mBooleanProperties & property) == property; 813 } 814 815 /** 816 * Sets a boolean property. 817 * 818 * @param property The property. 819 * @param value The value. 820 */ setBooleanProperty(int property, boolean value)821 private void setBooleanProperty(int property, boolean value) { 822 if (value) { 823 mBooleanProperties |= property; 824 } else { 825 mBooleanProperties &= ~property; 826 } 827 } 828 829 /** 830 * Instantiates a new record initialized with data from the 831 * given record. 832 * 833 * @deprecated Object pooling has been discontinued. Create a new instance using the 834 * constructor {@link #AccessibilityRecord()} instead. 835 * @return An instance. 836 */ 837 @Deprecated obtain(@onNull AccessibilityRecord record)838 public static @NonNull AccessibilityRecord obtain(@NonNull AccessibilityRecord record) { 839 AccessibilityRecord clone = AccessibilityRecord.obtain(); 840 clone.init(record); 841 return clone; 842 } 843 844 /** 845 * Instantiates a new record. 846 * 847 * @deprecated Object pooling has been discontinued. Create a new instance using the 848 * constructor {@link #AccessibilityRecord()} instead. 849 * @return An instance. 850 */ 851 @Deprecated obtain()852 public static @NonNull AccessibilityRecord obtain() { 853 return new AccessibilityRecord(); 854 } 855 856 /** 857 * Would previously return an instance back to be reused. 858 * 859 * @deprecated Object pooling has been discontinued. Calling this function now will have 860 * no effect. 861 */ 862 @Deprecated recycle()863 public void recycle() { } 864 865 /** 866 * Initialize this record from another one. 867 * 868 * @param record The to initialize from. 869 */ init(@onNull AccessibilityRecord record)870 void init(@NonNull AccessibilityRecord record) { 871 mSealed = record.mSealed; 872 mBooleanProperties = record.mBooleanProperties; 873 mCurrentItemIndex = record.mCurrentItemIndex; 874 mItemCount = record.mItemCount; 875 mFromIndex = record.mFromIndex; 876 mToIndex = record.mToIndex; 877 mScrollX = record.mScrollX; 878 mScrollY = record.mScrollY; 879 mMaxScrollX = record.mMaxScrollX; 880 mMaxScrollY = record.mMaxScrollY; 881 mScrollDeltaX = record.mScrollDeltaX; 882 mScrollDeltaY = record.mScrollDeltaY; 883 mAddedCount = record.mAddedCount; 884 mRemovedCount = record.mRemovedCount; 885 mClassName = record.mClassName; 886 mContentDescription = record.mContentDescription; 887 mBeforeText = record.mBeforeText; 888 mParcelableData = record.mParcelableData; 889 mText.addAll(record.mText); 890 mSourceWindowId = record.mSourceWindowId; 891 mSourceNodeId = record.mSourceNodeId; 892 mSourceDisplayId = record.mSourceDisplayId; 893 mConnectionId = record.mConnectionId; 894 } 895 896 /** 897 * Clears the state of this instance. 898 */ clear()899 void clear() { 900 mSealed = false; 901 mBooleanProperties = 0; 902 mCurrentItemIndex = UNDEFINED; 903 mItemCount = UNDEFINED; 904 mFromIndex = UNDEFINED; 905 mToIndex = UNDEFINED; 906 mScrollX = 0; 907 mScrollY = 0; 908 mMaxScrollX = 0; 909 mMaxScrollY = 0; 910 mScrollDeltaX = UNDEFINED; 911 mScrollDeltaY = UNDEFINED; 912 mAddedCount = UNDEFINED; 913 mRemovedCount = UNDEFINED; 914 mClassName = null; 915 mContentDescription = null; 916 mBeforeText = null; 917 mParcelableData = null; 918 mText.clear(); 919 mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; 920 mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; 921 mSourceDisplayId = Display.INVALID_DISPLAY; 922 mConnectionId = UNDEFINED; 923 } 924 925 @Override toString()926 public String toString() { 927 return appendTo(new StringBuilder()).toString(); 928 } 929 appendTo(StringBuilder builder)930 StringBuilder appendTo(StringBuilder builder) { 931 builder.append(" [ ClassName: ").append(mClassName); 932 if (!DEBUG_CONCISE_TOSTRING || !isEmpty(mText)) { 933 appendPropName(builder, "Text").append(mText); 934 } 935 append(builder, "ContentDescription", mContentDescription); 936 append(builder, "ItemCount", mItemCount); 937 append(builder, "CurrentItemIndex", mCurrentItemIndex); 938 939 appendUnless(true, PROPERTY_ENABLED, builder); 940 appendUnless(false, PROPERTY_PASSWORD, builder); 941 appendUnless(false, PROPERTY_CHECKED, builder); 942 appendUnless(false, PROPERTY_FULL_SCREEN, builder); 943 appendUnless(false, PROPERTY_SCROLLABLE, builder); 944 945 append(builder, "BeforeText", mBeforeText); 946 append(builder, "FromIndex", mFromIndex); 947 append(builder, "ToIndex", mToIndex); 948 append(builder, "ScrollX", mScrollX); 949 append(builder, "ScrollY", mScrollY); 950 append(builder, "MaxScrollX", mMaxScrollX); 951 append(builder, "MaxScrollY", mMaxScrollY); 952 append(builder, "ScrollDeltaX", mScrollDeltaX); 953 append(builder, "ScrollDeltaY", mScrollDeltaY); 954 append(builder, "AddedCount", mAddedCount); 955 append(builder, "RemovedCount", mRemovedCount); 956 append(builder, "ParcelableData", mParcelableData); 957 builder.append(" ]"); 958 return builder; 959 } 960 appendUnless(boolean defValue, int prop, StringBuilder builder)961 private void appendUnless(boolean defValue, int prop, StringBuilder builder) { 962 boolean value = getBooleanProperty(prop); 963 if (DEBUG_CONCISE_TOSTRING && value == defValue) return; 964 appendPropName(builder, singleBooleanPropertyToString(prop)) 965 .append(value); 966 } 967 singleBooleanPropertyToString(int prop)968 private static String singleBooleanPropertyToString(int prop) { 969 switch (prop) { 970 case PROPERTY_CHECKED: return "Checked"; 971 case PROPERTY_ENABLED: return "Enabled"; 972 case PROPERTY_PASSWORD: return "Password"; 973 case PROPERTY_FULL_SCREEN: return "FullScreen"; 974 case PROPERTY_SCROLLABLE: return "Scrollable"; 975 case PROPERTY_IMPORTANT_FOR_ACCESSIBILITY: 976 return "ImportantForAccessibility"; 977 default: return Integer.toHexString(prop); 978 } 979 } 980 append(StringBuilder builder, String propName, int propValue)981 private void append(StringBuilder builder, String propName, int propValue) { 982 if (DEBUG_CONCISE_TOSTRING && propValue == UNDEFINED) return; 983 appendPropName(builder, propName).append(propValue); 984 } 985 append(StringBuilder builder, String propName, Object propValue)986 private void append(StringBuilder builder, String propName, Object propValue) { 987 if (DEBUG_CONCISE_TOSTRING && propValue == null) return; 988 appendPropName(builder, propName).append(propValue); 989 } 990 appendPropName(StringBuilder builder, String propName)991 private StringBuilder appendPropName(StringBuilder builder, String propName) { 992 return builder.append("; ").append(propName).append(": "); 993 } 994 } 995