| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package android.view.accessibility; |
| |
| import static com.android.internal.util.CollectionUtils.isEmpty; |
| |
| import android.annotation.Nullable; |
| import android.compat.annotation.UnsupportedAppUsage; |
| import android.os.Parcelable; |
| import android.view.View; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| /** |
| * Represents a record in an {@link AccessibilityEvent} and contains information |
| * about state change of its source {@link android.view.View}. When a view fires |
| * an accessibility event it requests from its parent to dispatch the |
| * constructed event. The parent may optionally append a record for itself |
| * for providing more context to |
| * {@link android.accessibilityservice.AccessibilityService}s. Hence, |
| * accessibility services can facilitate additional accessibility records |
| * to enhance feedback. |
| * </p> |
| * <p> |
| * Once the accessibility event containing a record is dispatched the record is |
| * made immutable and calling a state mutation method generates an error. |
| * </p> |
| * <p> |
| * <strong>Note:</strong> Not all properties are applicable to all accessibility |
| * event types. For detailed information please refer to {@link AccessibilityEvent}. |
| * </p> |
| * |
| * <div class="special reference"> |
| * <h3>Developer Guides</h3> |
| * <p>For more information about creating and processing AccessibilityRecords, read the |
| * <a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a> |
| * developer guide.</p> |
| * </div> |
| * |
| * @see AccessibilityEvent |
| * @see AccessibilityManager |
| * @see android.accessibilityservice.AccessibilityService |
| * @see AccessibilityNodeInfo |
| */ |
| public class AccessibilityRecord { |
| /** @hide */ |
| protected static final boolean DEBUG_CONCISE_TOSTRING = false; |
| |
| private static final int UNDEFINED = -1; |
| |
| private static final int PROPERTY_CHECKED = 0x00000001; |
| private static final int PROPERTY_ENABLED = 0x00000002; |
| private static final int PROPERTY_PASSWORD = 0x00000004; |
| private static final int PROPERTY_FULL_SCREEN = 0x00000080; |
| private static final int PROPERTY_SCROLLABLE = 0x00000100; |
| private static final int PROPERTY_IMPORTANT_FOR_ACCESSIBILITY = 0x00000200; |
| |
| private static final int GET_SOURCE_PREFETCH_FLAGS = |
| AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS |
| | AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS |
| | AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS; |
| |
| // Housekeeping |
| private static final int MAX_POOL_SIZE = 10; |
| private static final Object sPoolLock = new Object(); |
| private static AccessibilityRecord sPool; |
| private static int sPoolSize; |
| private AccessibilityRecord mNext; |
| private boolean mIsInPool; |
| |
| @UnsupportedAppUsage |
| boolean mSealed; |
| int mBooleanProperties = 0; |
| int mCurrentItemIndex = UNDEFINED; |
| int mItemCount = UNDEFINED; |
| int mFromIndex = UNDEFINED; |
| int mToIndex = UNDEFINED; |
| int mScrollX = UNDEFINED; |
| int mScrollY = UNDEFINED; |
| |
| int mScrollDeltaX = UNDEFINED; |
| int mScrollDeltaY = UNDEFINED; |
| int mMaxScrollX = UNDEFINED; |
| int mMaxScrollY = UNDEFINED; |
| |
| int mAddedCount= UNDEFINED; |
| int mRemovedCount = UNDEFINED; |
| @UnsupportedAppUsage |
| long mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_NODE_ID; |
| int mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; |
| |
| CharSequence mClassName; |
| CharSequence mContentDescription; |
| CharSequence mBeforeText; |
| Parcelable mParcelableData; |
| |
| final List<CharSequence> mText = new ArrayList<CharSequence>(); |
| |
| int mConnectionId = UNDEFINED; |
| |
| /* |
| * Hide constructor. |
| */ |
| AccessibilityRecord() { |
| } |
| |
| /** |
| * Sets the event source. |
| * |
| * @param source The source. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setSource(View source) { |
| setSource(source, AccessibilityNodeProvider.HOST_VIEW_ID); |
| } |
| |
| /** |
| * Sets the source to be a virtual descendant of the given <code>root</code>. |
| * If <code>virtualDescendantId</code> equals to {@link View#NO_ID} the root |
| * is set as the source. |
| * <p> |
| * A virtual descendant is an imaginary View that is reported as a part of the view |
| * hierarchy for accessibility purposes. This enables custom views that draw complex |
| * content to report them selves as a tree of virtual views, thus conveying their |
| * logical structure. |
| * </p> |
| * |
| * @param root The root of the virtual subtree. |
| * @param virtualDescendantId The id of the virtual descendant. |
| */ |
| public void setSource(@Nullable View root, int virtualDescendantId) { |
| enforceNotSealed(); |
| boolean important = true; |
| int rootViewId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; |
| mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; |
| if (root != null) { |
| important = root.isImportantForAccessibility(); |
| rootViewId = root.getAccessibilityViewId(); |
| mSourceWindowId = root.getAccessibilityWindowId(); |
| } |
| setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, important); |
| mSourceNodeId = AccessibilityNodeInfo.makeNodeId(rootViewId, virtualDescendantId); |
| } |
| |
| /** |
| * Set the source node ID directly |
| * |
| * @param sourceNodeId The source node Id |
| * @hide |
| */ |
| public void setSourceNodeId(long sourceNodeId) { |
| mSourceNodeId = sourceNodeId; |
| } |
| |
| /** |
| * Gets the {@link AccessibilityNodeInfo} of the event source. |
| * <p> |
| * <strong>Note:</strong> It is a client responsibility to recycle the received info |
| * by calling {@link AccessibilityNodeInfo#recycle() AccessibilityNodeInfo#recycle()} |
| * to avoid creating of multiple instances. |
| * </p> |
| * @return The info of the source. |
| */ |
| public AccessibilityNodeInfo getSource() { |
| enforceSealed(); |
| if ((mConnectionId == UNDEFINED) |
| || (mSourceWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) |
| || (AccessibilityNodeInfo.getAccessibilityViewId(mSourceNodeId) |
| == AccessibilityNodeInfo.UNDEFINED_ITEM_ID)) { |
| return null; |
| } |
| AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); |
| return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, mSourceWindowId, |
| mSourceNodeId, false, GET_SOURCE_PREFETCH_FLAGS, null); |
| } |
| |
| /** |
| * Sets the window id. |
| * |
| * @param windowId The window id. |
| * |
| * @hide |
| */ |
| public void setWindowId(int windowId) { |
| mSourceWindowId = windowId; |
| } |
| |
| /** |
| * Gets the id of the window from which the event comes from. |
| * |
| * @return The window id. |
| */ |
| public int getWindowId() { |
| return mSourceWindowId; |
| } |
| |
| /** |
| * Gets if the source is checked. |
| * |
| * @return True if the view is checked, false otherwise. |
| */ |
| public boolean isChecked() { |
| return getBooleanProperty(PROPERTY_CHECKED); |
| } |
| |
| /** |
| * Sets if the source is checked. |
| * |
| * @param isChecked True if the view is checked, false otherwise. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setChecked(boolean isChecked) { |
| enforceNotSealed(); |
| setBooleanProperty(PROPERTY_CHECKED, isChecked); |
| } |
| |
| /** |
| * Gets if the source is enabled. |
| * |
| * @return True if the view is enabled, false otherwise. |
| */ |
| public boolean isEnabled() { |
| return getBooleanProperty(PROPERTY_ENABLED); |
| } |
| |
| /** |
| * Sets if the source is enabled. |
| * |
| * @param isEnabled True if the view is enabled, false otherwise. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setEnabled(boolean isEnabled) { |
| enforceNotSealed(); |
| setBooleanProperty(PROPERTY_ENABLED, isEnabled); |
| } |
| |
| /** |
| * Gets if the source is a password field. |
| * |
| * @return True if the view is a password field, false otherwise. |
| */ |
| public boolean isPassword() { |
| return getBooleanProperty(PROPERTY_PASSWORD); |
| } |
| |
| /** |
| * Sets if the source is a password field. |
| * |
| * @param isPassword True if the view is a password field, false otherwise. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setPassword(boolean isPassword) { |
| enforceNotSealed(); |
| setBooleanProperty(PROPERTY_PASSWORD, isPassword); |
| } |
| |
| /** |
| * Gets if the source is taking the entire screen. |
| * |
| * @return True if the source is full screen, false otherwise. |
| */ |
| public boolean isFullScreen() { |
| return getBooleanProperty(PROPERTY_FULL_SCREEN); |
| } |
| |
| /** |
| * Sets if the source is taking the entire screen. |
| * |
| * @param isFullScreen True if the source is full screen, false otherwise. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setFullScreen(boolean isFullScreen) { |
| enforceNotSealed(); |
| setBooleanProperty(PROPERTY_FULL_SCREEN, isFullScreen); |
| } |
| |
| /** |
| * Gets if the source is scrollable. |
| * |
| * @return True if the source is scrollable, false otherwise. |
| */ |
| public boolean isScrollable() { |
| return getBooleanProperty(PROPERTY_SCROLLABLE); |
| } |
| |
| /** |
| * Sets if the source is scrollable. |
| * |
| * @param scrollable True if the source is scrollable, false otherwise. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setScrollable(boolean scrollable) { |
| enforceNotSealed(); |
| setBooleanProperty(PROPERTY_SCROLLABLE, scrollable); |
| } |
| |
| /** |
| * Gets if the source is important for accessibility. |
| * |
| * <strong>Note:</strong> Used only internally to determine whether |
| * to deliver the event to a given accessibility service since some |
| * services may want to regard all views for accessibility while others |
| * may want to regard only the important views for accessibility. |
| * |
| * @return True if the source is important for accessibility, |
| * false otherwise. |
| * |
| * @hide |
| */ |
| public boolean isImportantForAccessibility() { |
| return getBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY); |
| } |
| |
| /** |
| * Sets if the source is important for accessibility. |
| * |
| * @param importantForAccessibility True if the source is important for accessibility, |
| * false otherwise. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| * @hide |
| */ |
| public void setImportantForAccessibility(boolean importantForAccessibility) { |
| enforceNotSealed(); |
| setBooleanProperty(PROPERTY_IMPORTANT_FOR_ACCESSIBILITY, importantForAccessibility); |
| } |
| |
| /** |
| * Gets the number of items that can be visited. |
| * |
| * @return The number of items. |
| */ |
| public int getItemCount() { |
| return mItemCount; |
| } |
| |
| /** |
| * Sets the number of items that can be visited. |
| * |
| * @param itemCount The number of items. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setItemCount(int itemCount) { |
| enforceNotSealed(); |
| mItemCount = itemCount; |
| } |
| |
| /** |
| * Gets the index of the source in the list of items the can be visited. |
| * |
| * @return The current item index. |
| */ |
| public int getCurrentItemIndex() { |
| return mCurrentItemIndex; |
| } |
| |
| /** |
| * Sets the index of the source in the list of items that can be visited. |
| * |
| * @param currentItemIndex The current item index. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setCurrentItemIndex(int currentItemIndex) { |
| enforceNotSealed(); |
| mCurrentItemIndex = currentItemIndex; |
| } |
| |
| /** |
| * Gets the index of the first character of the changed sequence, |
| * or the beginning of a text selection or the index of the first |
| * visible item when scrolling. |
| * |
| * @return The index of the first character or selection |
| * start or the first visible item. |
| */ |
| public int getFromIndex() { |
| return mFromIndex; |
| } |
| |
| /** |
| * Sets the index of the first character of the changed sequence |
| * or the beginning of a text selection or the index of the first |
| * visible item when scrolling. |
| * |
| * @param fromIndex The index of the first character or selection |
| * start or the first visible item. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setFromIndex(int fromIndex) { |
| enforceNotSealed(); |
| mFromIndex = fromIndex; |
| } |
| |
| /** |
| * Gets the index of text selection end or the index of the last |
| * visible item when scrolling. |
| * |
| * @return The index of selection end or last item index. |
| */ |
| public int getToIndex() { |
| return mToIndex; |
| } |
| |
| /** |
| * Sets the index of text selection end or the index of the last |
| * visible item when scrolling. |
| * |
| * @param toIndex The index of selection end or last item index. |
| */ |
| public void setToIndex(int toIndex) { |
| enforceNotSealed(); |
| mToIndex = toIndex; |
| } |
| |
| /** |
| * Gets the scroll offset of the source left edge in pixels. |
| * |
| * @return The scroll. |
| */ |
| public int getScrollX() { |
| return mScrollX; |
| } |
| |
| /** |
| * Sets the scroll offset of the source left edge in pixels. |
| * |
| * @param scrollX The scroll. |
| */ |
| public void setScrollX(int scrollX) { |
| enforceNotSealed(); |
| mScrollX = scrollX; |
| } |
| |
| /** |
| * Gets the scroll offset of the source top edge in pixels. |
| * |
| * @return The scroll. |
| */ |
| public int getScrollY() { |
| return mScrollY; |
| } |
| |
| /** |
| * Sets the scroll offset of the source top edge in pixels. |
| * |
| * @param scrollY The scroll. |
| */ |
| public void setScrollY(int scrollY) { |
| enforceNotSealed(); |
| mScrollY = scrollY; |
| } |
| |
| /** |
| * Gets the difference in pixels between the horizontal position before the scroll and the |
| * current horizontal position |
| * |
| * @return the scroll delta x |
| */ |
| public int getScrollDeltaX() { |
| return mScrollDeltaX; |
| } |
| |
| /** |
| * Sets the difference in pixels between the horizontal position before the scroll and the |
| * current horizontal position |
| * |
| * @param scrollDeltaX the scroll delta x |
| */ |
| public void setScrollDeltaX(int scrollDeltaX) { |
| enforceNotSealed(); |
| mScrollDeltaX = scrollDeltaX; |
| } |
| |
| /** |
| * Gets the difference in pixels between the vertical position before the scroll and the |
| * current vertical position |
| * |
| * @return the scroll delta y |
| */ |
| public int getScrollDeltaY() { |
| return mScrollDeltaY; |
| } |
| |
| /** |
| * Sets the difference in pixels between the vertical position before the scroll and the |
| * current vertical position |
| * |
| * @param scrollDeltaY the scroll delta y |
| */ |
| public void setScrollDeltaY(int scrollDeltaY) { |
| enforceNotSealed(); |
| mScrollDeltaY = scrollDeltaY; |
| } |
| |
| /** |
| * Gets the max scroll offset of the source left edge in pixels. |
| * |
| * @return The max scroll. |
| */ |
| public int getMaxScrollX() { |
| return mMaxScrollX; |
| } |
| |
| /** |
| * Sets the max scroll offset of the source left edge in pixels. |
| * |
| * @param maxScrollX The max scroll. |
| */ |
| public void setMaxScrollX(int maxScrollX) { |
| enforceNotSealed(); |
| mMaxScrollX = maxScrollX; |
| } |
| |
| /** |
| * Gets the max scroll offset of the source top edge in pixels. |
| * |
| * @return The max scroll. |
| */ |
| public int getMaxScrollY() { |
| return mMaxScrollY; |
| } |
| |
| /** |
| * Sets the max scroll offset of the source top edge in pixels. |
| * |
| * @param maxScrollY The max scroll. |
| */ |
| public void setMaxScrollY(int maxScrollY) { |
| enforceNotSealed(); |
| mMaxScrollY = maxScrollY; |
| } |
| |
| /** |
| * Gets the number of added characters. |
| * |
| * @return The number of added characters. |
| */ |
| public int getAddedCount() { |
| return mAddedCount; |
| } |
| |
| /** |
| * Sets the number of added characters. |
| * |
| * @param addedCount The number of added characters. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setAddedCount(int addedCount) { |
| enforceNotSealed(); |
| mAddedCount = addedCount; |
| } |
| |
| /** |
| * Gets the number of removed characters. |
| * |
| * @return The number of removed characters. |
| */ |
| public int getRemovedCount() { |
| return mRemovedCount; |
| } |
| |
| /** |
| * Sets the number of removed characters. |
| * |
| * @param removedCount The number of removed characters. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setRemovedCount(int removedCount) { |
| enforceNotSealed(); |
| mRemovedCount = removedCount; |
| } |
| |
| /** |
| * Gets the class name of the source. |
| * |
| * @return The class name. |
| */ |
| public CharSequence getClassName() { |
| return mClassName; |
| } |
| |
| /** |
| * Sets the class name of the source. |
| * |
| * @param className The lass name. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setClassName(CharSequence className) { |
| enforceNotSealed(); |
| mClassName = className; |
| } |
| |
| /** |
| * Gets the text of the event. The index in the list represents the priority |
| * of the text. Specifically, the lower the index the higher the priority. |
| * |
| * @return The text. |
| */ |
| public List<CharSequence> getText() { |
| return mText; |
| } |
| |
| /** |
| * Gets the text before a change. |
| * |
| * @return The text before the change. |
| */ |
| public CharSequence getBeforeText() { |
| return mBeforeText; |
| } |
| |
| /** |
| * Sets the text before a change. |
| * |
| * @param beforeText The text before the change. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setBeforeText(CharSequence beforeText) { |
| enforceNotSealed(); |
| mBeforeText = (beforeText == null) ? null |
| : beforeText.subSequence(0, beforeText.length()); |
| } |
| |
| /** |
| * Gets the description of the source. |
| * |
| * @return The description. |
| */ |
| public CharSequence getContentDescription() { |
| return mContentDescription; |
| } |
| |
| /** |
| * Sets the description of the source. |
| * |
| * @param contentDescription The description. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setContentDescription(CharSequence contentDescription) { |
| enforceNotSealed(); |
| mContentDescription = (contentDescription == null) ? null |
| : contentDescription.subSequence(0, contentDescription.length()); |
| } |
| |
| /** |
| * Gets the {@link Parcelable} data. |
| * |
| * @return The parcelable data. |
| */ |
| public Parcelable getParcelableData() { |
| return mParcelableData; |
| } |
| |
| /** |
| * Sets the {@link Parcelable} data of the event. |
| * |
| * @param parcelableData The parcelable data. |
| * |
| * @throws IllegalStateException If called from an AccessibilityService. |
| */ |
| public void setParcelableData(Parcelable parcelableData) { |
| enforceNotSealed(); |
| mParcelableData = parcelableData; |
| } |
| |
| /** |
| * Gets the id of the source node. |
| * |
| * @return The id. |
| * |
| * @hide |
| */ |
| @UnsupportedAppUsage |
| public long getSourceNodeId() { |
| return mSourceNodeId; |
| } |
| |
| /** |
| * Sets the unique id of the IAccessibilityServiceConnection over which |
| * this instance can send requests to the system. |
| * |
| * @param connectionId The connection id. |
| * |
| * @hide |
| */ |
| public void setConnectionId(int connectionId) { |
| enforceNotSealed(); |
| mConnectionId = connectionId; |
| } |
| |
| /** |
| * Sets if this instance is sealed. |
| * |
| * @param sealed Whether is sealed. |
| * |
| * @hide |
| */ |
| public void setSealed(boolean sealed) { |
| mSealed = sealed; |
| } |
| |
| /** |
| * Gets if this instance is sealed. |
| * |
| * @return Whether is sealed. |
| */ |
| boolean isSealed() { |
| return mSealed; |
| } |
| |
| /** |
| * Enforces that this instance is sealed. |
| * |
| * @throws IllegalStateException If this instance is not sealed. |
| */ |
| void enforceSealed() { |
| if (!isSealed()) { |
| throw new IllegalStateException("Cannot perform this " |
| + "action on a not sealed instance."); |
| } |
| } |
| |
| /** |
| * Enforces that this instance is not sealed. |
| * |
| * @throws IllegalStateException If this instance is sealed. |
| */ |
| void enforceNotSealed() { |
| if (isSealed()) { |
| throw new IllegalStateException("Cannot perform this " |
| + "action on a sealed instance."); |
| } |
| } |
| |
| /** |
| * Gets the value of a boolean property. |
| * |
| * @param property The property. |
| * @return The value. |
| */ |
| private boolean getBooleanProperty(int property) { |
| return (mBooleanProperties & property) == property; |
| } |
| |
| /** |
| * Sets a boolean property. |
| * |
| * @param property The property. |
| * @param value The value. |
| */ |
| private void setBooleanProperty(int property, boolean value) { |
| if (value) { |
| mBooleanProperties |= property; |
| } else { |
| mBooleanProperties &= ~property; |
| } |
| } |
| |
| /** |
| * Returns a cached instance if such is available or a new one is |
| * instantiated. The instance is initialized with data from the |
| * given record. |
| * |
| * @return An instance. |
| */ |
| public static AccessibilityRecord obtain(AccessibilityRecord record) { |
| AccessibilityRecord clone = AccessibilityRecord.obtain(); |
| clone.init(record); |
| return clone; |
| } |
| |
| /** |
| * Returns a cached instance if such is available or a new one is |
| * instantiated. |
| * |
| * @return An instance. |
| */ |
| public static AccessibilityRecord obtain() { |
| synchronized (sPoolLock) { |
| if (sPool != null) { |
| AccessibilityRecord record = sPool; |
| sPool = sPool.mNext; |
| sPoolSize--; |
| record.mNext = null; |
| record.mIsInPool = false; |
| return record; |
| } |
| return new AccessibilityRecord(); |
| } |
| } |
| |
| /** |
| * Return an instance back to be reused. |
| * <p> |
| * <strong>Note:</strong> You must not touch the object after calling this function. |
| * |
| * @throws IllegalStateException If the record is already recycled. |
| */ |
| public void recycle() { |
| if (mIsInPool) { |
| throw new IllegalStateException("Record already recycled!"); |
| } |
| clear(); |
| synchronized (sPoolLock) { |
| if (sPoolSize <= MAX_POOL_SIZE) { |
| mNext = sPool; |
| sPool = this; |
| mIsInPool = true; |
| sPoolSize++; |
| } |
| } |
| } |
| |
| /** |
| * Initialize this record from another one. |
| * |
| * @param record The to initialize from. |
| */ |
| void init(AccessibilityRecord record) { |
| mSealed = record.mSealed; |
| mBooleanProperties = record.mBooleanProperties; |
| mCurrentItemIndex = record.mCurrentItemIndex; |
| mItemCount = record.mItemCount; |
| mFromIndex = record.mFromIndex; |
| mToIndex = record.mToIndex; |
| mScrollX = record.mScrollX; |
| mScrollY = record.mScrollY; |
| mMaxScrollX = record.mMaxScrollX; |
| mMaxScrollY = record.mMaxScrollY; |
| mAddedCount = record.mAddedCount; |
| mRemovedCount = record.mRemovedCount; |
| mClassName = record.mClassName; |
| mContentDescription = record.mContentDescription; |
| mBeforeText = record.mBeforeText; |
| mParcelableData = record.mParcelableData; |
| mText.addAll(record.mText); |
| mSourceWindowId = record.mSourceWindowId; |
| mSourceNodeId = record.mSourceNodeId; |
| mConnectionId = record.mConnectionId; |
| } |
| |
| /** |
| * Clears the state of this instance. |
| */ |
| void clear() { |
| mSealed = false; |
| mBooleanProperties = 0; |
| mCurrentItemIndex = UNDEFINED; |
| mItemCount = UNDEFINED; |
| mFromIndex = UNDEFINED; |
| mToIndex = UNDEFINED; |
| mScrollX = UNDEFINED; |
| mScrollY = UNDEFINED; |
| mMaxScrollX = UNDEFINED; |
| mMaxScrollY = UNDEFINED; |
| mAddedCount = UNDEFINED; |
| mRemovedCount = UNDEFINED; |
| mClassName = null; |
| mContentDescription = null; |
| mBeforeText = null; |
| mParcelableData = null; |
| mText.clear(); |
| mSourceNodeId = AccessibilityNodeInfo.UNDEFINED_ITEM_ID; |
| mSourceWindowId = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID; |
| mConnectionId = UNDEFINED; |
| } |
| |
| @Override |
| public String toString() { |
| return appendTo(new StringBuilder()).toString(); |
| } |
| |
| StringBuilder appendTo(StringBuilder builder) { |
| builder.append(" [ ClassName: ").append(mClassName); |
| if (!DEBUG_CONCISE_TOSTRING || !isEmpty(mText)) { |
| appendPropName(builder, "Text").append(mText); |
| } |
| append(builder, "ContentDescription", mContentDescription); |
| append(builder, "ItemCount", mItemCount); |
| append(builder, "CurrentItemIndex", mCurrentItemIndex); |
| |
| appendUnless(true, PROPERTY_ENABLED, builder); |
| appendUnless(false, PROPERTY_PASSWORD, builder); |
| appendUnless(false, PROPERTY_CHECKED, builder); |
| appendUnless(false, PROPERTY_FULL_SCREEN, builder); |
| appendUnless(false, PROPERTY_SCROLLABLE, builder); |
| |
| append(builder, "BeforeText", mBeforeText); |
| append(builder, "FromIndex", mFromIndex); |
| append(builder, "ToIndex", mToIndex); |
| append(builder, "ScrollX", mScrollX); |
| append(builder, "ScrollY", mScrollY); |
| append(builder, "MaxScrollX", mMaxScrollX); |
| append(builder, "MaxScrollY", mMaxScrollY); |
| append(builder, "AddedCount", mAddedCount); |
| append(builder, "RemovedCount", mRemovedCount); |
| append(builder, "ParcelableData", mParcelableData); |
| builder.append(" ]"); |
| return builder; |
| } |
| |
| private void appendUnless(boolean defValue, int prop, StringBuilder builder) { |
| boolean value = getBooleanProperty(prop); |
| if (DEBUG_CONCISE_TOSTRING && value == defValue) return; |
| appendPropName(builder, singleBooleanPropertyToString(prop)) |
| .append(value); |
| } |
| |
| private static String singleBooleanPropertyToString(int prop) { |
| switch (prop) { |
| case PROPERTY_CHECKED: return "Checked"; |
| case PROPERTY_ENABLED: return "Enabled"; |
| case PROPERTY_PASSWORD: return "Password"; |
| case PROPERTY_FULL_SCREEN: return "FullScreen"; |
| case PROPERTY_SCROLLABLE: return "Scrollable"; |
| case PROPERTY_IMPORTANT_FOR_ACCESSIBILITY: |
| return "ImportantForAccessibility"; |
| default: return Integer.toHexString(prop); |
| } |
| } |
| |
| private void append(StringBuilder builder, String propName, int propValue) { |
| if (DEBUG_CONCISE_TOSTRING && propValue == UNDEFINED) return; |
| appendPropName(builder, propName).append(propValue); |
| } |
| |
| private void append(StringBuilder builder, String propName, Object propValue) { |
| if (DEBUG_CONCISE_TOSTRING && propValue == null) return; |
| appendPropName(builder, propName).append(propValue); |
| } |
| |
| private StringBuilder appendPropName(StringBuilder builder, String propName) { |
| return builder.append("; ").append(propName).append(": "); |
| } |
| } |