AccessibilityEvent.java revision 736c2756bf3c14ae9fef7255c119057f7a2be1ed
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 mPoolLock = 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 (mPoolLock) { 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 @Override 397 public void recycle() { 398 if (mIsInPool) { 399 return; 400 } 401 clear(); 402 synchronized (mPoolLock) { 403 if (sPoolSize <= MAX_POOL_SIZE) { 404 mNext = sPool; 405 sPool = this; 406 mIsInPool = true; 407 sPoolSize++; 408 } 409 } 410 } 411 412 /** 413 * Clears the state of this instance. 414 */ 415 @Override 416 protected void clear() { 417 super.clear(); 418 mEventType = 0; 419 mPackageName = null; 420 mEventTime = 0; 421 while (!mRecords.isEmpty()) { 422 AccessibilityRecord record = mRecords.remove(0); 423 record.recycle(); 424 } 425 } 426 427 /** 428 * Creates a new instance from a {@link Parcel}. 429 * 430 * @param parcel A parcel containing the state of a {@link AccessibilityEvent}. 431 */ 432 public void initFromParcel(Parcel parcel) { 433 mEventType = parcel.readInt(); 434 mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 435 mEventTime = parcel.readLong(); 436 readAccessibilityRecordFromParcel(this, parcel); 437 438 // Read the records. 439 final int recordCount = parcel.readInt(); 440 for (int i = 0; i < recordCount; i++) { 441 AccessibilityRecord record = AccessibilityRecord.obtain(); 442 readAccessibilityRecordFromParcel(record, parcel); 443 mRecords.add(record); 444 } 445 } 446 447 /** 448 * Reads an {@link AccessibilityRecord} from a parcel. 449 * 450 * @param record The record to initialize. 451 * @param parcel The parcel to read from. 452 */ 453 private void readAccessibilityRecordFromParcel(AccessibilityRecord record, 454 Parcel parcel) { 455 record.mBooleanProperties = parcel.readInt(); 456 record.mCurrentItemIndex = parcel.readInt(); 457 record.mItemCount = parcel.readInt(); 458 record.mFromIndex = parcel.readInt(); 459 record.mAddedCount = parcel.readInt(); 460 record.mRemovedCount = parcel.readInt(); 461 record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 462 record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 463 record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel); 464 record.mParcelableData = parcel.readParcelable(null); 465 parcel.readList(record.mText, null); 466 } 467 468 /** 469 * {@inheritDoc} 470 */ 471 public void writeToParcel(Parcel parcel, int flags) { 472 parcel.writeInt(mEventType); 473 TextUtils.writeToParcel(mPackageName, parcel, 0); 474 parcel.writeLong(mEventTime); 475 writeAccessibilityRecordToParcel(this, parcel, flags); 476 477 // Write the records. 478 final int recordCount = getRecordCount(); 479 parcel.writeInt(recordCount); 480 for (int i = 0; i < recordCount; i++) { 481 AccessibilityRecord record = mRecords.get(i); 482 writeAccessibilityRecordToParcel(record, parcel, flags); 483 } 484 } 485 486 /** 487 * Writes an {@link AccessibilityRecord} to a parcel. 488 * 489 * @param record The record to write. 490 * @param parcel The parcel to which to write. 491 */ 492 private void writeAccessibilityRecordToParcel(AccessibilityRecord record, Parcel parcel, 493 int flags) { 494 parcel.writeInt(record.mBooleanProperties); 495 parcel.writeInt(record.mCurrentItemIndex); 496 parcel.writeInt(record.mItemCount); 497 parcel.writeInt(record.mFromIndex); 498 parcel.writeInt(record.mAddedCount); 499 parcel.writeInt(record.mRemovedCount); 500 TextUtils.writeToParcel(record.mClassName, parcel, flags); 501 TextUtils.writeToParcel(record.mContentDescription, parcel, flags); 502 TextUtils.writeToParcel(record.mBeforeText, parcel, flags); 503 parcel.writeParcelable(record.mParcelableData, flags); 504 parcel.writeList(record.mText); 505 } 506 507 /** 508 * {@inheritDoc} 509 */ 510 public int describeContents() { 511 return 0; 512 } 513 514 @Override 515 public String toString() { 516 StringBuilder builder = new StringBuilder(); 517 builder.append("; EventType: " + mEventType); 518 builder.append("; EventTime: " + mEventTime); 519 builder.append("; PackageName: " + mPackageName); 520 builder.append(" \n{\n"); 521 builder.append(super.toString()); 522 builder.append("\n"); 523 for (int i = 0; i < mRecords.size(); i++) { 524 AccessibilityRecord record = mRecords.get(i); 525 builder.append(" Record "); 526 builder.append(i); 527 builder.append(":"); 528 builder.append(record.toString()); 529 builder.append("\n"); 530 } 531 builder.append("}\n"); 532 return builder.toString(); 533 } 534 535 /** 536 * @see Parcelable.Creator 537 */ 538 public static final Parcelable.Creator<AccessibilityEvent> CREATOR = 539 new Parcelable.Creator<AccessibilityEvent>() { 540 public AccessibilityEvent createFromParcel(Parcel parcel) { 541 AccessibilityEvent event = AccessibilityEvent.obtain(); 542 event.initFromParcel(parcel); 543 return event; 544 } 545 546 public AccessibilityEvent[] newArray(int size) { 547 return new AccessibilityEvent[size]; 548 } 549 }; 550} 551