/* * Copyright (C) 2015 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.support.v4.view.accessibility; import android.graphics.Rect; import android.os.Build; /** * Helper for accessing {@link android.view.accessibility.AccessibilityWindowInfo} * introduced after API level 4 in a backwards compatible fashion. */ public class AccessibilityWindowInfoCompat { private static interface AccessibilityWindowInfoImpl { public Object obtain(); public Object obtain(Object info); public int getType(Object info); public int getLayer(Object info); public Object getRoot(Object info); public Object getParent(Object info); public int getId(Object info); public void getBoundsInScreen(Object info, Rect outBounds); public boolean isActive(Object info); public boolean isFocused(Object info); public boolean isAccessibilityFocused(Object info); public int getChildCount(Object info); public Object getChild(Object info, int index); public CharSequence getTitle(Object info); public Object getAnchor(Object info); public void recycle(Object info); } private static class AccessibilityWindowInfoStubImpl implements AccessibilityWindowInfoImpl { @Override public Object obtain() { return null; } @Override public Object obtain(Object info) { return null; } @Override public int getType(Object info) { return UNDEFINED; } @Override public int getLayer(Object info) { return UNDEFINED; } @Override public Object getRoot(Object info) { return null; } @Override public Object getParent(Object info) { return null; } @Override public int getId(Object info) { return UNDEFINED; } @Override public void getBoundsInScreen(Object info, Rect outBounds) { } @Override public boolean isActive(Object info) { return true; } @Override public boolean isFocused(Object info) { return true; } @Override public boolean isAccessibilityFocused(Object info) { return true; } @Override public int getChildCount(Object info) { return 0; } @Override public Object getChild(Object info, int index) { return null; } @Override public void recycle(Object info) { } @Override public CharSequence getTitle(Object info) { return null; } @Override public Object getAnchor(Object info) { return null; } } private static class AccessibilityWindowInfoApi21Impl extends AccessibilityWindowInfoStubImpl { @Override public Object obtain() { return AccessibilityWindowInfoCompatApi21.obtain(); } @Override public Object obtain(Object info) { return AccessibilityWindowInfoCompatApi21.obtain(info); } @Override public int getType(Object info) { return AccessibilityWindowInfoCompatApi21.getType(info); } @Override public int getLayer(Object info) { return AccessibilityWindowInfoCompatApi21.getLayer(info); } @Override public Object getRoot(Object info) { return AccessibilityWindowInfoCompatApi21.getRoot(info); } @Override public Object getParent(Object info) { return AccessibilityWindowInfoCompatApi21.getParent(info); } @Override public int getId(Object info) { return AccessibilityWindowInfoCompatApi21.getId(info); } @Override public void getBoundsInScreen(Object info, Rect outBounds) { AccessibilityWindowInfoCompatApi21.getBoundsInScreen(info, outBounds); } @Override public boolean isActive(Object info) { return AccessibilityWindowInfoCompatApi21.isActive(info); } @Override public boolean isFocused(Object info) { return AccessibilityWindowInfoCompatApi21.isFocused(info); } @Override public boolean isAccessibilityFocused(Object info) { return AccessibilityWindowInfoCompatApi21.isAccessibilityFocused(info); } @Override public int getChildCount(Object info) { return AccessibilityWindowInfoCompatApi21.getChildCount(info); } @Override public Object getChild(Object info, int index) { return AccessibilityWindowInfoCompatApi21.getChild(info, index); } @Override public void recycle(Object info) { AccessibilityWindowInfoCompatApi21.recycle(info); } } private static class AccessibilityWindowInfoApi24Impl extends AccessibilityWindowInfoApi21Impl { @Override public CharSequence getTitle(Object info) { return AccessibilityWindowInfoCompatApi24.getTitle(info); } @Override public Object getAnchor(Object info) { return AccessibilityWindowInfoCompatApi24.getAnchor(info); } } static { if (Build.VERSION.SDK_INT >= 24) { IMPL = new AccessibilityWindowInfoApi24Impl(); } else if (Build.VERSION.SDK_INT >= 21) { IMPL = new AccessibilityWindowInfoApi21Impl(); } else { IMPL = new AccessibilityWindowInfoStubImpl(); } } private static final AccessibilityWindowInfoImpl IMPL; private Object mInfo; private static final int UNDEFINED = -1; /** * 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; /** * Window type: Windows that are overlaid only by an {@link * android.accessibilityservice.AccessibilityService} for interception of * user interactions without changing the windows an accessibility service * can introspect. In particular, an accessibility service can introspect * only windows that a sighted user can interact with which they can touch * these windows or can type into these windows. For example, if there * is a full screen accessibility overlay that is touchable, the windows * below it will be introspectable by an accessibility service regardless * they are covered by a touchable window. */ public static final int TYPE_ACCESSIBILITY_OVERLAY = 4; /** * Creates a wrapper for info implementation. * * @param object The info to wrap. * @return A wrapper for if the object is not null, null otherwise. */ static AccessibilityWindowInfoCompat wrapNonNullInstance(Object object) { if (object != null) { return new AccessibilityWindowInfoCompat(object); } return null; } private AccessibilityWindowInfoCompat(Object info) { mInfo = info; } /** * 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 IMPL.getType(mInfo); } /** * 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 IMPL.getLayer(mInfo); } /** * Gets the root node in the window's hierarchy. * * @return The root node. */ public AccessibilityNodeInfoCompat getRoot() { return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getRoot(mInfo)); } /** * Gets the parent window if such. * * @return The parent window. */ public AccessibilityWindowInfoCompat getParent() { return wrapNonNullInstance(IMPL.getParent(mInfo)); } /** * Gets the unique window id. * * @return windowId The window id. */ public int getId() { return IMPL.getId(mInfo); } /** * Gets the bounds of this window in the screen. * * @param outBounds The out window bounds. */ public void getBoundsInScreen(Rect outBounds) { IMPL.getBoundsInScreen(mInfo, outBounds); } /** * 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 IMPL.isActive(mInfo); } /** * Gets if this window has input focus. * * @return Whether has input focus. */ public boolean isFocused() { return IMPL.isFocused(mInfo); } /** * Gets if this window has accessibility focus. * * @return Whether has accessibility focus. */ public boolean isAccessibilityFocused() { return IMPL.isAccessibilityFocused(mInfo); } /** * Gets the number of child windows. * * @return The child count. */ public int getChildCount() { return IMPL.getChildCount(mInfo); } /** * Gets the child window at a given index. * * @param index The index. * @return The child. */ public AccessibilityWindowInfoCompat getChild(int index) { return wrapNonNullInstance(IMPL.getChild(mInfo, index)); } /** * Gets the title of the window. * * @return The title of the window, or the application label for the window if no title was * explicitly set, or {@code null} if neither is available. */ public CharSequence getTitle() { return IMPL.getTitle(mInfo); } /** * Gets the node that anchors this window to another. * * @return The anchor node, or {@code null} if none exists. */ public AccessibilityNodeInfoCompat getAnchor() { return AccessibilityNodeInfoCompat.wrapNonNullInstance(IMPL.getAnchor(mInfo)); } /** * Returns a cached instance if such is available or a new one is * created. * * @return An instance. */ public static AccessibilityWindowInfoCompat obtain() { return wrapNonNullInstance(IMPL.obtain()); } /** * 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 AccessibilityWindowInfoCompat obtain(AccessibilityWindowInfoCompat info) { return wrapNonNullInstance(IMPL.obtain(info.mInfo)); } /** * 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() { IMPL.recycle(mInfo); } @Override public int hashCode() { return (mInfo == null) ? 0 : mInfo.hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } AccessibilityWindowInfoCompat other = (AccessibilityWindowInfoCompat) obj; if (mInfo == null) { if (other.mInfo != null) { return false; } } else if (!mInfo.equals(other.mInfo)) { return false; } return true; } @Override public String toString() { StringBuilder builder = new StringBuilder(); Rect bounds = new Rect(); getBoundsInScreen(bounds); builder.append("AccessibilityWindowInfo["); builder.append("id=").append(getId()); builder.append(", type=").append(typeToString(getType())); builder.append(", layer=").append(getLayer()); builder.append(", bounds=").append(bounds); builder.append(", focused=").append(isFocused()); builder.append(", active=").append(isActive()); builder.append(", hasParent=").append(getParent() != null); builder.append(", hasChildren=").append(getChildCount() > 0); builder.append(']'); return builder.toString(); } 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"; } default: return ""; } } }