Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5dfa47d authored by Svetoslav Ganov's avatar Svetoslav Ganov
Browse files

Improving accessibility focus traversal.

1. Now the views considered during the accessibility focus search
   are the ones that would get accessibility focus when thovered
   over. This way the user will get the same items i.e. feedback
   if he touch explores the screen and uses focus traversal. This
   is imperative for a good user experience.

2. Updated which focusables are considered when searching for access
   focus in ViewGroup. Generally accessibility focus ignores focus
   before/after descendants.

3. Implemented focus search strategy in AbsListView that will traverse
   the items of the current list (and the stuff withing one item
   before moving to the next) before continuing the search if
   forward and backward accessibility focus direction.

4. View focus search stops at root namespace. This is not the right
   way to prevent some stuff that is not supposed to get a focus in
   a container for a specific state. Actually the addFocusables
   for that container has to be overriden. Further this approach
   leads to focus getting stuck. The accessibility focus ignores
   root names space since we want to traverse the entire screen.

5. Fixed an bug in AccessibilityInteractionController which was not
   starting to search from the root of a virtual node tree.

6. Fixed a couple of bugs in FocusFinder where it was possible to
   get index out of bounds exception if the focusables list is empty.

bug:5932640

Change-Id: Ic3bdd11767a7d40fbb21f35dcd79a4746af784d4
parent 0d607fbe
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -330,7 +330,7 @@ final class AccessibilityInteractionController {
                                if (provider != null) {
                                if (provider != null) {
                                    List<AccessibilityNodeInfo> infosFromProvider =
                                    List<AccessibilityNodeInfo> infosFromProvider =
                                        provider.findAccessibilityNodeInfosByText(text,
                                        provider.findAccessibilityNodeInfosByText(text,
                                                virtualDescendantId);
                                                AccessibilityNodeInfo.UNDEFINED);
                                    if (infosFromProvider != null) {
                                    if (infosFromProvider != null) {
                                        infos.addAll(infosFromProvider);
                                        infos.addAll(infosFromProvider);
                                    }
                                    }
+8 −2
Original line number Original line Diff line number Diff line
@@ -276,8 +276,11 @@ public class FocusFinder {
                return focusables.get(position + 1);
                return focusables.get(position + 1);
            }
            }
        }
        }
        if (!focusables.isEmpty()) {
            return focusables.get(0);
            return focusables.get(0);
        }
        }
        return null;
    }


    private static View getBackwardFocusable(ViewGroup root, View focused,
    private static View getBackwardFocusable(ViewGroup root, View focused,
                                             ArrayList<View> focusables, int count) {
                                             ArrayList<View> focusables, int count) {
@@ -293,8 +296,11 @@ public class FocusFinder {
                return focusables.get(position - 1);
                return focusables.get(position - 1);
            }
            }
        }
        }
        if (!focusables.isEmpty()) {
            return focusables.get(count - 1);
            return focusables.get(count - 1);
        }
        }
        return null;
    }


    /**
    /**
     * Is rect1 a better candidate than rect2 for a focus search in a particular
     * Is rect1 a better candidate than rect2 for a focus search in a particular
+24 −51
Original line number Original line Diff line number Diff line
@@ -6029,8 +6029,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
            return;
            return;
        }
        }
        if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
        if ((focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
            if (AccessibilityManager.getInstance(mContext).isEnabled()
            if (canTakeAccessibilityFocusFromHover()) {
                    && includeForAccessibility()) {
                views.add(this);
                views.add(this);
                return;
                return;
            }
            }
@@ -6183,57 +6182,28 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
        }
        }
    }
    }
    /**
    private void requestAccessibilityFocusFromHover() {
     * Find the best view to take accessibility focus from a hover.
        if (includeForAccessibility() && isActionableForAccessibility()) {
     * This function finds the deepest actionable view and if that
            requestAccessibilityFocus();
     * fails ask the parent to take accessibility focus from hover.
        } else {
     *
            if (mParent != null) {
     * @param x The X hovered location in this view coorditantes.
                View nextFocus = mParent.findViewToTakeAccessibilityFocusFromHover(this, this);
     * @param y The Y hovered location in this view coorditantes.
                if (nextFocus != null) {
     * @return Whether the request was handled.
                    nextFocus.requestAccessibilityFocus();
     *
     * @hide
     */
    public boolean requestAccessibilityFocusFromHover(float x, float y) {
        if (onRequestAccessibilityFocusFromHover(x, y)) {
            return true;
                }
                }
        ViewParent parent = mParent;
        if (parent instanceof View) {
            View parentView = (View) parent;
            float[] position = mAttachInfo.mTmpTransformLocation;
            position[0] = x;
            position[1] = y;
            // Compensate for the transformation of the current matrix.
            if (!hasIdentityMatrix()) {
                getMatrix().mapPoints(position);
            }
            }
            // Compensate for the parent scroll and the offset
            // of this view stop from the parent top.
            position[0] += mLeft - parentView.mScrollX;
            position[1] += mTop - parentView.mScrollY;
            return parentView.requestAccessibilityFocusFromHover(position[0], position[1]);
        }
        }
        return false;
    }
    }
    /**
    /**
     * Requests to give this View focus from hover.
     *
     * @param x The X hovered location in this view coorditantes.
     * @param y The Y hovered location in this view coorditantes.
     * @return Whether the request was handled.
     *
     * @hide
     * @hide
     */
     */
    public boolean onRequestAccessibilityFocusFromHover(float x, float y) {
    public boolean canTakeAccessibilityFocusFromHover() {
        if (includeForAccessibility()
        if (includeForAccessibility() && isActionableForAccessibility()) {
                && (isActionableForAccessibility() || hasListenersForAccessibility())) {
            return true;
            return requestAccessibilityFocus();
        }
        if (mParent != null) {
            return (mParent.findViewToTakeAccessibilityFocusFromHover(this, this) == this);
        }
        }
        return false;
        return false;
    }
    }
