AccessibilityRecord.java revision a20cdc06e599c6fef784a0a479e8329f95e4bd09
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.os.Parcelable; 21import android.os.RemoteException; 22import android.view.View; 23 24import java.util.ArrayList; 25import java.util.List; 26 27/** 28 * Represents a record in an accessibility event. This class encapsulates 29 * the information for a {@link android.view.View}. Note that not all properties 30 * are applicable to all view types. For detailed information please refer to 31 * {@link AccessibilityEvent}. 32 * 33 * @see AccessibilityEvent 34 */ 35public class AccessibilityRecord { 36 37 private static final int INVALID_POSITION = -1; 38 39 private static final int PROPERTY_CHECKED = 0x00000001; 40 private static final int PROPERTY_ENABLED = 0x00000002; 41 private static final int PROPERTY_PASSWORD = 0x00000004; 42 private static final int PROPERTY_FULL_SCREEN = 0x00000080; 43 private static final int PROPERTY_SCROLLABLE = 0x00000100; 44 45 // Housekeeping 46 private static final int MAX_POOL_SIZE = 10; 47 private static final Object sPoolLock = new Object(); 48 private static AccessibilityRecord sPool; 49 private static int sPoolSize; 50 private AccessibilityRecord mNext; 51 private boolean mIsInPool; 52 53 boolean mSealed; 54 int mBooleanProperties; 55 int mCurrentItemIndex; 56 int mItemCount; 57 int mFromIndex; 58 int mToIndex; 59 int mScrollX; 60 int mScrollY; 61 62 int mAddedCount; 63 int mRemovedCount; 64 int mSourceViewId = View.NO_ID; 65 int mSourceWindowId = View.NO_ID; 66 67 CharSequence mClassName; 68 CharSequence mContentDescription; 69 CharSequence mBeforeText; 70 Parcelable mParcelableData; 71 72 final List<CharSequence> mText = new ArrayList<CharSequence>(); 73 IAccessibilityServiceConnection mConnection; 74 75 /* 76 * Hide constructor. 77 */ 78 AccessibilityRecord() { 79 } 80 81 /** 82 * Initialize this record from another one. 83 * 84 * @param record The to initialize from. 85 */ 86 void init(AccessibilityRecord record) { 87 mSealed = record.mSealed; 88 mBooleanProperties = record.mBooleanProperties; 89 mCurrentItemIndex = record.mCurrentItemIndex; 90 mItemCount = record.mItemCount; 91 mFromIndex = record.mFromIndex; 92 mToIndex = record.mToIndex; 93 mScrollX = record.mScrollX; 94 mScrollY = record.mScrollY; 95 mAddedCount = record.mAddedCount; 96 mRemovedCount = record.mRemovedCount; 97 mClassName = record.mClassName; 98 mContentDescription = record.mContentDescription; 99 mBeforeText = record.mBeforeText; 100 mParcelableData = record.mParcelableData; 101 mText.addAll(record.mText); 102 mSourceWindowId = record.mSourceWindowId; 103 mSourceViewId = record.mSourceViewId; 104 mConnection = record.mConnection; 105 } 106 107 /** 108 * Sets the event source. 109 * 110 * @param source The source. 111 * 112 * @throws IllegalStateException If called from an AccessibilityService. 113 */ 114 public void setSource(View source) { 115 enforceNotSealed(); 116 if (source != null) { 117 mSourceWindowId = source.getAccessibilityWindowId(); 118 mSourceViewId = source.getAccessibilityViewId(); 119 } else { 120 mSourceWindowId = View.NO_ID; 121 mSourceViewId = View.NO_ID; 122 } 123 } 124 125 /** 126 * Gets the {@link AccessibilityNodeInfo} of the event source. 127 * <p> 128 * <strong> 129 * It is a client responsibility to recycle the received info by 130 * calling {@link AccessibilityNodeInfo#recycle()} to avoid creating 131 * of multiple instances. 132 * </strong> 133 * </p> 134 * @return The info. 135 */ 136 public AccessibilityNodeInfo getSource() { 137 enforceSealed(); 138 if (mSourceWindowId == View.NO_ID || mSourceViewId == View.NO_ID || mConnection == null) { 139 return null; 140 } 141 try { 142 return mConnection.findAccessibilityNodeInfoByAccessibilityId(mSourceWindowId, 143 mSourceViewId); 144 } catch (RemoteException e) { 145 /* ignore */ 146 } 147 return null; 148 } 149 150 /** 151 * Sets the connection for interacting with the AccessibilityManagerService. 152 * 153 * @param connection The connection. 154 * 155 * @hide 156 */ 157 public void setConnection(IAccessibilityServiceConnection connection) { 158 enforceNotSealed(); 159 mConnection = connection; 160 } 161 162 /** 163 * Gets the id of the window from which the event comes from. 164 * 165 * @return The window id. 166 */ 167 public int getWindowId() { 168 return mSourceWindowId; 169 } 170 171 /** 172 * Gets if the source is checked. 173 * 174 * @return True if the view is checked, false otherwise. 175 */ 176 public boolean isChecked() { 177 return getBooleanProperty(PROPERTY_CHECKED); 178 } 179 180 /** 181 * Sets if the source is checked. 182 * 183 * @param isChecked True if the view is checked, false otherwise. 184 * 185 * @throws IllegalStateException If called from an AccessibilityService. 186 */ 187 public void setChecked(boolean isChecked) { 188 enforceNotSealed(); 189 setBooleanProperty(PROPERTY_CHECKED, isChecked); 190 } 191 192 /** 193 * Gets if the source is enabled. 194 * 195 * @return True if the view is enabled, false otherwise. 196 */ 197 public boolean isEnabled() { 198 return getBooleanProperty(PROPERTY_ENABLED); 199 } 200 201 /** 202 * Sets if the source is enabled. 203 * 204 * @param isEnabled True if the view is enabled, false otherwise. 205 * 206 * @throws IllegalStateException If called from an AccessibilityService. 207 */ 208 public void setEnabled(boolean isEnabled) { 209 enforceNotSealed(); 210 setBooleanProperty(PROPERTY_ENABLED, isEnabled); 211 } 212 213 /** 214 * Gets if the source is a password field. 215 * 216 * @return True if the view is a password field, false otherwise. 217 */ 218 public boolean isPassword() { 219 return getBooleanProperty(PROPERTY_PASSWORD); 220 } 221 222 /** 223 * Sets if the source is a password field. 224 * 225 * @param isPassword True if the view is a password field, false otherwise. 226 * 227 * @throws IllegalStateException If called from an AccessibilityService. 228 */ 229 public void setPassword(boolean isPassword) { 230 enforceNotSealed(); 231 setBooleanProperty(PROPERTY_PASSWORD, isPassword); 232 } 233 234 /** 235 * Gets if the source is taking the entire screen. 236 * 237 * @return True if the source is full screen, false otherwise. 238 */ 239 public boolean isFullScreen() { 240 return getBooleanProperty(PROPERTY_FULL_SCREEN); 241 } 242 243 /** 244 * Sets if the source is taking the entire screen. 245 * 246 * @param isFullScreen True if the source is full screen, false otherwise. 247 * 248 * @throws IllegalStateException If called from an AccessibilityService. 249 */ 250 public void setFullScreen(boolean isFullScreen) { 251 enforceNotSealed(); 252 setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); 253 } 254 255 /** 256 * Gets if the source is scrollable. 257 * 258 * @return True if the source is scrollable, false otherwise. 259 */ 260 public boolean isScrollable() { 261 return getBooleanProperty(PROPERTY_SCROLLABLE); 262 } 263 264 /** 265 * Sets if the source is scrollable. 266 * 267 * @param scrollable True if the source is scrollable, false otherwise. 268 * 269 * @throws IllegalStateException If called from an AccessibilityService. 270 */ 271 public void setScrollable(boolean scrollable) { 272 enforceNotSealed(); 273 setBooleanProperty(PROPERTY_SCROLLABLE, scrollable); 274 } 275 276 /** 277 * Gets the number of items that can be visited. 278 * 279 * @return The number of items. 280 */ 281 public int getItemCount() { 282 return mItemCount; 283 } 284 285 /** 286 * Sets the number of items that can be visited. 287 * 288 * @param itemCount The number of items. 289 * 290 * @throws IllegalStateException If called from an AccessibilityService. 291 */ 292 public void setItemCount(int itemCount) { 293 enforceNotSealed(); 294 mItemCount = itemCount; 295 } 296 297 /** 298 * Gets the index of the source in the list of items the can be visited. 299 * 300 * @return The current item index. 301 */ 302 public int getCurrentItemIndex() { 303 return mCurrentItemIndex; 304 } 305 306 /** 307 * Sets the index of the source in the list of items that can be visited. 308 * 309 * @param currentItemIndex The current item index. 310 * 311 * @throws IllegalStateException If called from an AccessibilityService. 312 */ 313 public void setCurrentItemIndex(int currentItemIndex) { 314 enforceNotSealed(); 315 mCurrentItemIndex = currentItemIndex; 316 } 317 318 /** 319 * Gets the index of the first character of the changed sequence, 320 * or the beginning of a text selection or the index of the first 321 * visible item when scrolling. 322 * 323 * @return The index of the first character or selection 324 * start or the first visible item. 325 */ 326 public int getFromIndex() { 327 return mFromIndex; 328 } 329 330 /** 331 * Sets the index of the first character of the changed sequence 332 * or the beginning of a text selection or the index of the first 333 * visible item when scrolling. 334 * 335 * @param fromIndex The index of the first character or selection 336 * start or the first visible item. 337 * 338 * @throws IllegalStateException If called from an AccessibilityService. 339 */ 340 public void setFromIndex(int fromIndex) { 341 enforceNotSealed(); 342 mFromIndex = fromIndex; 343 } 344 345 /** 346 * Gets the index of text selection end or the index of the last 347 * visible item when scrolling. 348 * 349 * @return The index of selection end or last item index. 350 */ 351 public int getToIndex() { 352 return mToIndex; 353 } 354 355 /** 356 * Sets the index of text selection end or the index of the last 357 * visible item when scrolling. 358 * 359 * @param toIndex The index of selection end or last item index. 360 */ 361 public void setToIndex(int toIndex) { 362 enforceNotSealed(); 363 mToIndex = toIndex; 364 } 365 366 /** 367 * Gets the scroll position of the source along the X axis. 368 * 369 * @return The scroll along the X axis. 370 */ 371 public int getScrollX() { 372 return mScrollX; 373 } 374 375 /** 376 * Sets the scroll position of the source along the X axis. 377 * 378 * @param scrollX The scroll along the X axis. 379 */ 380 public void setScrollX(int scrollX) { 381 enforceNotSealed(); 382 mScrollX = scrollX; 383 } 384 385 /** 386 * Gets the scroll position of the source along the Y axis. 387 * 388 * @return The scroll along the Y axis. 389 */ 390 public int getScrollY() { 391 return mScrollY; 392 } 393 394 /** 395 * Sets the scroll position of the source along the Y axis. 396 * 397 * @param scrollY The scroll along the Y axis. 398 */ 399 public void setScrollY(int scrollY) { 400 enforceNotSealed(); 401 mScrollY = scrollY; 402 } 403 404 /** 405 * Gets the number of added characters. 406 * 407 * @return The number of added characters. 408 */ 409 public int getAddedCount() { 410 return mAddedCount; 411 } 412 413 /** 414 * Sets the number of added characters. 415 * 416 * @param addedCount The number of added characters. 417 * 418 * @throws IllegalStateException If called from an AccessibilityService. 419 */ 420 public void setAddedCount(int addedCount) { 421 enforceNotSealed(); 422 mAddedCount = addedCount; 423 } 424 425 /** 426 * Gets the number of removed characters. 427 * 428 * @return The number of removed characters. 429 */ 430 public int getRemovedCount() { 431 return mRemovedCount; 432 } 433 434 /** 435 * Sets the number of removed characters. 436 * 437 * @param removedCount The number of removed characters. 438 * 439 * @throws IllegalStateException If called from an AccessibilityService. 440 */ 441 public void setRemovedCount(int removedCount) { 442 enforceNotSealed(); 443 mRemovedCount = removedCount; 444 } 445 446 /** 447 * Gets the class name of the source. 448 * 449 * @return The class name. 450 */ 451 public CharSequence getClassName() { 452 return mClassName; 453 } 454 455 /** 456 * Sets the class name of the source. 457 * 458 * @param className The lass name. 459 * 460 * @throws IllegalStateException If called from an AccessibilityService. 461 */ 462 public void setClassName(CharSequence className) { 463 enforceNotSealed(); 464 mClassName = className; 465 } 466 467 /** 468 * Gets the text of the event. The index in the list represents the priority 469 * of the text. Specifically, the lower the index the higher the priority. 470 * 471 * @return The text. 472 */ 473 public List<CharSequence> getText() { 474 return mText; 475 } 476 477 /** 478 * Sets the text before a change. 479 * 480 * @return The text before the change. 481 */ 482 public CharSequence getBeforeText() { 483 return mBeforeText; 484 } 485 486 /** 487 * Sets the text before a change. 488 * 489 * @param beforeText The text before the change. 490 * 491 * @throws IllegalStateException If called from an AccessibilityService. 492 */ 493 public void setBeforeText(CharSequence beforeText) { 494 enforceNotSealed(); 495 mBeforeText = beforeText; 496 } 497 498 /** 499 * Gets the description of the source. 500 * 501 * @return The description. 502 */ 503 public CharSequence getContentDescription() { 504 return mContentDescription; 505 } 506 507 /** 508 * Sets the description of the source. 509 * 510 * @param contentDescription The description. 511 * 512 * @throws IllegalStateException If called from an AccessibilityService. 513 */ 514 public void setContentDescription(CharSequence contentDescription) { 515 enforceNotSealed(); 516 mContentDescription = contentDescription; 517 } 518 519 /** 520 * Gets the {@link Parcelable} data. 521 * 522 * @return The parcelable data. 523 */ 524 public Parcelable getParcelableData() { 525 return mParcelableData; 526 } 527 528 /** 529 * Sets the {@link Parcelable} data of the event. 530 * 531 * @param parcelableData The parcelable data. 532 * 533 * @throws IllegalStateException If called from an AccessibilityService. 534 */ 535 public void setParcelableData(Parcelable parcelableData) { 536 enforceNotSealed(); 537 mParcelableData = parcelableData; 538 } 539 540 /** 541 * Sets if this instance is sealed. 542 * 543 * @param sealed Whether is sealed. 544 * 545 * @hide 546 */ 547 public void setSealed(boolean sealed) { 548 mSealed = sealed; 549 } 550 551 /** 552 * Gets if this instance is sealed. 553 * 554 * @return Whether is sealed. 555 */ 556 boolean isSealed() { 557 return mSealed; 558 } 559 560 /** 561 * Enforces that this instance is sealed. 562 * 563 * @throws IllegalStateException If this instance is not sealed. 564 */ 565 void enforceSealed() { 566 if (!isSealed()) { 567 throw new IllegalStateException("Cannot perform this " 568 + "action on a not sealed instance."); 569 } 570 } 571 572 /** 573 * Enforces that this instance is not sealed. 574 * 575 * @throws IllegalStateException If this instance is sealed. 576 */ 577 void enforceNotSealed() { 578 if (isSealed()) { 579 throw new IllegalStateException("Cannot perform this " 580 + "action on an sealed instance."); 581 } 582 } 583 584 /** 585 * Gets the value of a boolean property. 586 * 587 * @param property The property. 588 * @return The value. 589 */ 590 private boolean getBooleanProperty(int property) { 591 return (mBooleanProperties & property) == property; 592 } 593 594 /** 595 * Sets a boolean property. 596 * 597 * @param property The property. 598 * @param value The value. 599 */ 600 private void setBooleanProperty(int property, boolean value) { 601 if (value) { 602 mBooleanProperties |= property; 603 } else { 604 mBooleanProperties &= ~property; 605 } 606 } 607 608 /** 609 * Returns a cached instance if such is available or a new one is 610 * instantiated. The instance is initialized with data from the 611 * given record. 612 * 613 * @return An instance. 614 */ 615 public static AccessibilityRecord obtain(AccessibilityRecord record) { 616 AccessibilityRecord clone = AccessibilityRecord.obtain(); 617 clone.init(record); 618 return clone; 619 } 620 621 /** 622 * Returns a cached instance if such is available or a new one is 623 * instantiated. 624 * 625 * @return An instance. 626 */ 627 public static AccessibilityRecord obtain() { 628 synchronized (sPoolLock) { 629 if (sPool != null) { 630 AccessibilityRecord record = sPool; 631 sPool = sPool.mNext; 632 sPoolSize--; 633 record.mNext = null; 634 record.mIsInPool = false; 635 return record; 636 } 637 return new AccessibilityRecord(); 638 } 639 } 640 641 /** 642 * Return an instance back to be reused. 643 * <p> 644 * <b>Note: You must not touch the object after calling this function.</b> 645 * 646 * @throws IllegalStateException If the record is already recycled. 647 */ 648 public void recycle() { 649 if (mIsInPool) { 650 throw new IllegalStateException("Record already recycled!"); 651 } 652 clear(); 653 synchronized (sPoolLock) { 654 if (sPoolSize <= MAX_POOL_SIZE) { 655 mNext = sPool; 656 sPool = this; 657 mIsInPool = true; 658 sPoolSize++; 659 } 660 } 661 } 662 663 /** 664 * Clears the state of this instance. 665 */ 666 void clear() { 667 mSealed = false; 668 mBooleanProperties = 0; 669 mCurrentItemIndex = INVALID_POSITION; 670 mItemCount = 0; 671 mFromIndex = 0; 672 mToIndex = 0; 673 mScrollX = 0; 674 mScrollY = 0; 675 mAddedCount = 0; 676 mRemovedCount = 0; 677 mClassName = null; 678 mContentDescription = null; 679 mBeforeText = null; 680 mParcelableData = null; 681 mText.clear(); 682 mSourceViewId = View.NO_ID; 683 mSourceWindowId = View.NO_ID; 684 } 685 686 @Override 687 public String toString() { 688 StringBuilder builder = new StringBuilder(); 689 builder.append(" [ ClassName: " + mClassName); 690 builder.append("; Text: " + mText); 691 builder.append("; ContentDescription: " + mContentDescription); 692 builder.append("; ItemCount: " + mItemCount); 693 builder.append("; CurrentItemIndex: " + mCurrentItemIndex); 694 builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED)); 695 builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD)); 696 builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED)); 697 builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN)); 698 builder.append("; Scrollable: " + getBooleanProperty(PROPERTY_SCROLLABLE)); 699 builder.append("; BeforeText: " + mBeforeText); 700 builder.append("; FromIndex: " + mFromIndex); 701 builder.append("; ToIndex: " + mToIndex); 702 builder.append("; ScrollX: " + mScrollX); 703 builder.append("; ScrollY: " + mScrollY); 704 builder.append("; AddedCount: " + mAddedCount); 705 builder.append("; RemovedCount: " + mRemovedCount); 706 builder.append("; ParcelableData: " + mParcelableData); 707 builder.append(" ]"); 708 return builder.toString(); 709 } 710} 711