Skip to content

Commit 8ce2d78

Browse files
sganovAndroid (Google) Code Review
authored andcommitted
Merge "Improving accessibility focus traversal." into jb-dev
2 parents a827843 + e5dfa47 commit 8ce2d78

File tree

8 files changed

+209
-87
lines changed

8 files changed

+209
-87
lines changed

core/java/android/view/AccessibilityInteractionController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ private void findAccessibilityNodeInfosByTextUiThread(Message message) {
330330
if (provider != null) {
331331
List<AccessibilityNodeInfo> infosFromProvider =
332332
provider.findAccessibilityNodeInfosByText(text,
333-
virtualDescendantId);
333+
AccessibilityNodeInfo.UNDEFINED);
334334
if (infosFromProvider != null) {
335335
infos.addAll(infosFromProvider);
336336
}

core/java/android/view/FocusFinder.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,10 @@ private static View getNextFocusable(View focused, ArrayList<View> focusables, i
276276
return focusables.get(position + 1);
277277
}
278278
}
279-
return focusables.get(0);
279+
if (!focusables.isEmpty()) {
280+
return focusables.get(0);
281+
}
282+
return null;
280283
}
281284

282285
private static View getBackwardFocusable(ViewGroup root, View focused,
@@ -293,7 +296,10 @@ private static View getPreviousFocusable(View focused, ArrayList<View> focusable
293296
return focusables.get(position - 1);
294297
}
295298
}
296-
return focusables.get(count - 1);
299+
if (!focusables.isEmpty()) {
300+
return focusables.get(count - 1);
301+
}
302+
return null;
297303
}
298304

