AccessibilityEvent.java revision 887e1a17eb9b12448f5929791b564565b2665aab
1/* 2 * Copyright (C) 2009 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.os.Parcel; 20import android.os.Parcelable; 21import android.text.TextUtils; 22 23import java.util.ArrayList; 24 25/** 26 * This class represents accessibility events that are sent by the system when 27 * something notable happens in the user interface. For example, when a 28 * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc. 29 * <p> 30 * An accessibility event is fired by an individual view which populates the event with 31 * a record for its state and requests from its parent to send the event to interested 32 * parties. The parent can optionally add a record for itself before dispatching a similar 33 * request to its parent. A parent can also choose not to respect the request for sending 34 * an event. The accessibility event is sent by the topmost view in the view tree. 35 * Therefore, an {@link android.accessibilityservice.AccessibilityService} can explore 36 * all records in an accessibility event to obtain more information about the context 37 * in which the event was fired. 38 * <p> 39 * A client can add, remove, and modify records. The getters and setters for individual 40 * properties operate on the current record which can be explicitly set by the client. By 41 * default current is the first record. Thus, querying a record would require setting 42 * it as the current one and interacting with the property getters and setters. 43 * <p> 44 * This class represents various semantically different accessibility event 45 * types. Each event type has associated a set of related properties. In other 46 * words, each event type is characterized via a subset of the properties exposed 47 * by this class. For each event type there is a corresponding constant defined 48 * in this class. Since some event types are semantically close there are mask 49 * constants that group them together. Follows a specification of the event 50 * types and their associated properties: 51 * <p> 52 * <b>VIEW TYPES</b> <br> 53 * <p> 54 * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View} 55 * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> 56 * Type:{@link #TYPE_VIEW_CLICKED} <br> 57 * Properties: 58 * {@link #getClassName()}, 59 * {@link #getPackageName()}, 60 * {@link #getEventTime()}, 61 * {@link #getText()}, 62 * {@link #isChecked()}, 63 * {@link #isEnabled()}, 64 * {@link #isPassword()}, 65 * {@link #getItemCount()}, 66 * {@link #getCurrentItemIndex()} 67 * <p> 68 * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View} 69 * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br> 70 * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br> 71 * Properties: 72 * {@link #getClassName()}, 73 * {@link #getPackageName()}, 74 * {@link #getEventTime()}, 75 * {@link #getText()}, 76 * {@link #isChecked()}, 77 * {@link #isEnabled()}, 78 * {@link #isPassword()}, 79 * {@link #getItemCount()}, 80 * {@link #getCurrentItemIndex()} 81 * <p> 82 * <b>View selected</b> - represents the event of selecting an item usually in 83 * the context of an {@link android.widget.AdapterView}. <br> 84 * Type: {@link #TYPE_VIEW_SELECTED} <br> 85 * Properties: 86 * {@link #getClassName()}, 87 * {@link #getPackageName()}, 88 * {@link #getEventTime()}, 89 * {@link #getText()}, 90 * {@link #isChecked()}, 91 * {@link #isEnabled()}, 92 * {@link #isPassword()}, 93 * {@link #getItemCount()}, 94 * {@link #getCurrentItemIndex()} 95 * <p> 96 * <b>View focused</b> - represents the event of focusing a 97 * {@link android.view.View}. <br> 98 * Type: {@link #TYPE_VIEW_FOCUSED} <br> 99 * Properties: 100 * {@link #getClassName()}, 101 * {@link #getPackageName()}, 102 * {@link #getEventTime()}, 103 * {@link #getText()}, 104 * {@link #isChecked()}, 105 * {@link #isEnabled()}, 106 * {@link #isPassword()}, 107 * {@link #getItemCount()}, 108 * {@link #getCurrentItemIndex()} 109 * <p> 110 * <b>View text changed</b> - represents the event of changing the text of an 111 * {@link android.widget.EditText}. <br> 112 * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br> 113 * Properties: 114 * {@link #getClassName()}, 115 * {@link #getPackageName()}, 116 * {@link #getEventTime()}, 117 * {@link #getText()}, 118 * {@link #isChecked()}, 119 * {@link #isEnabled()}, 120 * {@link #isPassword()}, 121 * {@link #getItemCount()}, 122 * {@link #getCurrentItemIndex()}, 123 * {@link #getFromIndex()}, 124 * {@link #getAddedCount()}, 125 * {@link #getRemovedCount()}, 126 * {@link #getBeforeText()} 127 * <p> 128 * <b>TRANSITION TYPES</b> <br> 129 * <p> 130 * <b>Window state changed</b> - represents the event of opening/closing a 131 * {@link android.widget.PopupWindow}, {@link android.view.Menu}, 132 * {@link android.app.Dialog}, etc. <br> 133 * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br> 134 * Properties: 135 * {@link #getClassName()}, 136 * {@link #getPackageName()}, 137 * {@link #getEventTime()}, 138 * {@link #getText()} 139 * <p> 140 * <b>NOTIFICATION TYPES</b> <br> 141 * <p> 142 * <b>Notification state changed</b> - represents the event showing/hiding 143 * {@link android.app.Notification}. 144 * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br> 145 * Properties: 146 * {@link #getClassName()}, 147 * {@link #getPackageName()}, 148 * {@link #getEventTime()}, 149 * {@link #getText()} 150 * {@link #getParcelableData()} 151 * <p> 152 * <b>Security note</b> 153 * <p> 154 * Since an event contains the text of its source privacy can be compromised by leaking of 155 * sensitive information such as passwords. To address this issue any event fired in response 156 * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password. 157 * 158 * @see android.view.accessibility.AccessibilityManager 159 * @see android.accessibilityservice.AccessibilityService 160 */ 161public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable { 162 163 /** 164 * Invalid selection/focus position. 165 * 166 * @see #getCurrentItemIndex() 167 */ 168 public static final int INVALID_POSITION = -1; 169 170 /** 171 * Maximum length of the text fields. 172 * 173 * @see #getBeforeText() 174 * @see #getText() 175 * </br> 176 * Note: This constant is no longer needed since there 177 * is no limit on the length of text that is contained 178 * in an accessibility event anymore. 179 */ 180 @Deprecated 181 public static final int MAX_TEXT_LENGTH = 500; 182 183 /** 184 * Represents the event of clicking on a {@link android.view.View} like 185 * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. 186 */ 187 public static final int TYPE_VIEW_CLICKED = 0x00000001; 188 189 /** 190 * Represents the event of long clicking on a {@link android.view.View} like 191 * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. 192 */ 193 public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002; 194 195 /** 196 * Represents the event of selecting an item usually in the context of an 197 * {@link android.widget.AdapterView}. 198 */ 199 public static final int TYPE_VIEW_SELECTED = 0x00000004; 200 201 /** 202 * Represents the event of focusing a {@link android.view.View}. 203 */ 204 public static final int TYPE_VIEW_FOCUSED = 0x00000008; 205 206 /** 207 * Represents the event of changing the text of an {@link android.widget.EditText}. 208 */ 209 public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010; 210 211 /** 212 * Represents the event of opening/closing a {@link android.widget.PopupWindow}, 213 * {@link android.view.Menu}, {@link android.app.Dialog}, etc. 214 */ 215 public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020; 216 217 /** 218 * Represents the event showing/hiding a {@link android.app.Notification}. 219 */ 220 public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040; 221 222 /** 223 * Represents the event of a hover enter over a {@link android.view.View}. 224 */ 225 public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080; 226 227 /** 228 * Represents the event of a hover exit over a {@link android.view.View}. 229 */ 230 public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100; 231 232 /** 233 * Represents the event of starting a touch exploration gesture. 234 */ 235 public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200; 236 237 /** 238 * Represents the event of ending a touch exploration gesture. 239 */ 240 public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400; 241 242 /** 243 * Mask for {@link AccessibilityEvent} all types. 244 * 245 * @see #TYPE_VIEW_CLICKED 246 * @see #TYPE_VIEW_LONG_CLICKED 247 * @see #TYPE_VIEW_SELECTED 248 * @see #TYPE_VIEW_FOCUSED 249 * @see #TYPE_VIEW_TEXT_CHANGED 250 * @see #TYPE_WINDOW_STATE_CHANGED 251 * @see #TYPE_NOTIFICATION_STATE_CHANGED 252 */ 253 public static final int TYPES_ALL_MASK = 0xFFFFFFFF; 254 255 private static final int MAX_POOL_SIZE = 10; 256 private static final Object sPoolLock = new Object(); 257 private static AccessibilityEvent sPool; 258 private static int sPoolSize; 259 260 private AccessibilityEvent mNext; 261 private boolean mIsInPool; 262 263 private int mEventType; 264 private CharSequence mPackageName; 265 private long mEventTime; 266 267 private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>(); 268 269 /* 270 * Hide constructor from clients. 271 */ 272 private AccessibilityEvent() { 273 274 } 275 276 /** 277 * Gets the number of records contained in the event. 278 * 279 * @return The number of records. 280 */ 281 public int getRecordCount() { 282 return mRecords.size(); 283 } 284 285 /** 286 * Appends an {@link AccessibilityRecord} to the end of event records. 287 * 288 * @param record The record to append. 289 */ 290 public void appendRecord(AccessibilityRecord record) { 291 mRecords.add(record); 292 } 293 294 /** 295 * Gets the records at a given index. 296 * 297 * @param index The index. 298 * @return The records at the specified index. 299 */ 300 public AccessibilityRecord getRecord(int index) { 301 return mRecords.get(index); 302 } 303 304 /** 305 * Gets the event type. 306 * 307 * @return The event type. 308 */ 309 public int getEventType() { 310 return mEventType; 311 } 312 313 /** 314 * Sets the event type. 315 * 316 * @param eventType The event type. 317 */ 318 public void setEventType(int eventType) { 319 mEventType = eventType; 320 } 321 322 /** 323 * Gets the time in which this event was sent. 324 * 325 * @return The event time. 326 */ 327 public long getEventTime() { 328 return mEventTime; 329 } 330 331 /** 332 * Sets the time in which this event was sent. 333 * 334 * @param eventTime The event time. 335 */ 336 public void setEventTime(long eventTime) { 337 mEventTime = eventTime; 338 } 339 340 /** 341 * Gets the package name of the source. 342 * 343 * @return The package name. 344 */ 345 public CharSequence getPackageName() { 346 return mPackageName; 347 } 348 349 /** 350 * Sets the package name of the source. 351 * 352 * @param packageName The package name. 353 */ 354 public void setPackageName(CharSequence packageName) { 355 mPackageName = packageName; 356 } 357 358 /** 359 * Returns a cached instance if such is available or a new one is 360 * instantiated with type property set. 361 * 362 * @param eventType The event type. 363 * @return An instance. 364 */ 365 public static AccessibilityEvent obtain(int eventType) { 366 AccessibilityEvent event = AccessibilityEvent.obtain(); 367 event.setEventType(eventType); 368 return event; 369 } 370 371 /** 372 * Returns a cached instance if such is available or a new one is 373 * instantiated. 374 * 375 * @return An instance. 376 */ 377 public static AccessibilityEvent obtain() { 378 synchronized (sPoolLock) { 379 if (sPool != null) { 380 AccessibilityEvent event = sPool; 381 sPool = sPool.mNext; 382 sPoolSize--; 383 event.mNext = null; 384 event.mIsInPool = false; 385 return event; 386 } 387 return new AccessibilityEvent(); 388 } 389 } 390 391 /** 392 * Return an instance back to be reused. 393 * <p> 394 * <b>Note: You must not touch the object after calling this function.</b> 395 * 396 * @throws IllegalStateException If the event is already recycled. 397 */ 398 @Override 399 public void recycle() { 400 if (mIsInPool) { 401 throw new IllegalStateException("Event already recycled!"); 402 } 403 clear(); 404 synchronized (sPoolLock) { 405 if (sPoolSize <= MAX_POOL_SIZE) { 406 mNext = sPool; 407 sPool = this; 408 mIsInPool = true; 409 sPoolSize++; 410 } 411 } 412 } 413 414 /** 415 * Clears the state of this instance. 416 */ 417 @Override 418 protected void clear() { 419 super.clear(); 420 mEventType = 0; 421 mPackageName = null; 422 mEventTime = 0; 423 while (!mRecords.isEmpty()) { 424 AccessibilityRecord record = mRecords.remove(0); 425 record.recycle(); 426 } 427 } 428 429 /** 430 * Creates a new instance from a {@link Parcel}. 431 * 432 * @param parcel A parcel containing the state of a {@link AccessibilityEvent}. 433 */ 434 public void initFromParcel(Parcel parcel) { 435 mEventType = parcel.readInt(); 436 mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 437 mEventTime = parcel.readLong(); 438 readAccessibilityRecordFromParcel(this, parcel); 439 440 // Read the records. 441 final int recordCount = parcel.readInt(); 442 for (int i = 0; i < recordCount; i++) { 443 AccessibilityRecord record = AccessibilityRecord.obtain(); 444 readAccessibilityRecordFromParcel(record, parcel); 445 mRecords.add(record); 446 } 447 } 448 449 /** 450 * Reads an {@link AccessibilityRecord} from a parcel. 451 * 452 * @param record The record to initialize. 453 * @param parcel The parcel to read from. 454 */ 455 private void readAccessibilityRecordFromParcel(AccessibilityRecord record, 456 Parcel parcel) { 457 record.mBooleanProperties = parcel.readInt(); 458 record.mCurrentItemIndex = parcel.readInt(); 459 record.mItemCount = parcel.readInt(); 460 record.mFromIndex = parcel.readInt(); 461 record.mAddedCount = parcel.readInt(); 462 record.mRemovedCount = parcel.readInt(); 463 record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 464 record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 465 record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 466 record.mParcelableData = parcel.readParcelable(null); 467 parcel.readList(record.mText, null); 468 } 469 470 /** 471 * {@inheritDoc} 472 */ 473 public void writeToParcel(Parcel parcel, int flags) { 474 parcel.writeInt(mEventType); 475 TextUtils.writeToParcel(mPackageName, parcel, 0); 476 parcel.writeLong(mEventTime); 477 writeAccessibilityRecordToParcel(this, parcel, flags); 478 479 // Write the records. 480 final int recordCount = getRecordCount(); 481 parcel.writeInt(recordCount); 482 for (int i = 0; i < recordCount; i++) { 483 AccessibilityRecord record = mRecords.get(i); 484 writeAccessibilityRecordToParcel(record, parcel, flags); 485 } 486 } 487 488 /** 489 * Writes an {@link AccessibilityRecord} to a parcel. 490 * 491 * @param record The record to write. 492 * @param parcel The parcel to which to write. 493 */ 494 private void writeAccessibilityRecordToParcel(AccessibilityRecord record, Parcel parcel, 495 int flags) { 496 parcel.writeInt(record.mBooleanProperties); 497 parcel.writeInt(record.mCurrentItemIndex); 498 parcel.writeInt(record.mItemCount); 499 parcel.writeInt(record.mFromIndex); 500 parcel.writeInt(record.mAddedCount); 501 parcel.writeInt(record.mRemovedCount); 502 TextUtils.writeToParcel(record.mClassName, parcel, flags); 503 TextUtils.writeToParcel(record.mContentDescription, parcel, flags); 504 TextUtils.writeToParcel(record.mBeforeText, parcel, flags); 505 parcel.writeParcelable(record.mParcelableData, flags); 506 parcel.writeList(record.mText); 507 } 508 509 /** 510 * {@inheritDoc} 511 */ 512 public int describeContents() { 513 return 0; 514 } 515 516 @Override 517 public String toString() { 518 StringBuilder builder = new StringBuilder(); 519 builder.append("; EventType: " + mEventType); 520 builder.append("; EventTime: " + mEventTime); 521 builder.append("; PackageName: " + mPackageName); 522 builder.append(" \n{\n"); 523 builder.append(super.toString()); 524 builder.append("\n"); 525 for (int i = 0; i < mRecords.size(); i++) { 526 AccessibilityRecord record = mRecords.get(i); 527 builder.append(" Record "); 528 builder.append(i); 529 builder.append(":"); 530 builder.append(record.toString()); 531 builder.append("\n"); 532 } 533 builder.append("}\n"); 534 return builder.toString(); 535 } 536 537 /** 538 * @see Parcelable.Creator 539 */ 540 public static final Parcelable.Creator<AccessibilityEvent> CREATOR = 541 new Parcelable.Creator<AccessibilityEvent>() { 542 public AccessibilityEvent createFromParcel(Parcel parcel) { 543 AccessibilityEvent event = AccessibilityEvent.obtain(); 544 event.initFromParcel(parcel); 545 return event; 546 } 547 548 public AccessibilityEvent[] newArray(int size) { 549 return new AccessibilityEvent[size]; 550 } 551 }; 552} 553