AccessibilityRecord.java revision 8643aa0179e598e78d938c59035389054535a229
1/*
2 * Copyright (C) 2011 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.Parcelable;
20
21import java.util.ArrayList;
22import java.util.List;
23
24/**
25 * Represents a record in an accessibility event. This class encapsulates
26 * the information for a {@link android.view.View}. Note that not all properties
27 * are applicable to all view types. For detailed information please refer to
28 * {@link AccessibilityEvent}.
29 *
30 * @see AccessibilityEvent
31 */
32public class AccessibilityRecord {
33
34    private static final int INVALID_POSITION = -1;
35
36    private static final int PROPERTY_CHECKED = 0x00000001;
37    private static final int PROPERTY_ENABLED = 0x00000002;
38    private static final int PROPERTY_PASSWORD = 0x00000004;
39    private static final int PROPERTY_FULL_SCREEN = 0x00000080;
40
41    // Housekeeping
42    private static final int MAX_POOL_SIZE = 10;
43    private static final Object sPoolLock = new Object();
44    private static AccessibilityRecord sPool;
45    private static int sPoolSize;
46    private AccessibilityRecord mNext;
47    private boolean mIsInPool;
48    private boolean mSealed;
49
50    protected int mBooleanProperties;
51    protected int mCurrentItemIndex;
52    protected int mItemCount;
53    protected int mFromIndex;
54    protected int mAddedCount;
55    protected int mRemovedCount;
56
57    protected CharSequence mClassName;
58    protected CharSequence mContentDescription;
59    protected CharSequence mBeforeText;
60    protected Parcelable mParcelableData;
61
62    protected final List<CharSequence> mText = new ArrayList<CharSequence>();
63
64    /*
65     * Hide constructor.
66     */
67    protected AccessibilityRecord() {
68
69    }
70
71    /**
72     * Initialize this record from another one.
73     *
74     * @param record The to initialize from.
75     */
76    void init(AccessibilityRecord record) {
77        mSealed = record.isSealed();
78        mBooleanProperties = record.mBooleanProperties;
79        mCurrentItemIndex = record.mCurrentItemIndex;
80        mItemCount = record.mItemCount;
81        mFromIndex = record.mFromIndex;
82        mAddedCount = record.mAddedCount;
83        mRemovedCount = record.mRemovedCount;
84        mClassName = record.mClassName;
85        mContentDescription = record.mContentDescription;
86        mBeforeText = record.mBeforeText;
87        mParcelableData = record.mParcelableData;
88        mText.addAll(record.mText);
89    }
90
91    /**
92     * Gets if the source is checked.
93     *
94     * @return True if the view is checked, false otherwise.
95     */
96    public boolean isChecked() {
97        return getBooleanProperty(PROPERTY_CHECKED);
98    }
99
100    /**
101     * Sets if the source is checked.
102     *
103     * @param isChecked True if the view is checked, false otherwise.
104     *
105     * @throws IllegalStateException If called from an AccessibilityService.
106     */
107    public void setChecked(boolean isChecked) {
108        enforceNotSealed();
109        setBooleanProperty(PROPERTY_CHECKED, isChecked);
110    }
111
112    /**
113     * Gets if the source is enabled.
114     *
115     * @return True if the view is enabled, false otherwise.
116     */
117    public boolean isEnabled() {
118        return getBooleanProperty(PROPERTY_ENABLED);
119    }
120
121    /**
122     * Sets if the source is enabled.
123     *
124     * @param isEnabled True if the view is enabled, false otherwise.
125     *
126     * @throws IllegalStateException If called from an AccessibilityService.
127     */
128    public void setEnabled(boolean isEnabled) {
129        enforceNotSealed();
130        setBooleanProperty(PROPERTY_ENABLED, isEnabled);
131    }
132
133    /**
134     * Gets if the source is a password field.
135     *
136     * @return True if the view is a password field, false otherwise.
137     */
138    public boolean isPassword() {
139        return getBooleanProperty(PROPERTY_PASSWORD);
140    }
141
142    /**
143     * Sets if the source is a password field.
144     *
145     * @param isPassword True if the view is a password field, false otherwise.
146     *
147     * @throws IllegalStateException If called from an AccessibilityService.
148     */
149    public void setPassword(boolean isPassword) {
150        enforceNotSealed();
151        setBooleanProperty(PROPERTY_PASSWORD, isPassword);
152    }
153
154    /**
155     * Gets if the source is taking the entire screen.
156     *
157     * @return True if the source is full screen, false otherwise.
158     */
159    public boolean isFullScreen() {
160        return getBooleanProperty(PROPERTY_FULL_SCREEN);
161    }
162
163    /**
164     * Sets if the source is taking the entire screen.
165     *
166     * @param isFullScreen True if the source is full screen, false otherwise.
167     *
168     * @throws IllegalStateException If called from an AccessibilityService.
169     */
170    public void setFullScreen(boolean isFullScreen) {
171        enforceNotSealed();
172        setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen);
173    }
174
175    /**
176     * Gets the number of items that can be visited.
177     *
178     * @return The number of items.
179     */
180    public int getItemCount() {
181        return mItemCount;
182    }
183
184    /**
185     * Sets the number of items that can be visited.
186     *
187     * @param itemCount The number of items.
188     *
189     * @throws IllegalStateException If called from an AccessibilityService.
190     */
191    public void setItemCount(int itemCount) {
192        enforceNotSealed();
193        mItemCount = itemCount;
194    }
195
196    /**
197     * Gets the index of the source in the list of items the can be visited.
198     *
199     * @return The current item index.
200     */
201    public int getCurrentItemIndex() {
202        return mCurrentItemIndex;
203    }
204
205    /**
206     * Sets the index of the source in the list of items that can be visited.
207     *
208     * @param currentItemIndex The current item index.
209     *
210     * @throws IllegalStateException If called from an AccessibilityService.
211     */
212    public void setCurrentItemIndex(int currentItemIndex) {
213        enforceNotSealed();
214        mCurrentItemIndex = currentItemIndex;
215    }
216
217    /**
218     * Gets the index of the first character of the changed sequence.
219     *
220     * @return The index of the first character.
221     */
222    public int getFromIndex() {
223        return mFromIndex;
224    }
225
226    /**
227     * Sets the index of the first character of the changed sequence.
228     *
229     * @param fromIndex The index of the first character.
230     *
231     * @throws IllegalStateException If called from an AccessibilityService.
232     */
233    public void setFromIndex(int fromIndex) {
234        enforceNotSealed();
235        mFromIndex = fromIndex;
236    }
237
238    /**
239     * Gets the number of added characters.
240     *
241     * @return The number of added characters.
242     */
243    public int getAddedCount() {
244        return mAddedCount;
245    }
246
247    /**
248     * Sets the number of added characters.
249     *
250     * @param addedCount The number of added characters.
251     *
252     * @throws IllegalStateException If called from an AccessibilityService.
253     */
254    public void setAddedCount(int addedCount) {
255        enforceNotSealed();
256        mAddedCount = addedCount;
257    }
258
259    /**
260     * Gets the number of removed characters.
261     *
262     * @return The number of removed characters.
263     */
264    public int getRemovedCount() {
265        return mRemovedCount;
266    }
267
268    /**
269     * Sets the number of removed characters.
270     *
271     * @param removedCount The number of removed characters.
272     *
273     * @throws IllegalStateException If called from an AccessibilityService.
274     */
275    public void setRemovedCount(int removedCount) {
276        enforceNotSealed();
277        mRemovedCount = removedCount;
278    }
279
280    /**
281     * Gets the class name of the source.
282     *
283     * @return The class name.
284     */
285    public CharSequence getClassName() {
286        return mClassName;
287    }
288
289    /**
290     * Sets the class name of the source.
291     *
292     * @param className The lass name.
293     *
294     * @throws IllegalStateException If called from an AccessibilityService.
295     */
296    public void setClassName(CharSequence className) {
297        enforceNotSealed();
298        mClassName = className;
299    }
300
301    /**
302     * Gets the text of the event. The index in the list represents the priority
303     * of the text. Specifically, the lower the index the higher the priority.
304     *
305     * @return The text.
306     */
307    public List<CharSequence> getText() {
308        return mText;
309    }
310
311    /**
312     * Sets the text before a change.
313     *
314     * @return The text before the change.
315     */
316    public CharSequence getBeforeText() {
317        return mBeforeText;
318    }
319
320    /**
321     * Sets the text before a change.
322     *
323     * @param beforeText The text before the change.
324     *
325     * @throws IllegalStateException If called from an AccessibilityService.
326     */
327    public void setBeforeText(CharSequence beforeText) {
328        enforceNotSealed();
329        mBeforeText = beforeText;
330    }
331
332    /**
333     * Gets the description of the source.
334     *
335     * @return The description.
336     */
337    public CharSequence getContentDescription() {
338        return mContentDescription;
339    }
340
341    /**
342     * Sets the description of the source.
343     *
344     * @param contentDescription The description.
345     *
346     * @throws IllegalStateException If called from an AccessibilityService.
347     */
348    public void setContentDescription(CharSequence contentDescription) {
349        enforceNotSealed();
350        mContentDescription = contentDescription;
351    }
352
353    /**
354     * Gets the {@link Parcelable} data.
355     *
356     * @return The parcelable data.
357     */
358    public Parcelable getParcelableData() {
359        return mParcelableData;
360    }
361
362    /**
363     * Sets the {@link Parcelable} data of the event.
364     *
365     * @param parcelableData The parcelable data.
366     *
367     * @throws IllegalStateException If called from an AccessibilityService.
368     */
369    public void setParcelableData(Parcelable parcelableData) {
370        enforceNotSealed();
371        mParcelableData = parcelableData;
372    }
373
374    /**
375     * Sets if this instance is sealed.
376     *
377     * @param sealed Whether is sealed.
378     *
379     * @hide
380     */
381    public void setSealed(boolean sealed) {
382        mSealed = sealed;
383    }
384
385    /**
386     * Gets if this instance is sealed.
387     *
388     * @return Whether is sealed.
389     *
390     * @hide
391     */
392    public boolean isSealed() {
393        return mSealed;
394    }
395
396    /**
397     * Enforces that this instance is sealed.
398     *
399     * @throws IllegalStateException If this instance is not sealed.
400     *
401     * @hide
402     */
403    protected void enforceSealed() {
404        if (!isSealed()) {
405            throw new IllegalStateException("Cannot perform this "
406                    + "action on a not sealed instance.");
407        }
408    }
409
410    /**
411     * Enforces that this instance is not sealed.
412     *
413     * @throws IllegalStateException If this instance is sealed.
414     *
415     * @hide
416     */
417    protected void enforceNotSealed() {
418        if (isSealed()) {
419            throw new IllegalStateException("Cannot perform this "
420                    + "action on an sealed instance.");
421        }
422    }
423
424    /**
425     * Gets the value of a boolean property.
426     *
427     * @param property The property.
428     * @return The value.
429     */
430    private boolean getBooleanProperty(int property) {
431        return (mBooleanProperties & property) == property;
432    }
433
434    /**
435     * Sets a boolean property.
436     *
437     * @param property The property.
438     * @param value The value.
439     */
440    private void setBooleanProperty(int property, boolean value) {
441        if (value) {
442            mBooleanProperties |= property;
443        } else {
444            mBooleanProperties &= ~property;
445        }
446    }
447
448    /**
449     * Returns a cached instance if such is available or a new one is
450     * instantiated. The instance is initialized with data from the
451     * given record.
452     *
453     * @return An instance.
454     */
455    public static AccessibilityRecord obtain(AccessibilityRecord record) {
456       AccessibilityRecord clone = AccessibilityRecord.obtain();
457       clone.init(record);
458       return clone;
459    }
460
461    /**
462     * Returns a cached instance if such is available or a new one is
463     * instantiated.
464     *
465     * @return An instance.
466     */
467    public static AccessibilityRecord obtain() {
468        synchronized (sPoolLock) {
469            if (sPool != null) {
470                AccessibilityRecord record = sPool;
471                sPool = sPool.mNext;
472                sPoolSize--;
473                record.mNext = null;
474                record.mIsInPool = false;
475                return record;
476            }
477            return new AccessibilityRecord();
478        }
479    }
480
481    /**
482     * Return an instance back to be reused.
483     * <p>
484     * <b>Note: You must not touch the object after calling this function.</b>
485     *
486     * @throws IllegalStateException If the record is already recycled.
487     */
488    public void recycle() {
489        if (mIsInPool) {
490            throw new IllegalStateException("Record already recycled!");
491        }
492        clear();
493        synchronized (sPoolLock) {
494            if (sPoolSize <= MAX_POOL_SIZE) {
495                mNext = sPool;
496                sPool = this;
497                mIsInPool = true;
498                sPoolSize++;
499            }
500        }
501    }
502
503    /**
504     * Clears the state of this instance.
505     *
506     * @hide
507     */
508    protected void clear() {
509        mSealed = false;
510        mBooleanProperties = 0;
511        mCurrentItemIndex = INVALID_POSITION;
512        mItemCount = 0;
513        mFromIndex = 0;
514        mAddedCount = 0;
515        mRemovedCount = 0;
516        mClassName = null;
517        mContentDescription = null;
518        mBeforeText = null;
519        mParcelableData = null;
520        mText.clear();
521    }
522
523    @Override
524    public String toString() {
525        StringBuilder builder = new StringBuilder();
526        builder.append(" [ ClassName: " + mClassName);
527        builder.append("; Text: " + mText);
528        builder.append("; ContentDescription: " + mContentDescription);
529        builder.append("; ItemCount: " + mItemCount);
530        builder.append("; CurrentItemIndex: " + mCurrentItemIndex);
531        builder.append("; IsEnabled: " + getBooleanProperty(PROPERTY_ENABLED));
532        builder.append("; IsPassword: " + getBooleanProperty(PROPERTY_PASSWORD));
533        builder.append("; IsChecked: " + getBooleanProperty(PROPERTY_CHECKED));
534        builder.append("; IsFullScreen: " + getBooleanProperty(PROPERTY_FULL_SCREEN));
535        builder.append("; BeforeText: " + mBeforeText);
536        builder.append("; FromIndex: " + mFromIndex);
537        builder.append("; AddedCount: " + mAddedCount);
538        builder.append("; RemovedCount: " + mRemovedCount);
539        builder.append("; ParcelableData: " + mParcelableData);
540        builder.append(" ]");
541        return builder.toString();
542    }
543}
544