Skip to content

Commit 0e29ac9

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "Accessibility focus traversal in virtual nodes." into jb-dev
2 parents 6675721 + 791fd31 commit 0e29ac9

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)