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