AccessibilityNodeInfo.java revision 9210ccbdc3629cead65a822d729e1783a773118c
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 17package android.view.accessibility; 18 19import android.accessibilityservice.IAccessibilityServiceConnection; 20import android.graphics.Rect; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.os.RemoteException; 24import android.text.TextUtils; 25import android.util.SparseArray; 26import android.util.SparseIntArray; 27import android.view.View; 28 29/** 30 * This class represents a node of the screen content. From the point of 31 * view of an accessibility service the screen content is presented as tree 32 * of accessibility nodes. 33 * 34 * TODO(svertoslavganov): Update the documentation, add sample, and describe 35 * the security policy. 36 */ 37public class AccessibilityNodeInfo implements Parcelable { 38 39 private static final boolean DEBUG = false; 40 41 // Actions. 42 43 /** 44 * Action that focuses the node. 45 */ 46 public static final int ACTION_FOCUS = 0x00000001; 47 48 /** 49 * Action that unfocuses the node. 50 */ 51 public static final int ACTION_CLEAR_FOCUS = 0x00000002; 52 53 /** 54 * Action that selects the node. 55 */ 56 public static final int ACTION_SELECT = 0x00000004; 57 58 /** 59 * Action that unselects the node. 60 */ 61 public static final int ACTION_CLEAR_SELECTION = 0x00000008; 62 63 // Boolean attributes. 64 65 private static final int PROPERTY_CHECKABLE = 0x00000001; 66 67 private static final int PROPERTY_CHECKED = 0x00000002; 68 69 private static final int PROPERTY_FOCUSABLE = 0x00000004; 70 71 private static final int PROPERTY_FOCUSED = 0x00000008; 72 73 private static final int PROPERTY_SELECTED = 0x00000010; 74 75 private static final int PROPERTY_CLICKABLE = 0x00000020; 76 77 private static final int PROPERTY_LONG_CLICKABLE = 0x00000040; 78 79 private static final int PROPERTY_ENABLED = 0x00000080; 80 81 private static final int PROPERTY_PASSWORD = 0x00000100; 82 83 // Readable representations - lazily initialized. 84 private static SparseArray<String> sActionSymbolicNames; 85 86 // Housekeeping. 87 private static final int MAX_POOL_SIZE = 50; 88 private static final Object sPoolLock = new Object(); 89 private static AccessibilityNodeInfo sPool; 90 private static int sPoolSize; 91 private AccessibilityNodeInfo mNext; 92 private boolean mIsInPool; 93 private boolean mSealed; 94 95 // Data. 96 private int mAccessibilityViewId = View.NO_ID; 97 private int mAccessibilityWindowId = View.NO_ID; 98 private int mParentAccessibilityViewId = View.NO_ID; 99 private int mBooleanProperties; 100 private final Rect mBounds = new Rect(); 101 102 private CharSequence mPackageName; 103 private CharSequence mClassName; 104 private CharSequence mText; 105 private CharSequence mContentDescription; 106 107 private final SparseIntArray mChildAccessibilityIds = new SparseIntArray(); 108 private int mActions; 109 110 private IAccessibilityServiceConnection mConnection; 111 112 /** 113 * Hide constructor from clients. 114 */ 115 private AccessibilityNodeInfo() { 116 /* do nothing */ 117 } 118 119 /** 120 * Sets the source. 121 * 122 * @param source The info source. 123 */ 124 public void setSource(View source) { 125 enforceNotSealed(); 126 mAccessibilityViewId = source.getAccessibilityViewId(); 127 mAccessibilityWindowId = source.getAccessibilityWindowId(); 128 } 129 130 /** 131 * Gets the id of the window from which the info comes from. 132 * 133 * @return The window id. 134 */ 135 public int getAccessibilityWindowId() { 136 return mAccessibilityWindowId; 137 } 138 139 /** 140 * Gets the number of children. 141 * 142 * @return The child count. 143 */ 144 public int getChildCount() { 145 return mChildAccessibilityIds.size(); 146 } 147 148 /** 149 * Get the child at given index. 150 * <p> 151 * <strong> 152 * It is a client responsibility to recycle the received info by 153 * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating 154 * of multiple instances. 155 * </strong> 156 * </p> 157 * @param index The child index. 158 * @return The child node. 159 * 160 * @throws IllegalStateException If called outside of an AccessibilityService. 161 * 162 */ 163 public AccessibilityNodeInfo getChild(int index) { 164 enforceSealed(); 165 final int childAccessibilityViewId = mChildAccessibilityIds.get(index); 166 try { 167 return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, 168 childAccessibilityViewId); 169 } catch (RemoteException e) { 170 return null; 171 } 172 } 173 174 /** 175 * Adds a child. 176 * <p> 177 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 178 * This class is made immutable before being delivered to an AccessibilityService. 179 * </p> 180 * @param child The child. 181 * 182 * @throws IllegalStateException If called from an AccessibilityService. 183 */ 184 public void addChild(View child) { 185 enforceNotSealed(); 186 final int childAccessibilityViewId = child.getAccessibilityViewId(); 187 final int index = mChildAccessibilityIds.size(); 188 mChildAccessibilityIds.put(index, childAccessibilityViewId); 189 } 190 191 /** 192 * Gets the actions that can be performed on the node. 193 * 194 * @return The bit mask of with actions. 195 * 196 * @see AccessibilityNodeInfo#ACTION_FOCUS 197 * @see AccessibilityNodeInfo#ACTION_CLEAR_FOCUS 198 * @see AccessibilityNodeInfo#ACTION_SELECT 199 * @see AccessibilityNodeInfo#ACTION_CLEAR_SELECTION 200 */ 201 public int getActions() { 202 return mActions; 203 } 204 205 /** 206 * Adds an action that can be performed on the node. 207 * <p> 208 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 209 * This class is made immutable before being delivered to an AccessibilityService. 210 * </p> 211 * @param action The action. 212 * 213 * @throws IllegalStateException If called from an AccessibilityService. 214 */ 215 public void addAction(int action) { 216 enforceNotSealed(); 217 mActions |= action; 218 } 219 220 /** 221 * Performs an action on the node. 222 * <p> 223 * Note: An action can be performed only if the request is made 224 * from an {@link android.accessibilityservice.AccessibilityService}. 225 * </p> 226 * @param action The action to perform. 227 * @return True if the action was performed. 228 * 229 * @throws IllegalStateException If called outside of an AccessibilityService. 230 */ 231 public boolean performAction(int action) { 232 enforceSealed(); 233 try { 234 return mConnection.performAccessibilityAction(mAccessibilityWindowId, 235 mAccessibilityViewId, action); 236 } catch (RemoteException e) { 237 return false; 238 } 239 } 240 241 /** 242 * Gets the unique id identifying this node's parent. 243 * <p> 244 * <strong> 245 * It is a client responsibility to recycle the received info by 246 * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating 247 * of multiple instances. 248 * </strong> 249 * </p> 250 * @return The node's patent id. 251 */ 252 public AccessibilityNodeInfo getParent() { 253 enforceSealed(); 254 try { 255 return mConnection.findAccessibilityNodeInfoByAccessibilityId(mAccessibilityWindowId, 256 mParentAccessibilityViewId); 257 } catch (RemoteException e) { 258 return null; 259 } 260 } 261 262 /** 263 * Sets the parent. 264 * <p> 265 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 266 * This class is made immutable before being delivered to an AccessibilityService. 267 * </p> 268 * @param parent The parent. 269 * 270 * @throws IllegalStateException If called from an AccessibilityService. 271 */ 272 public void setParent(View parent) { 273 enforceNotSealed(); 274 mParentAccessibilityViewId = parent.getAccessibilityViewId(); 275 } 276 277 /** 278 * Gets the node bounds in parent coordinates. 279 * 280 * @param outBounds The output node bounds. 281 */ 282 public void getBounds(Rect outBounds) { 283 outBounds.set(mBounds.left, mBounds.top, mBounds.right, mBounds.bottom); 284 } 285 286 /** 287 * Sets the node bounds in parent coordinates. 288 * <p> 289 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 290 * This class is made immutable before being delivered to an AccessibilityService. 291 * </p> 292 * @param bounds The node bounds. 293 * 294 * @throws IllegalStateException If called from an AccessibilityService. 295 */ 296 public void setBounds(Rect bounds) { 297 enforceNotSealed(); 298 mBounds.set(bounds.left, bounds.top, bounds.right, bounds.bottom); 299 } 300 301 /** 302 * Gets whether this node is checkable. 303 * 304 * @return True if the node is checkable. 305 */ 306 public boolean isCheckable() { 307 return getBooleanProperty(PROPERTY_CHECKABLE); 308 } 309 310 /** 311 * Sets whether this node is checkable. 312 * <p> 313 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 314 * This class is made immutable before being delivered to an AccessibilityService. 315 * </p> 316 * @param checkable True if the node is checkable. 317 * 318 * @throws IllegalStateException If called from an AccessibilityService. 319 */ 320 public void setCheckable(boolean checkable) { 321 setBooleanProperty(PROPERTY_CHECKABLE, checkable); 322 } 323 324 /** 325 * Gets whether this node is checked. 326 * 327 * @return True if the node is checked. 328 */ 329 public boolean isChecked() { 330 return getBooleanProperty(PROPERTY_CHECKED); 331 } 332 333 /** 334 * Sets whether this node is checked. 335 * <p> 336 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 337 * This class is made immutable before being delivered to an AccessibilityService. 338 * </p> 339 * @param checked True if the node is checked. 340 * 341 * @throws IllegalStateException If called from an AccessibilityService. 342 */ 343 public void setChecked(boolean checked) { 344 setBooleanProperty(PROPERTY_CHECKED, checked); 345 } 346 347 /** 348 * Gets whether this node is focusable. 349 * 350 * @return True if the node is focusable. 351 */ 352 public boolean isFocusable() { 353 return getBooleanProperty(PROPERTY_FOCUSABLE); 354 } 355 356 /** 357 * Sets whether this node is focusable. 358 * <p> 359 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 360 * This class is made immutable before being delivered to an AccessibilityService. 361 * </p> 362 * @param focusable True if the node is focusable. 363 * 364 * @throws IllegalStateException If called from an AccessibilityService. 365 */ 366 public void setFocusable(boolean focusable) { 367 setBooleanProperty(PROPERTY_FOCUSABLE, focusable); 368 } 369 370 /** 371 * Gets whether this node is focused. 372 * 373 * @return True if the node is focused. 374 */ 375 public boolean isFocused() { 376 return getBooleanProperty(PROPERTY_FOCUSED); 377 } 378 379 /** 380 * Sets whether this node is focused. 381 * <p> 382 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 383 * This class is made immutable before being delivered to an AccessibilityService. 384 * </p> 385 * @param focused True if the node is focused. 386 * 387 * @throws IllegalStateException If called from an AccessibilityService. 388 */ 389 public void setFocused(boolean focused) { 390 setBooleanProperty(PROPERTY_FOCUSED, focused); 391 } 392 393 /** 394 * Gets whether this node is selected. 395 * 396 * @return True if the node is selected. 397 */ 398 public boolean isSelected() { 399 return getBooleanProperty(PROPERTY_SELECTED); 400 } 401 402 /** 403 * Sets whether this node is selected. 404 * <p> 405 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 406 * This class is made immutable before being delivered to an AccessibilityService. 407 * </p> 408 * @param selected True if the node is selected. 409 * 410 * @throws IllegalStateException If called from an AccessibilityService. 411 */ 412 public void setSelected(boolean selected) { 413 setBooleanProperty(PROPERTY_SELECTED, selected); 414 } 415 416 /** 417 * Gets whether this node is clickable. 418 * 419 * @return True if the node is clickable. 420 */ 421 public boolean isClickable() { 422 return getBooleanProperty(PROPERTY_CLICKABLE); 423 } 424 425 /** 426 * Sets whether this node is clickable. 427 * <p> 428 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 429 * This class is made immutable before being delivered to an AccessibilityService. 430 * </p> 431 * @param clickable True if the node is clickable. 432 * 433 * @throws IllegalStateException If called from an AccessibilityService. 434 */ 435 public void setClickable(boolean clickable) { 436 setBooleanProperty(PROPERTY_CLICKABLE, clickable); 437 } 438 439 /** 440 * Gets whether this node is long clickable. 441 * 442 * @return True if the node is long clickable. 443 */ 444 public boolean isLongClickable() { 445 return getBooleanProperty(PROPERTY_LONG_CLICKABLE); 446 } 447 448 /** 449 * Sets whether this node is long clickable. 450 * <p> 451 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 452 * This class is made immutable before being delivered to an AccessibilityService. 453 * </p> 454 * @param longClickable True if the node is long clickable. 455 * 456 * @throws IllegalStateException If called from an AccessibilityService. 457 */ 458 public void setLongClickable(boolean longClickable) { 459 setBooleanProperty(PROPERTY_LONG_CLICKABLE, longClickable); 460 } 461 462 /** 463 * Gets whether this node is enabled. 464 * 465 * @return True if the node is enabled. 466 */ 467 public boolean isEnabled() { 468 return getBooleanProperty(PROPERTY_ENABLED); 469 } 470 471 /** 472 * Sets whether this node is enabled. 473 * <p> 474 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 475 * This class is made immutable before being delivered to an AccessibilityService. 476 * </p> 477 * @param enabled True if the node is enabled. 478 * 479 * @throws IllegalStateException If called from an AccessibilityService. 480 */ 481 public void setEnabled(boolean enabled) { 482 setBooleanProperty(PROPERTY_ENABLED, enabled); 483 } 484 485 /** 486 * Gets whether this node is a password. 487 * 488 * @return True if the node is a password. 489 */ 490 public boolean isPassword() { 491 return getBooleanProperty(PROPERTY_PASSWORD); 492 } 493 494 /** 495 * Sets whether this node is a password. 496 * <p> 497 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 498 * This class is made immutable before being delivered to an AccessibilityService. 499 * </p> 500 * @param password True if the node is a password. 501 * 502 * @throws IllegalStateException If called from an AccessibilityService. 503 */ 504 public void setPassword(boolean password) { 505 setBooleanProperty(PROPERTY_PASSWORD, password); 506 } 507 508 /** 509 * Gets the package this node comes from. 510 * 511 * @return The package name. 512 */ 513 public CharSequence getPackageName() { 514 return mPackageName; 515 } 516 517 /** 518 * Sets the package this node comes from. 519 * <p> 520 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 521 * This class is made immutable before being delivered to an AccessibilityService. 522 * </p> 523 * @param packageName The package name. 524 * 525 * @throws IllegalStateException If called from an AccessibilityService. 526 */ 527 public void setPackageName(CharSequence packageName) { 528 enforceNotSealed(); 529 mPackageName = packageName; 530 } 531 532 /** 533 * Gets the class this node comes from. 534 * 535 * @return The class name. 536 */ 537 public CharSequence getClassName() { 538 return mClassName; 539 } 540 541 /** 542 * Sets the class this node comes from. 543 * <p> 544 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 545 * This class is made immutable before being delivered to an AccessibilityService. 546 * </p> 547 * @param className The class name. 548 * 549 * @throws IllegalStateException If called from an AccessibilityService. 550 */ 551 public void setClassName(CharSequence className) { 552 enforceNotSealed(); 553 mClassName = className; 554 } 555 556 /** 557 * Gets the text of this node. 558 * 559 * @return The text. 560 */ 561 public CharSequence getText() { 562 return mText; 563 } 564 565 /** 566 * Sets the text of this node. 567 * <p> 568 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 569 * This class is made immutable before being delivered to an AccessibilityService. 570 * </p> 571 * @param text The text. 572 * 573 * @throws IllegalStateException If called from an AccessibilityService. 574 */ 575 public void setText(CharSequence text) { 576 enforceNotSealed(); 577 mText = text; 578 } 579 580 /** 581 * Gets the content description of this node. 582 * 583 * @return The content description. 584 */ 585 public CharSequence getContentDescription() { 586 return mContentDescription; 587 } 588 589 /** 590 * Sets the content description of this node. 591 * <p> 592 * Note: Cannot be called from an {@link android.accessibilityservice.AccessibilityService}. 593 * This class is made immutable before being delivered to an AccessibilityService. 594 * </p> 595 * @param contentDescription The content description. 596 * 597 * @throws IllegalStateException If called from an AccessibilityService. 598 */ 599 public void setContentDescription(CharSequence contentDescription) { 600 enforceNotSealed(); 601 mContentDescription = contentDescription; 602 } 603 604 /** 605 * Gets the value of a boolean property. 606 * 607 * @param property The property. 608 * @return The value. 609 */ 610 private boolean getBooleanProperty(int property) { 611 return (mBooleanProperties & property) != 0; 612 } 613 614 /** 615 * Sets a boolean property. 616 * 617 * @param property The property. 618 * @param value The value. 619 * 620 * @throws IllegalStateException If called from an AccessibilityService. 621 */ 622 private void setBooleanProperty(int property, boolean value) { 623 enforceNotSealed(); 624 if (value) { 625 mBooleanProperties |= property; 626 } else { 627 mBooleanProperties &= ~property; 628 } 629 } 630 631 /** 632 * Sets the connection for interacting with the system. 633 * 634 * @param connection The client token. 635 * 636 * @hide 637 */ 638 public final void setConnection(IAccessibilityServiceConnection connection) { 639 mConnection = connection; 640 } 641 642 /** 643 * {@inheritDoc} 644 */ 645 public int describeContents() { 646 return 0; 647 } 648 649 /** 650 * Sets if this instance is sealed. 651 * 652 * @param sealed Whether is sealed. 653 * 654 * @hide 655 */ 656 public void setSealed(boolean sealed) { 657 mSealed = sealed; 658 } 659 660 /** 661 * Gets if this instance is sealed. 662 * 663 * @return Whether is sealed. 664 * 665 * @hide 666 */ 667 public boolean isSealed() { 668 return mSealed; 669 } 670 671 /** 672 * Enforces that this instance is sealed. 673 * 674 * @throws IllegalStateException If this instance is not sealed. 675 * 676 * @hide 677 */ 678 protected void enforceSealed() { 679 if (!isSealed()) { 680 throw new IllegalStateException("Cannot perform this " 681 + "action on a not sealed instance."); 682 } 683 } 684 685 /** 686 * Enforces that this instance is not sealed. 687 * 688 * @throws IllegalStateException If this instance is sealed. 689 * 690 * @hide 691 */ 692 protected void enforceNotSealed() { 693 if (isSealed()) { 694 throw new IllegalStateException("Cannot perform this " 695 + "action on an sealed instance."); 696 } 697 } 698 699 /** 700 * Returns a cached instance if such is available otherwise a new one 701 * and sets the source. 702 * 703 * @return An instance. 704 * 705 * @see #setSource(View) 706 */ 707 public static AccessibilityNodeInfo obtain(View source) { 708 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 709 info.setSource(source); 710 return info; 711 } 712 713 /** 714 * Returns a cached instance if such is available otherwise a new one. 715 * 716 * @return An instance. 717 */ 718 public static AccessibilityNodeInfo obtain() { 719 synchronized (sPoolLock) { 720 if (sPool != null) { 721 AccessibilityNodeInfo info = sPool; 722 sPool = sPool.mNext; 723 sPoolSize--; 724 info.mNext = null; 725 info.mIsInPool = false; 726 return info; 727 } 728 return new AccessibilityNodeInfo(); 729 } 730 } 731 732 /** 733 * Return an instance back to be reused. 734 * <p> 735 * <b>Note: You must not touch the object after calling this function.</b> 736 * 737 * @throws IllegalStateException If the info is already recycled. 738 */ 739 public void recycle() { 740 if (mIsInPool) { 741 throw new IllegalStateException("Info already recycled!"); 742 } 743 clear(); 744 synchronized (sPoolLock) { 745 if (sPoolSize <= MAX_POOL_SIZE) { 746 mNext = sPool; 747 sPool = this; 748 mIsInPool = true; 749 sPoolSize++; 750 } 751 } 752 } 753 754 /** 755 * {@inheritDoc} 756 * <p> 757 * <b>Note: After the instance is written to a parcel it is recycled. 758 * You must not touch the object after calling this function.</b> 759 * </p> 760 */ 761 public void writeToParcel(Parcel parcel, int flags) { 762 if (mConnection == null) { 763 parcel.writeInt(0); 764 } else { 765 parcel.writeInt(1); 766 parcel.writeStrongBinder(mConnection.asBinder()); 767 } 768 parcel.writeInt(isSealed() ? 1 : 0); 769 parcel.writeInt(mAccessibilityViewId); 770 parcel.writeInt(mAccessibilityWindowId); 771 parcel.writeInt(mParentAccessibilityViewId); 772 773 SparseIntArray childIds = mChildAccessibilityIds; 774 final int childIdsSize = childIds.size(); 775 parcel.writeInt(childIdsSize); 776 for (int i = 0; i < childIdsSize; i++) { 777 parcel.writeInt(childIds.valueAt(i)); 778 } 779 780 parcel.writeInt(mBounds.top); 781 parcel.writeInt(mBounds.bottom); 782 parcel.writeInt(mBounds.left); 783 parcel.writeInt(mBounds.right); 784 785 parcel.writeInt(mActions); 786 787 parcel.writeInt(mBooleanProperties); 788 789 TextUtils.writeToParcel(mPackageName, parcel, flags); 790 TextUtils.writeToParcel(mClassName, parcel, flags); 791 TextUtils.writeToParcel(mText, parcel, flags); 792 TextUtils.writeToParcel(mContentDescription, parcel, flags); 793 794 // Since instances of this class are fetched via synchronous i.e. blocking 795 // calls in IPCs and we always recycle as soon as the instance is marshaled. 796 recycle(); 797 } 798 799 /** 800 * Creates a new instance from a {@link Parcel}. 801 * 802 * @param parcel A parcel containing the state of a {@link AccessibilityNodeInfo}. 803 */ 804 private void initFromParcel(Parcel parcel) { 805 if (parcel.readInt() == 1) { 806 mConnection = IAccessibilityServiceConnection.Stub.asInterface( 807 parcel.readStrongBinder()); 808 } 809 mSealed = (parcel.readInt() == 1); 810 mAccessibilityViewId = parcel.readInt(); 811 mAccessibilityWindowId = parcel.readInt(); 812 mParentAccessibilityViewId = parcel.readInt(); 813 814 SparseIntArray childIds = mChildAccessibilityIds; 815 final int childrenSize = parcel.readInt(); 816 for (int i = 0; i < childrenSize; i++) { 817 final int childId = parcel.readInt(); 818 childIds.put(i, childId); 819 } 820 821 mBounds.top = parcel.readInt(); 822 mBounds.bottom = parcel.readInt(); 823 mBounds.left = parcel.readInt(); 824 mBounds.right = parcel.readInt(); 825 826 mActions = parcel.readInt(); 827 828 mBooleanProperties = parcel.readInt(); 829 830 mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 831 mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 832 mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 833 mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 834 } 835 836 /** 837 * Clears the state of this instance. 838 */ 839 private void clear() { 840 mSealed = false; 841 mConnection = null; 842 mAccessibilityViewId = View.NO_ID; 843 mParentAccessibilityViewId = View.NO_ID; 844 mChildAccessibilityIds.clear(); 845 mBounds.set(0, 0, 0, 0); 846 mBooleanProperties = 0; 847 mPackageName = null; 848 mClassName = null; 849 mText = null; 850 mContentDescription = null; 851 mActions = 0; 852 } 853 854 /** 855 * Gets the human readable action symbolic name. 856 * 857 * @param action The action. 858 * @return The symbolic name. 859 */ 860 private static String getActionSymbolicName(int action) { 861 SparseArray<String> actionSymbolicNames = sActionSymbolicNames; 862 if (actionSymbolicNames == null) { 863 actionSymbolicNames = sActionSymbolicNames = new SparseArray<String>(); 864 actionSymbolicNames.put(ACTION_FOCUS, "ACTION_FOCUS"); 865 actionSymbolicNames.put(ACTION_CLEAR_FOCUS, "ACTION_UNFOCUS"); 866 actionSymbolicNames.put(ACTION_SELECT, "ACTION_SELECT"); 867 actionSymbolicNames.put(ACTION_CLEAR_SELECTION, "ACTION_UNSELECT"); 868 } 869 return actionSymbolicNames.get(action); 870 } 871 872 @Override 873 public int hashCode() { 874 final int prime = 31; 875 int result = 1; 876 result = prime * result + mAccessibilityViewId; 877 result = prime * result + mAccessibilityWindowId; 878 return result; 879 } 880 881 @Override 882 public String toString() { 883 StringBuilder builder = new StringBuilder(); 884 builder.append(super.toString()); 885 886 if (DEBUG) { 887 builder.append("; accessibilityId: " + mAccessibilityViewId); 888 builder.append("; parentAccessibilityId: " + mParentAccessibilityViewId); 889 SparseIntArray childIds = mChildAccessibilityIds; 890 builder.append("; childAccessibilityIds: ["); 891 for (int i = 0, count = childIds.size(); i < count; i++) { 892 builder.append(childIds.valueAt(i)); 893 if (i < count - 1) { 894 builder.append(", "); 895 } 896 } 897 builder.append("]"); 898 } 899 900 builder.append("; bounds: " + mBounds); 901 902 builder.append("; packageName: ").append(mPackageName); 903 builder.append("; className: ").append(mClassName); 904 builder.append("; text: ").append(mText); 905 builder.append("; contentDescription: ").append(mContentDescription); 906 907 builder.append("; checkable: ").append(isCheckable()); 908 builder.append("; checked: ").append(isChecked()); 909 builder.append("; focusable: ").append(isFocusable()); 910 builder.append("; focused: ").append(isFocused()); 911 builder.append("; selected: ").append(isSelected()); 912 builder.append("; clickable: ").append(isClickable()); 913 builder.append("; longClickable: ").append(isLongClickable()); 914 builder.append("; enabled: ").append(isEnabled()); 915 builder.append("; password: ").append(isPassword()); 916 917 builder.append("; ["); 918 919 for (int actionBits = mActions; actionBits != 0;) { 920 final int action = 1 << Integer.numberOfTrailingZeros(actionBits); 921 actionBits &= ~action; 922 builder.append(getActionSymbolicName(action)); 923 if (actionBits != 0) { 924 builder.append(", "); 925 } 926 } 927 928 builder.append("]"); 929 930 return builder.toString(); 931 } 932 933 /** 934 * @see Parcelable.Creator 935 */ 936 public static final Parcelable.Creator<AccessibilityNodeInfo> CREATOR = 937 new Parcelable.Creator<AccessibilityNodeInfo>() { 938 public AccessibilityNodeInfo createFromParcel(Parcel parcel) { 939 AccessibilityNodeInfo info = AccessibilityNodeInfo.obtain(); 940 info.initFromParcel(parcel); 941 return info; 942 } 943 944 public AccessibilityNodeInfo[] newArray(int size) { 945 return new AccessibilityNodeInfo[size]; 946 } 947 }; 948} 949