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