Merge "Query the cache for AccessibilityInteractionClient's findFocus"
This commit is contained in:
@@ -151,7 +151,8 @@ public final class AccessibilityInteractionController {
|
||||
if (interrogatingPid == mMyProcessId && interrogatingTid == mMyLooperThreadId
|
||||
&& mHandler.hasAccessibilityCallback(message)) {
|
||||
AccessibilityInteractionClient.getInstanceForThread(
|
||||
interrogatingTid).setSameThreadMessage(message);
|
||||
interrogatingTid, /* initializeCache= */true)
|
||||
.setSameThreadMessage(message);
|
||||
} else {
|
||||
// For messages without callback of interrogating client, just handle the
|
||||
// message immediately if this is UI thread.
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package android.view.accessibility;
|
||||
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;
|
||||
|
||||
import android.os.Build;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
@@ -70,6 +73,7 @@ public class AccessibilityCache {
|
||||
private long mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
|
||||
|
||||
private int mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
||||
private int mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
||||
|
||||
private boolean mIsAllWindowsCached;
|
||||
|
||||
@@ -190,6 +194,7 @@ public class AccessibilityCache {
|
||||
removeCachedNodeLocked(event.getWindowId(), mInputFocus);
|
||||
}
|
||||
mInputFocus = event.getSourceNodeId();
|
||||
mInputFocusWindow = event.getWindowId();
|
||||
nodeToRefresh = removeCachedNodeLocked(event.getWindowId(), mInputFocus);
|
||||
} break;
|
||||
|
||||
@@ -439,6 +444,7 @@ public class AccessibilityCache {
|
||||
}
|
||||
if (clone.isFocused()) {
|
||||
mInputFocus = sourceId;
|
||||
mInputFocusWindow = windowId;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -462,6 +468,7 @@ public class AccessibilityCache {
|
||||
mInputFocus = AccessibilityNodeInfo.UNDEFINED_ITEM_ID;
|
||||
|
||||
mAccessibilityFocusedWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
||||
mInputFocusWindow = AccessibilityWindowInfo.UNDEFINED_WINDOW_ID;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,6 +492,87 @@ public class AccessibilityCache {
|
||||
mIsAllWindowsCached = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a cached {@link AccessibilityNodeInfo} with focus according to focus type.
|
||||
*
|
||||
* Note: {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID} will return
|
||||
* null.
|
||||
*
|
||||
* @param focusType The focus type.
|
||||
* @param windowId A unique window id.
|
||||
* @param initialNodeId A unique view id or virtual descendant id from where to start the
|
||||
* search.
|
||||
* @return The cached {@link AccessibilityNodeInfo} if it has a11y focus or null if such not
|
||||
* found.
|
||||
*/
|
||||
public AccessibilityNodeInfo getFocus(int focusType, long initialNodeId, int windowId) {
|
||||
synchronized (mLock) {
|
||||
int currentFocusWindowId;
|
||||
long currentFocusId;
|
||||
if (focusType == FOCUS_ACCESSIBILITY) {
|
||||
currentFocusWindowId = mAccessibilityFocusedWindow;
|
||||
currentFocusId = mAccessibilityFocus;
|
||||
} else {
|
||||
currentFocusWindowId = mInputFocusWindow;
|
||||
currentFocusId = mInputFocus;
|
||||
}
|
||||
|
||||
if (currentFocusWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (windowId != AccessibilityWindowInfo.ANY_WINDOW_ID
|
||||
&& windowId != currentFocusWindowId) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LongSparseArray<AccessibilityNodeInfo> nodes =
|
||||
mNodeCache.get(currentFocusWindowId);
|
||||
if (nodes == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final AccessibilityNodeInfo currentFocusedNode = nodes.get(currentFocusId);
|
||||
if (currentFocusedNode == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (initialNodeId == currentFocusId || (isCachedNodeOrDescendantLocked(
|
||||
currentFocusedNode.getParentNodeId(), initialNodeId, nodes))) {
|
||||
if (VERBOSE) {
|
||||
Log.i(LOG_TAG, "getFocus(0x" + Long.toHexString(currentFocusId) + ") = "
|
||||
+ currentFocusedNode + " with type: "
|
||||
+ (focusType == FOCUS_ACCESSIBILITY
|
||||
? "FOCUS_ACCESSIBILITY"
|
||||
: "FOCUS_INPUT"));
|
||||
}
|
||||
// Return a copy since the client calls to AccessibilityNodeInfo#recycle()
|
||||
// will wipe the data of the cached info.
|
||||
return new AccessibilityNodeInfo(currentFocusedNode);
|
||||
}
|
||||
|
||||
if (VERBOSE) {
|
||||
Log.i(LOG_TAG, "getFocus is null with type: "
|
||||
+ (focusType == FOCUS_ACCESSIBILITY
|
||||
? "FOCUS_ACCESSIBILITY"
|
||||
: "FOCUS_INPUT"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isCachedNodeOrDescendantLocked(long nodeId, long ancestorId,
|
||||
LongSparseArray<AccessibilityNodeInfo> nodes) {
|
||||
if (ancestorId == nodeId) {
|
||||
return true;
|
||||
}
|
||||
AccessibilityNodeInfo node = nodes.get(nodeId);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
return isCachedNodeOrDescendantLocked(node.getParentNodeId(), ancestorId, nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears nodes for the window with the given id
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.accessibilityservice.AccessibilityTrace.FLAGS_ACCESSIBILIT
|
||||
import android.accessibilityservice.IAccessibilityServiceConnection;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
@@ -114,8 +115,7 @@ public final class AccessibilityInteractionClient
|
||||
from a window, mapping from windowId -> timestamp. */
|
||||
private static final SparseLongArray sScrollingWindows = new SparseLongArray();
|
||||
|
||||
private static AccessibilityCache sAccessibilityCache =
|
||||
new AccessibilityCache(new AccessibilityCache.AccessibilityNodeRefresher());
|
||||
private static AccessibilityCache sAccessibilityCache;
|
||||
|
||||
private final AtomicInteger mInteractionIdCounter = new AtomicInteger();
|
||||
|
||||
@@ -150,7 +150,7 @@ public final class AccessibilityInteractionClient
|
||||
@UnsupportedAppUsage()
|
||||
public static AccessibilityInteractionClient getInstance() {
|
||||
final long threadId = Thread.currentThread().getId();
|
||||
return getInstanceForThread(threadId);
|
||||
return getInstanceForThread(threadId, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -161,11 +161,17 @@ public final class AccessibilityInteractionClient
|
||||
*
|
||||
* @return The client for a given <code>threadId</code>.
|
||||
*/
|
||||
public static AccessibilityInteractionClient getInstanceForThread(long threadId) {
|
||||
public static AccessibilityInteractionClient getInstanceForThread(long threadId,
|
||||
boolean initializeCache) {
|
||||
synchronized (sStaticLock) {
|
||||
AccessibilityInteractionClient client = sClients.get(threadId);
|
||||
if (client == null) {
|
||||
client = new AccessibilityInteractionClient();
|
||||
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
|
||||
// Don't initialize a cache for the system process
|
||||
client = new AccessibilityInteractionClient(false);
|
||||
} else {
|
||||
client = new AccessibilityInteractionClient(initializeCache);
|
||||
}
|
||||
sClients.put(threadId, client);
|
||||
}
|
||||
return client;
|
||||
@@ -176,11 +182,20 @@ public final class AccessibilityInteractionClient
|
||||
* @return The client for the current thread.
|
||||
*/
|
||||
public static AccessibilityInteractionClient getInstance(Context context) {
|
||||
return getInstance(/* initializeCache= */true, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param initializeCache whether to initialize the cache in a new client instance
|
||||
* @return The client for the current thread.
|
||||
*/
|
||||
public static AccessibilityInteractionClient getInstance(boolean initializeCache,
|
||||
Context context) {
|
||||
final long threadId = Thread.currentThread().getId();
|
||||
if (context != null) {
|
||||
return getInstanceForThread(threadId, context);
|
||||
return getInstanceForThread(threadId, initializeCache, context);
|
||||
}
|
||||
return getInstanceForThread(threadId);
|
||||
return getInstanceForThread(threadId, initializeCache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -189,14 +204,19 @@ public final class AccessibilityInteractionClient
|
||||
* We do not have a thread local variable since other threads should be able to
|
||||
* look up the correct client knowing a thread id. See ViewRootImpl for details.
|
||||
*
|
||||
* @param initializeCache whether to initialize the cache in a new client instance
|
||||
* @return The client for a given <code>threadId</code>.
|
||||
*/
|
||||
public static AccessibilityInteractionClient getInstanceForThread(
|
||||
long threadId, Context context) {
|
||||
long threadId, boolean initializeCache, Context context) {
|
||||
synchronized (sStaticLock) {
|
||||
AccessibilityInteractionClient client = sClients.get(threadId);
|
||||
if (client == null) {
|
||||
client = new AccessibilityInteractionClient(context);
|
||||
if (Binder.getCallingUid() == Process.SYSTEM_UID) {
|
||||
client = new AccessibilityInteractionClient(false, context);
|
||||
} else {
|
||||
client = new AccessibilityInteractionClient(initializeCache, context);
|
||||
}
|
||||
sClients.put(threadId, client);
|
||||
}
|
||||
return client;
|
||||
@@ -249,13 +269,26 @@ public final class AccessibilityInteractionClient
|
||||
|
||||
private AccessibilityInteractionClient() {
|
||||
/* reducing constructor visibility */
|
||||
this(true);
|
||||
}
|
||||
|
||||
private AccessibilityInteractionClient(boolean initializeCache) {
|
||||
initializeCache(initializeCache);
|
||||
mAccessibilityManager = null;
|
||||
}
|
||||
|
||||
private AccessibilityInteractionClient(Context context) {
|
||||
private AccessibilityInteractionClient(boolean initializeCache, Context context) {
|
||||
initializeCache(initializeCache);
|
||||
mAccessibilityManager = context.getSystemService(AccessibilityManager.class);
|
||||
}
|
||||
|
||||
private static void initializeCache(boolean initialize) {
|
||||
if (initialize && sAccessibilityCache == null) {
|
||||
sAccessibilityCache = new AccessibilityCache(
|
||||
new AccessibilityCache.AccessibilityNodeRefresher());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the message to be processed if the interacted view hierarchy
|
||||
* and the interacting client are running in the same thread.
|
||||
@@ -311,7 +344,7 @@ public final class AccessibilityInteractionClient
|
||||
IAccessibilityServiceConnection connection = getConnection(connectionId);
|
||||
if (connection != null) {
|
||||
AccessibilityWindowInfo window;
|
||||
if (!bypassCache) {
|
||||
if (!bypassCache && sAccessibilityCache != null) {
|
||||
window = sAccessibilityCache.getWindow(accessibilityWindowId);
|
||||
if (window != null) {
|
||||
if (DEBUG) {
|
||||
@@ -341,7 +374,7 @@ public final class AccessibilityInteractionClient
|
||||
+ bypassCache);
|
||||
}
|
||||
|
||||
if (window != null) {
|
||||
if (window != null && sAccessibilityCache != null) {
|
||||
if (!bypassCache) {
|
||||
sAccessibilityCache.addWindow(window);
|
||||
}
|
||||
@@ -384,21 +417,24 @@ public final class AccessibilityInteractionClient
|
||||
try {
|
||||
IAccessibilityServiceConnection connection = getConnection(connectionId);
|
||||
if (connection != null) {
|
||||
SparseArray<List<AccessibilityWindowInfo>> windows =
|
||||
sAccessibilityCache.getWindowsOnAllDisplays();
|
||||
if (windows != null) {
|
||||
SparseArray<List<AccessibilityWindowInfo>> windows;
|
||||
if (sAccessibilityCache != null) {
|
||||
windows = sAccessibilityCache.getWindowsOnAllDisplays();
|
||||
if (windows != null) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Windows cache hit");
|
||||
}
|
||||
if (shouldTraceClient()) {
|
||||
logTraceClient(
|
||||
connection, "getWindows cache", "connectionId=" + connectionId);
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Windows cache hit");
|
||||
Log.i(LOG_TAG, "Windows cache miss");
|
||||
}
|
||||
if (shouldTraceClient()) {
|
||||
logTraceClient(
|
||||
connection, "getWindows cache", "connectionId=" + connectionId);
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Windows cache miss");
|
||||
}
|
||||
|
||||
final long identityToken = Binder.clearCallingIdentity();
|
||||
try {
|
||||
windows = connection.getWindows();
|
||||
@@ -409,7 +445,9 @@ public final class AccessibilityInteractionClient
|
||||
logTraceClient(connection, "getWindows", "connectionId=" + connectionId);
|
||||
}
|
||||
if (windows != null) {
|
||||
sAccessibilityCache.setWindowsOnAllDisplays(windows);
|
||||
if (sAccessibilityCache != null) {
|
||||
sAccessibilityCache.setWindowsOnAllDisplays(windows);
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
} else {
|
||||
@@ -493,7 +531,7 @@ public final class AccessibilityInteractionClient
|
||||
try {
|
||||
IAccessibilityServiceConnection connection = getConnection(connectionId);
|
||||
if (connection != null) {
|
||||
if (!bypassCache) {
|
||||
if (!bypassCache && sAccessibilityCache != null) {
|
||||
AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getNode(
|
||||
accessibilityWindowId, accessibilityNodeId);
|
||||
if (cachedInfo != null) {
|
||||
@@ -725,7 +763,9 @@ public final class AccessibilityInteractionClient
|
||||
* @param connectionId The id of a connection for interacting with the system.
|
||||
* @param accessibilityWindowId A unique window id. Use
|
||||
* {@link android.view.accessibility.AccessibilityWindowInfo#ACTIVE_WINDOW_ID}
|
||||
* to query the currently active window.
|
||||
* to query the currently active window. Use
|
||||
* {@link android.view.accessibility.AccessibilityWindowInfo#ANY_WINDOW_ID} to query all
|
||||
* windows
|
||||
* @param accessibilityNodeId A unique view id or virtual descendant id from
|
||||
* where to start the search. Use
|
||||
* {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID}
|
||||
@@ -733,11 +773,28 @@ public final class AccessibilityInteractionClient
|
||||
* @param focusType The focus type.
|
||||
* @return The accessibility focused {@link AccessibilityNodeInfo}.
|
||||
*/
|
||||
@SuppressLint("LongLogTag")
|
||||
public AccessibilityNodeInfo findFocus(int connectionId, int accessibilityWindowId,
|
||||
long accessibilityNodeId, int focusType) {
|
||||
try {
|
||||
IAccessibilityServiceConnection connection = getConnection(connectionId);
|
||||
if (connection != null) {
|
||||
if (sAccessibilityCache != null) {
|
||||
AccessibilityNodeInfo cachedInfo = sAccessibilityCache.getFocus(focusType,
|
||||
accessibilityNodeId, accessibilityWindowId);
|
||||
if (cachedInfo != null) {
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Focused node cache hit retrieved"
|
||||
+ idToString(cachedInfo.getWindowId(),
|
||||
cachedInfo.getSourceNodeId()));
|
||||
}
|
||||
return cachedInfo;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Log.i(LOG_TAG, "Focused node cache miss with "
|
||||
+ idToString(accessibilityWindowId, accessibilityNodeId));
|
||||
}
|
||||
}
|
||||
final int interactionId = mInteractionIdCounter.getAndIncrement();
|
||||
if (shouldTraceClient()) {
|
||||
logTraceClient(connection, "findFocus",
|
||||
@@ -901,7 +958,9 @@ public final class AccessibilityInteractionClient
|
||||
*/
|
||||
@UnsupportedAppUsage()
|
||||
public void clearCache() {
|
||||
sAccessibilityCache.clear();
|
||||
if (sAccessibilityCache != null) {
|
||||
sAccessibilityCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public void onAccessibilityEvent(AccessibilityEvent event) {
|
||||
@@ -917,7 +976,9 @@ public final class AccessibilityInteractionClient
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sAccessibilityCache.onAccessibilityEvent(event);
|
||||
if (sAccessibilityCache != null) {
|
||||
sAccessibilityCache.onAccessibilityEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1153,7 +1214,7 @@ public final class AccessibilityInteractionClient
|
||||
}
|
||||
}
|
||||
info.setSealed(true);
|
||||
if (!bypassCache) {
|
||||
if (!bypassCache && sAccessibilityCache != null) {
|
||||
sAccessibilityCache.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
|
||||
package android.view.accessibility;
|
||||
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_ACCESSIBILITY;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.FOCUS_INPUT;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ROOT_ITEM_ID;
|
||||
import static android.view.accessibility.AccessibilityNodeInfo.ROOT_NODE_ID;
|
||||
import static android.view.accessibility.AccessibilityWindowInfo.ANY_WINDOW_ID;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
@@ -596,6 +602,97 @@ public class AccessibilityCacheTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_focusedNodeAsInitialNode_returnsNodeWithA11yFocus() {
|
||||
AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY);
|
||||
assertFocus(nodeInfo, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(),
|
||||
nodeInfo.getWindowId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_focusedNodeAsInitialNode_returnsNodeWithInputFocus() {
|
||||
AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT);
|
||||
assertFocus(nodeInfo, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getFocus_fromAnyWindow_returnsNodeWithA11yFocus() {
|
||||
AccessibilityNodeInfo parentNodeInfo =
|
||||
getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1);
|
||||
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
||||
nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1));
|
||||
setFocus(nodeInfo, FOCUS_ACCESSIBILITY);
|
||||
mAccessibilityCache.add(parentNodeInfo);
|
||||
mAccessibilityCache.add(nodeInfo);
|
||||
|
||||
AccessibilityNodeInfo focusedNodeInfo =
|
||||
mAccessibilityCache.getFocus(FOCUS_ACCESSIBILITY, ROOT_NODE_ID, ANY_WINDOW_ID);
|
||||
try {
|
||||
assertEquals(focusedNodeInfo, nodeInfo);
|
||||
} finally {
|
||||
nodeInfo.recycle();
|
||||
parentNodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_fromAnyWindow_returnsNodeWithInputFocus() {
|
||||
AccessibilityNodeInfo parentNodeInfo =
|
||||
getNodeWithA11yAndWindowId(ROOT_ITEM_ID, WINDOW_ID_1);
|
||||
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
||||
nodeInfo.setParent(getMockViewWithA11yAndWindowIds(ROOT_ITEM_ID, WINDOW_ID_1));
|
||||
setFocus(nodeInfo, FOCUS_INPUT);
|
||||
mAccessibilityCache.add(parentNodeInfo);
|
||||
mAccessibilityCache.add(nodeInfo);
|
||||
|
||||
AccessibilityNodeInfo focusedNodeInfo =
|
||||
mAccessibilityCache.getFocus(FOCUS_INPUT, ROOT_NODE_ID, ANY_WINDOW_ID);
|
||||
try {
|
||||
assertEquals(focusedNodeInfo, nodeInfo);
|
||||
} finally {
|
||||
nodeInfo.recycle();
|
||||
parentNodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void getFocus_parentNodeAsInitialNode_returnsNodeWithA11yFocus() {
|
||||
findFocusReturnsFocus_initialNodeIsParent(FOCUS_ACCESSIBILITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_parentNodeAsInitialNode_returnsNodeWithInputFocus() {
|
||||
findFocusReturnsFocus_initialNodeIsParent(FOCUS_INPUT);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullA11yFocus() {
|
||||
findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_ACCESSIBILITY);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_nonFocusedChildNodeAsInitialNode_returnsNullInputFocus() {
|
||||
findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(FOCUS_INPUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_cacheCleared_returnsNullA11yFocus() {
|
||||
AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_ACCESSIBILITY);
|
||||
mAccessibilityCache.clear();
|
||||
assertFocus(null, FOCUS_ACCESSIBILITY, nodeInfo.getSourceNodeId(),
|
||||
nodeInfo.getWindowId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFocus_cacheCleared_returnsNullInputFocus() {
|
||||
AccessibilityNodeInfo nodeInfo = addFocusedNode(FOCUS_INPUT);
|
||||
mAccessibilityCache.clear();
|
||||
assertFocus(null, FOCUS_INPUT, nodeInfo.getSourceNodeId(), nodeInfo.getWindowId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nodeSourceOfInputFocusEvent_getsRefreshed() {
|
||||
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
||||
@@ -786,6 +883,86 @@ public class AccessibilityCacheTest {
|
||||
}
|
||||
}
|
||||
|
||||
private AccessibilityNodeInfo addFocusedNode(int focusType) {
|
||||
AccessibilityNodeInfo nodeInfo = getNodeWithA11yAndWindowId(SINGLE_VIEW_ID, WINDOW_ID_1);
|
||||
setFocus(nodeInfo, focusType);
|
||||
mAccessibilityCache.add(nodeInfo);
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
private void assertFocus(AccessibilityNodeInfo focus, int focusType, long nodeId,
|
||||
int windowId) {
|
||||
AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus(
|
||||
focusType, nodeId, windowId);
|
||||
try {
|
||||
assertEquals(focusedNodeInfo, focus);
|
||||
} finally {
|
||||
if (focus != null) {
|
||||
focus.recycle();
|
||||
}
|
||||
if (focusedNodeInfo != null) {
|
||||
focusedNodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void findFocusReturnNull_initialNodeIsNotFocusOrFocusAncestor(int focusType) {
|
||||
AccessibilityNodeInfo parentNodeInfo =
|
||||
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
|
||||
AccessibilityNodeInfo childNodeInfo =
|
||||
getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
|
||||
AccessibilityNodeInfo otherChildNodeInfo =
|
||||
getNodeWithA11yAndWindowId(OTHER_CHILD_VIEW_ID, WINDOW_ID_1);
|
||||
childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
|
||||
otherChildNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
|
||||
setFocus(childNodeInfo, focusType);
|
||||
mAccessibilityCache.add(parentNodeInfo);
|
||||
mAccessibilityCache.add(childNodeInfo);
|
||||
mAccessibilityCache.add(otherChildNodeInfo);
|
||||
|
||||
AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus(
|
||||
focusType, otherChildNodeInfo.getSourceNodeId(), WINDOW_ID_1);
|
||||
|
||||
try {
|
||||
assertNull(focusedNodeInfo);
|
||||
|
||||
} finally {
|
||||
parentNodeInfo.recycle();
|
||||
childNodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private void findFocusReturnsFocus_initialNodeIsParent(int focusType) {
|
||||
AccessibilityNodeInfo parentNodeInfo =
|
||||
getNodeWithA11yAndWindowId(PARENT_VIEW_ID, WINDOW_ID_1);
|
||||
AccessibilityNodeInfo childNodeInfo =
|
||||
getNodeWithA11yAndWindowId(CHILD_VIEW_ID, WINDOW_ID_1);
|
||||
childNodeInfo.setParent(getMockViewWithA11yAndWindowIds(PARENT_VIEW_ID, WINDOW_ID_1));
|
||||
setFocus(childNodeInfo, focusType);
|
||||
mAccessibilityCache.add(parentNodeInfo);
|
||||
mAccessibilityCache.add(childNodeInfo);
|
||||
|
||||
AccessibilityNodeInfo focusedNodeInfo = mAccessibilityCache.getFocus(
|
||||
focusType, parentNodeInfo.getSourceNodeId(), WINDOW_ID_1);
|
||||
|
||||
try {
|
||||
assertEquals(focusedNodeInfo, childNodeInfo);
|
||||
|
||||
} finally {
|
||||
parentNodeInfo.recycle();
|
||||
childNodeInfo.recycle();
|
||||
}
|
||||
}
|
||||
|
||||
private void setFocus(AccessibilityNodeInfo info, int focusType) {
|
||||
if (focusType == FOCUS_ACCESSIBILITY) {
|
||||
info.setAccessibilityFocused(true);
|
||||
} else {
|
||||
info.setFocused(true);
|
||||
}
|
||||
}
|
||||
|
||||
private AccessibilityWindowInfo obtainAccessibilityWindowInfo(int windowId, int layer) {
|
||||
AccessibilityWindowInfo windowInfo = AccessibilityWindowInfo.obtain();
|
||||
windowInfo.setId(windowId);
|
||||
|
||||
@@ -3432,7 +3432,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub
|
||||
|
||||
mConnectionId = service.mId;
|
||||
|
||||
mClient = AccessibilityInteractionClient.getInstance(mContext);
|
||||
mClient = AccessibilityInteractionClient.getInstance(/* initializeCache= */false,
|
||||
mContext);
|
||||
mClient.addConnection(mConnectionId, service);
|
||||
|
||||
//TODO: (multi-display) We need to support multiple displays.
|
||||
|
||||
Reference in New Issue
Block a user