下面列出了怎么用android.view.accessibility.AccessibilityWindowInfo的API类实例代码及写法,或者点击链接到github查看源代码。
/** Returns a list containing the root {@link AccessibilityNodeInfo}s for each active window */
@TargetApi(VERSION_CODES.LOLLIPOP)
public static Set<AccessibilityNodeInfo> getWindowRoots() {
Set<AccessibilityNodeInfo> roots = new HashSet();
// Start with the active window, which seems to sometimes be missing from the list returned
// by the UiAutomation.
UiAutomation uiAutomation = getUiAutomation();
AccessibilityNodeInfo activeRoot = uiAutomation.getRootInActiveWindow();
if (activeRoot != null) {
roots.add(activeRoot);
}
// Support multi-window searches for API level 21 and up.
for (AccessibilityWindowInfo window : uiAutomation.getWindows()) {
AccessibilityNodeInfo root = window.getRoot();
Log.i(TAG, String.format("Getting Layer: %d", window.getLayer()));
if (root == null) {
Log.w(TAG, String.format("Skipping null root node for window: %s", window.toString()));
continue;
}
roots.add(root);
}
return roots;
}
/**
* Returns a list containing the root {@link AccessibilityNodeInfo}s for
* each active window
*/
public AccessibilityNodeInfo[] getWindowRoots() {
ArrayList<AccessibilityNodeInfo> ret = new ArrayList<AccessibilityNodeInfo>();
if (getAutomatorBridge().getFastMode()) {
List<AccessibilityWindowInfo> windows = mUiAutomationBridge
.getUiAutomation().getWindows();
for (AccessibilityWindowInfo window : windows) {
if (window != null)
ret.add(window.getRoot());
}
if (ret.size() == 0) {
ret.add(mUiAutomationBridge.getUiAutomation()
.getRootInActiveWindow());
}
} else {
ret.add(mUiAutomationBridge.getUiAutomation()
.getRootInActiveWindow());
}
return ret.toArray(new AccessibilityNodeInfo[ret.size()]);
}
/**
* Returns a node instance, or null. Should only be called by this class and sub-classes. Uses
* factory argument to create sub-class instances, without creating unnecessary instances when
* result should be null. Method is protected so that it can be called by sub-classes without
* duplicating null-checking logic.
*
* @param windowBareArg The wrapped window info. Caller may retain responsibility to recycle.
* @param windowCompatArg The wrapped window info. Caller may retain responsibility to recycle.
* @param factory Creates instances of AccessibilityWindow or sub-classes.
* @return AccessibilityWindow instance, that caller must recycle.
*/
@Nullable
protected static <T extends AccessibilityWindow> T construct(
@Nullable AccessibilityWindowInfo windowBareArg,
@Nullable AccessibilityWindowInfoCompat windowCompatArg,
Factory<T> factory) {
// Check inputs.
if (windowBareArg == null && windowCompatArg == null) {
return null;
}
// Construct window wrapper.
T instance = factory.create();
AccessibilityWindow windowBase = instance;
windowBase.windowBare = windowBareArg;
windowBase.windowCompat = windowCompatArg;
return instance;
}
/** Logs the node trees for given list of windows. */
public static void logNodeTrees(List<AccessibilityWindowInfo> windows) {
if (windows == null) {
return;
}
for (AccessibilityWindowInfo window : windows) {
if (window == null) {
continue;
}
// TODO: Filter and print useful window information.
LogUtils.v(TAG, "Window: %s", window);
AccessibilityNodeInfoCompat root =
AccessibilityNodeInfoUtils.toCompat(AccessibilityWindowInfoUtils.getRoot(window));
logNodeTree(root);
AccessibilityNodeInfoUtils.recycleNodes(root);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Number of windows:").append(idToWindowInfoMap.size()).append('\n');
sb.append("System window: ")
.append(getWindowListString(AccessibilityWindowInfo.TYPE_SYSTEM))
.append('\n');
sb.append("Application window: ")
.append(getWindowListString(AccessibilityWindowInfo.TYPE_APPLICATION))
.append('\n');
sb.append("Accessibility window: ")
.append(getWindowListString(AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY))
.append('\n');
sb.append("InputMethod window: ")
.append(getWindowListString(AccessibilityWindowInfo.TYPE_INPUT_METHOD))
.append('\n');
sb.append("PicInPic window: ").append(getPicInPicWindowListString()).append('\n');
sb.append("isInSplitScreenMode:").append(isInSplitScreenMode());
return sb.toString();
}
/** returns true if there is no window with windowType before baseWindow */
public boolean isFirstWindow(AccessibilityWindowInfo baseWindow, int windowType) {
int index = getWindowIndex(baseWindow);
if (index <= 0) {
return true;
}
for (int i = index - 1; i > 0; i--) {
AccessibilityWindowInfo window = mWindows.get(i);
if (window != null && window.getType() == windowType) {
return false;
}
}
return true;
}
/**
* @return window that currently accessibilityFocused window. If there is no accessibility focused
* window it returns first window that has TYPE_APPLICATION or null if there is no window with
* TYPE_APPLICATION type
*/
public @Nullable AccessibilityWindowInfo getCurrentWindow(boolean useInputFocus) {
int currentWindowIndex =
getFocusedWindowIndex(mWindows, AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);
if (currentWindowIndex != WRONG_INDEX) {
return mWindows.get(currentWindowIndex);
}
if (!useInputFocus) {
return null;
}
currentWindowIndex = getFocusedWindowIndex(mWindows, AccessibilityNodeInfo.FOCUS_INPUT);
if (currentWindowIndex != WRONG_INDEX) {
return mWindows.get(currentWindowIndex);
}
return null;
}
/** Gets the window whose anchor equals the given node. */
public @Nullable AccessibilityWindowInfo getAnchoredWindow(
@Nullable AccessibilityNodeInfoCompat targetAnchor) {
if (!BuildVersionUtils.isAtLeastN() || targetAnchor == null) {
return null;
}
int windowCount = mWindows.size();
for (int i = 0; i < windowCount; ++i) {
AccessibilityWindowInfo window = mWindows.get(i);
if (window != null) {
AccessibilityNodeInfo anchor = window.getAnchor();
if (anchor != null) {
try {
if (anchor.equals(targetAnchor.unwrap())) {
return window;
}
} finally {
anchor.recycle();
}
}
}
}
return null;
}
private @Nullable AccessibilityWindowInfo getWindow(
AccessibilityWindowInfo pivotWindow, int direction) {
if (mWindows == null || pivotWindow == null || (direction != NEXT && direction != PREVIOUS)) {
return null;
}
int currentWindowIndex = getWindowIndex(pivotWindow);
int resultIndex;
if (direction == NEXT) {
resultIndex = getNextWindowIndex(currentWindowIndex);
} else {
resultIndex = getPreviousWindowIndex(currentWindowIndex);
}
if (resultIndex == WRONG_INDEX) {
return null;
}
return mWindows.get(resultIndex);
}
private static int getFocusedWindowIndex(List<AccessibilityWindowInfo> windows, int focusType) {
if (windows == null) {
return WRONG_INDEX;
}
for (int i = 0, size = windows.size(); i < size; i++) {
AccessibilityWindowInfo window = windows.get(i);
if (window == null) {
continue;
}
if (focusType == AccessibilityNodeInfo.FOCUS_ACCESSIBILITY
&& window.isAccessibilityFocused()) {
return i;
} else if (focusType == AccessibilityNodeInfo.FOCUS_INPUT && window.isFocused()) {
return i;
}
}
return WRONG_INDEX;
}
public CharSequence getWindowTitle(int windowId) {
// Try to get window title from the map.
CharSequence windowTitle = windowTitlesMap.get(windowId);
if (windowTitle != null) {
return windowTitle;
}
if (!BuildVersionUtils.isAtLeastN()) {
return null;
}
// Do not try to get system window title from AccessibilityWindowInfo.getTitle, it can
// return non-translated value.
if (isSystemWindow(windowId)) {
return null;
}
// Try to get window title from AccessibilityWindowInfo.
for (AccessibilityWindowInfo window : AccessibilityServiceCompatUtils.getWindows(service)) {
if (window.getId() == windowId) {
return window.getTitle();
}
}
return null;
}
public boolean isSplitScreenMode() {
if (!isSplitScreenModeAvailable) {
return false;
}
// TODO: Update this state when receiving a TYPE_WINDOWS_CHANGED event if possible.
List<AccessibilityWindowInfo> windows = AccessibilityServiceCompatUtils.getWindows(service);
List<AccessibilityWindowInfo> applicationWindows = new ArrayList<>();
for (AccessibilityWindowInfo window : windows) {
if (window.getType() == AccessibilityWindowInfo.TYPE_APPLICATION) {
if (window.getParent() == null
&& !AccessibilityWindowInfoUtils.isPictureInPicture(window)) {
applicationWindows.add(window);
}
}
}
// We consider user to be in split screen mode if there are two non-parented
// application windows.
return applicationWindows.size() == 2;
}
private static boolean hasOverlap(
AccessibilityWindowInfo windowA, AccessibilityWindowInfo windowB) {
Rect rectA = AccessibilityWindowInfoUtils.getBounds(windowA);
log("hasOverlap() windowA=%s rectA=%s", windowA, rectA);
if (rectA == null) {
return false;
}
Rect rectB = AccessibilityWindowInfoUtils.getBounds(windowB);
log("hasOverlap() windowB=%s rectB=%s", windowB, rectB);
if (rectB == null) {
return false;
}
return Rect.intersects(rectA, rectB);
}
private boolean isSystemWindow(int windowId) {
if (systemWindowIdsSet.contains(windowId)) {
return true;
}
if (!isSplitScreenModeAvailable) {
return false;
}
for (AccessibilityWindowInfo window : AccessibilityServiceCompatUtils.getWindows(service)) {
if (window.getId() == windowId && window.getType() == AccessibilityWindowInfo.TYPE_SYSTEM) {
return true;
}
}
return false;
}
/** Returns the root node of the tree of {@code windowInfo}. */
@Nullable
public static AccessibilityNodeInfo getRoot(AccessibilityWindowInfo windowInfo) {
AccessibilityNodeInfo nodeInfo = null;
if (windowInfo == null) {
return null;
}
try {
nodeInfo = windowInfo.getRoot();
} catch (SecurityException e) {
LogUtils.e(
TAG, "SecurityException occurred at AccessibilityWindowInfoUtils#getRoot(): %s", e);
}
return nodeInfo;
}
private void removeStatusBarButtonsFromWindowList(List<SwitchAccessWindowInfo> windowList) {
int statusBarHeight = DisplayUtils.getStatusBarHeightInPixel(service);
final Iterator<SwitchAccessWindowInfo> windowIterator = windowList.iterator();
while (windowIterator.hasNext()) {
SwitchAccessWindowInfo window = windowIterator.next();
/* Keep all non-system buttons */
if (window.getType() != AccessibilityWindowInfo.TYPE_SYSTEM) {
continue;
}
final Rect windowBounds = new Rect();
window.getBoundsInScreen(windowBounds);
/* Filter out items in the status bar */
if ((windowBounds.bottom <= statusBarHeight)) {
windowIterator.remove();
}
}
}
/** Returns {@code true} if current window is the last window on screen in traversal order. */
private static boolean needPauseWhenTraverseAcrossWindow(
WindowManager windowManager,
boolean isScreenRtl,
AccessibilityWindowInfo currentWindow,
@SearchDirection int searchDirection) {
if (!AccessibilityWindowInfoUtils.FILTER_WINDOW_DIRECTIONAL_NAVIGATION.accept(currentWindow)) {
// Need pause before looping traversal in non-application window
return true;
}
@TraversalStrategy.SearchDirection
int logicalDirection = TraversalStrategyUtils.getLogicalDirection(searchDirection, isScreenRtl);
if (logicalDirection == TraversalStrategy.SEARCH_FOCUS_FORWARD) {
return windowManager.isLastWindow(currentWindow, AccessibilityWindowInfo.TYPE_APPLICATION);
} else if (logicalDirection == TraversalStrategy.SEARCH_FOCUS_BACKWARD) {
return windowManager.isFirstWindow(currentWindow, AccessibilityWindowInfo.TYPE_APPLICATION);
} else {
throw new IllegalStateException("Unknown logical direction");
}
}
/**
* Sets initial focus in the active window.
*
* @return {@code true} if successfully set accessibility focus on a node.
*/
private boolean assignFocusOnWindow(
AccessibilityWindowInfo activeWindow, @Nullable CharSequence windowTitle, EventId eventId) {
// TODO: Initial focus is set with linear navigation strategy, which is inconsistent
// with directional navigation strategy on TV, and introduces some bugs. Enable it when we have
// a solid solution for this issue.
boolean enabled = !isTv;
AccessibilityNodeInfoCompat root =
AccessibilityNodeInfoUtils.toCompat(AccessibilityWindowInfoUtils.getRoot(activeWindow));
try {
return (enabled
&& restoreLastFocusedNode(
root, activeWindow.getType(), activeWindow.getId(), windowTitle, eventId))
|| syncA11yFocusToInputFocusedEditText(root, eventId)
|| (enabled && focusOnFirstFocusableNonTitleNode(root, windowTitle, eventId));
} finally {
AccessibilityNodeInfoUtils.recycleNodes(root);
}
}
private void handleScreenStateChangeWhenUIStabilized(EventId eventId, int attempt) {
AccessibilityWindowInfo activeWindow = latestScreenStateDuringTransition.getActiveWindow();
if ((activeWindow != null)
&& !AccessibilityWindowInfoUtils.isWindowContentVisible(activeWindow)
&& (attempt < MAXIMUM_ATTEMPTS)) {
// UI might not be stabilized yet. Try again later.
LogUtils.w(TAG, "Active window is invisible, try again later.");
handler.postHandleScreenStateChanges(eventId, attempt + 1, TIMEOUT_RETRY_MS);
return;
}
ScreenState oldScreenState = lastStableScreenState;
ScreenState newScreenState = latestScreenStateDuringTransition;
long transitionStartTime = screenTransitionStartTime;
setStableScreenState(latestScreenStateDuringTransition);
onScreenStateChanged(oldScreenState, newScreenState, transitionStartTime, eventId);
}
/**
* Returns title of window with given window ID.
*
* <p><strong>Note: </strong> This method returns null if the window has no title, or the window
* is not visible, or the window is IME or system window.
*/
@Nullable
public CharSequence getWindowTitle(int windowId) {
AccessibilityWindowInfo window = idToWindowInfoMap.get(windowId);
if ((window == null)
|| (window.getType() == AccessibilityWindowInfo.TYPE_INPUT_METHOD)
|| (window.getType() == AccessibilityWindowInfo.TYPE_SYSTEM)
|| (window.getType() == AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER)) {
// Only return title for application or accessibility windows.
return null;
}
CharSequence eventTitle = overriddenWindowTitles.get(windowId);
if (!TextUtils.isEmpty(eventTitle)) {
return eventTitle;
}
if (BuildVersionUtils.isAtLeastN()) {
// AccessibilityWindowInfo.getTitle() is available since API 24.
CharSequence infoTitle = window.getTitle();
if (!TextUtils.isEmpty(infoTitle)) {
return infoTitle;
}
}
return null;
}
/** Used by {@link #toString()}. */
private String getWindowDescription(AccessibilityWindowInfo window) {
if (window == null) {
return null;
}
return String.format(
"%d-%s%s;",
window.getId(),
getWindowTitle(window.getId()),
window.equals(activeWindow) ? "(active)" : "");
}
private void closeKeyboard() {
for (AccessibilityWindowInfo w : getInstrumentation().getUiAutomation().getWindows()) {
if (w.getType() == AccessibilityWindowInfo.TYPE_INPUT_METHOD) {
device.pressBack();
return;
}
}
}
@Override
public int hashCode() {
int h = 0;
Iterator<Entry<Integer, AccessibilityWindowInfo>> iterator =
idToWindowInfoMap.entrySet().iterator();
while (iterator.hasNext()) {
Entry<Integer, AccessibilityWindowInfo> entry = iterator.next();
h += entry.hashCode() ^ Objects.hashCode(getWindowTitle(entry.getKey()));
}
return h;
}
/**
* Takes ownership of window*Arg. Does not allow all-null arguments, because call chaining is
* already impossible, because intermediate objects have to be recycled. Caller must recycle
* returned AccessibilityWindow.
*/
@Nullable
public static AccessibilityWindow takeOwnership(
@Nullable AccessibilityWindowInfo windowBareArg,
@Nullable AccessibilityWindowInfoCompat windowCompatArg) {
return construct(windowBareArg, windowCompatArg, FACTORY);
}
private final void recycle(AccessibilityWindowInfo window, String caller) {
try {
window.recycle();
} catch (IllegalStateException e) {
logOrThrow(
e,
"Caught IllegalStateException from accessibility framework with %s trying to recycle"
+ " window %s",
caller,
window);
}
}
/** Returns root node info, which caller must recycle. */
@Nullable
public final AccessibilityNode getRoot() {
AccessibilityWindowInfoCompat compat = getCompat();
if (compat != null) {
return AccessibilityNode.takeOwnership(compat.getRoot());
}
AccessibilityWindowInfo bare = getBare();
if (bare != null) {
return AccessibilityNode.takeOwnership(bare.getRoot());
}
return null;
}
@Nullable
private static CharSequence getWindowTitleFromNodeTree(AccessibilityWindowInfo window) {
// Nodes to be recycled.
AccessibilityNodeInfoCompat root = null;
AccessibilityNodeInfoCompat windowTitleNode = null;
try {
root = AccessibilityNodeInfoUtils.toCompat(AccessibilityWindowInfoUtils.getRoot(window));
if (root == null) {
return null;
}
windowTitleNode = findFirstNodeWithText(root);
if (windowTitleNode == null) {
return null;
}
// TODO: Revisit the logic how we validate window title node.
boolean isValidWindowTitleNode =
!AccessibilityNodeInfoUtils.isOrHasMatchingAncestor(
windowTitleNode, AccessibilityNodeInfoUtils.FILTER_ILLEGAL_TITLE_NODE_ANCESTOR);
return isValidWindowTitleNode ? windowTitleNode.getText() : null;
} finally {
AccessibilityNodeInfoUtils.recycleNodes(root, windowTitleNode);
}
}
public static @Nullable AccessibilityWindowInfo getWindow(AccessibilityNodeInfo node) {
if (node == null) {
return null;
}
try {
return node.getWindow();
} catch (SecurityException e) {
LogUtils.e(TAG, "SecurityException in AccessibilityWindowInfo.getWindow()");
return null;
}
}
/**
* Returns a fresh copy of node by traversing the given window for a similar node. For example,
* the node that you want might be in a popup window that has closed and re-opened, causing the
* accessibility IDs of its views to be different. Note: you must recycle the node that is
* returned from this method.
*/
public static AccessibilityNodeInfoCompat refreshNodeFuzzy(
final AccessibilityNodeInfoCompat node, AccessibilityWindowInfo window) {
if (window == null || node == null) {
return null;
}
AccessibilityNodeInfo root = AccessibilityWindowInfoUtils.getRoot(window);
if (root == null) {
return null;
}
Filter<AccessibilityNodeInfoCompat> similarFilter =
new Filter<AccessibilityNodeInfoCompat>() {
@Override
public boolean accept(AccessibilityNodeInfoCompat other) {
return other != null && TextUtils.equals(node.getText(), other.getText());
}
};
AccessibilityNodeInfoCompat rootCompat = AccessibilityNodeInfoUtils.toCompat(root);
try {
return getMatchingDescendant(rootCompat, similarFilter);
} finally {
rootCompat.recycle();
}
}
/**
* Returns the node to which the given node's window is anchored, if there is an anchor. Note: you
* must recycle the node that is returned from this method.
*/
public static AccessibilityNodeInfoCompat getAnchor(@Nullable AccessibilityNodeInfoCompat node) {
if (!BuildVersionUtils.isAtLeastN()) {
return null;
}
if (node == null) {
return null;
}
AccessibilityNodeInfo nativeNode = node.unwrap();
if (nativeNode == null) {
return null;
}
AccessibilityWindowInfo nativeWindow = getWindow(nativeNode);
if (nativeWindow == null) {
return null;
}
AccessibilityNodeInfo nativeAnchor = nativeWindow.getAnchor();
if (nativeAnchor == null) {
return null;
}
return AccessibilityNodeInfoUtils.toCompat(nativeAnchor);
}