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;
24import java.util.List;
25
26/**
27 * This class represents accessibility events that are sent by the system when
28 * something notable happens in the user interface. For example, when a
29 * {@link android.widget.Button} is clicked, a {@link android.view.View} is focused, etc.
30 * <p>
31 * This class represents various semantically different accessibility event
32 * types. Each event type has associated a set of related properties. In other
33 * words, each event type is characterized via a subset of the properties exposed
34 * by this class. For each event type there is a corresponding constant defined
35 * in this class. Since some event types are semantically close there are mask
36 * constants that group them together. Follows a specification of the event
37 * types and their associated properties:
38 * <p>
39 * <b>VIEW TYPES</b> <br>
40 * <p>
41 * <b>View clicked</b> - represents the event of clicking on a {@link android.view.View}
42 * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
43 * Type:{@link #TYPE_VIEW_CLICKED} <br>
44 * Properties:
45 * {@link #getClassName()},
46 * {@link #getPackageName()},
47 * {@link #getEventTime()},
48 * {@link #getText()},
49 * {@link #isChecked()},
50 * {@link #isEnabled()},
51 * {@link #isPassword()},
52 * {@link #getItemCount()},
53 * {@link #getCurrentItemIndex()}
54 * <p>
55 * <b>View long clicked</b> - represents the event of long clicking on a {@link android.view.View}
56 * like {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc. <br>
57 * Type:{@link #TYPE_VIEW_LONG_CLICKED} <br>
58 * Properties:
59 * {@link #getClassName()},
60 * {@link #getPackageName()},
61 * {@link #getEventTime()},
62 * {@link #getText()},
63 * {@link #isChecked()},
64 * {@link #isEnabled()},
65 * {@link #isPassword()},
66 * {@link #getItemCount()},
67 * {@link #getCurrentItemIndex()}
68 * <p>
69 * <b>View selected</b> - represents the event of selecting an item usually in
70 * the context of an {@link android.widget.AdapterView}. <br>
71 * Type: {@link #TYPE_VIEW_SELECTED} <br>
72 * Properties:
73 * {@link #getClassName()},
74 * {@link #getPackageName()},
75 * {@link #getEventTime()},
76 * {@link #getText()},
77 * {@link #isChecked()},
78 * {@link #isEnabled()},
79 * {@link #isPassword()},
80 * {@link #getItemCount()},
81 * {@link #getCurrentItemIndex()}
82 * <p>
83 * <b>View focused</b> - represents the event of focusing a
84 * {@link android.view.View}. <br>
85 * Type: {@link #TYPE_VIEW_FOCUSED} <br>
86 * Properties:
87 * {@link #getClassName()},
88 * {@link #getPackageName()},
89 * {@link #getEventTime()},
90 * {@link #getText()},
91 * {@link #isChecked()},
92 * {@link #isEnabled()},
93 * {@link #isPassword()},
94 * {@link #getItemCount()},
95 * {@link #getCurrentItemIndex()}
96 * <p>
97 * <b>View text changed</b> - represents the event of changing the text of an
98 * {@link android.widget.EditText}. <br>
99 * Type: {@link #TYPE_VIEW_TEXT_CHANGED} <br>
100 * Properties:
101 * {@link #getClassName()},
102 * {@link #getPackageName()},
103 * {@link #getEventTime()},
104 * {@link #getText()},
105 * {@link #isChecked()},
106 * {@link #isEnabled()},
107 * {@link #isPassword()},
108 * {@link #getItemCount()},
109 * {@link #getCurrentItemIndex()},
110 * {@link #getFromIndex()},
111 * {@link #getAddedCount()},
112 * {@link #getRemovedCount()},
113 * {@link #getBeforeText()}
114 * <p>
115 * <b>TRANSITION TYPES</b> <br>
116 * <p>
117 * <b>Window state changed</b> - represents the event of opening/closing a
118 * {@link android.widget.PopupWindow}, {@link android.view.Menu},
119 * {@link android.app.Dialog}, etc. <br>
120 * Type: {@link #TYPE_WINDOW_STATE_CHANGED} <br>
121 * Properties:
122 * {@link #getClassName()},
123 * {@link #getPackageName()},
124 * {@link #getEventTime()},
125 * {@link #getText()}
126 * <p>
127 * <b>NOTIFICATION TYPES</b> <br>
128 * <p>
129 * <b>Notification state changed</b> - represents the event showing/hiding
130 * {@link android.app.Notification}.
131 * Type: {@link #TYPE_NOTIFICATION_STATE_CHANGED} <br>
132 * Properties:
133 * {@link #getClassName()},
134 * {@link #getPackageName()},
135 * {@link #getEventTime()},
136 * {@link #getText()}
137 * {@link #getParcelableData()}
138 * <p>
139 * <b>Security note</b>
140 * <p>
141 * Since an event contains the text of its source privacy can be compromised by leaking of
142 * sensitive information such as passwords. To address this issue any event fired in response
143 * to manipulation of a PASSWORD field does NOT CONTAIN the text of the password.
144 *
145 * @see android.view.accessibility.AccessibilityManager
146 * @see android.accessibilityservice.AccessibilityService
147 */
148public final class AccessibilityEvent implements Parcelable {
149
150    /**
151     * Invalid selection/focus position.
152     *
153     * @see #getCurrentItemIndex()
154     */
155    public static final int INVALID_POSITION = -1;
156
157    /**
158     * Maximum length of the text fields.
159     *
160     * @see #getBeforeText()
161     * @see #getText()
162     */
163    public static final int MAX_TEXT_LENGTH = 500;
164
165    /**
166     * Represents the event of clicking on a {@link android.view.View} like
167     * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
168     */
169    public static final int TYPE_VIEW_CLICKED = 0x00000001;
170
171    /**
172     * Represents the event of long clicking on a {@link android.view.View} like
173     * {@link android.widget.Button}, {@link android.widget.CompoundButton}, etc.
174     */
175    public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;
176
177    /**
178     * Represents the event of selecting an item usually in the context of an
179     * {@link android.widget.AdapterView}.
180     */
181    public static final int TYPE_VIEW_SELECTED = 0x00000004;
182
183    /**
184     * Represents the event of focusing a {@link android.view.View}.
185     */
186    public static final int TYPE_VIEW_FOCUSED = 0x00000008;
187
188    /**
189     * Represents the event of changing the text of an {@link android.widget.EditText}.
190     */
191    public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010;
192
193    /**
194     * Represents the event of opening/closing a {@link android.widget.PopupWindow},
195     * {@link android.view.Menu}, {@link android.app.Dialog}, etc.
196     */
197    public static final int TYPE_WINDOW_STATE_CHANGED = 0x00000020;
198
199    /**
200     * Represents the event showing/hiding a {@link android.app.Notification}.
201     */
202    public static final int TYPE_NOTIFICATION_STATE_CHANGED = 0x00000040;
203
204    /**
205     * Mask for {@link AccessibilityEvent} all types.
206     *
207     * @see #TYPE_VIEW_CLICKED
208     * @see #TYPE_VIEW_LONG_CLICKED
209     * @see #TYPE_VIEW_SELECTED
210     * @see #TYPE_VIEW_FOCUSED
211     * @see #TYPE_VIEW_TEXT_CHANGED
212     * @see #TYPE_WINDOW_STATE_CHANGED
213     * @see #TYPE_NOTIFICATION_STATE_CHANGED
214     */
215    public static final int TYPES_ALL_MASK = 0xFFFFFFFF;
216
217    private static final int MAX_POOL_SIZE = 2;
218    private static final Object mPoolLock = new Object();
219    private static AccessibilityEvent sPool;
220    private static int sPoolSize;
221
222    private static final int CHECKED = 0x00000001;
223    private static final int ENABLED = 0x00000002;
224    private static final int PASSWORD = 0x00000004;
225    private static final int FULL_SCREEN = 0x00000080;
226
227    private AccessibilityEvent mNext;
228
229    private int mEventType;
230    private int mBooleanProperties;
231    private int mCurrentItemIndex;
232    private int mItemCount;
233    private int mFromIndex;
234    private int mAddedCount;
235    private int mRemovedCount;
236
237    private long mEventTime;
238
239    private CharSequence mClassName;
240    private CharSequence mPackageName;
241    private CharSequence mContentDescription;
242    private CharSequence mBeforeText;
243
244    private Parcelable mParcelableData;
245
246    private final List<CharSequence> mText = new ArrayList<CharSequence>();
247
248    private boolean mIsInPool;
249
250    /*
251     * Hide constructor from clients.
252     */
253    private AccessibilityEvent() {
254        mCurrentItemIndex = INVALID_POSITION;
255    }
256
257    /**
258     * Gets if the source is checked.
259     *
260     * @return True if the view is checked, false otherwise.
261     */
262    public boolean isChecked() {
263        return getBooleanProperty(CHECKED);
264    }
265
266    /**
267     * Sets if the source is checked.
268     *
269     * @param isChecked True if the view is checked, false otherwise.
270     */
271    public void setChecked(boolean isChecked) {
272        setBooleanProperty(CHECKED, isChecked);
273    }
274
275    /**
276     * Gets if the source is enabled.
277     *
278     * @return True if the view is enabled, false otherwise.
279     */
280    public boolean isEnabled() {
281        return getBooleanProperty(ENABLED);
282    }
283
284    /**
285     * Sets if the source is enabled.
286     *
287     * @param isEnabled True if the view is enabled, false otherwise.
288     */
289    public void setEnabled(boolean isEnabled) {
290        setBooleanProperty(ENABLED, isEnabled);
291    }
292
293    /**
294     * Gets if the source is a password field.
295     *
296     * @return True if the view is a password field, false otherwise.
297     */
298    public boolean isPassword() {
299        return getBooleanProperty(PASSWORD);
300    }
301
302    /**
303     * Sets if the source is a password field.
304     *
305     * @param isPassword True if the view is a password field, false otherwise.
306     */
307    public void setPassword(boolean isPassword) {
308        setBooleanProperty(PASSWORD, isPassword);
309    }
310
311    /**
312     * Sets if the source is taking the entire screen.
313     *
314     * @param isFullScreen True if the source is full screen, false otherwise.
315     */
316    public void setFullScreen(boolean isFullScreen) {
317        setBooleanProperty(FULL_SCREEN, isFullScreen);
318    }
319
320    /**
321     * Gets if the source is taking the entire screen.
322     *
323     * @return True if the source is full screen, false otherwise.
324     */
325    public boolean isFullScreen() {
326        return getBooleanProperty(FULL_SCREEN);
327    }
328
329    /**
330     * Gets the event type.
331     *
332     * @return The event type.
333     */
334    public int getEventType() {
335        return mEventType;
336    }
337
338    /**
339     * Sets the event type.
340     *
341     * @param eventType The event type.
342     */
343    public void setEventType(int eventType) {
344        mEventType = eventType;
345    }
346
347    /**
348     * Gets the number of items that can be visited.
349     *
350     * @return The number of items.
351     */
352    public int getItemCount() {
353        return mItemCount;
354    }
355
356    /**
357     * Sets the number of items that can be visited.
358     *
359     * @param itemCount The number of items.
360     */
361    public void setItemCount(int itemCount) {
362        mItemCount = itemCount;
363    }
364
365    /**
366     * Gets the index of the source in the list of items the can be visited.
367     *
368     * @return The current item index.
369     */
370    public int getCurrentItemIndex() {
371        return mCurrentItemIndex;
372    }
373
374    /**
375     * Sets the index of the source in the list of items that can be visited.
376     *
377     * @param currentItemIndex The current item index.
378     */
379    public void setCurrentItemIndex(int currentItemIndex) {
380        mCurrentItemIndex = currentItemIndex;
381    }
382
383    /**
384     * Gets the index of the first character of the changed sequence.
385     *
386     * @return The index of the first character.
387     */
388    public int getFromIndex() {
389        return mFromIndex;
390    }
391
392    /**
393     * Sets the index of the first character of the changed sequence.
394     *
395     * @param fromIndex The index of the first character.
396     */
397    public void setFromIndex(int fromIndex) {
398        mFromIndex = fromIndex;
399    }
400
401    /**
402     * Gets the number of added characters.
403     *
404     * @return The number of added characters.
405     */
406    public int getAddedCount() {
407        return mAddedCount;
408    }
409
410    /**
411     * Sets the number of added characters.
412     *
413     * @param addedCount The number of added characters.
414     */
415    public void setAddedCount(int addedCount) {
416        mAddedCount = addedCount;
417    }
418
419    /**
420     * Gets the number of removed characters.
421     *
422     * @return The number of removed characters.
423     */
424    public int getRemovedCount() {
425        return mRemovedCount;
426    }
427
428    /**
429     * Sets the number of removed characters.
430     *
431     * @param removedCount The number of removed characters.
432     */
433    public void setRemovedCount(int removedCount) {
434        mRemovedCount = removedCount;
435    }
436
437    /**
438     * Gets the time in which this event was sent.
439     *
440     * @return The event time.
441     */
442    public long getEventTime() {
443        return mEventTime;
444    }
445
446    /**
447     * Sets the time in which this event was sent.
448     *
449     * @param eventTime The event time.
450     */
451    public void setEventTime(long eventTime) {
452        mEventTime = eventTime;
453    }
454
455    /**
456     * Gets the class name of the source.
457     *
458     * @return The class name.
459     */
460    public CharSequence getClassName() {
461        return mClassName;
462    }
463
464    /**
465     * Sets the class name of the source.
466     *
467     * @param className The lass name.
468     */
469    public void setClassName(CharSequence className) {
470        mClassName = className;
471    }
472
473    /**
474     * Gets the package name of the source.
475     *
476     * @return The package name.
477     */
478    public CharSequence getPackageName() {
479        return mPackageName;
480    }
481
482    /**
483     * Sets the package name of the source.
484     *
485     * @param packageName The package name.
486     */
487    public void setPackageName(CharSequence packageName) {
488        mPackageName = packageName;
489    }
490
491    /**
492     * Gets the text of the event. The index in the list represents the priority
493     * of the text. Specifically, the lower the index the higher the priority.
494     *
495     * @return The text.
496     */
497    public List<CharSequence> getText() {
498        return mText;
499    }
500
501    /**
502     * Sets the text before a change.
503     *
504     * @return The text before the change.
505     */
506    public CharSequence getBeforeText() {
507        return mBeforeText;
508    }
509
510    /**
511     * Sets the text before a change.
512     *
513     * @param beforeText The text before the change.
514     */
515    public void setBeforeText(CharSequence beforeText) {
516        mBeforeText = beforeText;
517    }
518
519    /**
520     * Gets the description of the source.
521     *
522     * @return The description.
523     */
524    public CharSequence getContentDescription() {
525        return mContentDescription;
526    }
527
528    /**
529     * Sets the description of the source.
530     *
531     * @param contentDescription The description.
532     */
533    public void setContentDescription(CharSequence contentDescription) {
534        mContentDescription = contentDescription;
535    }
536
537    /**
538     * Gets the {@link Parcelable} data.
539     *
540     * @return The parcelable data.
541     */
542    public Parcelable getParcelableData() {
543        return mParcelableData;
544    }
545
546    /**
547     * Sets the {@link Parcelable} data of the event.
548     *
549     * @param parcelableData The parcelable data.
550     */
551    public void setParcelableData(Parcelable parcelableData) {
552        mParcelableData = parcelableData;
553    }
554
555    /**
556     * Returns a cached instance if such is available or a new one is
557     * instantiated with type property set.
558     *
559     * @param eventType The event type.
560     * @return An instance.
561     */
562    public static AccessibilityEvent obtain(int eventType) {
563        AccessibilityEvent event = AccessibilityEvent.obtain();
564        event.setEventType(eventType);
565        return event;
566    }
567
568    /**
569     * Returns a cached instance if such is available or a new one is
570     * instantiated.
571     *
572     * @return An instance.
573     */
574    public static AccessibilityEvent obtain() {
575        synchronized (mPoolLock) {
576            if (sPool != null) {
577                AccessibilityEvent event = sPool;
578                sPool = sPool.mNext;
579                sPoolSize--;
580                event.mNext = null;
581                event.mIsInPool = false;
582                return event;
583            }
584            return new AccessibilityEvent();
585        }
586    }
587
588    /**
589     * Return an instance back to be reused.
590     * <p>
591     * <b>Note: You must not touch the object after calling this function.</b>
592     */
593    public void recycle() {
594        if (mIsInPool) {
595            return;
596        }
597
598        clear();
599        synchronized (mPoolLock) {
600            if (sPoolSize <= MAX_POOL_SIZE) {
601                mNext = sPool;
602                sPool = this;
603                mIsInPool = true;
604                sPoolSize++;
605            }
606        }
607    }
608
609    /**
610     * Clears the state of this instance.
611     */
612    private void clear() {
613        mEventType = 0;
614        mBooleanProperties = 0;
615        mCurrentItemIndex = INVALID_POSITION;
616        mItemCount = 0;
617        mFromIndex = 0;
618        mAddedCount = 0;
619        mRemovedCount = 0;
620        mEventTime = 0;
621        mClassName = null;
622        mPackageName = null;
623        mContentDescription = null;
624        mBeforeText = null;
625        mParcelableData = null;
626        mText.clear();
627    }
628
629    /**
630     * Gets the value of a boolean property.
631     *
632     * @param property The property.
633     * @return The value.
634     */
635    private boolean getBooleanProperty(int property) {
636        return (mBooleanProperties & property) == property;
637    }
638
639    /**
640     * Sets a boolean property.
641     *
642     * @param property The property.
643     * @param value The value.
644     */
645    private void setBooleanProperty(int property, boolean value) {
646        if (value) {
647            mBooleanProperties |= property;
648        } else {
649            mBooleanProperties &= ~property;
650        }
651    }
652
653    /**
654     * Creates a new instance from a {@link Parcel}.
655     *
656     * @param parcel A parcel containing the state of a {@link AccessibilityEvent}.
657     */
658    public void initFromParcel(Parcel parcel) {
659        mEventType = parcel.readInt();
660        mBooleanProperties = parcel.readInt();
661        mCurrentItemIndex = parcel.readInt();
662        mItemCount = parcel.readInt();
663        mFromIndex = parcel.readInt();
664        mAddedCount = parcel.readInt();
665        mRemovedCount = parcel.readInt();
666        mEventTime = parcel.readLong();
667        mClassName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
668        mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
669        mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
670        mBeforeText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
671        mParcelableData = parcel.readParcelable(null);
672        parcel.readList(mText, null);
673    }
674
675    public void writeToParcel(Parcel parcel, int flags) {
676        parcel.writeInt(mEventType);
677        parcel.writeInt(mBooleanProperties);
678        parcel.writeInt(mCurrentItemIndex);
679        parcel.writeInt(mItemCount);
680        parcel.writeInt(mFromIndex);
681        parcel.writeInt(mAddedCount);
682        parcel.writeInt(mRemovedCount);
683        parcel.writeLong(mEventTime);
684        TextUtils.writeToParcel(mClassName, parcel, 0);
685        TextUtils.writeToParcel(mPackageName, parcel, 0);
686        TextUtils.writeToParcel(mContentDescription, parcel, 0);
687        TextUtils.writeToParcel(mBeforeText, parcel, 0);
688        parcel.writeParcelable(mParcelableData, flags);
689        parcel.writeList(mText);
690    }
691
692    public int describeContents() {
693        return 0;
694    }
695
696    @Override
697    public String toString() {
698        StringBuilder builder = new StringBuilder();
699        builder.append(super.toString());
700        builder.append("; EventType: " + mEventType);
701        builder.append("; EventTime: " + mEventTime);
702        builder.append("; ClassName: " + mClassName);
703        builder.append("; PackageName: " + mPackageName);
704        builder.append("; Text: " + mText);
705        builder.append("; ContentDescription: " + mContentDescription);
706        builder.append("; ItemCount: " + mItemCount);
707        builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
708        builder.append("; IsEnabled: " + isEnabled());
709        builder.append("; IsPassword: " + isPassword());
710        builder.append("; IsChecked: " + isChecked());
711        builder.append("; IsFullScreen: " + isFullScreen());
712        builder.append("; BeforeText: " + mBeforeText);
713        builder.append("; FromIndex: " + mFromIndex);
714        builder.append("; AddedCount: " + mAddedCount);
715        builder.append("; RemovedCount: " + mRemovedCount);
716        builder.append("; ParcelableData: " + mParcelableData);
717        return builder.toString();
718    }
719
720    /**
721     * @see Parcelable.Creator
722     */
723    public static final Parcelable.Creator<AccessibilityEvent> CREATOR =
724            new Parcelable.Creator<AccessibilityEvent>() {
725        public AccessibilityEvent createFromParcel(Parcel parcel) {
726            AccessibilityEvent event = AccessibilityEvent.obtain();
727            event.initFromParcel(parcel);
728            return event;
729        }
730
731        public AccessibilityEvent[] newArray(int size) {
732            return new AccessibilityEvent[size];
733        }
734    };
735}
736