@@ -6495,14 +6465,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
     * important for accessibility are regarded.
     * important for accessibility are regarded.
     *
     *
     * @return Whether to regard the view for accessibility.
     * @return Whether to regard the view for accessibility.
     *
     * @hide
     */
     */
    boolean includeForAccessibility() {
    public boolean includeForAccessibility() {
        if (mAttachInfo != null) {
        if (mAttachInfo != null) {
            if (!mAttachInfo.mIncludeNotImportantViews) {
            if (!mAttachInfo.mIncludeNotImportantViews) {
                return isImportantForAccessibility();
                return isImportantForAccessibility();
            } else {
                return true;
            }
            }
            return true;
        }
        }
        return false;
        return false;
    }
    }
@@ -6513,8 +6484,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
     * accessiiblity.
     * accessiiblity.
     *
     *
     * @return True if the view is actionable for accessibility.
     * @return True if the view is actionable for accessibility.
     *
     * @hide
     */
     */
    private boolean isActionableForAccessibility() {
    public boolean isActionableForAccessibility() {
        return (isClickable() || isLongClickable() || isFocusable());
        return (isClickable() || isLongClickable() || isFocusable());
    }
    }
@@ -7687,7 +7660,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal
                    && pointInView(event.getX(), event.getY())) {
                    && pointInView(event.getX(), event.getY())) {
                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
                mSendingHoverAccessibilityEvents = true;
                mSendingHoverAccessibilityEvents = true;
                requestAccessibilityFocusFromHover((int) event.getX(), (int) event.getY());
                requestAccessibilityFocusFromHover();
            }
            }
        } else {
        } else {
            if (action == MotionEvent.ACTION_HOVER_EXIT
            if (action == MotionEvent.ACTION_HOVER_EXIT
+25 −13
Original line number Original line Diff line number Diff line
@@ -628,7 +628,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
     *        FOCUS_RIGHT, or 0 for not applicable.
     *        FOCUS_RIGHT, or 0 for not applicable.
     */
     */
    public View focusSearch(View focused, int direction) {
    public View focusSearch(View focused, int direction) {
        if (isRootNamespace()) {
        // If we are moving accessibility focus we want to consider all
        // views no matter if they are on the screen. It is responsibility
        // of the accessibility service to check whether the result is in
        // the screen.
        if (isRootNamespace() && (direction & FOCUS_ACCESSIBILITY) == 0) {
            // root namespace means we should consider ourselves the top of the
            // root namespace means we should consider ourselves the top of the
            // tree for focus searching; otherwise we could be focus searching
            // tree for focus searching; otherwise we could be focus searching
            // into other tabs.  see LocalActivityManager and TabHost for more info
            // into other tabs.  see LocalActivityManager and TabHost for more info
@@ -853,14 +857,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        return false;
        return false;
    }
    }


    /**
     * {@inheritDoc}
     */
    @Override
    public void addFocusables(ArrayList<View> views, int direction) {
        addFocusables(views, direction, FOCUSABLES_TOUCH_MODE);
    }

    /**
    /**
     * {@inheritDoc}
     * {@inheritDoc}
     */
     */
@@ -870,7 +866,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager


        final int descendantFocusability = getDescendantFocusability();
        final int descendantFocusability = getDescendantFocusability();


        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS
                || (focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
            final int count = mChildrenCount;
            final int count = mChildrenCount;
            final View[] children = mChildren;
            final View[] children = mChildren;


@@ -886,10 +883,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
        // to avoid the focus search finding layouts when a more precise search
        // to avoid the focus search finding layouts when a more precise search
        // among the focusable children would be more interesting.
        // among the focusable children would be more interesting.
        if (
        if (descendantFocusability != FOCUS_AFTER_DESCENDANTS
            descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
                // No focusable descendants
                // No focusable descendants
                (focusableCount == views.size())) {
                || (focusableCount == views.size())
                // We are collecting accessibility focusables.
                || (focusableMode & FOCUSABLES_ACCESSIBILITY) == FOCUSABLES_ACCESSIBILITY) {
            super.addFocusables(views, direction, focusableMode);
            super.addFocusables(views, direction, focusableMode);
        }
        }
    }
    }
@@ -1659,6 +1657,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
        }
        }
    }
    }


    /**
     * @hide
     */
    @Override
    public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant) {
        if (includeForAccessibility() && isActionableForAccessibility()) {
            return this;
        }
        if (mParent != null) {
            return mParent.findViewToTakeAccessibilityFocusFromHover(this, descendant);
        }
        return null;
    }

    /**
    /**
     * Implement this method to intercept hover events before they are handled
     * Implement this method to intercept hover events before they are handled
     * by child views.
     * by child views.
+12 −0
Original line number Original line Diff line number Diff line
@@ -295,4 +295,16 @@ public interface ViewParent {
     * @hide
     * @hide
     */
     */
    public void childAccessibilityStateChanged(View child);
    public void childAccessibilityStateChanged(View child);

    /**
     * A descendant requests this view to find a candidate to take accessibility
     * focus from hover.
     *
     * @param child The child making the call.
     * @param descendant The descendant that made the initial request.
     * @return A view to take accessibility focus.
     *
     * @hide
     */
    public View findViewToTakeAccessibilityFocusFromHover(View child, View descendant);
}
}
Loading