299305
/**

core/java/android/view/View.java

Lines changed: 24 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -6027,8 +6027,7 @@ public void addFocusables(ArrayList<View> views, int direction, int focusableMod
60276027
return;
60286028
}
60296029
if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
6030-
if (AccessibilityManager.getInstance(mContext).isEnabled()
6031-
&& includeForAccessibility()) {
6030+
if (canTakeAccessibilityFocusFromHover()) {
60326031
views.add(this);
60336032
return;
60346033
}
@@ -6181,57 +6180,28 @@ public void clearAccessibilityFocus() {
61816180
}
61826181
}
61836182

6184-
/**
6185-
* Find the best view to take accessibility focus from a hover.
6186-
* This function finds the deepest actionable view and if that
6187-
* fails ask the parent to take accessibility focus from hover.
6188-
*
6189-
* @param x The X hovered location in this view coorditantes.
6190-
* @param y The Y hovered location in this view coorditantes.
6191-
* @return Whether the request was handled.
6192-
*
6193-
* @hide
6194-
*/
6195-
public boolean requestAccessibilityFocusFromHover(float x, float y) {
6196-
if (onRequestAccessibilityFocusFromHover(x, y)) {
6197-
return true;
6198-
}
6199-
ViewParent parent = mParent;
6200-
if (parent instanceof View) {
6201-
View parentView = (View) parent;
6202-
6203-
float[] position = mAttachInfo.mTmpTransformLocation;
6204-
position[0] = x;
6205-
position[1] = y;
6206-
6207-
// Compensate for the transformation of the current matrix.
6208-
if (!hasIdentityMatrix()) {
6209-
getMatrix().mapPoints(position);
6183+
private void requestAccessibilityFocusFromHover() {
6184+
if (includeForAccessibility() && isActionableForAccessibility()) {
6185+
requestAccessibilityFocus();
6186+
} else {
6187+
if (mParent != null) {
6188+
View nextFocus = mParent.findViewToTakeAccessibilityFocusFromHover(this, this);
6189+
if (nextFocus != null) {
6190+
nextFocus.requestAccessibilityFocus();
6191+
}
62106192
}
6211-
6212-
// Compensate for the parent scroll and the offset
6213-
// of this view stop from the parent top.
6214-
position[0] += mLeft - parentView.mScrollX;
6215-
position[1] += mTop - parentView.mScrollY;
6216-
6217-
return parentView.requestAccessibilityFocusFromHover(position[0], position[1]);
62186193
}
6219-
return false;
62206194
}
62216195

62226196
/**
6223-
* Requests to give this View focus from hover.
6224-
*
6225-
* @param x The X hovered location in this view coorditantes.
6226-
* @param y The Y hovered location in this view coorditantes.
6227-
* @return Whether the request was handled.
6228-
*
62296197
* @hide
62306198
*/
6231-
public boolean onRequestAccessibilityFocusFromHover(float x, float y) {
6232-
if (includeForAccessibility()
6233-
&& (isActionableForAccessibility() || hasListenersForAccessibility())) {
6234-
return requestAccessibilityFocus();
6199+
public boolean canTakeAccessibilityFocusFromHover() {
6200+
if (includeForAccessibility() && isActionableForAccessibility()) {
6201+
return true;
6202+
}
6203+
if (mParent != null) {
6204+
return (mParent.findViewToTakeAccessibilityFocusFromHover(this, this) == this);
62356205
}
62366206
return false;
62376207
}
@@ -6493,14 +6463,15 @@ public void addChildrenForAccessibility(ArrayList<View> children) {
64936463
* important for accessibility are regarded.
64946464
*
64956465
* @return Whether to regard the view for accessibility.
6466+
*
6467+
* @hide
64966468
*/
6497-
boolean includeForAccessibility() {
6469+
public boolean includeForAccessibility() {
64986470
if (mAttachInfo != null) {
64996471
if (!mAttachInfo.mIncludeNotImportantViews) {
65006472
return isImportantForAccessibility();
6501-
} else {
6502-
return true;
65036473
}
6474+
return true;
65046475
}
65056476
return false;
65066477
}
@@ -6511,8 +6482,10 @@ boolean includeForAccessibility() {
65116482
* accessiiblity.
65126483
*
65136484
* @return True if the view is actionable for accessibility.
6485+
*
6486+
* @hide
65146487
*/
6515-
private boolean isActionableForAccessibility() {
6488+
public boolean isActionableForAccessibility() {
65166489
return (isClickable() || isLongClickable() || isFocusable());
65176490
}
65186491

@@ -7688,7 +7661,7 @@ public boolean onHoverEvent(MotionEvent event) {
76887661
&& pointInView(event.getX(), event.getY())) {
76897662
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
76907663
mSendingHoverAccessibilityEvents = true;
7691-
requestAccessibilityFocusFromHover((int) event.getX(), (int) event.getY());
7664+
requestAccessibilityFocusFromHover();
76927665
}
76937666
} else {
76947667
if (action == MotionEvent.ACTION_HOVER_EXIT

core/java/android/view/ViewGroup.java

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,11 @@ public ActionMode startActionModeForChild(View originalView, ActionMode.Callback
628628
* FOCUS_RIGHT, or 0 for not applicable.
629629
*/
630630
public View focusSearch(View focused, int direction) {
631-
if (isRootNamespace()) {
631+
// If we are moving accessibility focus we want to consider all
632+
// views no matter if they are on the screen. It is responsibility
633+
// of the accessibility service to check whether the result is in
634+
// the screen.
635+
if (isRootNamespace() && (direction & FOCUS_ACCESSIBILITY) == 0) {
632636
// root namespace means we should consider ourselves the top of the
633637
// tree for focus searching; otherwise we could be focus searching
634638
// into other tabs. see LocalActivityManager and TabHost for more info
@@ -853,14 +857,6 @@ public boolean hasFocusable() {
853857
return false;
854858
}
855859

856-
/**
857-
* {@inheritDoc}
858-
*/
859-
@Override
860-
public void addFocusables(ArrayList<View> views, int direction) {
861-
addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
862-
}
863-
864860
/**
865861
* {@inheritDoc}
866862
*/
@@ -870,7 +866,8 @@ public void addFocusables(ArrayList<View> views, int direction, int focusableMod
870866

871867
final int descendantFocusability = getDescendantFocusability();
872868

873-
if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
869+
if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS
870+
|| (focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
874871
final int count = mChildrenCount;
875872
final View[] children = mChildren;
876873

@@ -886,10 +883,11 @@ public void addFocusables(ArrayList<View> views, int direction, int focusableMod
886883
// FOCUS_AFTER_DESCENDANTS and there are some descendants focusable. this is
887884
// to avoid the focus search finding layouts when a more precise search
888885
// among the focusable children would be more interesting.
889-
if (
890-
descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
886+
if (descendantFocusability != FOCUS_AFTER_DESCENDANTS
891887
// No focusable descendants
892-
(focusableCount == views.size())) {
888+
|| (focusableCount == views.size())
889+
// We are collecting accessibility focusables.
890+
|| (focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
893891
super.addFocusables(views, direction, focusableMode);
894892
}
895893
}
@@ -1658,6 +1656,20 @@ public void childAccessibilityStateChanged(View child) {
16581656
}
16591657
}
16601658

1659+
/**
1660+
* @hide
1661+
*/
1662+
@Override
1663+
public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant) {
1664+
if (includeForAccessibility() && isActionableForAccessibility()) {
1665+
return this;
1666+
}
1667+
if (mParent != null) {
1668+
return mParent.findViewToTakeAccessibilityFocusFromHover(this, descendant);
1669+
}
1670+
return null;
1671+
}
1672+
16611673
/**
16621674
* Implement this method to intercept hover events before they are handled
16631675
* by child views.

core/java/android/view/ViewParent.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,4 +295,16 @@ public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
295295
* @hide
296296
*/
297297
public void childAccessibilityStateChanged(View child);
298+
299+
/**
300+
* A descendant requests this view to find a candidate to take accessibility
301+
* focus from hover.
302+
*
303+
* @param child The child making the call.
304+
* @param descendant The descendant that made the initial request.
305+
* @return A view to take accessibility focus.
306+
*
307+
* @hide
308+
*/
309+
public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant);
298310
}

core/java/android/view/ViewRootImpl.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2325,6 +2325,14 @@ private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int yoff,
23252325
return true;
23262326
}
23272327

2328+
@Override
2329+
public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant) {
2330+
if (descendant.includeForAccessibility()) {
2331+
return descendant;
2332+
}
2333+
return null;
2334+
}
2335+
23282336
/**
23292337
* We want to draw a highlight around the current accessibility focused.
23302338
* Since adding a style for all possible view is not a viable option we
@@ -2520,6 +2528,20 @@ boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
25202528
return handled;
25212529
}
25222530

2531+
/**
2532+
* @hide
2533+
*/
2534+
public View getAccessibilityFocusedHost() {
2535+
return mAccessibilityFocusedHost;
2536+
}
2537+
2538+
/**
2539+
* @hide
2540+
*/
2541+
public AccessibilityNodeInfo getAccessibilityFocusedVirtualView() {
2542+
return mAccessibilityFocusedVirtualView;
2543+
}
2544+
25232545
void setAccessibilityFocusedHost(View host) {
25242546
if (mAccessibilityFocusedHost != null && mAccessibilityFocusedVirtualView == null) {
25252547
mAccessibilityFocusedHost.clearAccessibilityFocusNoCallbacks();
@@ -2672,7 +2694,7 @@ void updateConfiguration(Configuration config, boolean force) {
26722694
/**
26732695
* Return true if child is an ancestor of parent, (or equal to the parent).
26742696
*/
2675-
static boolean isViewDescendantOf(View child, View parent) {
2697+
public static boolean isViewDescendantOf(View child, View parent) {
26762698
if (child == parent) {
26772699
return true;
26782700
}

0 commit comments

Comments
 (0)