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