AccessibilityEvent.java revision 35bfedeaba724aeadc6f6c890269cb6bf7ef42f5
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.accessibilityservice.IAccessibilityServiceConnection;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.text.TextUtils;
23
24import java.util.ArrayList;
25import java.util.List;
26
27/**
28 * <p>
29 * This class represents accessibility events that are sent by the system when
30 * something notable happens in the user interface. For example, when a
31 * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc.
32 * </p>
33 * <p>
34 * An accessibility event is fired by an individual view which populates the event with
35 * data for its state and requests from its parent to send the event to interested
36 * parties. The parent can optionally add an {@link AccessibilityRecord} for itself before
37 * dispatching a similar request to its parent. A parent can also choose not to respect the
38 * request for sending an event. The accessibility event is sent by the topmost view in the
39 * view tree. Therefore, an {@link android.accessibilityservice.AccessibilityService} can
40 * explore all records in an accessibility event to obtain more information about the
41 * context in which the event was fired.
42 * </p>
43 * <p>
44 * The main purpose of an accessibility event is to expose enough information for an
45 * {@link android.accessibilityservice.AccessibilityService} to provide meaningful feedback
46 * to the user. Sometimes however, an accessibility service may need more contextual
47 * information then the one in the event pay-load. In such cases the service can obtain
48 * the event source which is an {@link AccessibilityNodeInfo} (snapshot of a View state)
49 * which can be used for exploring the window content. Note that the privilege for accessing
50 * an event's source, thus the window content, has to be explicitly requested. For more
51 * details refer to {@link android.accessibilityservice.AccessibilityService}. If an
52 * accessibility service has not requested to retrieve the window content the event will
53 * not contain reference to its source. Also for events of type
54 * {@link #TYPE_NOTIFICATION_STATE_CHANGED} the source is never available.
55 * </p>
56 * <p>
57 * This class represents various semantically different accessibility event
58 * types. Each event type has an associated set of related properties. In other
59 * words, each event type is characterized via a subset of the properties exposed
60 * by this class. For each event type there is a corresponding constant defined
61 * in this class. Follows a specification of the event types and their associated properties:
62 * </p>
63 * <p>
64 * <b>VIEW TYPES</b></br>
65 * </p>
66 * <p>
67 * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View}
68 * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.</br>
69 * <em>Type:</em>{@link #TYPE_VIEW_CLICKED}</br>
70 * <em>Properties:</em></br>
71 * <ul>
72 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
73 *   <li>{@link #getClassName()} - The class name of the source.</li>
74 *   <li>{@link #getPackageName()} - The package name of the source.</li>
75 *   <li>{@link #getEventTime()}  - The event time.</li>
76 *   <li>{@link #getText()} - The text of the source.</li>
77 *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
78 *   <li>{@link #isPassword()} - Whether the source is password.</li>
79 *   <li>{@link #isChecked()} - Whether the source is checked.</li>
80 * </ul>
81 * </p>
82 * <p>
83 * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View}
84 * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc </br>
85 * <em>Type:</em>{@link #TYPE_VIEW_LONG_CLICKED}</br>
86 * <em>Properties:</em></br>
87 * <ul>
88 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
89 *   <li>{@link #getClassName()} - The class name of the source.</li>
90 *   <li>{@link #getPackageName()} - The package name of the source.</li>
91 *   <li>{@link #getEventTime()}  - The event time.</li>
92 *   <li>{@link #getText()} - The text of the source.</li>
93 *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
94 *   <li>{@link #isPassword()} - Whether the source is password.</li>
95 *   <li>{@link #isChecked()} - Whether the source is checked.</li>
96 * </ul>
97 * </p>
98 * <p>
99 * <b>View selected</b> - represents the event of selecting an item usually in
100 * the context of an {@link android.widget.AdapterView}.</br>
101 * <em>Type:</em> {@link #TYPE_VIEW_SELECTED}</br>
102 * <em>Properties:</em></br>
103 * <ul>
104 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
105 *   <li>{@link #getClassName()} - The class name of the source.</li>
106 *   <li>{@link #getPackageName()} - The package name of the source.</li>
107 *   <li>{@link #getEventTime()}  - The event time.</li>
108 *   <li>{@link #getText()} - The text of the source.</li>
109 *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
110 *   <li>{@link #isPassword()} - Whether the source is password.</li>
111 *   <li>{@link #isChecked()} - Whether the source is checked.</li>
112 *   <li>{@link #getItemCount()} - The number of selectable items of the source.</li>
113 *   <li>{@link #getCurrentItemIndex()} - The currently selected item index.</li>
114 * </ul>
115 * </p>
116 * <p>
117 * <b>View focused</b> - represents the event of focusing a
118 * {@link android.view.View}.</br>
119 * <em>Type:</em> {@link #TYPE_VIEW_FOCUSED}</br>
120 * <em>Properties:</em></br>
121 * <ul>
122 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
123 *   <li>{@link #getClassName()} - The class name of the source.</li>
124 *   <li>{@link #getPackageName()} - The package name of the source.</li>
125 *   <li>{@link #getEventTime()}  - The event time.</li>
126 *   <li>{@link #getText()} - The text of the source.</li>
127 *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
128 *   <li>{@link #isPassword()} - Whether the source is password.</li>
129 *   <li>{@link #isChecked()} - Whether the source is checked.</li>
130 *   <li>{@link #getItemCount()} - The number of focusable items on the screen.</li>
131 *   <li>{@link #getCurrentItemIndex()} - The currently focused item index.</li>
132 * </ul>
133 * </p>
134 * <p>
135 * <b>View text changed</b> - represents the event of changing the text of an
136 * {@link android.widget.EditText}.</br>
137 * <em>Type:</em> {@link #TYPE_VIEW_TEXT_CHANGED}</br>
138 * <em>Properties:</em></br>
139 * <ul>
140 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
141 *   <li>{@link #getClassName()} - The class name of the source.</li>
142 *   <li>{@link #getPackageName()} - The package name of the source.</li>
143 *   <li>{@link #getEventTime()}  - The event time.</li>
144 *   <li>{@link #getText()} - The text of the source.</li>
145 *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
146 *   <li>{@link #isPassword()} - Whether the source is password.</li>
147 *   <li>{@link #isChecked()} - Whether the source is checked.</li>
148 *   <li>{@link #getFromIndex()} - The text change start index.</li>
149 *   <li>{@link #getAddedCount()} - The number of added characters.</li>
150 *   <li>{@link #getRemovedCount()} - The number of removed characters.</li>
151 *   <li>{@link #getBeforeText()} - The text of the source before the change.</li>
152 * </ul>
153 * </p>
154 * <p>
155 * <b>View text selection changed</b> - represents the event of changing the text
156 * selection of an {@link android.widget.EditText}.</br>
157 * <em>Type:</em> {@link #TYPE_VIEW_TEXT_SELECTION_CHANGED} </br>
158 * <em>Properties:</em></br>
159 * <ul>
160 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
161 *   <li>{@link #getClassName()} - The class name of the source.</li>
162 *   <li>{@link #getPackageName()} - The package name of the source.</li>
163 *   <li>{@link #getEventTime()}  - The event time.</li>
164 *   <li>{@link #getText()} - The text of the source.</li>
165 *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
166 *   <li>{@link #isPassword()} - Whether the source is password.</li>
167 *   <li>{@link #getFromIndex()} - The selection start index.</li>
168 *   <li>{@link #getToIndex()} - The selection end index.</li>
169 *   <li>{@link #getItemCount()} - The length of the source text.</li>
170 * </ul>
171 * </p>
172 * <p>
173 * <b>View scrolled</b> - represents the event of scrolling a view. If
174 * the source is a descendant of {@link android.widget.AdapterView} the
175 * scroll is reported in terms of visible items - the first visible item,
176 * the last visible item, and the total items - because the the source
177 * is unaware if its pixel size since its adapter is responsible for
178 * creating views. In all other cases the scroll is reported as the current
179 * scroll on the X and Y axis respectively plus the height of the source in
180 * pixels.</br>
181 * <em>Type:</em> {@link #TYPE_VIEW_SCROLLED}</br>
182 * <em>Properties:</em></br>
183 * <ul>
184 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
185 *   <li>{@link #getClassName()} - The class name of the source.</li>
186 *   <li>{@link #getPackageName()} - The package name of the source.</li>
187 *   <li>{@link #getEventTime()}  - The event time.</li>
188 *   <li>{@link #getText()} - The text of the source.</li>
189 *   <li>{@link #isEnabled()} - Whether the source is enabled.</li>
190 *   <li>{@link #getScrollX()} - The horizontal offset of the source
191 *                                (without descendants of AdapterView)).</li>
192 *   <li>{@link #getScrollY()} - The vertical offset of the source
193 *                                (without descendants of AdapterView)).</li>
194 *   <li>{@link #getFromIndex()} - The index of the first visible item of the source
195 *                                 (for descendants of AdapterView).</li>
196 *   <li>{@link #getToIndex()} - The index of the last visible item of the source
197 *                               (for descendants of AdapterView).</li>
198 *   <li>{@link #getItemCount()} - The total items of the source (for descendants of AdapterView)
199 *                                 or the height of the source in pixels (all other cases).</li>
200 * </ul>
201 * </p>
202 * <p>
203 * <b>TRANSITION TYPES</b></br>
204 * </p>
205 * <b>Window state changed</b> - represents the event of opening a
206 * {@link android.widget.PopupWindow}, {@link android.view.Menu},
207 * {@link android.app.Dialog}, etc.</br>
208 * <em>Type:</em> {@link #TYPE_WINDOW_STATE_CHANGED}</br>
209 * <em>Properties:</em></br>
210 * <ul>
211 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
212 *   <li>{@link #getClassName()} - The class name of the source.</li>
213 *   <li>{@link #getPackageName()} - The package name of the source.</li>
214 *   <li>{@link #getEventTime()}  - The event time.</li>
215 *   <li>{@link #getText()} - The text of the source.</li>
216 * </ul>
217 * </p>
218 * <p>
219 * <b>Window content changed</b> - represents the event of change in the
220 * content of a window. This change can be adding/removing view, changing
221 * a view size, etc.</br>
222 * <p>
223 * <strong>Note:</strong> This event is fired only for the window source of the
224 * last accessibility event different from {@link #TYPE_NOTIFICATION_STATE_CHANGED})
225 * and its purpose is to notify clients that the content of the user interaction
226 * window has changed.
227 * </p>
228 * <em>Type:</em> {@link #TYPE_WINDOW_CONTENT_CHANGED}</br>
229 * <em>Properties:</em></br>
230 * <ul>
231 *   <li>{@link #getSource()} - The source info (for registered clients).</li>
232 *   <li>{@link #getClassName()} - The class name of the source.</li>
233 *   <li>{@link #getPackageName()} - The package name of the source.</li>
234 *   <li>{@link #getEventTime()}  - The event time.</li>
235 * </ul>
236 * <p>
237 * <b>NOTIFICATION TYPES</b></br>
238 * <p>
239 * <b>Notification state changed</b> - represents the event showing
240 * {@link android.app.Notification}.
241 * <em>Type:</em> {@link #TYPE_NOTIFICATION_STATE_CHANGED}</br>
242 * <em>Properties:</em></br>
243 * <ul>
244 *   <li>{@link #getClassName()} - The class name of the source.</li>
245 *   <li>{@link #getPackageName()} - The package name of the source.</li>
246 *   <li>{@link #getEventTime()}  - The event time.</li>
247 *   <li>{@link #getText()} - The text of the source.</li>
248 *   <li>{@link #getParcelableData()} - The posted {@link android.app.Notification}.</li>
249 * </ul>
250 * </p>
251 * <p>
252 * <b>Security note</b>
253 * <p>
254 * Since an event contains the text of its source privacy can be compromised by leaking
255 * sensitive information such as passwords. To address this issue any event fired in response
256 * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password.
257 *
258 * @see android.view.accessibility.AccessibilityManager
259 * @see android.accessibilityservice.AccessibilityService
260 * @see AccessibilityNodeInfo
261 */
262public final class AccessibilityEvent extends AccessibilityRecord implements Parcelable {
263    private static final boolean DEBUG = false;
264
265    /**
266     * Invalid selection/focus position.
267     *
268     * @see #getCurrentItemIndex()
269     */
270    public static final int INVALID_POSITION = -1;
271
272    /**
273     * Maximum length of the text fields.
274     *
275     * @see #getBeforeText()
276     * @see #getText()
277     * </br>
278     * Note: This constant is no longer needed since there
279     *       is no limit on the length of text that is contained
280     *       in an accessibility event anymore.
281     */
282    @Deprecated
283    public static final int MAX_TEXT_LENGTH = 500;
284
285    /**
286     * Represents the event of clicking on a {@link android.view.View} like
287     * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
288     */
289    public static final int TYPE_VIEW_CLICKED = 0x00000001;
290
291    /**
292     * Represents the event of long clicking on a {@link android.view.View} like
293     * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
294     */
295    public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;
296
297    /**
298     * Represents the event of selecting an item usually in the context of an
299     * {@link android.widget.AdapterView}.
300     */
301    public static final int TYPE_VIEW_SELECTED = 0x00000004;
302
303    /**
304     * Represents the event of focusing a {@link android.view.View}.
305     */
306    public static final int TYPE_VIEW_FOCUSED = 0x00000008;
307
308    /**
309     * Represents the event of changing the text of an {@link android.widget.EditText}.
310     */
311    public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
312
313    /**
314     * Represents the event of opening a {@link android.widget.PopupWindow},
315     * {@link android.view.Menu}, {@link android.app.Dialog}, etc.
316     */
317    public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
318
319    /**
320     * Represents the event showing a {@link android.app.Notification}.
321     */
322    public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
323
324    /**
325     * Represents the event of a hover enter over a {@link android.view.View}.
326     */
327    public static final int TYPE_VIEW_HOVER_ENTER = 0x00000080;
328
329    /**
330     * Represents the event of a hover exit over a {@link android.view.View}.
331     */
332    public static final int TYPE_VIEW_HOVER_EXIT = 0x00000100;
333
334    /**
335     * Represents the event of starting a touch exploration gesture.
336     */
337    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 0x00000200;
338
339    /**
340     * Represents the event of ending a touch exploration gesture.
341     */
342    public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 0x00000400;
343
344    /**
345     * Represents the event of changing the content of a window.
346     */
347    public static final int TYPE_WINDOW_CONTENT_CHANGED = 0x00000800;
348
349    /**
350     * Represents the event of scrolling a view.
351     */
352    public static final int TYPE_VIEW_SCROLLED = 0x00001000;
353
354    /**
355     * Represents the event of changing the selection in an {@link android.widget.EditText}.
356     */
357    public static final int TYPE_VIEW_TEXT_SELECTION_CHANGED = 0x00002000;
358
359    /**
360     * Mask for {@link AccessibilityEvent} all types.
361     *
362     * @see #TYPE_VIEW_CLICKED
363     * @see #TYPE_VIEW_LONG_CLICKED
364     * @see #TYPE_VIEW_SELECTED
365     * @see #TYPE_VIEW_FOCUSED
366     * @see #TYPE_VIEW_TEXT_CHANGED
367     * @see #TYPE_WINDOW_STATE_CHANGED
368     * @see #TYPE_NOTIFICATION_STATE_CHANGED
369     * @see #TYPE_VIEW_HOVER_ENTER
370     * @see #TYPE_VIEW_HOVER_EXIT
371     * @see #TYPE_TOUCH_EXPLORATION_GESTURE_START
372     * @see #TYPE_TOUCH_EXPLORATION_GESTURE_END
373     * @see #TYPE_WINDOW_CONTENT_CHANGED
374     * @see #TYPE_VIEW_SCROLLED
375     * @see #TYPE_VIEW_TEXT_SELECTION_CHANGED
376     */
377    public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
378
379    private static final int MAX_POOL_SIZE = 10;
380    private static final Object sPoolLock = new Object();
381    private static AccessibilityEvent sPool;
382    private static int sPoolSize;
383    private AccessibilityEvent mNext;
384    private boolean mIsInPool;
385
386    private int mEventType;
387    private CharSequence mPackageName;
388    private long mEventTime;
389
390    private final ArrayList<AccessibilityRecord> mRecords = new ArrayList<AccessibilityRecord>();
391
392    /*
393     * Hide constructor from clients.
394     */
395    private AccessibilityEvent() {
396    }
397
398    /**
399     * Initialize an event from another one.
400     *
401     * @param event The event to initialize from.
402     */
403    void init(AccessibilityEvent event) {
404        super.init(event);
405        mEventType = event.mEventType;
406        mEventTime = event.mEventTime;
407        mPackageName = event.mPackageName;
408    }
409
410    /**
411     * Sets the connection for interacting with the AccessibilityManagerService.
412     *
413     * @param connection The connection.
414     *
415     * @hide
416     */
417    @Override
418    public void setConnection(IAccessibilityServiceConnection connection) {
419        super.setConnection(connection);
420        List<AccessibilityRecord> records = mRecords;
421        final int recordCount = records.size();
422        for (int i = 0; i < recordCount; i++) {
423            AccessibilityRecord record = records.get(i);
424            record.setConnection(connection);
425        }
426    }
427
428    /**
429     * Sets if this instance is sealed.
430     *
431     * @param sealed Whether is sealed.
432     *
433     * @hide
434     */
435    @Override
436    public void setSealed(boolean sealed) {
437        super.setSealed(sealed);
438        List<AccessibilityRecord> records = mRecords;
439        final int recordCount = records.size();
440        for (int i = 0; i < recordCount; i++) {
441            AccessibilityRecord record = records.get(i);
442            record.setSealed(sealed);
443        }
444    }
445
446    /**
447     * Gets the number of records contained in the event.
448     *
449     * @return The number of records.
450     */
451    public int getRecordCount() {
452        return mRecords.size();
453    }
454
455    /**
456     * Appends an {@link AccessibilityRecord} to the end of event records.
457     *
458     * @param record The record to append.
459     *
460     * @throws IllegalStateException If called from an AccessibilityService.
461     */
462    public void appendRecord(AccessibilityRecord record) {
463        enforceNotSealed();
464        mRecords.add(record);
465    }
466
467    /**
468     * Gets the record at a given index.
469     *
470     * @param index The index.
471     * @return The record at the specified index.
472     */
473    public AccessibilityRecord getRecord(int index) {
474        return mRecords.get(index);
475    }
476
477    /**
478     * Gets the event type.
479     *
480     * @return The event type.
481     */
482    public int getEventType() {
483        return mEventType;
484    }
485
486    /**
487     * Sets the event type.
488     *
489     * @param eventType The event type.
490     *
491     * @throws IllegalStateException If called from an AccessibilityService.
492     */
493    public void setEventType(int eventType) {
494        enforceNotSealed();
495        mEventType = eventType;
496    }
497
498    /**
499     * Gets the time in which this event was sent.
500     *
501     * @return The event time.
502     */
503    public long getEventTime() {
504        return mEventTime;
505    }
506
507    /**
508     * Sets the time in which this event was sent.
509     *
510     * @param eventTime The event time.
511     *
512     * @throws IllegalStateException If called from an AccessibilityService.
513     */
514    public void setEventTime(long eventTime) {
515        enforceNotSealed();
516        mEventTime = eventTime;
517    }
518
519    /**
520     * Gets the package name of the source.
521     *
522     * @return The package name.
523     */
524    public CharSequence getPackageName() {
525        return mPackageName;
526    }
527
528    /**
529     * Sets the package name of the source.
530     *
531     * @param packageName The package name.
532     *
533     * @throws IllegalStateException If called from an AccessibilityService.
534     */
535    public void setPackageName(CharSequence packageName) {
536        enforceNotSealed();
537        mPackageName = packageName;
538    }
539
540    /**
541     * Returns a cached instance if such is available or a new one is
542     * instantiated with its type property set.
543     *
544     * @param eventType The event type.
545     * @return An instance.
546     */
547    public static AccessibilityEvent obtain(int eventType) {
548        AccessibilityEvent event = AccessibilityEvent.obtain();
549        event.setEventType(eventType);
550        return event;
551    }
552
553    /**
554     * Returns a cached instance if such is available or a new one is
555     * created. The returned instance is initialized from the given
556     * <code>event</code>.
557     *
558     * @param event The other event.
559     * @return An instance.
560     */
561    public static AccessibilityEvent obtain(AccessibilityEvent event) {
562        AccessibilityEvent eventClone = AccessibilityEvent.obtain();
563        eventClone.init(event);
564
565        final int recordCount = event.mRecords.size();
566        for (int i = 0; i < recordCount; i++) {
567            AccessibilityRecord record = event.mRecords.get(i);
568            AccessibilityRecord recordClone = AccessibilityRecord.obtain(record);
569            eventClone.mRecords.add(recordClone);
570        }
571
572        return eventClone;
573    }
574
575    /**
576     * Returns a cached instance if such is available or a new one is
577     * instantiated.
578     *
579     * @return An instance.
580     */
581    public static AccessibilityEvent obtain() {
582        synchronized (sPoolLock) {
583            if (sPool != null) {
584                AccessibilityEvent event = sPool;
585                sPool = sPool.mNext;
586                sPoolSize--;
587                event.mNext = null;
588                event.mIsInPool = false;
589                return event;
590            }
591            return new AccessibilityEvent();
592        }
593    }
594
595    /**
596     * Recycles an instance back to be reused.
597     * <p>
598     *   <b>Note: You must not touch the object after calling this function.</b>
599     * </p>
600     *
601     * @throws IllegalStateException If the event is already recycled.
602     */
603    @Override
604    public void recycle() {
605        if (mIsInPool) {
606            throw new IllegalStateException("Event already recycled!");
607        }
608        clear();
609        synchronized (sPoolLock) {
610            if (sPoolSize <= MAX_POOL_SIZE) {
611                mNext = sPool;
612                sPool = this;
613                mIsInPool = true;
614                sPoolSize++;
615            }
616        }
617    }
618
619    /**
620     * Clears the state of this instance.
621     *
622     * @hide
623     */
624    @Override
625    protected void clear() {
626        super.clear();
627        mEventType = 0;
628        mPackageName = null;
629        mEventTime = 0;
630        while (!mRecords.isEmpty()) {
631            AccessibilityRecord record = mRecords.remove(0);
632            record.recycle();
633        }
634    }
635
636    /**
637     * Creates a new instance from a {@link Parcel}.
638     *
639     * @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
640     */
641    public void initFromParcel(Parcel parcel) {
642        if (parcel.readInt() == 1) {
643            mConnection = IAccessibilityServiceConnection.Stub.asInterface(
644                    parcel.readStrongBinder());
645        }
646        setSealed(parcel.readInt() == 1);
647        mEventType = parcel.readInt();
648        mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
649        mEventTime = parcel.readLong();
650        readAccessibilityRecordFromParcel(this, parcel);
651
652        // Read the records.
653        final int recordCount = parcel.readInt();
654        for (int i = 0; i < recordCount; i++) {
655            AccessibilityRecord record = AccessibilityRecord.obtain();
656            // Do this to write the connection only once.
657            record.setConnection(mConnection);
658            readAccessibilityRecordFromParcel(record, parcel);
659            mRecords.add(record);
660        }
661    }
662
663    /**
664     * Reads an {@link AccessibilityRecord} from a parcel.
665     *
666     * @param record The record to initialize.
667     * @param parcel The parcel to read from.
668     */
669    private void readAccessibilityRecordFromParcel(AccessibilityRecord record,
670            Parcel parcel) {
671        record.mBooleanProperties = parcel.readInt();
672        record.mCurrentItemIndex = parcel.readInt();
673        record.mItemCount = parcel.readInt();
674        record.mFromIndex = parcel.readInt();
675        record.mToIndex = parcel.readInt();
676        record.mScrollX = parcel.readInt();
677        record.mScrollY =  parcel.readInt();
678        record.mAddedCount = parcel.readInt();
679        record.mRemovedCount = parcel.readInt();
680        record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
681        record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
682        record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
683        record.mParcelableData = parcel.readParcelable(null);
684        parcel.readList(record.mText, null);
685        record.mSourceWindowId = parcel.readInt();
686        record.mSourceViewId = parcel.readInt();
687        record.mSealed = (parcel.readInt() == 1);
688    }
689
690    /**
691     * {@inheritDoc}
692     */
693    public void writeToParcel(Parcel parcel, int flags) {
694        if (mConnection == null) {
695            parcel.writeInt(0);
696        } else {
697            parcel.writeInt(1);
698            parcel.writeStrongBinder(mConnection.asBinder());
699        }
700        parcel.writeInt(isSealed() ? 1 : 0);
701        parcel.writeInt(mEventType);
702        TextUtils.writeToParcel(mPackageName, parcel, 0);
703        parcel.writeLong(mEventTime);
704        writeAccessibilityRecordToParcel(this, parcel, flags);
705
706        // Write the records.
707        final int recordCount = getRecordCount();
708        parcel.writeInt(recordCount);
709        for (int i = 0; i < recordCount; i++) {
710            AccessibilityRecord record = mRecords.get(i);
711            writeAccessibilityRecordToParcel(record, parcel, flags);
712        }
713    }
714
715    /**
716     * Writes an {@link AccessibilityRecord} to a parcel.
717     *
718     * @param record The record to write.
719     * @param parcel The parcel to which to write.
720     */
721    private void writeAccessibilityRecordToParcel(AccessibilityRecord record, Parcel parcel,
722            int flags) {
723        parcel.writeInt(record.mBooleanProperties);
724        parcel.writeInt(record.mCurrentItemIndex);
725        parcel.writeInt(record.mItemCount);
726        parcel.writeInt(record.mFromIndex);
727        parcel.writeInt(record.mToIndex);
728        parcel.writeInt(record.mScrollX);
729        parcel.writeInt(record.mScrollY);
730        parcel.writeInt(record.mAddedCount);
731        parcel.writeInt(record.mRemovedCount);
732        TextUtils.writeToParcel(record.mClassName, parcel, flags);
733        TextUtils.writeToParcel(record.mContentDescription, parcel, flags);
734        TextUtils.writeToParcel(record.mBeforeText, parcel, flags);
735        parcel.writeParcelable(record.mParcelableData, flags);
736        parcel.writeList(record.mText);
737        parcel.writeInt(record.mSourceWindowId);
738        parcel.writeInt(record.mSourceViewId);
739        parcel.writeInt(record.mSealed ? 1 : 0);
740    }
741
742    /**
743     * {@inheritDoc}
744     */
745    public int describeContents() {
746        return 0;
747    }
748
749    @Override
750    public String toString() {
751        StringBuilder builder = new StringBuilder();
752        builder.append("EventType: ").append(eventTypeToString(mEventType));
753        builder.append("; EventTime: ").append(mEventTime);
754        builder.append("; PackageName: ").append(mPackageName);
755        builder.append(super.toString());
756        if (DEBUG) {
757            builder.append("\n");
758            builder.append("; sourceWindowId: ").append(mSourceWindowId);
759            builder.append("; sourceViewId: ").append(mSourceViewId);
760            for (int i = 0; i < mRecords.size(); i++) {
761                AccessibilityRecord record = mRecords.get(i);
762                builder.append("  Record ");
763                builder.append(i);
764                builder.append(":");
765                builder.append(" [ ClassName: " + record.mClassName);
766                builder.append("; Text: " + record.mText);
767                builder.append("; ContentDescription: " + record.mContentDescription);
768                builder.append("; ItemCount: " + record.mItemCount);
769                builder.append("; CurrentItemIndex: " + record.mCurrentItemIndex);
770                builder.append("; IsEnabled: " + record.isEnabled());
771                builder.append("; IsPassword: " + record.isPassword());
772                builder.append("; IsChecked: " + record.isChecked());
773                builder.append("; IsFullScreen: " + record.isFullScreen());
774                builder.append("; Scrollable: " + record.isScrollable());
775                builder.append("; BeforeText: " + record.mBeforeText);
776                builder.append("; FromIndex: " + record.mFromIndex);
777                builder.append("; ToIndex: " + record.mToIndex);
778                builder.append("; ScrollX: " + record.mScrollX);
779                builder.append("; ScrollY: " + record.mScrollY);
780                builder.append("; AddedCount: " + record.mAddedCount);
781                builder.append("; RemovedCount: " + record.mRemovedCount);
782                builder.append("; ParcelableData: " + record.mParcelableData);
783                builder.append(" ]");
784                builder.append("\n");
785            }
786        } else {
787            builder.append("; recordCount: ").append(getAddedCount());
788        }
789        return builder.toString();
790    }
791
792    /**
793     * Returns the string representation of an event type. For example,
794     * {@link #TYPE_VIEW_CLICKED} is represented by the string TYPE_VIEW_CLICKED.
795     *
796     * @param eventType The event type
797     * @return The string representation.
798     */
799    public static String eventTypeToString(int eventType) {
800        switch (eventType) {
801            case TYPE_VIEW_CLICKED:
802                return "TYPE_VIEW_CLICKED";
803            case TYPE_VIEW_LONG_CLICKED:
804                return "TYPE_VIEW_LONG_CLICKED";
805            case TYPE_VIEW_SELECTED:
806                return "TYPE_VIEW_SELECTED";
807            case TYPE_VIEW_FOCUSED:
808                return "TYPE_VIEW_FOCUSED";
809            case TYPE_VIEW_TEXT_CHANGED:
810                return "TYPE_VIEW_TEXT_CHANGED";
811            case TYPE_WINDOW_STATE_CHANGED:
812                return "TYPE_WINDOW_STATE_CHANGED";
813            case TYPE_VIEW_HOVER_ENTER:
814                return "TYPE_VIEW_HOVER_ENTER";
815            case TYPE_VIEW_HOVER_EXIT:
816                return "TYPE_VIEW_HOVER_EXIT";
817            case TYPE_NOTIFICATION_STATE_CHANGED:
818                return "TYPE_NOTIFICATION_STATE_CHANGED";
819            case TYPE_TOUCH_EXPLORATION_GESTURE_START:
820                return "TYPE_TOUCH_EXPLORATION_GESTURE_START";
821            case TYPE_TOUCH_EXPLORATION_GESTURE_END:
822                return "TYPE_TOUCH_EXPLORATION_GESTURE_END";
823            case TYPE_WINDOW_CONTENT_CHANGED:
824                return "TYPE_WINDOW_CONTENT_CHANGED";
825            case TYPE_VIEW_TEXT_SELECTION_CHANGED:
826                return "TYPE_VIEW_TEXT_SELECTION_CHANGED";
827            case TYPE_VIEW_SCROLLED:
828                return "TYPE_VIEW_SCROLLED";
829            default:
830                return null;
831        }
832    }
833
834    /**
835     * @see Parcelable.Creator
836     */
837    public static final Parcelable.Creator<AccessibilityEvent> CREATOR =
838            new Parcelable.Creator<AccessibilityEvent>() {
839        public AccessibilityEvent createFromParcel(Parcel parcel) {
840            AccessibilityEvent event = AccessibilityEvent.obtain();
841            event.initFromParcel(parcel);
842            return event;
843        }
844
845        public AccessibilityEvent[] newArray(int size) {
846            return new AccessibilityEvent[size];
847        }
848    };
849}
850