(MAX_POOL_SIZE);
private static AtomicInteger sNumInstancesInUse;
// Data.
private int mType = UNDEFINED_WINDOW_ID;
private int mLayer = UNDEFINED_WINDOW_ID;
private int mBooleanProperties;
private int mId = UNDEFINED_WINDOW_ID;
private int mParentId = UNDEFINED_WINDOW_ID;
private final Rect mBoundsInScreen = new Rect();
private LongArray mChildIds;
private CharSequence mTitle;
private int mAnchorId = UNDEFINED_WINDOW_ID;
private boolean mInPictureInPicture;
private int mConnectionId = UNDEFINED_WINDOW_ID;
private AccessibilityWindowInfo() {
/* do nothing - hide constructor */
}
/**
* Gets the title of the window.
*
* @return The title of the window, or {@code null} if none is available.
*/
@Nullable
public CharSequence getTitle() {
return mTitle;
}
/**
* Sets the title of the window.
*
* @param title The title.
*
* @hide
*/
public void setTitle(CharSequence title) {
mTitle = title;
}
/**
* Gets the type of the window.
*
* @return The type.
*
* @see #TYPE_APPLICATION
* @see #TYPE_INPUT_METHOD
* @see #TYPE_SYSTEM
* @see #TYPE_ACCESSIBILITY_OVERLAY
*/
public int getType() {
return mType;
}
/**
* Sets the type of the window.
*
* @param type 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 layer 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_WINDOW_ID) {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
mId, AccessibilityNodeInfo.ROOT_NODE_ID,
true, AccessibilityNodeInfo.FLAG_PREFETCH_DESCENDANTS, null);
}
/**
* Sets the anchor node's ID.
*
* @param anchorId The anchor's accessibility id in its window.
*
* @hide
*/
public void setAnchorId(int anchorId) {
mAnchorId = anchorId;
}
/**
* Gets the node that anchors this window to another.
*
* @return The anchor node, or {@code null} if none exists.
*/
public AccessibilityNodeInfo getAnchor() {
if ((mConnectionId == UNDEFINED_WINDOW_ID) || (mAnchorId == UNDEFINED_WINDOW_ID)
|| (mParentId == UNDEFINED_WINDOW_ID)) {
return null;
}
AccessibilityInteractionClient client = AccessibilityInteractionClient.getInstance();
return client.findAccessibilityNodeInfoByAccessibilityId(mConnectionId,
mParentId, mAnchorId, true, 0, null);
}
/** @hide */
public void setPictureInPicture(boolean pictureInPicture) {
mInPictureInPicture = pictureInPicture;
}
/**
* Check if the window is in picture-in-picture mode.
*
* @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
* @removed
*/
public boolean inPictureInPicture() {
return isInPictureInPictureMode();
}
/**
* Check if the window is in picture-in-picture mode.
*
* @return {@code true} if the window is in picture-in-picture mode, {@code false} otherwise.
*/
public boolean isInPictureInPictureMode() {
return mInPictureInPicture;
}
/**
* Gets the parent window.
*
* @return The parent window, or {@code null} if none exists.
*/
public AccessibilityWindowInfo getParent() {
if (mConnectionId == UNDEFINED_WINDOW_ID || mParentId == UNDEFINED_WINDOW_ID) {
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 id 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 active 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 focused Whether has input focus.
*
* @hide
*/
public void setFocused(boolean focused) {
setBooleanProperty(BOOLEAN_PROPERTY_FOCUSED, focused);
}
/**
* Gets if this window has accessibility focus.
*
* @return Whether has accessibility focus.
*/
public boolean isAccessibilityFocused() {
return getBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_FOCUSED);
}
/**
* Sets if this window has accessibility focus.
*
* @param focused Whether has accessibility focus.
*
* @hide
*/
public void setAccessibilityFocused(boolean focused) {
setBooleanProperty(BOOLEAN_PROPERTY_ACCESSIBILITY_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_WINDOW_ID) {
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();
}
if (sNumInstancesInUse != null) {
sNumInstancesInUse.incrementAndGet();
}
return info;
}
/**
* Returns a cached instance if such is available or a new one is
* created. The returned instance is initialized from the given
* info
.
*
* @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);
infoClone.mTitle = info.mTitle;
infoClone.mAnchorId = info.mAnchorId;
infoClone.mInPictureInPicture = info.mInPictureInPicture;
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;
}
/**
* Specify a counter that will be incremented on obtain() and decremented on recycle()
*
* @hide
*/
@TestApi
public static void setNumInstancesInUseCounter(AtomicInteger counter) {
if (sNumInstancesInUse != null) {
sNumInstancesInUse = counter;
}
}
/**
* Return an instance back to be reused.
*
* Note: You must not touch the object after calling this function.
*
*
* @throws IllegalStateException If the info is already recycled.
*/
public void recycle() {
clear();
sPool.release(this);
if (sNumInstancesInUse != null) {
sNumInstancesInUse.decrementAndGet();
}
}
@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);
parcel.writeCharSequence(mTitle);
parcel.writeInt(mAnchorId);
parcel.writeInt(mInPictureInPicture ? 1 : 0);
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);
mTitle = parcel.readCharSequence();
mAnchorId = parcel.readInt();
mInPictureInPicture = parcel.readInt() == 1;
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("title=").append(mTitle);
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());
builder.append(", pictureInPicture=").append(inPictureInPicture());
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_WINDOW_ID);
builder.append(", isAnchored=").append(mAnchorId != UNDEFINED_WINDOW_ID);
builder.append(", hasChildren=").append(mChildIds != null
&& mChildIds.size() > 0);
}
builder.append(']');
return builder.toString();
}
/**
* Clears the internal state.
*/
private void clear() {
mType = UNDEFINED_WINDOW_ID;
mLayer = UNDEFINED_WINDOW_ID;
mBooleanProperties = 0;
mId = UNDEFINED_WINDOW_ID;
mParentId = UNDEFINED_WINDOW_ID;
mBoundsInScreen.setEmpty();
if (mChildIds != null) {
mChildIds.clear();
}
mConnectionId = UNDEFINED_WINDOW_ID;
mAnchorId = UNDEFINED_WINDOW_ID;
mInPictureInPicture = false;
mTitle = null;
}
/**
* 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";
}
case TYPE_ACCESSIBILITY_OVERLAY: {
return "TYPE_ACCESSIBILITY_OVERLAY";
}
case TYPE_SPLIT_SCREEN_DIVIDER: {
return "TYPE_SPLIT_SCREEN_DIVIDER";
}
default:
return "";
}
}
/**
* 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(other.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 CREATOR =
new Creator() {
@Override
public AccessibilityWindowInfo createFromParcel(Parcel parcel) {
AccessibilityWindowInfo info = obtain();
info.initFromParcel(parcel);
return info;
}
@Override
public AccessibilityWindowInfo[] newArray(int size) {
return new AccessibilityWindowInfo[size];
}
};
}