AccessibilityEvent.java revision 38e8b4e5bc3c93affdffbc064fd9db5aeccc3e8e
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     * initialized with from the given <code>event</code>.
556     *
557     * @param event The other event.
558     * @return An instance.
559     */
560    public static AccessibilityEvent obtain(AccessibilityEvent event) {
561        AccessibilityEvent eventClone = AccessibilityEvent.obtain();
562        eventClone.init(event);
563
564        final int recordCount = event.mRecords.size();
565        for (int i = 0; i < recordCount; i++) {
566            AccessibilityRecord record = event.mRecords.get(i);
567            AccessibilityRecord recordClone = AccessibilityRecord.obtain(record);
568            eventClone.mRecords.add(recordClone);
569        }
570
571        return eventClone;
572    }
573
574    /**
575     * Returns a cached instance if such is available or a new one is
576     * instantiated.
577     *
578     * @return An instance.
579     */
580    public static AccessibilityEvent obtain() {
581        synchronized (sPoolLock) {
582            if (sPool != null) {
583                AccessibilityEvent event = sPool;
584                sPool = sPool.mNext;
585                sPoolSize--;
586                event.mNext = null;
587                event.mIsInPool = false;
588                return event;
589            }
590            return new AccessibilityEvent();
591        }
592    }
593
594    /**
595     * Recycles an instance back to be reused.
596     * <p>
597     *   <b>Note: You must not touch the object after calling this function.</b>
598     * </p>
599     *
600     * @throws IllegalStateException If the event is already recycled.
601     */
602    @Override
603    public void recycle() {
604        if (mIsInPool) {
605            throw new IllegalStateException("Event already recycled!");
606        }
607        clear();
608        synchronized (sPoolLock) {
609            if (sPoolSize <= MAX_POOL_SIZE) {
610                mNext = sPool;
611                sPool = this;
612                mIsInPool = true;
613                sPoolSize++;
614            }
615        }
616    }
617
618    /**
619     * Clears the state of this instance.
620     *
621     * @hide
622     */
623    @Override
624    protected void clear() {
625        super.clear();
626        mEventType = 0;
627        mPackageName = null;
628        mEventTime = 0;
629        while (!mRecords.isEmpty()) {
630            AccessibilityRecord record = mRecords.remove(0);
631            record.recycle();
632        }
633    }
634
635    /**
636     * Creates a new instance from a {@link Parcel}.
637     *
638     * @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
639     */
640    public void initFromParcel(Parcel parcel) {
641        if (parcel.readInt() == 1) {
642            mConnection = IAccessibilityServiceConnection.Stub.asInterface(
643                    parcel.readStrongBinder());
644        }
645        setSealed(parcel.readInt() == 1);
646        mEventType = parcel.readInt();
647        mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
648        mEventTime = parcel.readLong();
649        readAccessibilityRecordFromParcel(this, parcel);
650
651        // Read the records.
652        final int recordCount = parcel.readInt();
653        for (int i = 0; i < recordCount; i++) {
654            AccessibilityRecord record = AccessibilityRecord.obtain();
655            // Do this to write the connection only once.
656            record.setConnection(mConnection);
657            readAccessibilityRecordFromParcel(record, parcel);
658            mRecords.add(record);
659        }
660    }
661
662    /**
663     * Reads an {@link AccessibilityRecord} from a parcel.
664     *
665     * @param record The record to initialize.
666     * @param parcel The parcel to read from.
667     */
668    private void readAccessibilityRecordFromParcel(AccessibilityRecord record,
669            Parcel parcel) {
670        record.mBooleanProperties = parcel.readInt();
671        record.mCurrentItemIndex = parcel.readInt();
672        record.mItemCount = parcel.readInt();
673        record.mFromIndex = parcel.readInt();
674        record.mToIndex = parcel.readInt();
675        record.mScrollX = parcel.readInt();
676        record.mScrollY =  parcel.readInt();
677        record.mAddedCount = parcel.readInt();
678        record.mRemovedCount = parcel.readInt();
679        record.mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
680        record.mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
681        record.mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
682        record.mParcelableData = parcel.readParcelable(null);
683        parcel.readList(record.mText, null);
684        record.mSourceWindowId = parcel.readInt();
685        record.mSourceViewId = parcel.readInt();
686        record.mSealed = (parcel.readInt() == 1);
687    }
688
689    /**
690     * {@inheritDoc}
691     */
692    public void writeToParcel(Parcel parcel, int flags) {
693        if (mConnection == null) {
694            parcel.writeInt(0);
695        } else {
696            parcel.writeInt(1);
697            parcel.writeStrongBinder(mConnection.asBinder());
698        }
699        parcel.writeInt(isSealed() ? 1 : 0);
700        parcel.writeInt(mEventType);
701        TextUtils.writeToParcel(mPackageName, parcel, 0);
702        parcel.writeLong(mEventTime);
703        writeAccessibilityRecordToParcel(this, parcel, flags);
704
705        // Write the records.
706        final int recordCount = getRecordCount();
707        parcel.writeInt(recordCount);
708        for (int i = 0; i < recordCount; i++) {
709            AccessibilityRecord record = mRecords.get(i);
710            writeAccessibilityRecordToParcel(record, parcel, flags);
711        }
712    }
713
714    /**
715     * Writes an {@link AccessibilityRecord} to a parcel.
716     *
717     * @param record The record to write.
718     * @param parcel The parcel to which to write.
719     */
720    private void writeAccessibilityRecordToParcel(AccessibilityRecord record, Parcel parcel,
721            int flags) {
722        parcel.writeInt(record.mBooleanProperties);
723        parcel.writeInt(record.mCurrentItemIndex);
724        parcel.writeInt(record.mItemCount);
725        parcel.writeInt(record.mFromIndex);
726        parcel.writeInt(record.mToIndex);
727        parcel.writeInt(record.mScrollX);
728        parcel.writeInt(record.mScrollY);
729        parcel.writeInt(record.mAddedCount);
730        parcel.writeInt(record.mRemovedCount);
731        TextUtils.writeToParcel(record.mClassName, parcel, flags);
732        TextUtils.writeToParcel(record.mContentDescription, parcel, flags);
733        TextUtils.writeToParcel(record.mBeforeText, parcel, flags);
734        parcel.writeParcelable(record.mParcelableData, flags);
735        parcel.writeList(record.mText);
736        parcel.writeInt(record.mSourceWindowId);
737        parcel.writeInt(record.mSourceViewId);
738        parcel.writeInt(record.mSealed ? 1 : 0);
739    }
740
741    /**
742     * {@inheritDoc}
743     */
744    public int describeContents() {
745        return 0;
746    }
747
748    @Override
749    public String toString() {
750        StringBuilder builder = new StringBuilder();
751        builder.append("EventType: ").append(eventTypeToString(mEventType));
752        builder.append("; EventTime: ").append(mEventTime);
753        builder.append("; PackageName: ").append(mPackageName);
754        builder.append(super.toString());
755        if (DEBUG) {
756            builder.append("\n");
757            builder.append("; sourceWindowId: ").append(mSourceWindowId);
758            builder.append("; sourceViewId: ").append(mSourceViewId);
759            for (int i = 0; i < mRecords.size(); i++) {
760                AccessibilityRecord record = mRecords.get(i);
761                builder.append("  Record ");
762                builder.append(i);
763                builder.append(":");
764                builder.append(" [ ClassName: " + record.mClassName);
765                builder.append("; Text: " + record.mText);
766                builder.append("; ContentDescription: " + record.mContentDescription);
767                builder.append("; ItemCount: " + record.mItemCount);
768                builder.append("; CurrentItemIndex: " + record.mCurrentItemIndex);
769                builder.append("; IsEnabled: " + record.isEnabled());
770                builder.append("; IsPassword: " + record.isPassword());
771                builder.append("; IsChecked: " + record.isChecked());
772                builder.append("; IsFullScreen: " + record.isFullScreen());
773                builder.append("; Scrollable: " + record.isScrollable());
774                builder.append("; BeforeText: " + record.mBeforeText);
775                builder.append("; FromIndex: " + record.mFromIndex);
776                builder.append("; ToIndex: " + record.mToIndex);
777                builder.append("; ScrollX: " + record.mScrollX);
778                builder.append("; ScrollY: " + record.mScrollY);
779                builder.append("; AddedCount: " + record.mAddedCount);
780                builder.append("; RemovedCount: " + record.mRemovedCount);
781                builder.append("; ParcelableData: " + record.mParcelableData);
782                builder.append(" ]");
783                builder.append("\n");
784            }
785        } else {
786            builder.append("; recordCount: ").append(getAddedCount());
787        }
788        return builder.toString();
789    }
790
791    /**
792     * Returns the string representation of an event type. For example,
793     * {@link #TYPE_VIEW_CLICKED} is represented by the string TYPE_VIEW_CLICKED.
794     *
795     * @param eventType The event type
796     * @return The string representation.
797     */
798    public static String eventTypeToString(int eventType) {
799        switch (eventType) {
800            case TYPE_VIEW_CLICKED:
801                return "TYPE_VIEW_CLICKED";
802            case TYPE_VIEW_LONG_CLICKED:
803                return "TYPE_VIEW_LONG_CLICKED";
804            case TYPE_VIEW_SELECTED:
805                return "TYPE_VIEW_SELECTED";
806            case TYPE_VIEW_FOCUSED:
807                return "TYPE_VIEW_FOCUSED";
808            case TYPE_VIEW_TEXT_CHANGED:
809                return "TYPE_VIEW_TEXT_CHANGED";
810            case TYPE_WINDOW_STATE_CHANGED:
811                return "TYPE_WINDOW_STATE_CHANGED";
812            case TYPE_VIEW_HOVER_ENTER:
813                return "TYPE_VIEW_HOVER_ENTER";
814            case TYPE_VIEW_HOVER_EXIT:
815                return "TYPE_VIEW_HOVER_EXIT";
816            case TYPE_NOTIFICATION_STATE_CHANGED:
817                return "TYPE_NOTIFICATION_STATE_CHANGED";
818            case TYPE_TOUCH_EXPLORATION_GESTURE_START:
819                return "TYPE_TOUCH_EXPLORATION_GESTURE_START";
820            case TYPE_TOUCH_EXPLORATION_GESTURE_END:
821                return "TYPE_TOUCH_EXPLORATION_GESTURE_END";
822            case TYPE_WINDOW_CONTENT_CHANGED:
823                return "TYPE_WINDOW_CONTENT_CHANGED";
824            case TYPE_VIEW_TEXT_SELECTION_CHANGED:
825                return "TYPE_VIEW_TEXT_SELECTION_CHANGED";
826            case TYPE_VIEW_SCROLLED:
827                return "TYPE_VIEW_SCROLLED";
828            default:
829                return null;
830        }
831    }
832
833    /**
834     * @see Parcelable.Creator
835     */
836    public static final Parcelable.Creator<AccessibilityEvent> CREATOR =
837            new Parcelable.Creator<AccessibilityEvent>() {
838        public AccessibilityEvent createFromParcel(Parcel parcel) {
839            AccessibilityEvent event = AccessibilityEvent.obtain();
840            event.initFromParcel(parcel);
841            return event;
842        }
843
844        public AccessibilityEvent[] newArray(int size) {
845            return new AccessibilityEvent[size];
846        }
847    };
848}
849