Skip to content

Commit 791fd31

Browse files
committed
Accessibility focus traversal in virtual nodes.
1. Finished the implementation of support for maintaining accessibility focus in view with virtual descendants. 2. Finished the NumberPicker implementation of virtual subtree such that all requred attributes are reported and ensuring that it support accessibility focus in its virtual descentants. 3. Fixed a bug where if a predecessor of the view that is accessiiblity focused is removed the accessibliity focus host in ViewRootImpl is not cleared leading to a crash when trying to draw the accessibility focus highlight.: bug:6472646 bug:6433864 Change-Id: I3645642b87b4a26025c0b2ba9dfaad92d11a48f1
1 parent 8ce2d78 commit 791fd31

File tree

5 files changed

+347
-36
lines changed

5 files changed

+347
-36
lines changed

core/java/android/view/AccessibilityInteractionController.java

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -491,20 +491,28 @@ private void focusSearchUiThread(Message message) {
491491
if ((direction & View.FOCUS_ACCESSIBILITY) == View.FOCUS_ACCESSIBILITY) {
492492
AccessibilityNodeProvider provider = root.getAccessibilityNodeProvider();
493493
if (provider != null) {
494-
next = provider.accessibilityFocusSearch(direction,
495-
virtualDescendantId);
496-
} else if (virtualDescendantId == View.NO_ID) {
497-
View nextView = root.focusSearch(direction);
498-
if (nextView != null) {
499-
// If the focus search reached a node with a provider
500-
// we delegate to the provider to find the next one.
501-
provider = nextView.getAccessibilityNodeProvider();
502-
if (provider != null) {
503-
next = provider.accessibilityFocusSearch(direction,
504-
virtualDescendantId);
505-
} else {
506-
next = nextView.createAccessibilityNodeInfo();
507-
}
494+
next = provider.accessibilityFocusSearch(direction, virtualDescendantId);
495+
if (next != null) {
496+
return;
497+
}
498+
}
499+
View nextView = root.focusSearch(direction);
500+
while (nextView != null) {
501+
// If the focus search reached a node with a provider
502+
// we delegate to the provider to find the next one.
503+
// If the provider does not return a virtual view to
504+
// take accessibility focus we try the next view found
505+
// by the focus search algorithm.
506+
provider = nextView.getAccessibilityNodeProvider();
507+
if (provider != null) {
508+
next = provider.accessibilityFocusSearch(direction, View.NO_ID);
509+
if (next != null) {
510+
break;
511+
}
512+
nextView = nextView.focusSearch(direction);
513+
} else {
514+
next = nextView.createAccessibilityNodeInfo();
515+
break;
508516
}
509517
}
510518
} else {

core/java/android/view/View.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6027,7 +6027,7 @@ public void addFocusables(ArrayList<View> views, int direction, int focusableMod
60276027
return;
60286028
}
60296029
if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
6030-
if (canTakeAccessibilityFocusFromHover()) {
6030+
if (canTakeAccessibilityFocusFromHover() || getAccessibilityNodeProvider() != null) {
60316031
views.add(this);
60326032
return;
60336033
}
@@ -6156,12 +6156,15 @@ public boolean requestAccessibilityFocus() {
61566156
* @hide
61576157
*/
61586158
public void clearAccessibilityFocus() {
6159-
if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
6160-
mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
6161-
ViewRootImpl viewRootImpl = getViewRootImpl();
6162-
if (viewRootImpl != null) {
6159+
ViewRootImpl viewRootImpl = getViewRootImpl();
6160+
if (viewRootImpl != null) {
6161+
View focusHost = viewRootImpl.getAccessibilityFocusedHost();
6162+
if (focusHost != null && ViewRootImpl.isViewDescendantOf(focusHost, this)) {
61636163
viewRootImpl.setAccessibilityFocusedHost(null);
61646164
}
6165+
}
6166+
if ((mPrivateFlags2 & ACCESSIBILITY_FOCUSED) != 0) {
6167+
mPrivateFlags2 &= ~ACCESSIBILITY_FOCUSED;
61656168
invalidate();
61666169
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
61676170
notifyAccessibilityStateChanged();

core/java/android/view/ViewRootImpl.java

Lines changed: 74 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,9 @@ public void setView(View view, WindowManager.LayoutParams attrs, View panelParen
488488
mFallbackEventHandler.setView(view);
489489
mWindowAttributes.copyFrom(attrs);
490490
attrs = mWindowAttributes;
491-
491+
492+
setAccessibilityFocusedHost(null);
493+
492494
if (view instanceof RootViewSurfaceTaker) {
493495
mSurfaceHolderCallback =
494496
((RootViewSurfaceTaker)view).willYouTakeTheSurface();
@@ -556,6 +558,7 @@ public void setView(View view, WindowManager.LayoutParams attrs, View panelParen
556558
mInputChannel = null;
557559
mFallbackEventHandler.setView(null);
558560
unscheduleTraversals();
561+
setAccessibilityFocusedHost(null);
559562
throw new RuntimeException("Adding window failed", e);
560563
} finally {
561564
if (restore) {
@@ -575,6 +578,7 @@ public void setView(View view, WindowManager.LayoutParams attrs, View panelParen
575578
mAdded = false;
576579
mFallbackEventHandler.setView(null);
577580
unscheduleTraversals();
581+
setAccessibilityFocusedHost(null);
578582
switch (res) {
579583
case WindowManagerImpl.ADD_BAD_APP_TOKEN:
580584
case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
@@ -635,8 +639,6 @@ public void setView(View view, WindowManager.LayoutParams attrs, View panelParen
635639
if (view.getImportantForAccessibility() == View.IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
636640
view.setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_YES);
637641
}
638-
639-
setAccessibilityFocusedHost(null);
640642
}
641643
}
642644
}
@@ -2543,11 +2545,51 @@ public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() {
25432545
}
25442546

25452547
void setAccessibilityFocusedHost(View host) {
2546-
if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView == null) {
2548+
// If we have a virtual view with accessibility focus we need
2549+
// to clear the focus and invalidate the virtual view bounds.
2550+
if (mAccessibilityFocusedVirtualView != null) {
2551+
2552+
AccessibilityNodeInfo focusNode = mAccessibilityFocusedVirtualView;
2553+
View focusHost = mAccessibilityFocusedHost;
2554+
focusHost.clearAccessibilityFocusNoCallbacks();
2555+
2556+
// Wipe the state of the current accessibility focus since
2557+
// the call into the provider to clear accessibility focus
2558+
// will fire an accessibility event which will end up calling
2559+
// this method and we want to have clean state when this
2560+
// invocation happens.
2561+
mAccessibilityFocusedHost = null;
2562+
mAccessibilityFocusedVirtualView = null;
2563+
2564+
AccessibilityNodeProvider provider = focusHost.getAccessibilityNodeProvider();
2565+
if (provider != null) {
2566+
// Invalidate the area of the cleared accessibility focus.
2567+
focusNode.getBoundsInParent(mTempRect);
2568+
focusHost.invalidate(mTempRect);
2569+
// Clear accessibility focus in the virtual node.
2570+
final int virtualNodeId = AccessibilityNodeInfo.getVirtualDescendantId(
2571+
focusNode.getSourceNodeId());
2572+
provider.performAction(virtualNodeId,
2573+
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
2574+
}
2575+
}
2576+
if (mAccessibilityFocusedHost != null) {
2577+
// Clear accessibility focus in the view.
25472578
mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
25482579
}
2580+
2581+
// Set the new focus host.
25492582
mAccessibilityFocusedHost = host;
2550-
mAccessibilityFocusedVirtualView = null;
2583+
2584+
// If the host has a provide find the virtual descendant that has focus.
2585+
if (mAccessibilityFocusedHost != null) {
2586+
AccessibilityNodeProvider provider =
2587+
mAccessibilityFocusedHost.getAccessibilityNodeProvider();
2588+
if (provider != null) {
2589+
mAccessibilityFocusedVirtualView = provider.findAccessibilityFocus(View.NO_ID);
2590+
return;
2591+
}
2592+
}
25512593
}
25522594

25532595
public void requestChildFocus(View child, View focused) {
@@ -2633,6 +2675,8 @@ void dispatchDetachedFromWindow() {
26332675

26342676
destroyHardwareRenderer();
26352677

2678+
setAccessibilityFocusedHost(null);
2679+
26362680
mView = null;
26372681
mAttachInfo.mRootView = null;
26382682
mAttachInfo.mSurface = null;
@@ -4608,6 +4652,31 @@ public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent even
46084652
if (mView == null) {
46094653
return false;
46104654
}
4655+
// Watch for accessibility focus change events from virtual nodes
4656+
// to keep track of accessibility focus being on a virtual node.
4657+
final int eventType = event.getEventType();
4658+
switch (eventType) {
4659+
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED: {
4660+
final long sourceId = event.getSourceNodeId();
4661+
// If the event is not from a virtual node we are not interested.
4662+
final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId(sourceId);
4663+
if (virtualViewId == AccessibilityNodeInfo.UNDEFINED) {
4664+
break;
4665+
}
4666+
final int realViewId = AccessibilityNodeInfo.getAccessibilityViewId(sourceId);
4667+
View focusHost = mView.findViewByAccessibilityId(realViewId);
4668+
setAccessibilityFocusedHost(focusHost);
4669+
} break;
4670+
case AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED: {
4671+
final long sourceId = event.getSourceNodeId();
4672+
// If the event is not from a virtual node we are not interested.
4673+
final int virtualViewId = AccessibilityNodeInfo.getVirtualDescendantId(sourceId);
4674+
if (virtualViewId == AccessibilityNodeInfo.UNDEFINED) {
4675+
break;
4676+
}
4677+
setAccessibilityFocusedHost(null);
4678+
} break;
4679+
}
46114680
mAccessibilityManager.sendAccessibilityEvent(event);
46124681
return true;
46134682
}

0 commit comments

Comments
 (0)