Merge "Prefetching of accessibility node infos getting incorrect views." into jb-dev

This commit is contained in:
Svetoslav Ganov
2012-05-15 18:56:45 -07:00
committed by Android (Google) Code Review
4 changed files with 80 additions and 15 deletions

View File

@@ -29,7 +29,6 @@ import android.util.Poolable;
import android.util.PoolableManager;
import android.util.Pools;
import android.util.SparseLongArray;
import android.view.ViewGroup.ChildListForAccessibility;
import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.accessibility.AccessibilityNodeProvider;
@@ -623,6 +622,8 @@ final class AccessibilityInteractionController {
private static final int MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE = 50;
private final ArrayList<View> mTempViewList = new ArrayList<View>();
public void prefetchAccessibilityNodeInfos(View view, int virtualViewId, int prefetchFlags,
List<AccessibilityNodeInfo> outInfos) {
AccessibilityNodeProvider provider = view.getAccessibilityNodeProvider();
@@ -663,8 +664,6 @@ final class AccessibilityInteractionController {
while (parent instanceof View
&& outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
View parentView = (View) parent;
final long parentNodeId = AccessibilityNodeInfo.makeNodeId(
parentView.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED);
AccessibilityNodeInfo info = parentView.createAccessibilityNodeInfo();
if (info != null) {
outInfos.add(info);
@@ -678,19 +677,21 @@ final class AccessibilityInteractionController {
ViewParent parent = current.getParentForAccessibility();
if (parent instanceof ViewGroup) {
ViewGroup parentGroup = (ViewGroup) parent;
ChildListForAccessibility children = ChildListForAccessibility.obtain(parentGroup,
false);
ArrayList<View> children = mTempViewList;
children.clear();
try {
final int childCount = children.getChildCount();
parentGroup.addChildrenForAccessibility(children);
final int childCount = children.size();
for (int i = 0; i < childCount; i++) {
if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
return;
}
View child = children.getChildAt(i);
View child = children.get(i);
if (child.getAccessibilityViewId() != current.getAccessibilityViewId()
&& isShown(child)) {
AccessibilityNodeInfo info = null;
AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
AccessibilityNodeProvider provider =
child.getAccessibilityNodeProvider();
if (provider == null) {
info = child.createAccessibilityNodeInfo();
} else {
@@ -703,7 +704,7 @@ final class AccessibilityInteractionController {
}
}
} finally {
children.recycle();
children.clear();
}
}
}
@@ -716,14 +717,16 @@ final class AccessibilityInteractionController {
ViewGroup rootGroup = (ViewGroup) root;
HashMap<View, AccessibilityNodeInfo> addedChildren =
new HashMap<View, AccessibilityNodeInfo>();
ChildListForAccessibility children = ChildListForAccessibility.obtain(rootGroup, false);
ArrayList<View> children = mTempViewList;
children.clear();
try {
final int childCount = children.getChildCount();
root.addChildrenForAccessibility(children);
final int childCount = children.size();
for (int i = 0; i < childCount; i++) {
if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
return;
}
View child = children.getChildAt(i);
View child = children.get(i);
if (isShown(child)) {
AccessibilityNodeProvider provider = child.getAccessibilityNodeProvider();
if (provider == null) {
@@ -743,7 +746,7 @@ final class AccessibilityInteractionController {
}
}
} finally {
children.recycle();
children.clear();
}
if (outInfos.size() < MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
for (Map.Entry<View, AccessibilityNodeInfo> entry : addedChildren.entrySet()) {

View File

@@ -19,6 +19,7 @@ package android.view.accessibility;
import android.accessibilityservice.IAccessibilityServiceConnection;
import android.graphics.Rect;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Message;
import android.os.Process;
@@ -27,10 +28,14 @@ import android.os.SystemClock;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.SparseArray;
import android.util.SparseLongArray;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -74,6 +79,8 @@ public final class AccessibilityInteractionClient
private static final boolean DEBUG = false;
private static final boolean CHECK_INTEGRITY = true;
private static final long TIMEOUT_INTERACTION_MILLIS = 5000;
private static final Object sStaticLock = new Object();
@@ -491,6 +498,9 @@ public final class AccessibilityInteractionClient
result = Collections.emptyList();
}
clearResultLocked();
if (Build.IS_DEBUGGABLE && CHECK_INTEGRITY) {
checkFindAccessibilityNodeInfoResultIntegrity(result);
}
return result;
}
}
@@ -696,4 +706,56 @@ public final class AccessibilityInteractionClient
sConnectionCache.remove(connectionId);
}
}
/**
* Checks whether the infos are a fully connected tree with no duplicates.
*
* @param infos The result list to check.
*/
private void checkFindAccessibilityNodeInfoResultIntegrity(List<AccessibilityNodeInfo> infos) {
if (infos.size() == 0) {
return;
}
// Find the root node.
AccessibilityNodeInfo root = infos.get(0);
final int infoCount = infos.size();
for (int i = 1; i < infoCount; i++) {
for (int j = i; j < infoCount; j++) {
AccessibilityNodeInfo candidate = infos.get(j);
if (root.getParentNodeId() == candidate.getSourceNodeId()) {
root = candidate;
break;
}
}
}
if (root == null) {
Log.e(LOG_TAG, "No root.");
}
// Check for duplicates.
HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
fringe.add(root);
while (!fringe.isEmpty()) {
AccessibilityNodeInfo current = fringe.poll();
if (!seen.add(current)) {
Log.e(LOG_TAG, "Duplicate node.");
return;
}
SparseLongArray childIds = current.getChildNodeIds();
final int childCount = childIds.size();
for (int i = 0; i < childCount; i++) {
final long childId = childIds.valueAt(i);
for (int j = 0; j < infoCount; j++) {
AccessibilityNodeInfo child = infos.get(j);
if (child.getSourceNodeId() == childId) {
fringe.add(child);
}
}
}
}
final int disconnectedCount = infos.size() - seen.size();
if (disconnectedCount > 0) {
Log.e(LOG_TAG, disconnectedCount + " Disconnected nodes.");
}
}
}

View File

@@ -244,7 +244,7 @@ public class AccessibilityNodeInfoCache {
/**
* We are enforcing the invariant for a single accessibility focus.
*
* @param currentInputFocusId The current input focused node.
* @param currentAccessibilityFocusId The current input focused node.
*/
private void clearSubtreeWithOldAccessibilityFocusLocked(long currentAccessibilityFocusId) {
final int cacheSize = mCacheImpl.size();

View File

@@ -2490,7 +2490,7 @@ public class NumberPicker extends LinearLayout {
info.addChild(NumberPicker.this, VIRTUAL_VIEW_ID_INCREMENT);
}
info.setParent((View) getParent());
info.setParent((View) getParentForAccessibility());
info.setEnabled(NumberPicker.this.isEnabled());
info.setScrollable(true);
Rect boundsInParent = mTempRect;