diff options
Diffstat (limited to 'core/java/android/view/accessibility/AccessibilityWindowInfo.java')
-rw-r--r-- | core/java/android/view/accessibility/AccessibilityWindowInfo.java | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/core/java/android/view/accessibility/AccessibilityWindowInfo.java b/core/java/android/view/accessibility/AccessibilityWindowInfo.java new file mode 100644 index 0000000..80b5c50 --- /dev/null +++ b/core/java/android/view/accessibility/AccessibilityWindowInfo.java @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2014 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 android.graphics.Rect; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.LongArray; +import android.util.Pools.SynchronizedPool; + +/** + * This class represents a state snapshot of a window for accessibility + * purposes. The screen content contains one or more windows where some + * windows can be descendants of other windows, which is the windows are + * hierarchically ordered. Note that there is no root window. Hence, the + * screen content can be seen as a collection of window trees. + */ +public final class AccessibilityWindowInfo implements Parcelable { + + private static final boolean DEBUG = false; + + /** + * Window type: This is an application window. Such a window shows UI for + * interacting with an application. + */ + public static final int TYPE_APPLICATION = 1; + + /** + * Window type: This is an input method window. Such a window shows UI for + * inputting text such as keyboard, suggestions, etc. + */ + public static final int TYPE_INPUT_METHOD = 2; + + /** + * Window type: This is an system window. Such a window shows UI for + * interacting with the system. + */ + public static final int TYPE_SYSTEM = 3; + + private static final int UNDEFINED = -1; + + private static final int BOOLEAN_PROPERTY_ACTIVE = 1 << 0; + private static final int BOOLEAN_PROPERTY_FOCUSED = 1 << 1; + + // Housekeeping. + private static final int MAX_POOL_SIZE = 10; + private static final SynchronizedPool<AccessibilityWindowInfo> sPool = + new SynchronizedPool<AccessibilityWindowInfo>(MAX_POOL_SIZE); + + // Data. + private int mType = UNDEFINED; + private int mLayer = UNDEFINED; + private int mBooleanProperties; + private int mId = UNDEFINED; + private int mParentId = UNDEFINED; + private final Rect mBoundsInScreen = new Rect(); + private LongArray mChildIds; + + private int mConnectionId = UNDEFINED; + + private AccessibilityWindowInfo() { + /* do nothing - hide constructor */ + } + + /** + * Gets the type of the window. + * + * @return The type. + * + * @see #TYPE_APPLICATION + * @see #TYPE_INPUT_METHOD + * @see #TYPE_SYSTEM + */ + public int getType() { + return mType; + } + + /** + * Sets the type of the window. + * + * @param The type + * + * @hide + */ + public void setType(int type) { + mType = type; + } + + /** + * Gets the layer which determines the Z-order of the window. Windows + * with greater layer appear on top of windows with lesser layer. + * + * @return The window layer. + */ + public int getLayer() { + return mLayer; + } + + /** + * Sets the layer which determines the Z-order of the window. Windows + * with greater layer appear on top of windows with lesser layer. + * + * @param The window layer. + * + * @hide + */ + public void setLayer(int layer) { + mLayer = layer; + } + + /** + * Gets the root node in the window's hierarchy. + * + * @return The root node. + */ + public AccessibilityNodeInfo getRoot() { + if (mConnectionId == UNDEFINED) { + return null; + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId, + mId, AccessibilityNodeInfo.ROOT_NODE_ID, + true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS); + } + + /** + * Gets the parent window if such. + * + * @return The parent window. + */ + public AccessibilityWindowInfo getParent() { + if (mConnectionId == UNDEFINED || mParentId == UNDEFINED) { + return null; + } + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.getWindow(mConnectionId, mParentId); + } + + /** + * Sets the parent window id. + * + * @param parentId The parent id. + * + * @hide + */ + public void setParentId(int parentId) { + mParentId = parentId; + } + + /** + * Gets the unique window id. + * + * @return windowId The window id. + */ + public int getId() { + return mId; + } + + /** + * Sets the unique window id. + * + * @param windowId The window id. + * + * @hide + */ + public void setId(int id) { + mId = id; + } + + /** + * 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) { + mConnectionId = connectionId; + } + + /** + * Gets the bounds of this window in the screen. + * + * @param outBounds The out window bounds. + */ + public void getBoundsInScreen(Rect outBounds) { + outBounds.set(mBoundsInScreen); + } + + /** + * Sets the bounds of this window in the screen. + * + * @param bounds The out window bounds. + * + * @hide + */ + public void setBoundsInScreen(Rect bounds) { + mBoundsInScreen.set(bounds); + } + + /** + * Gets if this window is active. An active window is the one + * the user is currently touching or the window has input focus + * and the user is not touching any window. + * + * @return Whether this is the active window. + */ + public boolean isActive() { + return getBooleanProperty(BOOLEAN_PROPERTY_ACTIVE); + } + + /** + * Sets if this window is active, which is this is the window + * the user is currently touching or the window has input focus + * and the user is not touching any window. + * + * @param Whether this is the active window. + * + * @hide + */ + public void setActive(boolean active) { + setBooleanProperty(BOOLEAN_PROPERTY_ACTIVE, active); + } + + /** + * Gets if this window has input focus. + * + * @return Whether has input focus. + */ + public boolean isFocused() { + return getBooleanProperty(BOOLEAN_PROPERTY_FOCUSED); + } + + /** + * Sets if this window has input focus. + * + * @param Whether has input focus. + * + * @hide + */ + public void setFocused(boolean focused) { + setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused); + } + + /** + * Gets the number of child windows. + * + * @return The child count. + */ + public int getChildCount() { + return (mChildIds != null) ? mChildIds.size() : 0; + } + + /** + * Gets the child window at a given index. + * + * @param index The index. + * @return The child. + */ + public AccessibilityWindowInfo getChild(int index) { + if (mChildIds == null) { + throw new IndexOutOfBoundsException(); + } + if (mConnectionId == UNDEFINED) { + return null; + } + final int childId = (int) mChildIds.get(index); + AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance(); + return client.getWindow(mConnectionId, childId); + } + + /** + * Adds a child window. + * + * @param childId The child window id. + * + * @hide + */ + public void addChild(int childId) { + if (mChildIds == null) { + mChildIds = new LongArray(); + } + mChildIds.add(childId); + } + + /** + * Returns a cached instance if such is available or a new one is + * created. + * + * @return An instance. + */ + public static AccessibilityWindowInfo obtain() { + AccessibilityWindowInfo info = sPool.acquire(); + if (info == null) { + info = new AccessibilityWindowInfo(); + } + return info; + } + + /** + * Returns a cached instance if such is available or a new one is + * created. The returned instance is initialized from the given + * <code>info</code>. + * + * @param info The other info. + * @return An instance. + */ + public static AccessibilityWindowInfo obtain(AccessibilityWindowInfo info) { + AccessibilityWindowInfo infoClone = obtain(); + + infoClone.mType = info.mType; + infoClone.mLayer = info.mLayer; + infoClone.mBooleanProperties = info.mBooleanProperties; + infoClone.mId = info.mId; + infoClone.mParentId = info.mParentId; + infoClone.mBoundsInScreen.set(info.mBoundsInScreen); + + if (info.mChildIds != null && info.mChildIds.size() > 0) { + if (infoClone.mChildIds == null) { + infoClone.mChildIds = info.mChildIds.clone(); + } else { + infoClone.mChildIds.addAll(info.mChildIds); + } + } + + infoClone.mConnectionId = info.mConnectionId; + + return infoClone; + } + + /** + * Return an instance back to be reused. + * <p> + * <strong>Note:</strong> You must not touch the object after calling this function. + * </p> + * + * @throws IllegalStateException If the info is already recycled. + */ + public void recycle() { + clear(); + sPool.release(this); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mType); + parcel.writeInt(mLayer); + parcel.writeInt(mBooleanProperties); + parcel.writeInt(mId); + parcel.writeInt(mParentId); + mBoundsInScreen.writeToParcel(parcel, flags); + + final LongArray childIds = mChildIds; + if (childIds == null) { + parcel.writeInt(0); + } else { + final int childCount = childIds.size(); + parcel.writeInt(childCount); + for (int i = 0; i < childCount; i++) { + parcel.writeInt((int) childIds.get(i)); + } + } + + parcel.writeInt(mConnectionId); + } + + private void initFromParcel(Parcel parcel) { + mType = parcel.readInt(); + mLayer = parcel.readInt(); + mBooleanProperties = parcel.readInt(); + mId = parcel.readInt(); + mParentId = parcel.readInt(); + mBoundsInScreen.readFromParcel(parcel); + + final int childCount = parcel.readInt(); + if (childCount > 0) { + if (mChildIds == null) { + mChildIds = new LongArray(childCount); + } + for (int i = 0; i < childCount; i++) { + final int childId = parcel.readInt(); + mChildIds.add(childId); + } + } + + mConnectionId = parcel.readInt(); + } + + @Override + public int hashCode() { + return mId; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + AccessibilityWindowInfo other = (AccessibilityWindowInfo) obj; + return (mId == other.mId); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("AccessibilityWindowInfo["); + builder.append("id=").append(mId); + builder.append(", type=").append(typeToString(mType)); + builder.append(", layer=").append(mLayer); + builder.append(", bounds=").append(mBoundsInScreen); + builder.append(", focused=").append(isFocused()); + builder.append(", active=").append(isActive()); + if (DEBUG) { + builder.append(", parent=").append(mParentId); + builder.append(", children=["); + if (mChildIds != null) { + final int childCount = mChildIds.size(); + for (int i = 0; i < childCount; i++) { + builder.append(mChildIds.get(i)); + if (i < childCount - 1) { + builder.append(','); + } + } + } else { + builder.append("null"); + } + builder.append(']'); + } else { + builder.append(", hasParent=").append(mParentId != UNDEFINED); + builder.append(", hasChildren=").append(mChildIds != null + && mChildIds.size() > 0); + } + builder.append(']'); + return builder.toString(); + } + + /** + * Clears the internal state. + */ + private void clear() { + mType = UNDEFINED; + mLayer = UNDEFINED; + mBooleanProperties = 0; + mId = UNDEFINED; + mParentId = UNDEFINED; + mBoundsInScreen.setEmpty(); + if (mChildIds != null) { + mChildIds.clear(); + } + mConnectionId = UNDEFINED; + } + + /** + * Gets the value of a boolean property. + * + * @param property The property. + * @return The value. + */ + private boolean getBooleanProperty(int property) { + return (mBooleanProperties & property) != 0; + } + + /** + * Sets a boolean property. + * + * @param property The property. + * @param value The value. + * + * @throws IllegalStateException If called from an AccessibilityService. + */ + private void setBooleanProperty(int property, boolean value) { + if (value) { + mBooleanProperties |= property; + } else { + mBooleanProperties &= ~property; + } + } + + private static String typeToString(int type) { + switch (type) { + case TYPE_APPLICATION: { + return "TYPE_APPLICATION"; + } + case TYPE_INPUT_METHOD: { + return "TYPE_INPUT_METHOD"; + } + case TYPE_SYSTEM: { + return "TYPE_SYSTEM"; + } + default: + return "<UNKNOWN>"; + } + } + + /** + * Checks whether this window changed. The argument should be + * another state of the same window, which is have the same id + * and type as they never change. + * + * @param other The new state. + * @return Whether something changed. + * + * @hide + */ + public boolean changed(AccessibilityWindowInfo other) { + if (other.mId != mId) { + throw new IllegalArgumentException("Not same window."); + } + if (other.mType != mType) { + throw new IllegalArgumentException("Not same type."); + } + if (!mBoundsInScreen.equals(mBoundsInScreen)) { + return true; + } + if (mLayer != other.mLayer) { + return true; + } + if (mBooleanProperties != other.mBooleanProperties) { + return true; + } + if (mParentId != other.mParentId) { + return true; + } + if (mChildIds == null) { + if (other.mChildIds != null) { + return true; + } + } else if (!mChildIds.equals(other.mChildIds)) { + return true; + } + return false; + } + + public static final Parcelable.Creator<AccessibilityWindowInfo> CREATOR = + new Creator<AccessibilityWindowInfo>() { + @Override + public AccessibilityWindowInfo createFromParcel(Parcel parcel) { + AccessibilityWindowInfo info = obtain(); + info.initFromParcel(parcel); + return info; + } + + @Override + public AccessibilityWindowInfo[] newArray(int size) { + return new AccessibilityWindowInfo[size]; + } + }; +} |